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--.babelrc.js45
-rw-r--r--.eslintrc.yml3
-rw-r--r--.gitlab-ci.yml147
-rw-r--r--.gitlab/issue_templates/Database Reviewer.md32
-rw-r--r--.gitlab/issue_templates/Doc Review.md20
-rw-r--r--.gitlab/issue_templates/Documentation.md75
-rw-r--r--.gitlab/issue_templates/Feature proposal.md41
-rw-r--r--.gitlab/issue_templates/Security developer workflow.md1
-rw-r--r--.gitlab/issue_templates/Test plan.md2
-rw-r--r--.gitlab/merge_request_templates/Documentation.md40
-rw-r--r--.gitlab/merge_request_templates/Security Release.md8
-rw-r--r--.prettierrc10
-rw-r--r--.rubocop.yml9
-rw-r--r--.stylelintrc137
-rw-r--r--CHANGELOG.md581
-rw-r--r--Dangerfile1
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--GITLAB_SHELL_VERSION2
-rw-r--r--Gemfile39
-rw-r--r--Gemfile.lock122
-rw-r--r--PROCESS.md37
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/api.js17
-rw-r--r--app/assets/javascripts/awards_handler.js22
-rw-r--r--app/assets/javascripts/badges/components/badge_form.vue2
-rw-r--r--app/assets/javascripts/behaviors/gl_emoji.js97
-rw-r--r--app/assets/javascripts/behaviors/markdown/copy_as_gfm.js10
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_gfm.js2
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_mermaid.js19
-rw-r--r--app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js4
-rw-r--r--app/assets/javascripts/blob_edit/blob_bundle.js2
-rw-r--r--app/assets/javascripts/boards/components/issue_due_date.vue2
-rw-r--r--app/assets/javascripts/boards/index.js2
-rw-r--r--app/assets/javascripts/boards/services/board_service.js3
-rw-r--r--app/assets/javascripts/clusters/clusters_bundle.js41
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue23
-rw-r--r--app/assets/javascripts/clusters/components/applications.vue299
-rw-r--r--app/assets/javascripts/clusters/constants.js1
-rw-r--r--app/assets/javascripts/clusters/services/clusters_service.js7
-rw-r--r--app/assets/javascripts/clusters/stores/clusters_store.js12
-rw-r--r--app/assets/javascripts/commons/jquery.js4
-rw-r--r--app/assets/javascripts/commons/polyfills.js1
-rw-r--r--app/assets/javascripts/contextual_sidebar.js52
-rw-r--r--app/assets/javascripts/diffs/components/app.vue44
-rw-r--r--app/assets/javascripts/diffs/components/compare_versions.vue4
-rw-r--r--app/assets/javascripts/diffs/components/diff_file.vue12
-rw-r--r--app/assets/javascripts/diffs/components/diff_file_header.vue37
-rw-r--r--app/assets/javascripts/diffs/components/diff_line_note_form.vue4
-rw-r--r--app/assets/javascripts/diffs/components/edit_button.vue27
-rw-r--r--app/assets/javascripts/diffs/components/inline_diff_view.vue9
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_table_row.vue4
-rw-r--r--app/assets/javascripts/diffs/components/parallel_diff_view.vue52
-rw-r--r--app/assets/javascripts/diffs/components/tree_list.vue52
-rw-r--r--app/assets/javascripts/diffs/constants.js5
-rw-r--r--app/assets/javascripts/diffs/mixins/draft_comments.js7
-rw-r--r--app/assets/javascripts/diffs/store/actions.js49
-rw-r--r--app/assets/javascripts/diffs/store/getters.js7
-rw-r--r--app/assets/javascripts/diffs/store/modules/diff_state.js8
-rw-r--r--app/assets/javascripts/diffs/store/mutation_types.js5
-rw-r--r--app/assets/javascripts/diffs/store/mutations.js64
-rw-r--r--app/assets/javascripts/diffs/store/utils.js40
-rw-r--r--app/assets/javascripts/emoji/index.js77
-rw-r--r--app/assets/javascripts/emoji/support/index.js3
-rw-r--r--app/assets/javascripts/environments/components/confirm_rollback_modal.vue108
-rw-r--r--app/assets/javascripts/environments/components/container.vue20
-rw-r--r--app/assets/javascripts/environments/components/environment_item.vue16
-rw-r--r--app/assets/javascripts/environments/components/environment_rollback.vue43
-rw-r--r--app/assets/javascripts/environments/components/environments_app.vue17
-rw-r--r--app/assets/javascripts/environments/components/environments_table.vue79
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_bundle.js3
-rw-r--r--app/assets/javascripts/environments/folder/environments_folder_view.vue11
-rw-r--r--app/assets/javascripts/environments/index.js3
-rw-r--r--app/assets/javascripts/environments/mixins/canary_callout_mixin.js5
-rw-r--r--app/assets/javascripts/environments/mixins/container_mixin.js29
-rw-r--r--app/assets/javascripts/environments/mixins/environment_item_mixin.js13
-rw-r--r--app/assets/javascripts/environments/mixins/environments_app_mixin.js32
-rw-r--r--app/assets/javascripts/environments/mixins/environments_folder_view_mixin.js29
-rw-r--r--app/assets/javascripts/environments/mixins/environments_mixin.js40
-rw-r--r--app/assets/javascripts/environments/mixins/environments_table_mixin.js10
-rw-r--r--app/assets/javascripts/environments/stores/environments_store.js25
-rw-r--r--app/assets/javascripts/environments/stores/helpers.js8
-rw-r--r--app/assets/javascripts/error_tracking/components/error_tracking_list.vue26
-rw-r--r--app/assets/javascripts/error_tracking/store/actions.js29
-rw-r--r--app/assets/javascripts/error_tracking_settings/components/app.vue129
-rw-r--r--app/assets/javascripts/error_tracking_settings/components/error_tracking_form.vue91
-rw-r--r--app/assets/javascripts/error_tracking_settings/components/project_dropdown.vue82
-rw-r--r--app/assets/javascripts/error_tracking_settings/index.js27
-rw-r--r--app/assets/javascripts/error_tracking_settings/store/actions.js91
-rw-r--r--app/assets/javascripts/error_tracking_settings/store/getters.js44
-rw-r--r--app/assets/javascripts/error_tracking_settings/store/index.js16
-rw-r--r--app/assets/javascripts/error_tracking_settings/store/mutation_types.js11
-rw-r--r--app/assets/javascripts/error_tracking_settings/store/mutations.js61
-rw-r--r--app/assets/javascripts/error_tracking_settings/store/state.js12
-rw-r--r--app/assets/javascripts/error_tracking_settings/utils.js18
-rw-r--r--app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js28
-rw-r--r--app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js8
-rw-r--r--app/assets/javascripts/filtered_search/available_dropdown_mappings.js164
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_ajax_filter.js68
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js83
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js103
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js13
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_token_keys.js14
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js153
-rw-r--r--app/assets/javascripts/filtered_search/visual_token_value.js125
-rw-r--r--app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue10
-rw-r--r--app/assets/javascripts/gfm_auto_complete.js20
-rw-r--r--app/assets/javascripts/gl_dropdown.js18
-rw-r--r--app/assets/javascripts/groups/components/app.vue2
-rw-r--r--app/assets/javascripts/groups/components/group_item.vue2
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue7
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/index.vue2
-rw-r--r--app/assets/javascripts/ide/components/new_dropdown/modal.vue58
-rw-r--r--app/assets/javascripts/ide/lib/files.js113
-rw-r--r--app/assets/javascripts/ide/services/index.js3
-rw-r--r--app/assets/javascripts/ide/stores/actions.js70
-rw-r--r--app/assets/javascripts/ide/stores/actions/merge_request.js32
-rw-r--r--app/assets/javascripts/ide/stores/actions/project.js29
-rw-r--r--app/assets/javascripts/ide/stores/actions/tree.js46
-rw-r--r--app/assets/javascripts/ide/stores/modules/commit/getters.js4
-rw-r--r--app/assets/javascripts/ide/stores/mutations.js16
-rw-r--r--app/assets/javascripts/ide/stores/utils.js5
-rw-r--r--app/assets/javascripts/ide/stores/workers/files_decorator_worker.js100
-rw-r--r--app/assets/javascripts/import_projects/index.js3
-rw-r--r--app/assets/javascripts/import_projects/store/index.js13
-rw-r--r--app/assets/javascripts/issuable_suggestions/index.js4
-rw-r--r--app/assets/javascripts/issue_show/components/description.vue2
-rw-r--r--app/assets/javascripts/jobs/components/commit_block.vue2
-rw-r--r--app/assets/javascripts/jobs/components/job_app.vue10
-rw-r--r--app/assets/javascripts/jobs/components/stages_dropdown.vue51
-rw-r--r--app/assets/javascripts/jobs/store/getters.js11
-rw-r--r--app/assets/javascripts/labels_select.js32
-rw-r--r--app/assets/javascripts/lib/graphql.js14
-rw-r--r--app/assets/javascripts/lib/utils/autosave.js32
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js23
-rw-r--r--app/assets/javascripts/lib/utils/http_status.js1
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js19
-rw-r--r--app/assets/javascripts/lib/utils/poll.js20
-rw-r--r--app/assets/javascripts/lib/utils/simple_poll.js4
-rw-r--r--app/assets/javascripts/lib/utils/webpack.js2
-rw-r--r--app/assets/javascripts/members.js27
-rw-r--r--app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js2
-rw-r--r--app/assets/javascripts/mirrors/ssh_mirror.js8
-rw-r--r--app/assets/javascripts/monitoring/components/charts/area.vue131
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue38
-rw-r--r--app/assets/javascripts/monitoring/constants.js10
-rw-r--r--app/assets/javascripts/mr_notes/index.js69
-rw-r--r--app/assets/javascripts/mr_notes/init_notes.js70
-rw-r--r--app/assets/javascripts/mr_popover/components/mr_popover.vue110
-rw-r--r--app/assets/javascripts/mr_popover/constants.js10
-rw-r--r--app/assets/javascripts/mr_popover/index.js62
-rw-r--r--app/assets/javascripts/mr_popover/queries/merge_request.graphql14
-rw-r--r--app/assets/javascripts/notes.js4
-rw-r--r--app/assets/javascripts/notes/components/diff_with_note.vue8
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter.vue26
-rw-r--r--app/assets/javascripts/notes/components/discussion_filter_note.vue52
-rw-r--r--app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue34
-rw-r--r--app/assets/javascripts/notes/components/note_actions.vue1
-rw-r--r--app/assets/javascripts/notes/components/note_actions/reply_button.vue2
-rw-r--r--app/assets/javascripts/notes/components/note_body.vue1
-rw-r--r--app/assets/javascripts/notes/components/note_form.vue21
-rw-r--r--app/assets/javascripts/notes/components/note_header.vue48
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue82
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue2
-rw-r--r--app/assets/javascripts/notes/components/notes_app.vue10
-rw-r--r--app/assets/javascripts/notes/constants.js6
-rw-r--r--app/assets/javascripts/notes/discussion_filters.js13
-rw-r--r--app/assets/javascripts/notes/index.js7
-rw-r--r--app/assets/javascripts/notes/mixins/diff_line_note_form.js10
-rw-r--r--app/assets/javascripts/notes/mixins/resolvable.js4
-rw-r--r--app/assets/javascripts/notes/stores/getters.js3
-rw-r--r--app/assets/javascripts/notes/stores/mutations.js4
-rw-r--r--app/assets/javascripts/pages/dashboard/merge_requests/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/clusters/index/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/details/index.js5
-rw-r--r--app/assets/javascripts/pages/groups/group_members/index/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/issues/index.js4
-rw-r--r--app/assets/javascripts/pages/groups/merge_requests/index.js3
-rw-r--r--app/assets/javascripts/pages/groups/shared/group_details.js31
-rw-r--r--app/assets/javascripts/pages/groups/shared/group_tabs.js (renamed from app/assets/javascripts/pages/groups/show/group_tabs.js)0
-rw-r--r--app/assets/javascripts/pages/groups/show/index.js27
-rw-r--r--app/assets/javascripts/pages/profiles/show/index.js48
-rw-r--r--app/assets/javascripts/pages/projects/clusters/index/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/issues/edit/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/issues/form.js2
-rw-r--r--app/assets/javascripts/pages/projects/issues/index/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/issues/new/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/index/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js2
-rw-r--r--app/assets/javascripts/pages/projects/project_members/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/settings/operations/show/index.js5
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js15
-rw-r--r--app/assets/javascripts/pages/users/user_tabs.js3
-rw-r--r--app/assets/javascripts/persistent_user_callout.js8
-rw-r--r--app/assets/javascripts/pipelines/components/graph/stage_column_component.vue5
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue97
-rw-r--r--app/assets/javascripts/pipelines/components/pipeline_url.vue8
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue4
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table.vue40
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue5
-rw-r--r--app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js16
-rw-r--r--app/assets/javascripts/pipelines/mixins/pipelines.js9
-rw-r--r--app/assets/javascripts/pipelines/mixins/stage_column_mixin.js7
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_bundle.js17
-rw-r--r--app/assets/javascripts/pipelines/pipeline_details_mediator.js16
-rw-r--r--app/assets/javascripts/pipelines/services/pipeline_service.js4
-rw-r--r--app/assets/javascripts/pipelines/stores/pipeline_store.js1
-rw-r--r--app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/project_new.js36
-rw-r--r--app/assets/javascripts/releases/store/actions.js4
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue28
-rw-r--r--app/assets/javascripts/users_select.js27
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/deployment.vue14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue14
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue103
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/commits_header.vue18
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue87
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mixins/mr_widget_pipeline.js15
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_icon.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue70
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/empty_component.js3
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue116
-rw-r--r--app/assets/javascripts/vue_shared/components/loading_button.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue29
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestions.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/system_note.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/project_avatar/default.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/select2_select.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/table_pagination.vue37
-rw-r--r--app/assets/javascripts/vue_shared/mixins/is_ee.js10
-rw-r--r--app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js155
-rw-r--r--app/assets/stylesheets/application.scss14
-rw-r--r--app/assets/stylesheets/bootstrap_migration.scss10
-rw-r--r--app/assets/stylesheets/components/dashboard_skeleton.scss80
-rw-r--r--app/assets/stylesheets/components/popover.scss7
-rw-r--r--app/assets/stylesheets/components/related_items_list.scss3
-rw-r--r--app/assets/stylesheets/framework.scss3
-rw-r--r--app/assets/stylesheets/framework/animations.scss28
-rw-r--r--app/assets/stylesheets/framework/asciidoctor.scss2
-rw-r--r--app/assets/stylesheets/framework/avatar.scss92
-rw-r--r--app/assets/stylesheets/framework/awards.scss8
-rw-r--r--app/assets/stylesheets/framework/blank.scss28
-rw-r--r--app/assets/stylesheets/framework/blocks.scss6
-rw-r--r--app/assets/stylesheets/framework/buttons.scss5
-rw-r--r--app/assets/stylesheets/framework/common.scss32
-rw-r--r--app/assets/stylesheets/framework/contextual_sidebar.scss133
-rw-r--r--app/assets/stylesheets/framework/emojis.scss2
-rw-r--r--app/assets/stylesheets/framework/files.scss6
-rw-r--r--app/assets/stylesheets/framework/filters.scss22
-rw-r--r--app/assets/stylesheets/framework/forms.scss5
-rw-r--r--app/assets/stylesheets/framework/gfm.scss2
-rw-r--r--app/assets/stylesheets/framework/header.scss18
-rw-r--r--app/assets/stylesheets/framework/icons.scss1
-rw-r--r--app/assets/stylesheets/framework/lists.scss12
-rw-r--r--app/assets/stylesheets/framework/logo.scss35
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss36
-rw-r--r--app/assets/stylesheets/framework/mixins.scss62
-rw-r--r--app/assets/stylesheets/framework/modal.scss6
-rw-r--r--app/assets/stylesheets/framework/panels.scss1
-rw-r--r--app/assets/stylesheets/framework/secondary_navigation_elements.scss27
-rw-r--r--app/assets/stylesheets/framework/selects.scss12
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss52
-rw-r--r--app/assets/stylesheets/framework/sortable.scss92
-rw-r--r--app/assets/stylesheets/framework/spinner.scss51
-rw-r--r--app/assets/stylesheets/framework/system_messages.scss110
-rw-r--r--app/assets/stylesheets/framework/tables.scss1
-rw-r--r--app/assets/stylesheets/framework/terms.scss1
-rw-r--r--app/assets/stylesheets/framework/toggle.scss14
-rw-r--r--app/assets/stylesheets/framework/typography.scss128
-rw-r--r--app/assets/stylesheets/framework/variables.scss54
-rw-r--r--app/assets/stylesheets/framework/variables_overrides.scss9
-rw-r--r--app/assets/stylesheets/framework/vue_transitions.scss6
-rw-r--r--app/assets/stylesheets/framework/wells.scss4
-rw-r--r--app/assets/stylesheets/highlight/common.scss18
-rw-r--r--app/assets/stylesheets/highlight/dark.scss259
-rw-r--r--app/assets/stylesheets/highlight/embedded.scss2
-rw-r--r--app/assets/stylesheets/highlight/monokai.scss257
-rw-r--r--app/assets/stylesheets/highlight/none.scss238
-rw-r--r--app/assets/stylesheets/highlight/solarized_dark.scss284
-rw-r--r--app/assets/stylesheets/highlight/solarized_light.scss292
-rw-r--r--app/assets/stylesheets/highlight/themes/dark.scss261
-rw-r--r--app/assets/stylesheets/highlight/themes/monokai.scss259
-rw-r--r--app/assets/stylesheets/highlight/themes/none.scss238
-rw-r--r--app/assets/stylesheets/highlight/themes/solarized-dark.scss286
-rw-r--r--app/assets/stylesheets/highlight/themes/solarized-light.scss294
-rw-r--r--app/assets/stylesheets/highlight/themes/white.scss3
-rw-r--r--app/assets/stylesheets/highlight/white.scss3
-rw-r--r--app/assets/stylesheets/highlight/white_base.scss74
-rw-r--r--app/assets/stylesheets/mailers/highlighted_diff_email.scss67
-rw-r--r--app/assets/stylesheets/notify.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/xterm.scss6
-rw-r--r--app/assets/stylesheets/pages/boards.scss17
-rw-r--r--app/assets/stylesheets/pages/builds.scss18
-rw-r--r--app/assets/stylesheets/pages/commits.scss4
-rw-r--r--app/assets/stylesheets/pages/cycle_analytics.scss1
-rw-r--r--app/assets/stylesheets/pages/diff.scss365
-rw-r--r--app/assets/stylesheets/pages/environments.scss4
-rw-r--r--app/assets/stylesheets/pages/graph.scss2
-rw-r--r--app/assets/stylesheets/pages/groups.scss49
-rw-r--r--app/assets/stylesheets/pages/help.scss10
-rw-r--r--app/assets/stylesheets/pages/import.scss12
-rw-r--r--app/assets/stylesheets/pages/issuable.scss29
-rw-r--r--app/assets/stylesheets/pages/issues.scss7
-rw-r--r--app/assets/stylesheets/pages/labels.scss8
-rw-r--r--app/assets/stylesheets/pages/login.scss3
-rw-r--r--app/assets/stylesheets/pages/members.scss50
-rw-r--r--app/assets/stylesheets/pages/merge_conflicts.scss96
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss18
-rw-r--r--app/assets/stylesheets/pages/milestone.scss5
-rw-r--r--app/assets/stylesheets/pages/note_form.scss8
-rw-r--r--app/assets/stylesheets/pages/notes.scss71
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss18
-rw-r--r--app/assets/stylesheets/pages/profile.scss65
-rw-r--r--app/assets/stylesheets/pages/projects.scss9
-rw-r--r--app/assets/stylesheets/pages/search.scss6
-rw-r--r--app/assets/stylesheets/pages/settings.scss61
-rw-r--r--app/assets/stylesheets/pages/stat_graph.scss2
-rw-r--r--app/assets/stylesheets/pages/status.scss5
-rw-r--r--app/assets/stylesheets/pages/todos.scss18
-rw-r--r--app/assets/stylesheets/pages/tree.scss20
-rw-r--r--app/assets/stylesheets/pages/ui_dev_kit.scss2
-rw-r--r--app/assets/stylesheets/pages/wiki.scss6
-rw-r--r--app/assets/stylesheets/print.scss18
-rw-r--r--app/controllers/admin/appearances_controller.rb15
-rw-r--r--app/controllers/admin/application_settings_controller.rb8
-rw-r--r--app/controllers/admin/applications_controller.rb4
-rw-r--r--app/controllers/admin/broadcast_messages_controller.rb4
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb2
-rw-r--r--app/controllers/admin/groups_controller.rb8
-rw-r--r--app/controllers/admin/hooks_controller.rb4
-rw-r--r--app/controllers/admin/identities_controller.rb8
-rw-r--r--app/controllers/admin/impersonation_tokens_controller.rb6
-rw-r--r--app/controllers/admin/keys_controller.rb4
-rw-r--r--app/controllers/admin/labels_controller.rb6
-rw-r--r--app/controllers/admin/projects_controller.rb4
-rw-r--r--app/controllers/admin/runners_controller.rb16
-rw-r--r--app/controllers/admin/spam_logs_controller.rb6
-rw-r--r--app/controllers/admin/users_controller.rb44
-rw-r--r--app/controllers/application_controller.rb23
-rw-r--r--app/controllers/autocomplete_controller.rb9
-rw-r--r--app/controllers/clusters/applications_controller.rb29
-rw-r--r--app/controllers/clusters/clusters_controller.rb2
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb6
-rw-r--r--app/controllers/concerns/boards_actions.rb38
-rw-r--r--app/controllers/concerns/continue_params.rb2
-rw-r--r--app/controllers/concerns/creates_commit.rb25
-rw-r--r--app/controllers/concerns/issuable_actions.rb2
-rw-r--r--app/controllers/concerns/issuable_collections.rb1
-rw-r--r--app/controllers/concerns/lfs_request.rb8
-rw-r--r--app/controllers/concerns/membership_actions.rb35
-rw-r--r--app/controllers/concerns/milestone_actions.rb2
-rw-r--r--app/controllers/concerns/notes_actions.rb59
-rw-r--r--app/controllers/concerns/renders_notes.rb2
-rw-r--r--app/controllers/concerns/spammable_actions.rb6
-rw-r--r--app/controllers/concerns/uploads_actions.rb4
-rw-r--r--app/controllers/dashboard/projects_controller.rb12
-rw-r--r--app/controllers/dashboard/todos_controller.rb4
-rw-r--r--app/controllers/explore/projects_controller.rb6
-rw-r--r--app/controllers/google_api/authorizations_controller.rb32
-rw-r--r--app/controllers/graphql_controller.rb14
-rw-r--r--app/controllers/groups/boards_controller.rb39
-rw-r--r--app/controllers/groups/group_members_controller.rb1
-rw-r--r--app/controllers/groups/runners_controller.rb10
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb20
-rw-r--r--app/controllers/groups_controller.rb36
-rw-r--r--app/controllers/import/bitbucket_controller.rb2
-rw-r--r--app/controllers/import/bitbucket_server_controller.rb12
-rw-r--r--app/controllers/import/fogbugz_controller.rb6
-rw-r--r--app/controllers/import/gitea_controller.rb2
-rw-r--r--app/controllers/import/gitlab_controller.rb2
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb4
-rw-r--r--app/controllers/import/google_code_controller.rb12
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb4
-rw-r--r--app/controllers/profiles/accounts_controller.rb2
-rw-r--r--app/controllers/profiles/active_sessions_controller.rb11
-rw-r--r--app/controllers/profiles/preferences_controller.rb10
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb25
-rw-r--r--app/controllers/profiles_controller.rb1
-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/blob_controller.rb60
-rw-r--r--app/controllers/projects/boards_controller.rb39
-rw-r--r--app/controllers/projects/branches_controller.rb4
-rw-r--r--app/controllers/projects/commit_controller.rb6
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb2
-rw-r--r--app/controllers/projects/git_http_client_controller.rb14
-rw-r--r--app/controllers/projects/git_http_controller.rb18
-rw-r--r--app/controllers/projects/graphs_controller.rb9
-rw-r--r--app/controllers/projects/group_links_controller.rb7
-rw-r--r--app/controllers/projects/hooks_controller.rb2
-rw-r--r--app/controllers/projects/imports_controller.rb4
-rw-r--r--app/controllers/projects/issues_controller.rb4
-rw-r--r--app/controllers/projects/jobs_controller.rb2
-rw-r--r--app/controllers/projects/labels_controller.rb2
-rw-r--r--app/controllers/projects/lfs_api_controller.rb4
-rw-r--r--app/controllers/projects/merge_requests/conflicts_controller.rb8
-rw-r--r--app/controllers/projects/merge_requests/creations_controller.rb6
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb4
-rw-r--r--app/controllers/projects/merge_requests_controller.rb4
-rw-r--r--app/controllers/projects/mirrors_controller.rb4
-rw-r--r--app/controllers/projects/pages_controller.rb3
-rw-r--r--app/controllers/projects/pipeline_schedules_controller.rb8
-rw-r--r--app/controllers/projects/runners_controller.rb10
-rw-r--r--app/controllers/projects/services_controller.rb10
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb6
-rw-r--r--app/controllers/projects/settings/operations_controller.rb43
-rw-r--r--app/controllers/projects/tree_controller.rb2
-rw-r--r--app/controllers/projects/triggers_controller.rb14
-rw-r--r--app/controllers/projects/wikis_controller.rb12
-rw-r--r--app/controllers/search_controller.rb7
-rw-r--r--app/controllers/snippets/notes_controller.rb4
-rw-r--r--app/controllers/uploads_controller.rb6
-rw-r--r--app/finders/admin/runners_finder.rb11
-rw-r--r--app/finders/autocomplete/acts_as_taggable_on/tags_finder.rb44
-rw-r--r--app/finders/concerns/finder_methods.rb4
-rw-r--r--app/finders/group_descendants_finder.rb2
-rw-r--r--app/finders/issuable_finder.rb50
-rw-r--r--app/finders/issues_finder.rb11
-rw-r--r--app/finders/merge_requests_finder.rb11
-rw-r--r--app/finders/projects/daily_statistics_finder.rb21
-rw-r--r--app/finders/projects_finder.rb2
-rw-r--r--app/finders/snippets_finder.rb2
-rw-r--r--app/graphql/resolvers/issues_resolver.rb25
-rw-r--r--app/graphql/resolvers/metadata_resolver.rb11
-rw-r--r--app/graphql/types/ci/detailed_status_type.rb17
-rw-r--r--app/graphql/types/ci/pipeline_type.rb4
-rw-r--r--app/graphql/types/issuable_state_enum.rb12
-rw-r--r--app/graphql/types/issue_state_enum.rb8
-rw-r--r--app/graphql/types/issue_type.rb12
-rw-r--r--app/graphql/types/merge_request_state_enum.rb10
-rw-r--r--app/graphql/types/merge_request_type.rb6
-rw-r--r--app/graphql/types/metadata_type.rb10
-rw-r--r--app/graphql/types/project_type.rb12
-rw-r--r--app/graphql/types/query_type.rb13
-rw-r--r--app/helpers/appearances_helper.rb34
-rw-r--r--app/helpers/application_helper.rb17
-rw-r--r--app/helpers/auth_helper.rb8
-rw-r--r--app/helpers/auto_devops_helper.rb13
-rw-r--r--app/helpers/blob_helper.rb3
-rw-r--r--app/helpers/ci_status_helper.rb17
-rw-r--r--app/helpers/clusters_helper.rb6
-rw-r--r--app/helpers/count_helper.rb2
-rw-r--r--app/helpers/emails_helper.rb38
-rw-r--r--app/helpers/groups_helper.rb1
-rw-r--r--app/helpers/issuables_helper.rb4
-rw-r--r--app/helpers/labels_helper.rb4
-rw-r--r--app/helpers/markup_helper.rb2
-rw-r--r--app/helpers/merge_requests_helper.rb6
-rw-r--r--app/helpers/notes_helper.rb2
-rw-r--r--app/helpers/preferences_helper.rb7
-rw-r--r--app/helpers/projects_helper.rb22
-rw-r--r--app/helpers/search_helper.rb17
-rw-r--r--app/helpers/tree_helper.rb11
-rw-r--r--app/helpers/user_callouts_helper.rb3
-rw-r--r--app/mailers/abuse_report_mailer.rb4
-rw-r--r--app/mailers/email_rejection_mailer.rb4
-rw-r--r--app/mailers/emails/issues.rb1
-rw-r--r--app/mailers/emails/pipelines.rb2
-rw-r--r--app/mailers/repository_check_mailer.rb4
-rw-r--r--app/models/active_session.rb6
-rw-r--r--app/models/appearance.rb16
-rw-r--r--app/models/application_setting.rb284
-rw-r--r--app/models/application_setting_implementation.rb281
-rw-r--r--app/models/board_group_recent_visit.rb9
-rw-r--r--app/models/board_project_recent_visit.rb9
-rw-r--r--app/models/broadcast_message.rb2
-rw-r--r--app/models/ci/bridge.rb10
-rw-r--r--app/models/ci/build.rb228
-rw-r--r--app/models/ci/build_trace_chunk.rb4
-rw-r--r--app/models/ci/group_variable.rb1
-rw-r--r--app/models/ci/pipeline.rb100
-rw-r--r--app/models/ci/pipeline_chat_data.rb13
-rw-r--r--app/models/ci/pipeline_enums.rb3
-rw-r--r--app/models/ci/runner.rb3
-rw-r--r--app/models/ci/stage.rb7
-rw-r--r--app/models/ci/variable.rb1
-rw-r--r--app/models/clusters/applications/ingress.rb1
-rw-r--r--app/models/clusters/applications/jupyter.rb15
-rw-r--r--app/models/clusters/applications/knative.rb1
-rw-r--r--app/models/clusters/applications/prometheus.rb5
-rw-r--r--app/models/clusters/applications/runner.rb17
-rw-r--r--app/models/clusters/cluster.rb1
-rw-r--r--app/models/clusters/concerns/application_core.rb6
-rw-r--r--app/models/clusters/kubernetes_namespace.rb2
-rw-r--r--app/models/clusters/platforms/kubernetes.rb6
-rw-r--r--app/models/commit_collection.rb35
-rw-r--r--app/models/commit_range.rb8
-rw-r--r--app/models/commit_status.rb21
-rw-r--r--app/models/commit_status_enums.rb3
-rw-r--r--app/models/concerns/blob_language_from_git_attributes.rb2
-rw-r--r--app/models/concerns/cache_markdown_field.rb24
-rw-r--r--app/models/concerns/ci/contextable.rb108
-rw-r--r--app/models/concerns/ci/processable.rb4
-rw-r--r--app/models/concerns/closed_at_filterable.rb14
-rw-r--r--app/models/concerns/fast_destroy_all.rb2
-rw-r--r--app/models/concerns/feature_gate.rb2
-rw-r--r--app/models/concerns/has_ref.rb13
-rw-r--r--app/models/concerns/has_status.rb17
-rw-r--r--app/models/concerns/has_variable.rb6
-rw-r--r--app/models/concerns/iid_routes.rb2
-rw-r--r--app/models/concerns/issuable.rb18
-rw-r--r--app/models/concerns/maskable.rb22
-rw-r--r--app/models/concerns/milestoneish.rb23
-rw-r--r--app/models/concerns/mirror_authentication.rb2
-rw-r--r--app/models/concerns/reactive_caching.rb2
-rw-r--r--app/models/concerns/sha_attribute.rb10
-rw-r--r--app/models/concerns/token_authenticatable.rb19
-rw-r--r--app/models/concerns/token_authenticatable_strategies/base.rb16
-rw-r--r--app/models/concerns/token_authenticatable_strategies/encrypted.rb52
-rw-r--r--app/models/deployment.rb4
-rw-r--r--app/models/diff_note.rb14
-rw-r--r--app/models/email.rb2
-rw-r--r--app/models/environment.rb8
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb55
-rw-r--r--app/models/group.rb2
-rw-r--r--app/models/individual_note_discussion.rb2
-rw-r--r--app/models/issue.rb6
-rw-r--r--app/models/label.rb7
-rw-r--r--app/models/label_note.rb2
-rw-r--r--app/models/legacy_diff_note.rb2
-rw-r--r--app/models/member.rb2
-rw-r--r--app/models/merge_request.rb131
-rw-r--r--app/models/merge_request_assignee.rb6
-rw-r--r--app/models/merge_request_diff.rb19
-rw-r--r--app/models/merge_request_diff_file.rb2
-rw-r--r--app/models/milestone.rb5
-rw-r--r--app/models/namespace.rb19
-rw-r--r--app/models/network/graph.rb9
-rw-r--r--app/models/note.rb8
-rw-r--r--app/models/notification_recipient.rb6
-rw-r--r--app/models/personal_access_token.rb5
-rw-r--r--app/models/project.rb141
-rw-r--r--app/models/project_daily_statistic.rb10
-rw-r--r--app/models/project_feature.rb15
-rw-r--r--app/models/project_services/campfire_service.rb2
-rw-r--r--app/models/project_services/irker_service.rb2
-rw-r--r--app/models/project_services/jira_service.rb15
-rw-r--r--app/models/project_services/kubernetes_service.rb6
-rw-r--r--app/models/project_services/prometheus_service.rb6
-rw-r--r--app/models/project_services/slack_slash_commands_service.rb4
-rw-r--r--app/models/project_services/youtrack_service.rb40
-rw-r--r--app/models/project_wiki.rb6
-rw-r--r--app/models/protected_branch.rb14
-rw-r--r--app/models/repository.rb37
-rw-r--r--app/models/service.rb1
-rw-r--r--app/models/ssh_host_key.rb2
-rw-r--r--app/models/suggestion.rb51
-rw-r--r--app/models/todo.rb9
-rw-r--r--app/models/user.rb14
-rw-r--r--app/models/user_interacted_project.rb18
-rw-r--r--app/models/wiki_page.rb2
-rw-r--r--app/policies/global_policy.rb4
-rw-r--r--app/policies/group_policy.rb5
-rw-r--r--app/policies/identity_provider_policy.rb15
-rw-r--r--app/policies/issuable_policy.rb1
-rw-r--r--app/policies/merge_request_policy.rb3
-rw-r--r--app/policies/project_policy.rb10
-rw-r--r--app/presenters/blobs/unfold_presenter.rb75
-rw-r--r--app/presenters/ci/build_runner_presenter.rb42
-rw-r--r--app/presenters/ci/pipeline_presenter.rb53
-rw-r--r--app/presenters/clusterable_presenter.rb4
-rw-r--r--app/presenters/commit_status_presenter.rb3
-rw-r--r--app/presenters/group_clusterable_presenter.rb5
-rw-r--r--app/presenters/merge_request_presenter.rb12
-rw-r--r--app/presenters/project_clusterable_presenter.rb5
-rw-r--r--app/presenters/project_presenter.rb14
-rw-r--r--app/serializers/acts_as_taggable_on/tag_entity.rb6
-rw-r--r--app/serializers/acts_as_taggable_on/tag_serializer.rb5
-rw-r--r--app/serializers/cluster_application_entity.rb1
-rw-r--r--app/serializers/concerns/user_status_tooltip.rb2
-rw-r--r--app/serializers/detailed_status_entity.rb16
-rw-r--r--app/serializers/diff_file_base_entity.rb10
-rw-r--r--app/serializers/diff_file_entity.rb6
-rw-r--r--app/serializers/environment_entity.rb3
-rw-r--r--app/serializers/merge_request_for_pipeline_entity.rb17
-rw-r--r--app/serializers/pipeline_entity.rb15
-rw-r--r--app/serializers/pipeline_serializer.rb1
-rw-r--r--app/services/after_branch_delete_service.rb5
-rw-r--r--app/services/application_settings/update_service.rb2
-rw-r--r--app/services/auth/container_registry_authentication_service.rb2
-rw-r--r--app/services/boards/visits/latest_service.rb14
-rw-r--r--app/services/ci/create_pipeline_service.rb9
-rw-r--r--app/services/ci/destroy_pipeline_service.rb2
-rw-r--r--app/services/ci/pipeline_trigger_service.rb4
-rw-r--r--app/services/ci/prepare_build_service.rb25
-rw-r--r--app/services/clusters/applications/base_helm_service.rb4
-rw-r--r--app/services/clusters/applications/base_service.rb76
-rw-r--r--app/services/clusters/applications/check_ingress_ip_address_service.rb16
-rw-r--r--app/services/clusters/applications/create_service.rb59
-rw-r--r--app/services/clusters/applications/install_service.rb22
-rw-r--r--app/services/clusters/applications/patch_service.rb24
-rw-r--r--app/services/clusters/applications/schedule_installation_service.rb31
-rw-r--r--app/services/clusters/applications/update_service.rb34
-rw-r--r--app/services/commits/create_service.rb13
-rw-r--r--app/services/concerns/suggestible.rb30
-rw-r--r--app/services/concerns/users/participable_service.rb15
-rw-r--r--app/services/error_tracking/list_issues_service.rb10
-rw-r--r--app/services/error_tracking/list_projects_service.rb4
-rw-r--r--app/services/files/multi_service.rb3
-rw-r--r--app/services/git/branch_push_service.rb244
-rw-r--r--app/services/git/tag_push_service.rb68
-rw-r--r--app/services/git_push_service.rb240
-rw-r--r--app/services/git_tag_push_service.rb66
-rw-r--r--app/services/groups/auto_devops_service.rb17
-rw-r--r--app/services/groups/create_service.rb2
-rw-r--r--app/services/groups/nested_create_service.rb2
-rw-r--r--app/services/groups/transfer_service.rb10
-rw-r--r--app/services/groups/update_service.rb2
-rw-r--r--app/services/issuable_base_service.rb8
-rw-r--r--app/services/issues/build_service.rb4
-rw-r--r--app/services/merge_requests/base_service.rb2
-rw-r--r--app/services/merge_requests/build_service.rb1
-rw-r--r--app/services/merge_requests/merge_base_service.rb63
-rw-r--r--app/services/merge_requests/merge_service.rb60
-rw-r--r--app/services/merge_requests/merge_to_ref_service.rb73
-rw-r--r--app/services/merge_requests/refresh_service.rb9
-rw-r--r--app/services/merge_requests/reopen_service.rb2
-rw-r--r--app/services/notes/create_service.rb11
-rw-r--r--app/services/notes/quick_actions_service.rb26
-rw-r--r--app/services/projects/download_service.rb2
-rw-r--r--app/services/projects/fetch_statistics_increment_service.rb32
-rw-r--r--app/services/projects/fork_service.rb12
-rw-r--r--app/services/projects/group_links/create_service.rb10
-rw-r--r--app/services/projects/hashed_storage/base_attachment_service.rb51
-rw-r--r--app/services/projects/hashed_storage/base_repository_service.rb22
-rw-r--r--app/services/projects/hashed_storage/migrate_attachments_service.rb49
-rw-r--r--app/services/projects/hashed_storage/migrate_repository_service.rb16
-rw-r--r--app/services/projects/hashed_storage/rollback_attachments_service.rb34
-rw-r--r--app/services/projects/hashed_storage/rollback_repository_service.rb40
-rw-r--r--app/services/projects/hashed_storage/rollback_service.rb37
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_link_list_service.rb16
-rw-r--r--app/services/projects/lfs_pointers/lfs_download_service.rb20
-rw-r--r--app/services/projects/operations/update_service.rb23
-rw-r--r--app/services/prometheus/adapter_service.rb2
-rw-r--r--app/services/push_event_payload_service.rb2
-rw-r--r--app/services/quick_actions/interpret_service.rb5
-rw-r--r--app/services/releases/destroy_service.rb1
-rw-r--r--app/services/search/global_service.rb3
-rw-r--r--app/services/search/group_service.rb6
-rw-r--r--app/services/search/project_service.rb7
-rw-r--r--app/services/suggestions/apply_service.rb14
-rw-r--r--app/services/suggestions/create_service.rb42
-rw-r--r--app/services/suggestions/outdate_service.rb19
-rw-r--r--app/services/system_note_service.rb2
-rw-r--r--app/services/upload_service.rb2
-rw-r--r--app/services/validate_new_branch_service.rb4
-rw-r--r--app/uploaders/file_mover.rb8
-rw-r--r--app/validators/devise_email_validator.rb36
-rw-r--r--app/validators/email_validator.rb7
-rw-r--r--app/validators/sha_validator.rb9
-rw-r--r--app/validators/url_validator.rb6
-rw-r--r--app/views/admin/appearances/_form.html.haml1
-rw-r--r--app/views/admin/appearances/_system_header_footer_form.html.haml33
-rw-r--r--app/views/admin/application_settings/_account_and_limit.html.haml29
-rw-r--r--app/views/admin/application_settings/_ci_cd.html.haml29
-rw-r--r--app/views/admin/application_settings/_email.html.haml16
-rw-r--r--app/views/admin/application_settings/_help_page.html.haml12
-rw-r--r--app/views/admin/application_settings/_repository_mirrors_form.html.haml10
-rw-r--r--app/views/admin/application_settings/_visibility_and_access.html.haml16
-rw-r--r--app/views/admin/dashboard/index.html.haml7
-rw-r--r--app/views/admin/deploy_keys/edit.html.haml8
-rw-r--r--app/views/admin/deploy_keys/index.html.haml20
-rw-r--r--app/views/admin/groups/_group.html.haml2
-rw-r--r--app/views/admin/groups/show.html.haml2
-rw-r--r--app/views/admin/projects/_projects.html.haml2
-rw-r--r--app/views/admin/runners/_runner.html.haml4
-rw-r--r--app/views/admin/runners/index.html.haml19
-rw-r--r--app/views/admin/users/show.html.haml5
-rw-r--r--app/views/ci/status/_icon.html.haml16
-rw-r--r--app/views/clusters/clusters/_form.html.haml10
-rw-r--r--app/views/clusters/clusters/_sidebar.html.haml2
-rw-r--r--app/views/clusters/clusters/gcp/_form.html.haml8
-rw-r--r--app/views/clusters/clusters/show.html.haml9
-rw-r--r--app/views/clusters/clusters/user/_form.html.haml1
-rw-r--r--app/views/dashboard/_snippets_head.html.haml4
-rw-r--r--app/views/dashboard/activity.html.haml2
-rw-r--r--app/views/dashboard/groups/_groups.html.haml4
-rw-r--r--app/views/dashboard/groups/index.html.haml2
-rw-r--r--app/views/dashboard/issues.html.haml2
-rw-r--r--app/views/dashboard/merge_requests.html.haml2
-rw-r--r--app/views/dashboard/projects/_starred_empty_state.html.haml9
-rw-r--r--app/views/dashboard/projects/index.html.haml2
-rw-r--r--app/views/dashboard/projects/starred.html.haml11
-rw-r--r--app/views/dashboard/snippets/index.html.haml12
-rw-r--r--app/views/dashboard/todos/index.html.haml2
-rw-r--r--app/views/discussions/_diff_with_notes.html.haml4
-rw-r--r--app/views/doorkeeper/applications/index.html.haml2
-rw-r--r--app/views/explore/groups/_groups.html.haml4
-rw-r--r--app/views/explore/groups/index.html.haml2
-rw-r--r--app/views/explore/projects/index.html.haml2
-rw-r--r--app/views/explore/projects/starred.html.haml2
-rw-r--r--app/views/explore/projects/trending.html.haml2
-rw-r--r--app/views/explore/snippets/index.html.haml2
-rw-r--r--app/views/groups/_archived_projects.html.haml4
-rw-r--r--app/views/groups/_home_panel.html.haml6
-rw-r--r--app/views/groups/_shared_projects.html.haml4
-rw-r--r--app/views/groups/_subgroups_and_projects.html.haml4
-rw-r--r--app/views/groups/edit.html.haml2
-rw-r--r--app/views/groups/settings/_general.html.haml2
-rw-r--r--app/views/groups/settings/ci_cd/_auto_devops_form.html.haml15
-rw-r--r--app/views/groups/settings/ci_cd/show.html.haml14
-rw-r--r--app/views/help/_shortcuts.html.haml12
-rw-r--r--app/views/help/index.html.haml2
-rw-r--r--app/views/help/instance_configuration.html.haml2
-rw-r--r--app/views/help/show.html.haml2
-rw-r--r--app/views/help/ui.html.haml2
-rw-r--r--app/views/layouts/_head.html.haml2
-rw-r--r--app/views/layouts/_mailer.html.haml4
-rw-r--r--app/views/layouts/application.html.haml4
-rw-r--r--app/views/layouts/devise.html.haml2
-rw-r--r--app/views/layouts/devise_empty.html.haml2
-rw-r--r--app/views/layouts/empty_mailer.html.haml5
-rw-r--r--app/views/layouts/empty_mailer.text.erb5
-rw-r--r--app/views/layouts/header/_new_dropdown.haml2
-rw-r--r--app/views/layouts/mailer.text.erb4
-rw-r--r--app/views/layouts/nav/_dashboard.html.haml6
-rw-r--r--app/views/layouts/nav/sidebar/_admin.html.haml2
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml15
-rw-r--r--app/views/layouts/nav/sidebar/_project.html.haml4
-rw-r--r--app/views/layouts/notify.html.haml2
-rw-r--r--app/views/layouts/notify.text.erb4
-rw-r--r--app/views/notify/issue_moved_email.html.haml11
-rw-r--r--app/views/notify/issue_moved_email.text.erb4
-rw-r--r--app/views/profiles/_email_settings.html.haml16
-rw-r--r--app/views/profiles/accounts/_providers.html.haml21
-rw-r--r--app/views/profiles/accounts/show.html.haml19
-rw-r--r--app/views/profiles/active_sessions/_active_session.html.haml6
-rw-r--r--app/views/profiles/notifications/_email_settings.html.haml6
-rw-r--r--app/views/profiles/notifications/show.html.haml4
-rw-r--r--app/views/profiles/preferences/show.html.haml6
-rw-r--r--app/views/profiles/show.html.haml20
-rw-r--r--app/views/projects/_flash_messages.html.haml3
-rw-r--r--app/views/projects/_home_panel.html.haml22
-rw-r--r--app/views/projects/_md_preview.html.haml6
-rw-r--r--app/views/projects/_merge_request_merge_settings.html.haml1
-rw-r--r--app/views/projects/_new_project_fields.html.haml8
-rw-r--r--app/views/projects/_wiki.html.haml5
-rw-r--r--app/views/projects/artifacts/browse.html.haml2
-rw-r--r--app/views/projects/blob/_header_content.html.haml2
-rw-r--r--app/views/projects/blob/_markdown_buttons.html.haml20
-rw-r--r--app/views/projects/blob/diff.html.haml6
-rw-r--r--app/views/projects/blob/preview.html.haml41
-rw-r--r--app/views/projects/blob/viewers/_dependency_manager.html.haml5
-rw-r--r--app/views/projects/blob/viewers/_markup.html.haml2
-rw-r--r--app/views/projects/branches/_branch.html.haml3
-rw-r--r--app/views/projects/buttons/_clone.html.haml5
-rw-r--r--app/views/projects/commits/_commit.html.haml9
-rw-r--r--app/views/projects/cycle_analytics/show.html.haml2
-rw-r--r--app/views/projects/deployments/_actions.haml2
-rw-r--r--app/views/projects/deployments/_confirm_rollback_modal.html.haml23
-rw-r--r--app/views/projects/deployments/_rollback.haml3
-rw-r--r--app/views/projects/diffs/_line.html.haml2
-rw-r--r--app/views/projects/diffs/_parallel_view.html.haml8
-rw-r--r--app/views/projects/edit.html.haml50
-rw-r--r--app/views/projects/empty.html.haml133
-rw-r--r--app/views/projects/forks/_fork_button.html.haml4
-rw-r--r--app/views/projects/issues/_discussion.html.haml2
-rw-r--r--app/views/projects/issues/_issue.html.haml2
-rw-r--r--app/views/projects/issues/_merge_requests.html.haml2
-rw-r--r--app/views/projects/issues/_related_branches.html.haml2
-rw-r--r--app/views/projects/issues/show.html.haml12
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml4
-rw-r--r--app/views/projects/merge_requests/_merge_requests.html.haml2
-rw-r--r--app/views/projects/merge_requests/_mr_box.html.haml2
-rw-r--r--app/views/projects/merge_requests/_mr_title.html.haml6
-rw-r--r--app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml2
-rw-r--r--app/views/projects/merge_requests/conflicts/show.html.haml4
-rw-r--r--app/views/projects/milestones/show.html.haml5
-rw-r--r--app/views/projects/mirrors/_authentication_method.html.haml1
-rw-r--r--app/views/projects/mirrors/_mirror_repos.html.haml2
-rw-r--r--app/views/projects/pages/_destroy.haml2
-rw-r--r--app/views/projects/pages/_https_only.html.haml2
-rw-r--r--app/views/projects/pipelines/_info.html.haml16
-rw-r--r--app/views/projects/pipelines/_with_tabs.html.haml30
-rw-r--r--app/views/projects/pipelines/charts.html.haml2
-rw-r--r--app/views/projects/pipelines/index.html.haml4
-rw-r--r--app/views/projects/pipelines/new.html.haml8
-rw-r--r--app/views/projects/pipelines/show.html.haml10
-rw-r--r--app/views/projects/settings/ci_cd/_autodevops_form.html.haml8
-rw-r--r--app/views/projects/settings/ci_cd/show.html.haml2
-rw-r--r--app/views/projects/settings/operations/_error_tracking.html.haml28
-rw-r--r--app/views/projects/settings/operations/show.html.haml4
-rw-r--r--app/views/projects/snippets/index.html.haml18
-rw-r--r--app/views/projects/snippets/new.html.haml4
-rw-r--r--app/views/projects/tags/_tag.html.haml5
-rw-r--r--app/views/projects/tags/show.html.haml7
-rw-r--r--app/views/projects/wikis/show.html.haml2
-rw-r--r--app/views/search/_category.html.haml10
-rw-r--r--app/views/search/_results.html.haml4
-rw-r--r--app/views/search/results/_blob.html.haml2
-rw-r--r--app/views/search/results/_snippet_blob.html.haml2
-rw-r--r--app/views/search/results/_user.html.haml10
-rw-r--r--app/views/search/results/_wiki_blob.html.haml2
-rw-r--r--app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml2
-rw-r--r--app/views/shared/_file_highlight.html.haml2
-rw-r--r--app/views/shared/_issuable_meta_data.html.haml2
-rw-r--r--app/views/shared/_mobile_clone_panel.html.haml1
-rw-r--r--app/views/shared/deploy_keys/_form.html.haml9
-rw-r--r--app/views/shared/empty_states/_snippets.html.haml20
-rw-r--r--app/views/shared/groups/_group.html.haml2
-rw-r--r--app/views/shared/issuable/_search_bar.html.haml14
-rw-r--r--app/views/shared/issuable/_sort_dropdown.html.haml2
-rw-r--r--app/views/shared/milestones/_milestone.html.haml4
-rw-r--r--app/views/shared/milestones/_tabs.html.haml6
-rw-r--r--app/views/shared/milestones/_top.html.haml5
-rw-r--r--app/views/shared/notes/_note.html.haml15
-rw-r--r--app/views/shared/projects/_project.html.haml10
-rw-r--r--app/views/shared/snippets/_form.html.haml6
-rw-r--r--app/views/shared/snippets/_header.html.haml10
-rw-r--r--app/views/shared/snippets/_list.html.haml12
-rw-r--r--app/views/snippets/index.html.haml2
-rw-r--r--app/views/users/_groups.html.haml2
-rw-r--r--app/workers/all_queues.yml8
-rw-r--r--app/workers/build_finished_worker.rb1
-rw-r--r--app/workers/chat_notification_worker.rb33
-rw-r--r--app/workers/ci/build_prepare_worker.rb16
-rw-r--r--app/workers/cluster_configure_worker.rb2
-rw-r--r--app/workers/cluster_patch_app_worker.rb13
-rw-r--r--app/workers/cluster_project_configure_worker.rb2
-rw-r--r--app/workers/concerns/waitable_worker.rb8
-rw-r--r--app/workers/create_gpg_signature_worker.rb12
-rw-r--r--app/workers/emails_on_push_worker.rb34
-rw-r--r--app/workers/expire_pipeline_cache_worker.rb6
-rw-r--r--app/workers/hashed_storage/base_worker.rb21
-rw-r--r--app/workers/hashed_storage/project_migrate_worker.rb26
-rw-r--r--app/workers/hashed_storage/project_rollback_worker.rb26
-rw-r--r--app/workers/hashed_storage/rollbacker_worker.rb16
-rw-r--r--app/workers/object_storage/migrate_uploads_worker.rb8
-rw-r--r--app/workers/pipeline_metrics_worker.rb2
-rw-r--r--app/workers/pipeline_schedule_worker.rb19
-rw-r--r--app/workers/post_receive.rb8
-rw-r--r--app/workers/project_cache_worker.rb1
-rw-r--r--app/workers/project_daily_statistics_worker.rb13
-rw-r--r--app/workers/project_migrate_hashed_storage_worker.rb43
-rw-r--r--app/workers/remove_expired_members_worker.rb8
-rw-r--r--babel.config.js51
-rwxr-xr-xbin/background_jobs9
-rw-r--r--changelogs/unreleased/10029-env-item.yml5
-rw-r--r--changelogs/unreleased/10081-env-table.yml5
-rw-r--r--changelogs/unreleased/19745-forms-with-task-lists-can-be-overwritten-when-editing-simultaneously.yml5
-rw-r--r--changelogs/unreleased/2105-add-setting-for-first-day-of-the-week.yml5
-rw-r--r--changelogs/unreleased/24642-activity_service_optimization.yml5
-rw-r--r--changelogs/unreleased/24680-support-bamboo-api-polymorphism.yml5
-rw-r--r--changelogs/unreleased/24875-label.yml5
-rw-r--r--changelogs/unreleased/24971-align-emailvalidator-to-validate_email-gem-implementation.yml5
-rw-r--r--changelogs/unreleased/25043-empty-states.yml5
-rw-r--r--changelogs/unreleased/25569-changing-wording-to-delete-when-referring-to-removing-a-branch.yml5
-rw-r--r--changelogs/unreleased/25942-remove-fake-repository-path-response.yml5
-rw-r--r--changelogs/unreleased/26375-markdown-footnotes-not-working.yml5
-rw-r--r--changelogs/unreleased/28500-empty-states-for-profile-page.yml5
-rw-r--r--changelogs/unreleased/30120-add-flat-square-badge-style.yml5
-rw-r--r--changelogs/unreleased/36445-better-indication-that-an-issue-has-been-moved-or-marked-as-duplicated.yml5
-rw-r--r--changelogs/unreleased/37990-task-list-bracket.yml5
-rw-r--r--changelogs/unreleased/39676-wiki-api-problems-on-update-parameters-and-500-error.yml5
-rw-r--r--changelogs/unreleased/40997-gitlab-pages-deploy-jobs-have-a-null-status.yml5
-rw-r--r--changelogs/unreleased/42769-remove-expansion-hover-animation-from-status-icon-buttons.yml5
-rw-r--r--changelogs/unreleased/43297-authorized-application-count.yml5
-rw-r--r--changelogs/unreleased/43681-display-last-activity-and-created-at-datetimes-for-users-in-admin-users.yml5
-rw-r--r--changelogs/unreleased/44332-add-openid-profile-scopes.yml5
-rw-r--r--changelogs/unreleased/44698-recaptcha.yml5
-rw-r--r--changelogs/unreleased/44740-api-to-verify-a-given-user-has-right-to-merge-a-given-mergerequest.yml5
-rw-r--r--changelogs/unreleased/45779-fix-default-visibility-level-for-projects.yml5
-rw-r--r--changelogs/unreleased/45791-number-of-repositories-usage-ping.yml5
-rw-r--r--changelogs/unreleased/46448-add-timestamps-for-each-stage-of-gitlab-rake-gitlab-backup-restore.yml5
-rw-r--r--changelogs/unreleased/46787-create-project-label-window-is-cut-off-at-the-bottom.yml5
-rw-r--r--changelogs/unreleased/47007-related-merge-requests-in-issue-design-restyle.yml5
-rw-r--r--changelogs/unreleased/47150-update-sshkey.yml5
-rw-r--r--changelogs/unreleased/47988-improve-milestone-queries-with-subqueries.yml5
-rw-r--r--changelogs/unreleased/48132-display-output-from-pre-receive-scripts.yml5
-rw-r--r--changelogs/unreleased/48297-fix-code-selection.yml6
-rw-r--r--changelogs/unreleased/49502-gpg-signature-api-endpoint.yml5
-rw-r--r--changelogs/unreleased/49856-upgrade-bootstrap-form-gem.yml5
-rw-r--r--changelogs/unreleased/49863-ingress-ip-loading-state.yml5
-rw-r--r--changelogs/unreleased/49910-reopening-a-closed-milestone-from-the-closed-milestones-page-fails2.yml5
-rw-r--r--changelogs/unreleased/50006-expose-textcolor-from-public-labels-api.yml5
-rw-r--r--changelogs/unreleased/50013-add-browser-platform-flags.yml5
-rw-r--r--changelogs/unreleased/50352-sort-save.yml5
-rw-r--r--changelogs/unreleased/50433-make-emoji-picker-bigger.yml5
-rw-r--r--changelogs/unreleased/50521-block-emojis-and-symbol-characters-from-user-s-full-names-2.yml5
-rw-r--r--changelogs/unreleased/51754-admin-view-private-personal-snippets.yml5
-rw-r--r--changelogs/unreleased/51759-filter-by-language.yml5
-rw-r--r--changelogs/unreleased/51913-api-getting-projects-for-users-with-dot-gets-404.yml5
-rw-r--r--changelogs/unreleased/51988-install-group-runner-on-group-cluster.yml5
-rw-r--r--changelogs/unreleased/52275-fix-master-to-be-hyperlink.yml5
-rw-r--r--changelogs/unreleased/52278-squash-checkbox-fix.yml5
-rw-r--r--changelogs/unreleased/52347-lines-changed-statistics-is-not-easily-visible-in-mr-changes-view.yml5
-rw-r--r--changelogs/unreleased/52363-modifies-environment-scope-field-on-cluster-page.yml5
-rw-r--r--changelogs/unreleased/52363-ui-changes-to-cluster-and-ado-pages.yml5
-rw-r--r--changelogs/unreleased/52366-improved-group-lists-ui-spinners.yml5
-rw-r--r--changelogs/unreleased/52424-goodbye-hipchat.yml5
-rw-r--r--changelogs/unreleased/52447-auto-devops-at-group-level.yml5
-rw-r--r--changelogs/unreleased/52568-external-mr-diffs.yml5
-rw-r--r--changelogs/unreleased/52674-api-v4-projects-project_id-jobs-endpoint-hits-statement-timeout.yml5
-rw-r--r--changelogs/unreleased/52971-merge-request-file-browser-should-always-be-possible-show-hide.yml5
-rw-r--r--changelogs/unreleased/53104-redesign-group-overview-ui-mvc.yml5
-rw-r--r--changelogs/unreleased/53139-hide-tree-single-file.yml5
-rw-r--r--changelogs/unreleased/53431-fix-upcoming-milestone-filter-for-groups.yml5
-rw-r--r--changelogs/unreleased/53671-redirect-projects-id-to-project-page.yml5
-rw-r--r--changelogs/unreleased/53676-ip-address-of-gitlab-runner-is-wrong-in-the-runners-description.yml5
-rw-r--r--changelogs/unreleased/53714-inconsistent-text-color-for-labels.yml5
-rw-r--r--changelogs/unreleased/53856-changing-group-visibility-does-not-re-enable-save-button.yml6
-rw-r--r--changelogs/unreleased/53950-commit-comments-displayed-on-a-merge-request.yml5
-rw-r--r--changelogs/unreleased/54167-rename-project-tags-to-project-topics.yml5
-rw-r--r--changelogs/unreleased/54213-standardize-token-value-capitalization-in-filter-bar.yml5
-rw-r--r--changelogs/unreleased/54250-upstream-kubeclient-redirect-patch.yml5
-rw-r--r--changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml5
-rw-r--r--changelogs/unreleased/54544-update-project-topics-styling-to-use-badges-design.yml5
-rw-r--r--changelogs/unreleased/54725-fix-emoji-button-active-state.yml5
-rw-r--r--changelogs/unreleased/54796-api-sort-tie-breaker-for-pagination.yml5
-rw-r--r--changelogs/unreleased/54850-pages-domain-show-view-is-not-protected-by-access-control.yml5
-rw-r--r--changelogs/unreleased/54905-milestone-search.yml5
-rw-r--r--changelogs/unreleased/54916-extended-tooltip-for-merge-request-links.yml5
-rw-r--r--changelogs/unreleased/55098-ui-bug-adding-group-members-with-lower-permissions.yml5
-rw-r--r--changelogs/unreleased/55109-jira-integration-api-doesn-t-respect-available-format.yml5
-rw-r--r--changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml5
-rw-r--r--changelogs/unreleased/55209-tool-tip-hides-menu-item.yml5
-rw-r--r--changelogs/unreleased/55242-skeleton-loading-releases.yml5
-rw-r--r--changelogs/unreleased/55312-svg.yml5
-rw-r--r--changelogs/unreleased/55376-related_merge_requests-api-call-returns-merge-requests-that-are-not-related-to-the-issue.yml5
-rw-r--r--changelogs/unreleased/55447-validate-k8s-ca-cert.yml5
-rw-r--r--changelogs/unreleased/55495-teamcity-use-revision-in-query.yml5
-rw-r--r--changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml5
-rw-r--r--changelogs/unreleased/55820-adds-common-name-chart-value.yml5
-rw-r--r--changelogs/unreleased/55884-adjust-emoji-and-cancel-buttons-height-in-user-status-modal-when-emoji-is-changed.yml5
-rw-r--r--changelogs/unreleased/55893-artifacts-download.yml5
-rw-r--r--changelogs/unreleased/55945-suggested-change-preview-highlight.yml5
-rw-r--r--changelogs/unreleased/55966-when-ref-is-ambiguous-createpipelineservice-raises-an-error.yml5
-rw-r--r--changelogs/unreleased/56010-user-profile-page-horizonal-whitespace-between-overview-columns-breaks-two-column-layout.yml5
-rw-r--r--changelogs/unreleased/56014-api-merge-request-squash-commit-messages.yml5
-rw-r--r--changelogs/unreleased/56015-remove-remote-timeout.yml5
-rw-r--r--changelogs/unreleased/56019-archived-stuck.yml5
-rw-r--r--changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml4
-rw-r--r--changelogs/unreleased/56089-merge-gitlab-keys.yml5
-rw-r--r--changelogs/unreleased/56110-cluster-kubernetes-api-500-error-on-post-request.yml5
-rw-r--r--changelogs/unreleased/56172-docs-fix-add-include-to-ci-param-list.yml5
-rw-r--r--changelogs/unreleased/56237-api-truncated-commit-title.yml5
-rw-r--r--changelogs/unreleased/56332-exclude-public-group-milestones-from-count.yml5
-rw-r--r--changelogs/unreleased/56334-runners-ipv6-address-overlaps-other-values.yml5
-rw-r--r--changelogs/unreleased/56363-inconsitent-file-size-indication-across-different-ci-pages.yml6
-rw-r--r--changelogs/unreleased/56371-don-t-check-confidential-issues-for-spam.yml5
-rw-r--r--changelogs/unreleased/56379-pipeline-stages-job-action-button-icon-is-not-aligned.yml5
-rw-r--r--changelogs/unreleased/56389-remove-unwanted-suggestion-flash-margin.yml5
-rw-r--r--changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml5
-rw-r--r--changelogs/unreleased/56417-update-helm-to-2-12-2.yml5
-rw-r--r--changelogs/unreleased/56485-implement-graphql-mergerequestsresolver.yml5
-rw-r--r--changelogs/unreleased/56507-sh-bump-katex-0.10.0.yml5
-rw-r--r--changelogs/unreleased/56543-project-lists-further-iteration-improvements.yml5
-rw-r--r--changelogs/unreleased/56547-limit-sidekiq-logging-based-on-argument-size.yml5
-rw-r--r--changelogs/unreleased/56556-fix-markdown-table-border.yml5
-rw-r--r--changelogs/unreleased/56622-admin-settings-cannot-read-property-addeventlistener-of-null.yml5
-rw-r--r--changelogs/unreleased/56636-hashed-storage-afterrenameservice.yml5
-rw-r--r--changelogs/unreleased/56694-mark-group-level-labels-in-label-api-as-such.yml5
-rw-r--r--changelogs/unreleased/56764-poor-ui-on-milestone-validation-error-page.yml5
-rw-r--r--changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml5
-rw-r--r--changelogs/unreleased/56788-unicorn-metric-labels.yml5
-rw-r--r--changelogs/unreleased/56833-project-improve-empty-repository-state-ui-fe.yml5
-rw-r--r--changelogs/unreleased/56851-blank-values-in-reactive-cache.yml5
-rw-r--r--changelogs/unreleased/56864-reopen-locked-mr.yml5
-rw-r--r--changelogs/unreleased/56937-edit-knative-domain-after-it-has-been-deployed.yml5
-rw-r--r--changelogs/unreleased/56938-diff-file-headers-on-compare-not-quite-right.yml5
-rw-r--r--changelogs/unreleased/56954-improve-knative-after-installing-tiller.yml5
-rw-r--r--changelogs/unreleased/57063-implement-new-arguments-iid-for-issuesresolver-in-graphql.yml5
-rw-r--r--changelogs/unreleased/57101-api-docs-for-hangouts-chat-service-incorrect.yml5
-rw-r--r--changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml5
-rw-r--r--changelogs/unreleased/57160-merge-request-tabs-header-is-missing-bottom-border.yml5
-rw-r--r--changelogs/unreleased/57223-wiki-finder.yml5
-rw-r--r--changelogs/unreleased/57227-absolute-uri-missing-hierarchical-segment.yml5
-rw-r--r--changelogs/unreleased/57353-git-push-fails-on-large-lfs-files-where-the-push-take-a-long-time.yml5
-rw-r--r--changelogs/unreleased/57357-automate-base-domain-help-text.yml5
-rw-r--r--changelogs/unreleased/57409-loading-button-transition.yml5
-rw-r--r--changelogs/unreleased/57410-api-create-release-link-with-ftp-address-return-400-bad-request.yml5
-rw-r--r--changelogs/unreleased/57540-filename-trailing-space.yml5
-rw-r--r--changelogs/unreleased/57544-web-ide-new-directory-dialog-shows-file-templates.yml5
-rw-r--r--changelogs/unreleased/57564-contributing-button-border.yml5
-rw-r--r--changelogs/unreleased/57579-gitlab-project-import-fails-sidekiq-undefined-method-import_jid.yml5
-rw-r--r--changelogs/unreleased/57589-update-workhorse.yml5
-rw-r--r--changelogs/unreleased/57648-make-emoji-picker-full-width-on-mobile.yml5
-rw-r--r--changelogs/unreleased/57650-remove-tld-validation-from-cluster.yml5
-rw-r--r--changelogs/unreleased/57655-fix-markdown-tables-border.yml5
-rw-r--r--changelogs/unreleased/57671-fix_merge_request_base_pipeline.yml5
-rw-r--r--changelogs/unreleased/57768-remove-vertical-line.yml5
-rw-r--r--changelogs/unreleased/57894-buttons-on-group-page-are-misaligned.yml5
-rw-r--r--changelogs/unreleased/57984-store-branch-name.yml5
-rw-r--r--changelogs/unreleased/58208-explicitly-set-masterauth.yml6
-rw-r--r--changelogs/unreleased/58410-change-pixel-size-of-instance-header-footer-message-to-16px.yml5
-rw-r--r--changelogs/unreleased/58482-update-airminc-clair-local-scan-to-2-0-6.yml5
-rw-r--r--changelogs/unreleased/58739-hashed-storage-prevent-a-migration-and-rollback-running-at-the-same-time.yml5
-rw-r--r--changelogs/unreleased/58781-silent-progress-in-auto-devops.yml5
-rw-r--r--changelogs/unreleased/58789-some-system-notes-on-issuable-are-folded-on-mobile.yml5
-rw-r--r--changelogs/unreleased/58797-broken-ui-on-a-closed-merge-request-from-a-deleted-source-project.yml5
-rw-r--r--changelogs/unreleased/58805-allow-incomplete-commit-data-to-be-fetched-from-collection.yml5
-rw-r--r--changelogs/unreleased/58883-fix-fetching-comments.yml5
-rw-r--r--changelogs/unreleased/58889-spinners-are-active-prematurely-in-bitbucket-cloud-import.yml5
-rw-r--r--changelogs/unreleased/58933-broken-ui-on-commits-on-mobile.yml5
-rw-r--r--changelogs/unreleased/59057-buttons-on-top-from-a-user-profile-page-on-mobile.yml5
-rw-r--r--changelogs/unreleased/59117-inconsistent-hover-behavior-on-navbar-items.yml5
-rw-r--r--changelogs/unreleased/59189-long-names-in-project-path-namespace-dropdown-breaks-past-container.yml5
-rw-r--r--changelogs/unreleased/59296-add-filter-by-title-milestones-api.yml5
-rw-r--r--changelogs/unreleased/59352-fix-mr-discussion-expansion.yml5
-rw-r--r--changelogs/unreleased/59502-fix-breadcrumb-artifacts.yml5
-rw-r--r--changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml5
-rw-r--r--changelogs/unreleased/MaxWinterstein-master-patch-23232.yml5
-rw-r--r--changelogs/unreleased/Projects--dropdown-is-misaligned-on-issue-boards-page.yml5
-rw-r--r--changelogs/unreleased/ab-54270-github-iid.yml5
-rw-r--r--changelogs/unreleased/ac-pages-subgroups.yml5
-rw-r--r--changelogs/unreleased/actioncontroller-parameters-deprecations.yml5
-rw-r--r--changelogs/unreleased/add-badge-count-to-projects-and-groups.yml5
-rw-r--r--changelogs/unreleased/add-related-merge-request-count-to-api-response.yml5
-rw-r--r--changelogs/unreleased/add-title-attribute-to-file-row.yml5
-rw-r--r--changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml5
-rw-r--r--changelogs/unreleased/adrianmoisey-GITLAB_PAGES_PREDEFINED_VARIABLES.yml5
-rw-r--r--changelogs/unreleased/adriel-remove-feature-flag.yml5
-rw-r--r--changelogs/unreleased/allow-filtering-labels-by-a-single-character.yml5
-rw-r--r--changelogs/unreleased/an-dtracing-test-for-invalid-tracers.yml5
-rw-r--r--changelogs/unreleased/an-gilab-process-name.yml5
-rw-r--r--changelogs/unreleased/an-opentracing-active-record-tracing.yml5
-rw-r--r--changelogs/unreleased/an-opentracing-factory.yml5
-rw-r--r--changelogs/unreleased/an-opentracing-propagation.yml5
-rw-r--r--changelogs/unreleased/an-opentracing-render-tracing.yml5
-rw-r--r--changelogs/unreleased/an-peek-jaeger.yml5
-rw-r--r--changelogs/unreleased/api-group-labels.yml5
-rw-r--r--changelogs/unreleased/api-nested-group-permission.yml5
-rw-r--r--changelogs/unreleased/api-tags-search.yml5
-rw-r--r--changelogs/unreleased/api-wiki-dot-slug.yml5
-rw-r--r--changelogs/unreleased/auto-devops-custom-domains.yml5
-rw-r--r--changelogs/unreleased/auto-devops-kubectl-1-11-6.yml5
-rw-r--r--changelogs/unreleased/avoid_es_loading_project_ci_status.yml5
-rw-r--r--changelogs/unreleased/backup_aws_sse-c.yml5
-rw-r--r--changelogs/unreleased/backup_restore_fix_issue_46891.yml5
-rw-r--r--changelogs/unreleased/bump-ingress-chart-112.yml5
-rw-r--r--changelogs/unreleased/bvl-fix-race-condition-creating-signature.yml5
-rw-r--r--changelogs/unreleased/ce-56153-error-tracking-counts.yml5
-rw-r--r--changelogs/unreleased/changelogs-readme.yml5
-rw-r--r--changelogs/unreleased/chore-update-js-regex.yml5
-rw-r--r--changelogs/unreleased/cleanup-leagcy-artifact-migration.yml5
-rw-r--r--changelogs/unreleased/cluster_application_version_updated.yml5
-rw-r--r--changelogs/unreleased/cluster_status_for_ugprading.yml5
-rw-r--r--changelogs/unreleased/container-repository-cleanup-api.yml5
-rw-r--r--changelogs/unreleased/custom-helm-chart-repo.yml5
-rw-r--r--changelogs/unreleased/delete-release-when-delete-tag.yml5
-rw-r--r--changelogs/unreleased/deploy-keys-ext.yml5
-rw-r--r--changelogs/unreleased/deprecated-force-reload.yml6
-rw-r--r--changelogs/unreleased/deprecated-migration-inheritance.yml5
-rw-r--r--changelogs/unreleased/diff-file-finder.yml5
-rw-r--r--changelogs/unreleased/diff-tree-collapse-directories.yml5
-rw-r--r--changelogs/unreleased/diff-tree-resizable.yml5
-rw-r--r--changelogs/unreleased/dm-copy-suggestion-as-gfm.yml5
-rw-r--r--changelogs/unreleased/dm-trim-discussion-truncated-line-first-chars.yml5
-rw-r--r--changelogs/unreleased/do-not-force-2fa.yml6
-rw-r--r--changelogs/unreleased/docs-push-mirror-GitLab-GitHub.yml5
-rw-r--r--changelogs/unreleased/expire-job-artifacts-worker.yml5
-rw-r--r--changelogs/unreleased/expose-group-id-on-home-panel.yml5
-rw-r--r--changelogs/unreleased/fast-destroy-uploads.yml5
-rw-r--r--changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml5
-rw-r--r--changelogs/unreleased/feature-users-search-results.yml5
-rw-r--r--changelogs/unreleased/features-document-graphicsmagick-source-installation.yml5
-rw-r--r--changelogs/unreleased/filter-merge-requests-by-target-branch.yml5
-rw-r--r--changelogs/unreleased/filter-note-parameters.yml5
-rw-r--r--changelogs/unreleased/fix-39759-new-project-icon-vertical-align.yml5
-rw-r--r--changelogs/unreleased/fix-403-page-is-rendered-but-404-is-the-response.yml5
-rw-r--r--changelogs/unreleased/fix-49388.yml5
-rw-r--r--changelogs/unreleased/fix-55956-oversized-dropdown-button-custom-notifications.yml5
-rw-r--r--changelogs/unreleased/fix-56558-move-primary-button.yml5
-rw-r--r--changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml5
-rw-r--r--changelogs/unreleased/fix-hidden-statistics.yml5
-rw-r--r--changelogs/unreleased/fix-ide-web-worker-relative-url.yml5
-rw-r--r--changelogs/unreleased/fix-missing-border.yml5
-rw-r--r--changelogs/unreleased/fix-new-merge-request-diff-headers-sticky-position.yml5
-rw-r--r--changelogs/unreleased/fix-pipeline-entity.yml5
-rw-r--r--changelogs/unreleased/fix-repo-settings-file-upload-error.yml5
-rw-r--r--changelogs/unreleased/fix-review-app-env-url.yml5
-rw-r--r--changelogs/unreleased/fix_-56347.yml5
-rw-r--r--changelogs/unreleased/fix_jira_integration_VCS1019.yml5
-rw-r--r--changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml5
-rw-r--r--changelogs/unreleased/force-redeploy-on-updated-secrets.yml5
-rw-r--r--changelogs/unreleased/gitaly-update-1-13-0.yml5
-rw-r--r--changelogs/unreleased/gitaly-update-1.18.0.yml5
-rw-r--r--changelogs/unreleased/gitaly-version-v1.29.0.yml5
-rw-r--r--changelogs/unreleased/gitlab-workhorse-update-8.1.0.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-clusters.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-email_rejection_mailer.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-instance_statistics.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-ci.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-commit.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-milestones.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-pages_domains.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-pipelines.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml5
-rw-r--r--changelogs/unreleased/gt-externalize-app-views-sent_notifications.yml5
-rw-r--r--changelogs/unreleased/gt-remove-unused-button-class.yml5
-rw-r--r--changelogs/unreleased/gt-rename-gray-theme-color-variables.yml5
-rw-r--r--changelogs/unreleased/gt-update-new-password-breadcrumb.yml5
-rw-r--r--changelogs/unreleased/gt-update-string-struture-for-group-runners.yml5
-rw-r--r--changelogs/unreleased/hnk-master-patch-61932.yml5
-rw-r--r--changelogs/unreleased/homepage-proj-descr-cutoff.yml5
-rw-r--r--changelogs/unreleased/import-go-to-project-cta.yml5
-rw-r--r--changelogs/unreleased/improve-performance-for-diverging-commit-counts.yml5
-rw-r--r--changelogs/unreleased/introduce-environment-search-endpoint.yml5
-rw-r--r--changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml6
-rw-r--r--changelogs/unreleased/issue_55744.yml5
-rw-r--r--changelogs/unreleased/issue_58547.yml5
-rw-r--r--changelogs/unreleased/jej-avoid-csrf-check-on-saml-failure.yml5
-rw-r--r--changelogs/unreleased/jej-feature-gates-can-be-set-by-group-path.yml5
-rw-r--r--changelogs/unreleased/jlenny-AddPagesTemplates.yml5
-rw-r--r--changelogs/unreleased/jlenny-NewAndroidTemplate.yml5
-rw-r--r--changelogs/unreleased/jprovazn-remove-redcarpet.yml5
-rw-r--r--changelogs/unreleased/k8s_new_deployment_labels.yml5
-rw-r--r--changelogs/unreleased/kinolaev-master-patch-87865.yml5
-rw-r--r--changelogs/unreleased/knative-list.yml5
-rw-r--r--changelogs/unreleased/knative-show-page.yml5
-rw-r--r--changelogs/unreleased/local-markdown-version-bkp3.yml5
-rw-r--r--changelogs/unreleased/mg-fix-bad-cluster-update-entrypoint.yml5
-rw-r--r--changelogs/unreleased/monospace-registry-tags.yml5
-rw-r--r--changelogs/unreleased/move-job-cancel-btn.yml5
-rw-r--r--changelogs/unreleased/move-permission-check-manual-actions-on-deployments.yml5
-rw-r--r--changelogs/unreleased/mr-file-tree-blob-truncate-improvements.yml5
-rw-r--r--changelogs/unreleased/mr-rebase-failing-tests.yml5
-rw-r--r--changelogs/unreleased/nfriend-update-job-detail-view-sidebar.yml5
-rw-r--r--changelogs/unreleased/nfriend-update-merge-request-widget-pipeline-block.yml6
-rw-r--r--changelogs/unreleased/nfriend-update-pipeline-detail-view.yml5
-rw-r--r--changelogs/unreleased/nfriend-update-pipeline-list-view.yml5
-rw-r--r--changelogs/unreleased/not-run-pipeline-on-empty-merge-request.yml5
-rw-r--r--changelogs/unreleased/notebook-multiple-outputs.yml5
-rw-r--r--changelogs/unreleased/notes-awards-double-tooltip-fix.yml5
-rw-r--r--changelogs/unreleased/only-counted-active-milestones-as-started.yml5
-rw-r--r--changelogs/unreleased/osw-enforces-project-removal-with-past-failed-attempts.yml5
-rw-r--r--changelogs/unreleased/osw-fix-bottom-expansion-diff-comment.yml5
-rw-r--r--changelogs/unreleased/osw-multi-line-suggestions-creation-strategy.yml5
-rw-r--r--changelogs/unreleased/osw-multi-line-suggestions-parsing.yml5
-rw-r--r--changelogs/unreleased/patch-38.yml5
-rw-r--r--changelogs/unreleased/pl-serialize-ac-parameters.yml5
-rw-r--r--changelogs/unreleased/pravi-gitlab-ce-update-recaptcha.yml5
-rw-r--r--changelogs/unreleased/profile-project-empty-state.yml5
-rw-r--r--changelogs/unreleased/raise-on-unfiltered-params.yml5
-rw-r--r--changelogs/unreleased/ravlen-fix-spaces-unicode.yml5
-rw-r--r--changelogs/unreleased/rd-update-last_activity_on-on-logins-and-browsing-activity-54947.yml5
-rw-r--r--changelogs/unreleased/recreate-all-diffs-on-import.yml5
-rw-r--r--changelogs/unreleased/refactor-56366-extract-resolve-discussion-button.yml5
-rw-r--r--changelogs/unreleased/refactor-56369-extract-jump-to-next-discussion-button.yml5
-rw-r--r--changelogs/unreleased/refactor-56370-extract-reply-placeholder-component.yml5
-rw-r--r--changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml5
-rw-r--r--changelogs/unreleased/remove-diff-coloring.yml5
-rw-r--r--changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml5
-rw-r--r--changelogs/unreleased/remove-second-primary-button-on-wiki-edit.yml5
-rw-r--r--changelogs/unreleased/rs-admin-user-case-insensitive.yml5
-rw-r--r--changelogs/unreleased/search-title.yml5
-rw-r--r--changelogs/unreleased/security-22076-sanitize-url-in-names.yml6
-rw-r--r--changelogs/unreleased/security-2770-verify-bundle-import-files.yml5
-rw-r--r--changelogs/unreleased/security-55320-stored-xss-in-user-status.yml5
-rw-r--r--changelogs/unreleased/security-stored-xss-via-katex.yml5
-rw-r--r--changelogs/unreleased/sh-cache-root-ref-asymetrically.yml5
-rw-r--r--changelogs/unreleased/sh-clear-pipeline-status-cache-upon-destroy.yml5
-rw-r--r--changelogs/unreleased/sh-disable-nil-user-id-identity-validation.yml5
-rw-r--r--changelogs/unreleased/sh-encode-content-disposition.yml5
-rw-r--r--changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml5
-rw-r--r--changelogs/unreleased/sh-fix-bitbucket-server-error-handling.yml5
-rw-r--r--changelogs/unreleased/sh-fix-board-user-assigns.yml5
-rw-r--r--changelogs/unreleased/sh-fix-import-redirect-vulnerability.yml5
-rw-r--r--changelogs/unreleased/sh-fix-issue-59065.yml5
-rw-r--r--changelogs/unreleased/sh-fix-pages-zip-constant.yml5
-rw-r--r--changelogs/unreleased/sh-fix-snippet-uploads-path-lookup.yml5
-rw-r--r--changelogs/unreleased/sh-fix-upload-snippets-with-relative-url-root.yml5
-rw-r--r--changelogs/unreleased/sh-import-source-branch-github-forks.yml5
-rw-r--r--changelogs/unreleased/sh-include-project-path-for-internal-api.yml5
-rw-r--r--changelogs/unreleased/sh-issue-53419-fix.yml5
-rw-r--r--changelogs/unreleased/sh-log-rails-queue-duration.yml5
-rw-r--r--changelogs/unreleased/sh-optimize-projects-api.yml5
-rw-r--r--changelogs/unreleased/sh-preload-associations-for-group-api.yml5
-rw-r--r--changelogs/unreleased/sh-reject-info-refs-head-requests.yml5
-rw-r--r--changelogs/unreleased/sh-skip-sti-tables-reltuples.yml5
-rw-r--r--changelogs/unreleased/shared_with_group_path.yml5
-rw-r--r--changelogs/unreleased/support-chunking-in-client.yml5
-rw-r--r--changelogs/unreleased/support-only-changes-on-mr-pipelines.yml5
-rw-r--r--changelogs/unreleased/syntax-highlighting-again.yml5
-rw-r--r--changelogs/unreleased/test-permissions.yml5
-rw-r--r--changelogs/unreleased/tooltips-to-top.yml5
-rw-r--r--changelogs/unreleased/tpresa-add-highest-role-to-user.yml5
-rw-r--r--changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml5
-rw-r--r--changelogs/unreleased/update-gitaly.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-1-45.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-3-0.yml5
-rw-r--r--changelogs/unreleased/update-gitlab-styles.yml5
-rw-r--r--changelogs/unreleased/update-pages-config-only-when-changed.yml5
-rw-r--r--changelogs/unreleased/update-pages-extensionless-urls.yml5
-rw-r--r--changelogs/unreleased/update-rack-oauth2.yml5
-rw-r--r--changelogs/unreleased/update-sidekiq-cron.yml6
-rw-r--r--changelogs/unreleased/update-smooshpack.yml5
-rw-r--r--changelogs/unreleased/update-spriteicon-from-icon-on-profile.yml5
-rw-r--r--changelogs/unreleased/update-ui-admin-appearance.yml5
-rw-r--r--changelogs/unreleased/update-workhorse-8-2-0.yml5
-rw-r--r--changelogs/unreleased/use-deployment-relation-to-fetch-environment-ce.yml5
-rw-r--r--changelogs/unreleased/use-only-all-pipelines.yml5
-rw-r--r--changelogs/unreleased/use_upgrade_install_for_helm_apps.yml5
-rw-r--r--changelogs/unreleased/web-ide-commit-header-icon-alignment-fix.yml5
-rw-r--r--changelogs/unreleased/winh-add-list-dropdown-height.yml5
-rw-r--r--changelogs/unreleased/winh-toggle-comment-draft.yml5
-rw-r--r--changelogs/unreleased/workhorse-8-3-0.yml5
-rw-r--r--changelogs/unreleased/yoginth-avatar-on-settings-sidebar.yml5
-rw-r--r--changelogs/unreleased/zj-feature-gate-set-project-path.yml5
-rw-r--r--config/application.rb4
-rw-r--r--config/gitlab.yml.example71
-rw-r--r--config/initializers/1_settings.rb21
-rw-r--r--config/initializers/console_message.rb2
-rw-r--r--config/initializers/fog_core_patch.rb52
-rw-r--r--config/initializers/graphql.rb4
-rw-r--r--config/initializers/rspec_profiling.rb50
-rw-r--r--config/initializers/sentry.rb15
-rw-r--r--config/initializers/sidekiq.rb9
-rw-r--r--config/initializers/trusted_proxies.rb6
-rw-r--r--config/karma.config.js68
-rw-r--r--config/locales/en.yml4
-rw-r--r--config/routes.rb2
-rw-r--r--config/routes/admin.rb4
-rw-r--r--config/routes/git_http.rb2
-rw-r--r--config/routes/group.rb4
-rw-r--r--config/routes/project.rb3
-rw-r--r--config/sidekiq_queues.yml3
-rw-r--r--config/webpack.config.js71
-rw-r--r--danger/commit_messages/Dangerfile243
-rw-r--r--danger/documentation/Dangerfile28
-rw-r--r--danger/roulette/Dangerfile4
-rw-r--r--danger/single_codebase/Dangerfile56
-rw-r--r--db/fixtures/development/02_settings.rb (renamed from db/fixtures/development/03_settings.rb)0
-rw-r--r--db/fixtures/development/03_project.rb137
-rw-r--r--db/fixtures/development/04_labels.rb49
-rw-r--r--db/fixtures/development/04_project.rb137
-rw-r--r--db/fixtures/development/09_issues.rb6
-rw-r--r--db/fixtures/development/10_merge_requests.rb6
-rw-r--r--db/fixtures/development/22_labeled_issues_seed.rb103
-rw-r--r--db/fixtures/development/25_api_personal_access_token.rb15
-rw-r--r--db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb9
-rw-r--r--db/migrate/20180314145917_add_header_and_footer_banners_to_appearances_table.rb18
-rw-r--r--db/migrate/20181205171941_create_project_daily_statistics.rb18
-rw-r--r--db/migrate/20190206193120_add_index_to_tags.rb18
-rw-r--r--db/migrate/20190215154930_add_merge_pipelines_enabled_to_ci_cd_settings.rb11
-rw-r--r--db/migrate/20190218134158_add_masked_to_ci_variables.rb21
-rw-r--r--db/migrate/20190218134209_add_masked_to_ci_group_variables.rb21
-rw-r--r--db/migrate/20190220142344_add_email_header_and_footer_enabled_flag_to_appearances_table.rb17
-rw-r--r--db/migrate/20190220150130_add_extra_shas_to_ci_pipelines.rb12
-rw-r--r--db/migrate/20190225152525_add_auto_dev_ops_enabled_to_namespaces.rb9
-rw-r--r--db/migrate/20190225160300_steal_encrypt_runners_tokens.rb19
-rw-r--r--db/migrate/20190225160301_add_runner_tokens_indexes.rb24
-rw-r--r--db/migrate/20190228192410_add_multi_line_attributes_to_suggestion.rb19
-rw-r--r--db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb10
-rw-r--r--db/migrate/20190315191339_create_merge_request_assignees_table.rb22
-rw-r--r--db/post_migrate/20181101091005_steal_digest_column.rb17
-rw-r--r--db/post_migrate/20181101091124_remove_token_from_personal_access_tokens.rb11
-rw-r--r--db/post_migrate/20190301081611_migrate_project_migrate_sidekiq_queue.rb17
-rw-r--r--db/post_migrate/20190322132835_schedule_populate_merge_request_assignees_table.rb23
-rw-r--r--db/schema.rb44
-rw-r--r--doc/README.md5
-rw-r--r--doc/administration/auth/authentiq.md2
-rw-r--r--doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md4
-rw-r--r--doc/administration/auth/ldap.md61
-rw-r--r--doc/administration/auth/okta.md11
-rw-r--r--doc/administration/build_artifacts.md4
-rw-r--r--doc/administration/compliance.md10
-rw-r--r--doc/administration/container_registry.md1
-rw-r--r--doc/administration/custom_hooks.md20
-rw-r--r--doc/administration/git_protocol.md11
-rw-r--r--doc/administration/gitaly/index.md107
-rw-r--r--doc/administration/high_availability/gitlab.md8
-rw-r--r--doc/administration/high_availability/nfs.md29
-rw-r--r--doc/administration/high_availability/redis.md11
-rw-r--r--doc/administration/high_availability/redis_source.md4
-rw-r--r--doc/administration/img/custom_hooks_error_msg.pngbin44892 -> 80442 bytes
-rw-r--r--doc/administration/incoming_email.md385
-rw-r--r--doc/administration/index.md8
-rw-r--r--doc/administration/job_artifacts.md4
-rw-r--r--doc/administration/merge_request_diffs.md33
-rw-r--r--doc/administration/monitoring/index.md2
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md2
-rw-r--r--doc/administration/monitoring/performance/introduction.md4
-rw-r--r--doc/administration/monitoring/performance/performance_bar.md6
-rw-r--r--doc/administration/monitoring/performance/prometheus.md4
-rw-r--r--doc/administration/monitoring/prometheus/gitlab_metrics.md5
-rw-r--r--doc/administration/monitoring/prometheus/index.md3
-rw-r--r--doc/administration/operations.md4
-rw-r--r--doc/administration/operations/index.md24
-rw-r--r--doc/administration/operations/sidekiq_memory_killer.md5
-rw-r--r--doc/administration/operations/speed_up_ssh.md4
-rw-r--r--doc/administration/pages/index.md11
-rw-r--r--doc/administration/pages/source.md15
-rw-r--r--doc/administration/polling.md18
-rw-r--r--doc/administration/raketasks/maintenance.md1
-rw-r--r--doc/administration/raketasks/storage.md48
-rw-r--r--doc/administration/raketasks/uploads/migrate.md2
-rw-r--r--doc/administration/repository_checks.md2
-rw-r--r--doc/administration/repository_storage_paths.md2
-rw-r--r--doc/administration/repository_storage_types.md113
-rw-r--r--doc/administration/repository_storages.md4
-rw-r--r--doc/administration/uploads.md5
-rw-r--r--doc/administration/user_settings.md35
-rw-r--r--doc/api/README.md4
-rw-r--r--doc/api/boards.md6
-rw-r--r--doc/api/build_triggers.md6
-rw-r--r--doc/api/builds.md4
-rw-r--r--doc/api/commits.md3
-rw-r--r--doc/api/group_milestones.md3
-rw-r--r--doc/api/issues.md7
-rw-r--r--doc/api/jobs.md71
-rw-r--r--doc/api/merge_requests.md34
-rw-r--r--doc/api/milestones.md17
-rw-r--r--doc/api/namespaces.md12
-rw-r--r--doc/api/pipeline_schedules.md8
-rw-r--r--doc/api/project_snippets.md1
-rw-r--r--doc/api/project_statistics.md49
-rw-r--r--doc/api/projects.md2
-rw-r--r--doc/api/releases/links.md10
-rw-r--r--doc/api/repository_files.md1
-rw-r--r--doc/api/runners.md40
-rw-r--r--doc/api/search.md71
-rw-r--r--doc/api/services.md44
-rw-r--r--doc/api/settings.md2
-rw-r--r--doc/api/suggestions.md2
-rw-r--r--doc/api/users.md3
-rw-r--r--doc/articles/artifactory_and_gitlab/index.md4
-rw-r--r--doc/articles/how_to_configure_ldap_gitlab_ce/index.md4
-rw-r--r--doc/articles/how_to_install_git/index.md4
-rw-r--r--doc/articles/index.md4
-rw-r--r--doc/articles/laravel_with_gitlab_and_envoy/index.md4
-rw-r--r--doc/articles/numerous_undo_possibilities_in_git/index.md4
-rw-r--r--doc/articles/runner_autoscale_aws/index.md4
-rw-r--r--doc/ci/README.md181
-rw-r--r--doc/ci/autodeploy/index.md4
-rw-r--r--doc/ci/autodeploy/quick_start_guide.md4
-rw-r--r--doc/ci/build_artifacts/README.md4
-rw-r--r--doc/ci/caching/index.md6
-rw-r--r--doc/ci/chatops/README.md61
-rw-r--r--doc/ci/chatops/img/gitlab-chatops-icon-small.pngbin0 -> 2922 bytes
-rw-r--r--doc/ci/chatops/img/gitlab-chatops-icon.pngbin0 -> 12308 bytes
-rw-r--r--doc/ci/docker/README.md8
-rw-r--r--doc/ci/docker/using_docker_build.md3
-rw-r--r--doc/ci/docker/using_docker_images.md4
-rw-r--r--doc/ci/environments.md16
-rw-r--r--doc/ci/examples/README.md154
-rw-r--r--doc/ci/examples/artifactory_and_gitlab/index.md10
-rw-r--r--doc/ci/examples/container_scanning.md4
-rw-r--r--doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md4
-rw-r--r--doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md23
-rw-r--r--doc/ci/examples/end_to_end_testing_webdriverio/index.md6
-rw-r--r--doc/ci/examples/laravel_with_gitlab_and_envoy/index.md11
-rw-r--r--doc/ci/examples/sast_docker.md6
-rw-r--r--doc/ci/examples/test-and-deploy-python-application-to-heroku.md4
-rw-r--r--doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md6
-rw-r--r--doc/ci/examples/test-scala-application.md11
-rw-r--r--doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md8
-rw-r--r--doc/ci/img/cicd_pipeline_infograph.pngbin32493 -> 0 bytes
-rw-r--r--doc/ci/img/pipelines-goal.pngbin15284 -> 0 bytes
-rw-r--r--doc/ci/img/types-of-pipelines.pngbin12268 -> 0 bytes
-rw-r--r--doc/ci/introduction/img/gitlab_workflow_example.pngbin0 -> 55394 bytes
-rwxr-xr-xdoc/ci/introduction/img/job_running.pngbin0 -> 237781 bytes
-rwxr-xr-xdoc/ci/introduction/img/pipeline_status.pngbin0 -> 54243 bytes
-rwxr-xr-xdoc/ci/introduction/img/rollback.pngbin0 -> 41693 bytes
-rw-r--r--doc/ci/introduction/index.md193
-rw-r--r--doc/ci/junit_test_reports.md6
-rw-r--r--doc/ci/merge_request_pipelines/index.md58
-rw-r--r--doc/ci/permissions/README.md6
-rw-r--r--doc/ci/pipelines.md414
-rw-r--r--doc/ci/review_apps/index.md4
-rw-r--r--doc/ci/services/README.md2
-rw-r--r--doc/ci/services/postgres.md2
-rw-r--r--doc/ci/ssh_keys/README.md2
-rw-r--r--doc/ci/triggers/README.md13
-rw-r--r--doc/ci/variables/README.md39
-rw-r--r--doc/ci/variables/where_variables_can_be_used.md4
-rw-r--r--doc/ci/yaml/README.md576
-rw-r--r--doc/container_registry/README.md4
-rw-r--r--doc/container_registry/troubleshooting.md4
-rw-r--r--doc/customization/system_header_and_footer_messages.md22
-rw-r--r--doc/customization/system_header_and_footer_messages/appearance.pngbin0 -> 124214 bytes
-rw-r--r--doc/customization/system_header_and_footer_messages/custom_header_footer.pngbin0 -> 484001 bytes
-rw-r--r--doc/customization/system_header_and_footer_messages/sign_up_custom_header_and_footer.pngbin0 -> 360832 bytes
-rw-r--r--doc/customization/welcome_message.md4
-rw-r--r--doc/development/README.md1
-rw-r--r--doc/development/api_graphql_styleguide.md24
-rw-r--r--doc/development/architecture.md20
-rw-r--r--doc/development/automatic_ce_ee_merge.md20
-rw-r--r--doc/development/changelog.md24
-rw-r--r--doc/development/code_review.md22
-rw-r--r--doc/development/contributing/index.md3
-rw-r--r--doc/development/contributing/issue_workflow.md18
-rw-r--r--doc/development/contributing/merge_request_workflow.md326
-rw-r--r--doc/development/distributed_tracing.md182
-rw-r--r--doc/development/documentation/feature-change-workflow.md189
-rw-r--r--doc/development/documentation/improvement-workflow.md38
-rw-r--r--doc/development/documentation/index.md8
-rw-r--r--doc/development/documentation/site_architecture/global_nav.md2
-rw-r--r--doc/development/documentation/site_architecture/index.md2
-rw-r--r--doc/development/documentation/structure.md4
-rw-r--r--doc/development/documentation/styleguide.md29
-rw-r--r--doc/development/documentation/workflow.md6
-rw-r--r--doc/development/ee_features.md98
-rw-r--r--doc/development/fe_guide/architecture.md7
-rw-r--r--doc/development/fe_guide/development_process.md8
-rw-r--r--doc/development/fe_guide/droplab/plugins/filter.md2
-rw-r--r--doc/development/fe_guide/droplab/plugins/input_setter.md2
-rw-r--r--doc/development/fe_guide/event_tracking.md91
-rw-r--r--doc/development/fe_guide/graphql.md30
-rw-r--r--doc/development/fe_guide/index.md35
-rw-r--r--doc/development/fe_guide/performance.md11
-rw-r--r--doc/development/fe_guide/principles.md15
-rw-r--r--doc/development/fe_guide/style_guide_js.md22
-rw-r--r--doc/development/fe_guide/testing.md6
-rw-r--r--doc/development/fe_guide/vue.md12
-rw-r--r--doc/development/fe_guide/vuex.md4
-rw-r--r--doc/development/feature_flags.md5
-rw-r--r--doc/development/git_object_deduplication.md261
-rw-r--r--doc/development/gitaly.md128
-rw-r--r--doc/development/go_guide/index.md35
-rw-r--r--doc/development/i18n/merging_translations.md3
-rw-r--r--doc/development/i18n/proofreader.md1
-rw-r--r--doc/development/i18n_guide.md6
-rw-r--r--doc/development/img/distributed_tracing_jaeger_ui.pngbin0 -> 1032713 bytes
-rw-r--r--doc/development/img/distributed_tracing_performance_bar.pngbin0 -> 108809 bytes
-rw-r--r--doc/development/import_export.md2
-rw-r--r--doc/development/logging.md30
-rw-r--r--doc/development/new_fe_guide/development/accessibility.md3
-rw-r--r--doc/development/new_fe_guide/development/design_patterns.md3
-rw-r--r--doc/development/new_fe_guide/development/index.md12
-rw-r--r--doc/development/new_fe_guide/development/network_requests.md3
-rw-r--r--doc/development/new_fe_guide/development/security.md14
-rw-r--r--doc/development/new_fe_guide/event_tracking.md74
-rw-r--r--doc/development/new_fe_guide/index.md11
-rw-r--r--doc/development/new_fe_guide/initiatives.md3
-rw-r--r--doc/development/new_fe_guide/principles.md35
-rw-r--r--doc/development/new_fe_guide/style/html.md32
-rw-r--r--doc/development/new_fe_guide/style/javascript.md372
-rw-r--r--doc/development/new_fe_guide/tips.md16
-rw-r--r--doc/development/ordering_table_columns.md2
-rw-r--r--doc/development/performance.md16
-rw-r--r--doc/development/policies.md4
-rw-r--r--doc/development/polling.md1
-rw-r--r--doc/development/profiling.md10
-rw-r--r--doc/development/rake_tasks.md1
-rw-r--r--doc/development/rolling_out_changes_using_feature_flags.md37
-rw-r--r--doc/development/testing.md6
-rw-r--r--doc/development/testing_guide/best_practices.md24
-rw-r--r--doc/development/testing_guide/ci.md4
-rw-r--r--doc/development/testing_guide/end_to_end_tests.md77
-rw-r--r--doc/development/testing_guide/frontend_testing.md104
-rw-r--r--doc/development/testing_guide/img/qa_on_merge_requests_cicd_architecture.pngbin0 -> 64862 bytes
-rw-r--r--doc/development/testing_guide/review_apps.md28
-rw-r--r--doc/development/testing_guide/smoke.md14
-rw-r--r--doc/development/testing_guide/testing_levels.md10
-rw-r--r--doc/development/ux_guide/users.md4
-rw-r--r--doc/gitlab-basics/add-merge-request.md2
-rw-r--r--doc/gitlab-basics/create-group.md3
-rw-r--r--doc/gitlab-basics/create-issue.md3
-rw-r--r--doc/gitlab-basics/create-project.md8
-rw-r--r--doc/gitlab-basics/create-your-ssh-keys.md42
-rw-r--r--doc/gitlab-basics/img/profile_settings.pngbin2842 -> 0 bytes
-rw-r--r--doc/gitlab-basics/img/profile_settings_ssh_keys.pngbin16531 -> 0 bytes
-rw-r--r--doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.pngbin13436 -> 0 bytes
-rw-r--r--doc/gitlab-basics/img/profile_settings_ssh_keys_title.pngbin1867 -> 0 bytes
-rw-r--r--doc/hooks/custom_hooks.md4
-rw-r--r--doc/incoming_email/README.md4
-rw-r--r--doc/incoming_email/postfix.md4
-rw-r--r--doc/install/aws/index.md16
-rw-r--r--doc/install/azure/index.md20
-rw-r--r--doc/install/docker.md2
-rw-r--r--doc/install/installation.md6
-rw-r--r--doc/install/kubernetes/preparation/eks.md1
-rw-r--r--doc/install/kubernetes/preparation/tiller.md18
-rw-r--r--doc/install/openshift_and_gitlab/index.md2
-rw-r--r--doc/install/requirements.md7
-rw-r--r--doc/integration/README.md3
-rw-r--r--doc/integration/auth0.md14
-rw-r--r--doc/integration/chat_commands.md4
-rw-r--r--doc/integration/crowd.md4
-rw-r--r--doc/integration/external-issue-tracker.md18
-rw-r--r--doc/integration/facebook.md2
-rw-r--r--doc/integration/jira.md4
-rw-r--r--doc/integration/ldap.md4
-rw-r--r--doc/integration/omniauth.md4
-rw-r--r--doc/integration/slack.md4
-rw-r--r--doc/integration/slash_commands.md3
-rw-r--r--doc/logs/logs.md4
-rw-r--r--doc/markdown/markdown.md6
-rw-r--r--doc/monitoring/health_check.md4
-rw-r--r--doc/monitoring/performance/gitlab_configuration.md4
-rw-r--r--doc/monitoring/performance/grafana_configuration.md4
-rw-r--r--doc/monitoring/performance/influxdb_configuration.md4
-rw-r--r--doc/monitoring/performance/influxdb_schema.md4
-rw-r--r--doc/monitoring/performance/introduction.md4
-rw-r--r--doc/operations/README.md4
-rw-r--r--doc/operations/cleaning_up_redis_sessions.md6
-rw-r--r--doc/operations/moving_repositories.md6
-rw-r--r--doc/operations/sidekiq_memory_killer.md6
-rw-r--r--doc/operations/unicorn.md6
-rw-r--r--doc/pages/README.md6
-rw-r--r--doc/pages/administration.md6
-rw-r--r--doc/pages/getting_started_part_one.md4
-rw-r--r--doc/pages/getting_started_part_three.md4
-rw-r--r--doc/pages/getting_started_part_two.md4
-rw-r--r--doc/permissions/permissions.md4
-rw-r--r--doc/profile/README.md4
-rw-r--r--doc/profile/preferences.md4
-rw-r--r--doc/profile/two_factor_authentication.md4
-rw-r--r--doc/project_services/bamboo.md4
-rw-r--r--doc/project_services/bugzilla.md4
-rw-r--r--doc/project_services/emails_on_push.md4
-rw-r--r--doc/project_services/irker.md4
-rw-r--r--doc/project_services/jira.md4
-rw-r--r--doc/project_services/kubernetes.md4
-rw-r--r--doc/project_services/mattermost.md4
-rw-r--r--doc/project_services/mattermost_slash_commands.md4
-rw-r--r--doc/project_services/project_services.md4
-rw-r--r--doc/project_services/redmine.md4
-rw-r--r--doc/project_services/services_templates.md4
-rw-r--r--doc/project_services/slack.md4
-rw-r--r--doc/project_services/slack_slash_commands.md4
-rw-r--r--doc/public_access/public_access.md2
-rw-r--r--doc/raketasks/backup_restore.md31
-rw-r--r--doc/raketasks/check.md6
-rw-r--r--doc/raketasks/maintenance.md6
-rw-r--r--doc/security/img/ssh_keys_restricted_key_icon.pngbin0 -> 4887 bytes
-rw-r--r--doc/security/ssh_keys_restrictions.md8
-rw-r--r--doc/system_hooks/system_hooks.md6
-rw-r--r--doc/topics/authentication/index.md4
-rw-r--r--doc/topics/autodevops/index.md99
-rw-r--r--doc/topics/autodevops/quick_start_guide.md14
-rw-r--r--doc/university/README.md7
-rw-r--r--doc/university/high-availability/aws/README.md10
-rw-r--r--doc/university/training/end-user/README.md2
-rw-r--r--doc/university/training/topics/stash.md2
-rw-r--r--doc/update/mysql_to_postgresql.md4
-rw-r--r--doc/update/restore_after_failure.md2
-rw-r--r--doc/update/upgrading_from_source.md4
-rw-r--r--doc/user/account/security.md4
-rw-r--r--doc/user/account/two_factor_authentication.md4
-rw-r--r--doc/user/admin_area/index.md29
-rw-r--r--doc/user/admin_area/monitoring/health_check.md24
-rw-r--r--doc/user/admin_area/settings/continuous_integration.md10
-rw-r--r--doc/user/discussions/img/reply_to_comment.gifbin0 -> 508115 bytes
-rw-r--r--doc/user/discussions/img/reply_to_comment_button.pngbin0 -> 17224 bytes
-rw-r--r--doc/user/discussions/index.md87
-rw-r--r--doc/user/gitlab_com/index.md12
-rw-r--r--doc/user/group/clusters/index.md30
-rw-r--r--doc/user/group/index.md41
-rw-r--r--doc/user/group/subgroups/index.md12
-rw-r--r--doc/user/index.md64
-rw-r--r--doc/user/instance_statistics/convdev.md23
-rw-r--r--doc/user/markdown.md15
-rw-r--r--doc/user/permissions.md19
-rw-r--r--doc/user/profile/account/index.md3
-rw-r--r--doc/user/profile/account/two_factor_authentication.md3
-rw-r--r--doc/user/profile/active_sessions.md8
-rw-r--r--doc/user/profile/img/active_sessions_list.pngbin22266 -> 19360 bytes
-rw-r--r--doc/user/profile/img/personal_access_tokens.pngbin18553 -> 0 bytes
-rw-r--r--doc/user/profile/index.md6
-rw-r--r--doc/user/profile/personal_access_tokens.md18
-rw-r--r--doc/user/profile/preferences.md14
-rw-r--r--doc/user/project/badges.md6
-rw-r--r--doc/user/project/builds/artifacts.md4
-rw-r--r--doc/user/project/bulk_editing.md1
-rw-r--r--doc/user/project/clusters/index.md250
-rw-r--r--doc/user/project/clusters/runbooks/index.md8
-rw-r--r--doc/user/project/clusters/serverless/img/app-domain.pngbin209263 -> 0 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/dns-entry.pngbin19583 -> 66116 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/install-knative.pngbin13003 -> 86225 bytes
-rw-r--r--doc/user/project/clusters/serverless/img/serverless-page.pngbin62369 -> 191568 bytes
-rw-r--r--doc/user/project/clusters/serverless/index.md150
-rw-r--r--doc/user/project/container_registry.md6
-rw-r--r--doc/user/project/cycle_analytics.md38
-rw-r--r--doc/user/project/gpg_signed_commits/index.md4
-rw-r--r--doc/user/project/img/cycle_analytics_landing_page.pngbin42114 -> 184131 bytes
-rw-r--r--doc/user/project/img/issue_boards_multiple.pngbin22623 -> 68373 bytes
-rw-r--r--doc/user/project/import/bitbucket_server.md12
-rw-r--r--doc/user/project/import/fogbugz.md2
-rw-r--r--doc/user/project/import/github.md2
-rw-r--r--doc/user/project/import/svn.md2
-rw-r--r--doc/user/project/index.md54
-rw-r--r--doc/user/project/integrations/img/issue_configuration.pngbin11882 -> 0 bytes
-rw-r--r--doc/user/project/integrations/irker.md8
-rw-r--r--doc/user/project/integrations/jira.md2
-rw-r--r--doc/user/project/integrations/kubernetes.md4
-rw-r--r--doc/user/project/integrations/mattermost_slash_commands.md2
-rw-r--r--doc/user/project/integrations/project_services.md1
-rw-r--r--doc/user/project/integrations/prometheus.md4
-rw-r--r--doc/user/project/integrations/prometheus_library/metrics.md4
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx_ingress.md2
-rw-r--r--doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md2
-rw-r--r--doc/user/project/integrations/redmine.md10
-rw-r--r--doc/user/project/integrations/webhooks.md25
-rw-r--r--doc/user/project/integrations/youtrack.md38
-rw-r--r--doc/user/project/issue_board.md30
-rw-r--r--doc/user/project/issues/automatic_issue_closing.md1
-rw-r--r--doc/user/project/issues/confidential_issues.md7
-rw-r--r--doc/user/project/issues/create_new_issue.md4
-rw-r--r--doc/user/project/issues/crosslinking_issues.md9
-rw-r--r--doc/user/project/issues/due_dates.md8
-rw-r--r--doc/user/project/issues/index.md6
-rw-r--r--doc/user/project/issues/issues_functionalities.md18
-rw-r--r--doc/user/project/merge_requests.md4
-rw-r--r--doc/user/project/merge_requests/img/filter_wip_merge_requests.pngbin6285 -> 28572 bytes
-rw-r--r--doc/user/project/merge_requests/img/wip_blocked_accept_button.pngbin4152 -> 7141 bytes
-rw-r--r--doc/user/project/merge_requests/img/wip_mark_as_wip.pngbin7961 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/img/wip_unmark_as_wip.pngbin8424 -> 0 bytes
-rw-r--r--doc/user/project/merge_requests/index.md2
-rw-r--r--doc/user/project/merge_requests/maintainer_access.md4
-rw-r--r--doc/user/project/merge_requests/merge_request_discussion_resolution.md4
-rw-r--r--doc/user/project/merge_requests/merge_when_build_succeeds.md8
-rw-r--r--doc/user/project/merge_requests/merge_when_pipeline_succeeds.md2
-rw-r--r--doc/user/project/merge_requests/versions.md1
-rw-r--r--doc/user/project/merge_requests/work_in_progress_merge_requests.md57
-rw-r--r--doc/user/project/milestones/index.md2
-rw-r--r--doc/user/project/new_ci_build_permissions_model.md6
-rw-r--r--doc/user/project/operations/error_tracking.md10
-rw-r--r--doc/user/project/pages/getting_started_part_four.md30
-rw-r--r--doc/user/project/pages/getting_started_part_one.md44
-rw-r--r--doc/user/project/pages/getting_started_part_three.md36
-rw-r--r--doc/user/project/pages/getting_started_part_two.md131
-rw-r--r--doc/user/project/pages/img/pages_project_templates_11-8.pngbin0 -> 69702 bytes
-rw-r--r--doc/user/project/pages/index.md67
-rw-r--r--doc/user/project/pages/introduction.md13
-rw-r--r--doc/user/project/pipelines/job_artifacts.md13
-rw-r--r--doc/user/project/pipelines/schedules.md5
-rw-r--r--doc/user/project/pipelines/settings.md4
-rw-r--r--doc/user/project/protected_branches.md23
-rw-r--r--doc/user/project/releases.md4
-rw-r--r--doc/user/project/repository/branches/index.md2
-rw-r--r--doc/user/project/repository/index.md18
-rw-r--r--doc/user/project/slash_commands.md4
-rw-r--r--doc/user/project/web_ide/index.md20
-rw-r--r--doc/user/project/wiki/index.md15
-rw-r--r--doc/user/reserved_names.md1
-rw-r--r--doc/user/search/index.md2
-rw-r--r--doc/user/snippets.md2
-rw-r--r--doc/web_hooks/web_hooks.md4
-rw-r--r--doc/workflow/add-user/add-user.md4
-rw-r--r--doc/workflow/authorization_for_merge_requests.md4
-rw-r--r--doc/workflow/award_emoji.md4
-rw-r--r--doc/workflow/cherry_pick_changes.md6
-rw-r--r--doc/workflow/groups.md3
-rw-r--r--doc/workflow/importing/README.md4
-rw-r--r--doc/workflow/importing/import_projects_from_bitbucket.md4
-rw-r--r--doc/workflow/importing/import_projects_from_fogbugz.md4
-rw-r--r--doc/workflow/importing/import_projects_from_gitea.md4
-rw-r--r--doc/workflow/importing/import_projects_from_github.md4
-rw-r--r--doc/workflow/importing/import_projects_from_gitlab_com.md4
-rw-r--r--doc/workflow/importing/migrating_from_svn.md4
-rw-r--r--doc/workflow/labels.md4
-rw-r--r--doc/workflow/merge_requests.md4
-rw-r--r--doc/workflow/merge_when_build_succeeds.md4
-rw-r--r--doc/workflow/milestones.md4
-rw-r--r--doc/workflow/project_features.md4
-rw-r--r--doc/workflow/protected_branches.md4
-rw-r--r--doc/workflow/repository_mirroring.md8
-rw-r--r--doc/workflow/revert_changes.md4
-rw-r--r--doc/workflow/share_projects_with_other_groups.md4
-rw-r--r--doc/workflow/share_with_group.md4
-rw-r--r--doc/workflow/shortcuts.md2
-rw-r--r--doc/workflow/time_tracking.md2
-rw-r--r--doc/workflow/web_editor.md4
-rw-r--r--doc/workflow/wip_merge_requests.md4
-rw-r--r--jest.config.js11
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/commits.rb11
-rw-r--r--lib/api/discussions.rb4
-rw-r--r--lib/api/entities.rb104
-rw-r--r--lib/api/environments.rb8
-rw-r--r--lib/api/groups.rb35
-rw-r--r--lib/api/helpers.rb21
-rw-r--r--lib/api/helpers/custom_validators.rb13
-rw-r--r--lib/api/helpers/discussions_helpers.rb13
-rw-r--r--lib/api/helpers/graphql_helpers.rb22
-rw-r--r--lib/api/helpers/internal_helpers.rb16
-rw-r--r--lib/api/helpers/issues_helpers.rb23
-rw-r--r--lib/api/helpers/notes_helpers.rb20
-rw-r--r--lib/api/helpers/pagination.rb77
-rw-r--r--lib/api/helpers/projects_helpers.rb39
-rw-r--r--lib/api/helpers/resource_label_events_helpers.rb13
-rw-r--r--lib/api/helpers/runner.rb2
-rw-r--r--lib/api/helpers/search_helpers.rb22
-rw-r--r--lib/api/helpers/services_helpers.rb721
-rw-r--r--lib/api/internal.rb17
-rw-r--r--lib/api/issues.rb34
-rw-r--r--lib/api/job_artifacts.rb16
-rw-r--r--lib/api/merge_requests.rb42
-rw-r--r--lib/api/milestone_responses.rb2
-rw-r--r--lib/api/notes.rb4
-rw-r--r--lib/api/project_milestones.rb15
-rw-r--r--lib/api/project_statistics.rb23
-rw-r--r--lib/api/project_templates.rb5
-rw-r--r--lib/api/projects.rb98
-rw-r--r--lib/api/protected_branches.rb24
-rw-r--r--lib/api/release/links.rb2
-rw-r--r--lib/api/repositories.rb18
-rw-r--r--lib/api/resource_label_events.rb4
-rw-r--r--lib/api/runners.rb6
-rw-r--r--lib/api/search.rb38
-rw-r--r--lib/api/services.rb677
-rw-r--r--lib/api/settings.rb43
-rw-r--r--lib/api/snippets.rb25
-rw-r--r--lib/api/todos.rb32
-rw-r--r--lib/api/triggers.rb2
-rw-r--r--lib/api/users.rb10
-rw-r--r--lib/api/validations/types/labels_list.rb24
-rw-r--r--lib/api/variables.rb18
-rw-r--r--lib/api/version.rb18
-rw-r--r--lib/backup/database.rb3
-rw-r--r--lib/backup/files.rb4
-rw-r--r--lib/backup/helper.rb8
-rw-r--r--lib/backup/manager.rb6
-rw-r--r--lib/backup/uploads.rb2
-rw-r--r--lib/banzai/filter/abstract_reference_filter.rb11
-rw-r--r--lib/banzai/filter/merge_request_reference_filter.rb12
-rw-r--r--lib/banzai/filter/output_safety.rb11
-rw-r--r--lib/banzai/filter/reference_filter.rb5
-rw-r--r--lib/banzai/filter/relative_link_filter.rb5
-rw-r--r--lib/banzai/filter/suggestion_filter.rb18
-rw-r--r--lib/banzai/filter/syntax_highlight_filter.rb21
-rw-r--r--lib/banzai/pipeline/markup_pipeline.rb3
-rw-r--r--lib/banzai/suggestions_parser.rb2
-rw-r--r--lib/bitbucket_server/collection.rb4
-rw-r--r--lib/bitbucket_server/connection.rb1
-rw-r--r--lib/constraints/project_url_constrainer.rb3
-rw-r--r--lib/declarative_policy/rule.rb6
-rw-r--r--lib/event_filter.rb2
-rw-r--r--lib/gitlab.rb4
-rw-r--r--lib/gitlab/access.rb14
-rw-r--r--lib/gitlab/auth/ldap/config.rb59
-rw-r--r--lib/gitlab/auth/ldap/person.rb4
-rw-r--r--lib/gitlab/auth/o_auth/user.rb17
-rw-r--r--lib/gitlab/auth/omniauth_identity_linker_base.rb6
-rw-r--r--lib/gitlab/auth/saml/auth_hash.rb4
-rw-r--r--lib/gitlab/authorized_keys.rb149
-rw-r--r--lib/gitlab/background_migration/archive_legacy_traces.rb9
-rw-r--r--lib/gitlab/background_migration/encrypt_columns.rb3
-rw-r--r--lib/gitlab/background_migration/migrate_events_to_push_event_payloads.rb2
-rw-r--r--lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb12
-rw-r--r--lib/gitlab/background_migration/populate_merge_request_assignees_table.rb36
-rw-r--r--lib/gitlab/background_migration/populate_untracked_uploads.rb14
-rw-r--r--lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb2
-rw-r--r--lib/gitlab/badge/pipeline/template.rb1
-rw-r--r--lib/gitlab/bare_repository_import/importer.rb2
-rw-r--r--lib/gitlab/bitbucket_import/importer.rb144
-rw-r--r--lib/gitlab/bitbucket_server_import/importer.rb42
-rw-r--r--lib/gitlab/chat.rb10
-rw-r--r--lib/gitlab/chat/command.rb94
-rw-r--r--lib/gitlab/chat/output.rb93
-rw-r--r--lib/gitlab/chat/responder.rb22
-rw-r--r--lib/gitlab/chat/responder/base.rb40
-rw-r--r--lib/gitlab/chat/responder/slack.rb80
-rw-r--r--lib/gitlab/checks/branch_check.rb38
-rw-r--r--lib/gitlab/ci/ansi2html.rb2
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata.rb4
-rw-r--r--lib/gitlab/ci/build/artifacts/metadata/entry.rb2
-rw-r--r--lib/gitlab/ci/build/policy/refs.rb8
-rw-r--r--lib/gitlab/ci/build/prerequisite/base.rb27
-rw-r--r--lib/gitlab/ci/build/prerequisite/factory.rb33
-rw-r--r--lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb40
-rw-r--r--lib/gitlab/ci/config.rb3
-rw-r--r--lib/gitlab/ci/config/entry/global.rb3
-rw-r--r--lib/gitlab/ci/config/entry/include.rb23
-rw-r--r--lib/gitlab/ci/config/entry/includes.rb32
-rw-r--r--lib/gitlab/ci/config/external/file/base.rb30
-rw-r--r--lib/gitlab/ci/config/external/file/local.rb7
-rw-r--r--lib/gitlab/ci/config/external/file/project.rb7
-rw-r--r--lib/gitlab/ci/config/external/mapper.rb36
-rw-r--r--lib/gitlab/ci/config/external/processor.rb6
-rw-r--r--lib/gitlab/ci/model.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/build.rb2
-rw-r--r--lib/gitlab/ci/pipeline/chain/command.rb5
-rw-r--r--lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb8
-rw-r--r--lib/gitlab/ci/status/build/factory.rb1
-rw-r--r--lib/gitlab/ci/status/build/failed.rb3
-rw-r--r--lib/gitlab/ci/status/build/preparing.rb28
-rw-r--r--lib/gitlab/ci/status/preparing.rb33
-rw-r--r--lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml96
-rw-r--r--lib/gitlab/ci/templates/Bash.gitlab-ci.yml14
-rw-r--r--lib/gitlab/ci/templates/C++.gitlab-ci.yml6
-rw-r--r--lib/gitlab/ci/templates/Chef.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Clojure.gitlab-ci.yml8
-rw-r--r--lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml17
-rw-r--r--lib/gitlab/ci/templates/Django.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Gradle.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Grails.gitlab-ci.yml10
-rw-r--r--lib/gitlab/ci/templates/Julia.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Laravel.gitlab-ci.yml15
-rw-r--r--lib/gitlab/ci/templates/Maven.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Mono.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml3
-rw-r--r--lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml13
-rw-r--r--lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml1
-rw-r--r--lib/gitlab/ci/templates/Python.gitlab-ci.yml4
-rw-r--r--lib/gitlab/ci/templates/Ruby.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml48
-rw-r--r--lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml59
-rw-r--r--lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml41
-rw-r--r--lib/gitlab/ci/templates/Security/License-Management.gitlab-ci.yml27
-rw-r--r--lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml43
-rw-r--r--lib/gitlab/ci/templates/Serverless.gitlab-ci.yml41
-rw-r--r--lib/gitlab/ci/templates/dotNET.gitlab-ci.yml2
-rw-r--r--lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml28
-rw-r--r--lib/gitlab/ci/variables/collection.rb2
-rw-r--r--lib/gitlab/ci/variables/collection/item.rb10
-rw-r--r--lib/gitlab/contributions_calendar.rb1
-rw-r--r--lib/gitlab/cycle_analytics/plan_event_fetcher.rb4
-rw-r--r--lib/gitlab/danger/helper.rb23
-rw-r--r--lib/gitlab/danger/teammate.rb8
-rw-r--r--lib/gitlab/database.rb10
-rw-r--r--lib/gitlab/database/count/reltuples_count_strategy.rb18
-rw-r--r--lib/gitlab/database/count/tablesample_count_strategy.rb13
-rw-r--r--lib/gitlab/database/multi_threaded_migration.rb10
-rw-r--r--lib/gitlab/database/sha_attribute.rb2
-rw-r--r--lib/gitlab/dependency_linker/base_linker.rb18
-rw-r--r--lib/gitlab/dependency_linker/composer_json_linker.rb4
-rw-r--r--lib/gitlab/dependency_linker/gemfile_linker.rb30
-rw-r--r--lib/gitlab/dependency_linker/gemspec_linker.rb2
-rw-r--r--lib/gitlab/dependency_linker/method_linker.rb10
-rw-r--r--lib/gitlab/dependency_linker/package.rb19
-rw-r--r--lib/gitlab/dependency_linker/package_json_linker.rb21
-rw-r--r--lib/gitlab/dependency_linker/parser/gemfile.rb40
-rw-r--r--lib/gitlab/dependency_linker/podfile_linker.rb11
-rw-r--r--lib/gitlab/dependency_linker/podspec_linker.rb2
-rw-r--r--lib/gitlab/diff/file.rb35
-rw-r--r--lib/gitlab/diff/suggestion.rb52
-rw-r--r--lib/gitlab/diff/suggestion_diff.rb37
-rw-r--r--lib/gitlab/diff/suggestions_parser.rb51
-rw-r--r--lib/gitlab/email/reply_parser.rb2
-rw-r--r--lib/gitlab/fake_application_settings.rb19
-rw-r--r--lib/gitlab/favicon.rb8
-rw-r--r--lib/gitlab/fogbugz_import/importer.rb2
-rw-r--r--lib/gitlab/git/blob.rb6
-rw-r--r--lib/gitlab/git/commit.rb41
-rw-r--r--lib/gitlab/git/pre_receive_error.rb31
-rw-r--r--lib/gitlab/git/ref.rb1
-rw-r--r--lib/gitlab/git/repository.rb20
-rw-r--r--lib/gitlab/git/rugged_impl/blob.rb106
-rw-r--r--lib/gitlab/git/rugged_impl/commit.rb89
-rw-r--r--lib/gitlab/git/rugged_impl/ref.rb20
-rw-r--r--lib/gitlab/git/rugged_impl/repository.rb78
-rw-r--r--lib/gitlab/git/rugged_impl/tree.rb105
-rw-r--r--lib/gitlab/git/tree.rb8
-rw-r--r--lib/gitlab/gitaly_client.rb65
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/blobs_stitcher.rb20
-rw-r--r--lib/gitlab/gitaly_client/operation_service.rb35
-rw-r--r--lib/gitlab/gitaly_client/ref_service.rb11
-rw-r--r--lib/gitlab/gitaly_client/remote_service.rb2
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb21
-rw-r--r--lib/gitlab/gitaly_client/storage_settings.rb8
-rw-r--r--lib/gitlab/github_import/importer/milestones_importer.rb1
-rw-r--r--lib/gitlab/github_import/importer/pull_request_importer.rb2
-rw-r--r--lib/gitlab/github_import/importer/repository_importer.rb16
-rw-r--r--lib/gitlab/gl_repository.rb35
-rw-r--r--lib/gitlab/gl_repository/repo_type.rb42
-rw-r--r--lib/gitlab/gon_helper.rb1
-rw-r--r--lib/gitlab/graphql/authorize.rb15
-rw-r--r--lib/gitlab/graphql/authorize/authorize_resource.rb17
-rw-r--r--lib/gitlab/graphql/authorize/instrumentation.rb10
-rw-r--r--lib/gitlab/group_search_results.rb30
-rw-r--r--lib/gitlab/hashed_storage/migrator.rb54
-rw-r--r--lib/gitlab/hashed_storage/rake_helper.rb12
-rw-r--r--lib/gitlab/highlight.rb2
-rw-r--r--lib/gitlab/hook_data/issue_builder.rb2
-rw-r--r--lib/gitlab/hook_data/merge_request_builder.rb2
-rw-r--r--lib/gitlab/i18n/metadata_entry.rb2
-rw-r--r--lib/gitlab/import/merge_request_helpers.rb16
-rw-r--r--lib/gitlab/import_export/import_export.yml3
-rw-r--r--lib/gitlab/import_export/json_hash_builder.rb2
-rw-r--r--lib/gitlab/import_export/merge_request_parser.rb11
-rw-r--r--lib/gitlab/import_export/relation_factory.rb2
-rw-r--r--lib/gitlab/incoming_email.rb2
-rw-r--r--lib/gitlab/json_cache.rb31
-rw-r--r--lib/gitlab/kubernetes.rb24
-rw-r--r--lib/gitlab/kubernetes/helm.rb4
-rw-r--r--lib/gitlab/kubernetes/helm/install_command.rb3
-rw-r--r--lib/gitlab/kubernetes/kube_client.rb8
-rw-r--r--lib/gitlab/legacy_github_import/client.rb2
-rw-r--r--lib/gitlab/legacy_github_import/importer.rb56
-rw-r--r--lib/gitlab/legacy_github_import/user_formatter.rb4
-rw-r--r--lib/gitlab/metrics/influx_db.rb6
-rw-r--r--lib/gitlab/middleware/basic_health_check.rb8
-rw-r--r--lib/gitlab/project_search_results.rb6
-rw-r--r--lib/gitlab/project_template.rb11
-rw-r--r--lib/gitlab/quick_actions/command_definition.rb9
-rw-r--r--lib/gitlab/recaptcha.rb4
-rw-r--r--lib/gitlab/repo_path.rb25
-rw-r--r--lib/gitlab/request_context.rb8
-rw-r--r--lib/gitlab/search_results.rb14
-rw-r--r--lib/gitlab/shell.rb146
-rw-r--r--lib/gitlab/sidekiq_config.rb4
-rw-r--r--lib/gitlab/sidekiq_middleware/memory_killer.rb82
-rw-r--r--lib/gitlab/sidekiq_middleware/shutdown.rb135
-rw-r--r--lib/gitlab/sidekiq_signals.rb45
-rw-r--r--lib/gitlab/slash_commands/application_help.rb25
-rw-r--r--lib/gitlab/slash_commands/command.rb3
-rw-r--r--lib/gitlab/slash_commands/presenters/error.rb17
-rw-r--r--lib/gitlab/slash_commands/presenters/run.rb33
-rw-r--r--lib/gitlab/slash_commands/run.rb44
-rw-r--r--lib/gitlab/sql/pattern.rb8
-rw-r--r--lib/gitlab/template/gitlab_ci_yml_template.rb5
-rw-r--r--lib/gitlab/tracing.rb9
-rw-r--r--lib/gitlab/tracing/jaeger_factory.rb2
-rw-r--r--lib/gitlab/tree_summary.rb2
-rw-r--r--lib/gitlab/usage_data.rb3
-rw-r--r--lib/gitlab/user_access.rb4
-rw-r--r--lib/gitlab/user_extractor.rb5
-rw-r--r--lib/gitlab/utils.rb6
-rw-r--r--lib/gitlab/workhorse.rb4
-rw-r--r--lib/google_api/cloud_platform/client.rb7
-rw-r--r--lib/sentry/client.rb4
-rw-r--r--lib/system_check/app/git_user_default_ssh_config_check.rb2
-rw-r--r--lib/tasks/gemojione.rake15
-rw-r--r--lib/tasks/gitlab/artifacts/migrate.rake13
-rw-r--r--lib/tasks/gitlab/backup.rake2
-rw-r--r--lib/tasks/gitlab/cleanup.rake2
-rw-r--r--lib/tasks/gitlab/features.rake24
-rw-r--r--lib/tasks/gitlab/info.rake10
-rw-r--r--lib/tasks/gitlab/lfs/migrate.rake11
-rw-r--r--lib/tasks/gitlab/shell.rake15
-rw-r--r--lib/tasks/gitlab/storage.rake63
-rw-r--r--lib/tasks/gitlab/traces.rake11
-rw-r--r--lib/tasks/karma.rake15
-rw-r--r--lib/tasks/lint.rake8
-rw-r--r--lib/tasks/migrate/migrate_iids.rake42
-rw-r--r--lib/unfold_form.rb14
-rw-r--r--locale/ar_SA/gitlab.po660
-rw-r--r--locale/bg/gitlab.po624
-rw-r--r--locale/ca_ES/gitlab.po624
-rw-r--r--locale/cs_CZ/gitlab.po642
-rw-r--r--locale/cy_GB/gitlab.po660
-rw-r--r--locale/da_DK/gitlab.po624
-rw-r--r--locale/de/gitlab.po632
-rw-r--r--locale/el_GR/gitlab.po624
-rw-r--r--locale/eo/gitlab.po624
-rw-r--r--locale/es/gitlab.po830
-rw-r--r--locale/et_EE/gitlab.po624
-rw-r--r--locale/fil_PH/gitlab.po624
-rw-r--r--locale/fr/gitlab.po656
-rw-r--r--locale/gitlab.pot1113
-rw-r--r--locale/gl_ES/gitlab.po624
-rw-r--r--locale/he_IL/gitlab.po642
-rw-r--r--locale/hi_IN/gitlab.po624
-rw-r--r--locale/hr_HR/gitlab.po633
-rw-r--r--locale/hu_HU/gitlab.po624
-rw-r--r--locale/id_ID/gitlab.po615
-rw-r--r--locale/it/gitlab.po624
-rw-r--r--locale/ja/gitlab.po1337
-rw-r--r--locale/ko/gitlab.po733
-rw-r--r--locale/mn_MN/gitlab.po624
-rw-r--r--locale/nb_NO/gitlab.po624
-rw-r--r--locale/nl_NL/gitlab.po624
-rw-r--r--locale/pa_IN/gitlab.po624
-rw-r--r--locale/pl_PL/gitlab.po646
-rw-r--r--locale/pt_BR/gitlab.po1248
-rw-r--r--locale/pt_PT/gitlab.po624
-rw-r--r--locale/ro_RO/gitlab.po633
-rw-r--r--locale/ru/gitlab.po648
-rw-r--r--locale/sk_SK/gitlab.po642
-rw-r--r--locale/sq_AL/gitlab.po624
-rw-r--r--locale/sr_CS/gitlab.po633
-rw-r--r--locale/sr_SP/gitlab.po633
-rw-r--r--locale/sv_SE/gitlab.po624
-rw-r--r--locale/sw_KE/gitlab.po624
-rw-r--r--locale/tr_TR/gitlab.po1706
-rw-r--r--locale/uk/gitlab.po2252
-rw-r--r--locale/zh_CN/gitlab.po2113
-rw-r--r--locale/zh_HK/gitlab.po615
-rw-r--r--locale/zh_TW/gitlab.po623
-rw-r--r--package.json47
-rw-r--r--public/-/emojis/1/100.png (renamed from app/assets/images/emoji/100.png)bin793 -> 793 bytes
-rw-r--r--public/-/emojis/1/1234.png (renamed from app/assets/images/emoji/1234.png)bin676 -> 676 bytes
-rw-r--r--public/-/emojis/1/1F627.png (renamed from app/assets/images/emoji/1F627.png)bin821 -> 821 bytes
-rw-r--r--public/-/emojis/1/8ball.png (renamed from app/assets/images/emoji/8ball.png)bin810 -> 810 bytes
-rw-r--r--public/-/emojis/1/a.png (renamed from app/assets/images/emoji/a.png)bin469 -> 469 bytes
-rw-r--r--public/-/emojis/1/ab.png (renamed from app/assets/images/emoji/ab.png)bin505 -> 505 bytes
-rw-r--r--public/-/emojis/1/abc.png (renamed from app/assets/images/emoji/abc.png)bin646 -> 646 bytes
-rw-r--r--public/-/emojis/1/abcd.png (renamed from app/assets/images/emoji/abcd.png)bin670 -> 670 bytes
-rw-r--r--public/-/emojis/1/accept.png (renamed from app/assets/images/emoji/accept.png)bin491 -> 491 bytes
-rw-r--r--public/-/emojis/1/aerial_tramway.png (renamed from app/assets/images/emoji/aerial_tramway.png)bin759 -> 759 bytes
-rw-r--r--public/-/emojis/1/airplane.png (renamed from app/assets/images/emoji/airplane.png)bin1152 -> 1152 bytes
-rw-r--r--public/-/emojis/1/airplane_arriving.png (renamed from app/assets/images/emoji/airplane_arriving.png)bin1101 -> 1101 bytes
-rw-r--r--public/-/emojis/1/airplane_departure.png (renamed from app/assets/images/emoji/airplane_departure.png)bin1111 -> 1111 bytes
-rw-r--r--public/-/emojis/1/airplane_small.png (renamed from app/assets/images/emoji/airplane_small.png)bin1229 -> 1229 bytes
-rw-r--r--public/-/emojis/1/alarm_clock.png (renamed from app/assets/images/emoji/alarm_clock.png)bin1044 -> 1044 bytes
-rw-r--r--public/-/emojis/1/alembic.png (renamed from app/assets/images/emoji/alembic.png)bin953 -> 953 bytes
-rw-r--r--public/-/emojis/1/alien.png (renamed from app/assets/images/emoji/alien.png)bin839 -> 839 bytes
-rw-r--r--public/-/emojis/1/ambulance.png (renamed from app/assets/images/emoji/ambulance.png)bin1238 -> 1238 bytes
-rw-r--r--public/-/emojis/1/amphora.png (renamed from app/assets/images/emoji/amphora.png)bin1044 -> 1044 bytes
-rw-r--r--public/-/emojis/1/anchor.png (renamed from app/assets/images/emoji/anchor.png)bin779 -> 779 bytes
-rw-r--r--public/-/emojis/1/angel.png (renamed from app/assets/images/emoji/angel.png)bin2077 -> 2077 bytes
-rw-r--r--public/-/emojis/1/angel_tone1.png (renamed from app/assets/images/emoji/angel_tone1.png)bin2088 -> 2088 bytes
-rw-r--r--public/-/emojis/1/angel_tone2.png (renamed from app/assets/images/emoji/angel_tone2.png)bin2075 -> 2075 bytes
-rw-r--r--public/-/emojis/1/angel_tone3.png (renamed from app/assets/images/emoji/angel_tone3.png)bin2078 -> 2078 bytes
-rw-r--r--public/-/emojis/1/angel_tone4.png (renamed from app/assets/images/emoji/angel_tone4.png)bin2076 -> 2076 bytes
-rw-r--r--public/-/emojis/1/angel_tone5.png (renamed from app/assets/images/emoji/angel_tone5.png)bin2078 -> 2078 bytes
-rw-r--r--public/-/emojis/1/anger.png (renamed from app/assets/images/emoji/anger.png)bin594 -> 594 bytes
-rw-r--r--public/-/emojis/1/anger_right.png (renamed from app/assets/images/emoji/anger_right.png)bin551 -> 551 bytes
-rw-r--r--public/-/emojis/1/angry.png (renamed from app/assets/images/emoji/angry.png)bin845 -> 845 bytes
-rw-r--r--public/-/emojis/1/ant.png (renamed from app/assets/images/emoji/ant.png)bin1412 -> 1412 bytes
-rw-r--r--public/-/emojis/1/apple.png (renamed from app/assets/images/emoji/apple.png)bin655 -> 655 bytes
-rw-r--r--public/-/emojis/1/aquarius.png (renamed from app/assets/images/emoji/aquarius.png)bin648 -> 648 bytes
-rw-r--r--public/-/emojis/1/aries.png (renamed from app/assets/images/emoji/aries.png)bin711 -> 711 bytes
-rw-r--r--public/-/emojis/1/arrow_backward.png (renamed from app/assets/images/emoji/arrow_backward.png)bin429 -> 429 bytes
-rw-r--r--public/-/emojis/1/arrow_double_down.png (renamed from app/assets/images/emoji/arrow_double_down.png)bin543 -> 543 bytes
-rw-r--r--public/-/emojis/1/arrow_double_up.png (renamed from app/assets/images/emoji/arrow_double_up.png)bin535 -> 535 bytes
-rw-r--r--public/-/emojis/1/arrow_down.png (renamed from app/assets/images/emoji/arrow_down.png)bin512 -> 512 bytes
-rw-r--r--public/-/emojis/1/arrow_down_small.png (renamed from app/assets/images/emoji/arrow_down_small.png)bin455 -> 455 bytes
-rw-r--r--public/-/emojis/1/arrow_forward.png (renamed from app/assets/images/emoji/arrow_forward.png)bin429 -> 429 bytes
-rw-r--r--public/-/emojis/1/arrow_heading_down.png (renamed from app/assets/images/emoji/arrow_heading_down.png)bin563 -> 563 bytes
-rw-r--r--public/-/emojis/1/arrow_heading_up.png (renamed from app/assets/images/emoji/arrow_heading_up.png)bin559 -> 559 bytes
-rw-r--r--public/-/emojis/1/arrow_left.png (renamed from app/assets/images/emoji/arrow_left.png)bin471 -> 471 bytes
-rw-r--r--public/-/emojis/1/arrow_lower_left.png (renamed from app/assets/images/emoji/arrow_lower_left.png)bin520 -> 520 bytes
-rw-r--r--public/-/emojis/1/arrow_lower_right.png (renamed from app/assets/images/emoji/arrow_lower_right.png)bin526 -> 526 bytes
-rw-r--r--public/-/emojis/1/arrow_right.png (renamed from app/assets/images/emoji/arrow_right.png)bin468 -> 468 bytes
-rw-r--r--public/-/emojis/1/arrow_right_hook.png (renamed from app/assets/images/emoji/arrow_right_hook.png)bin644 -> 644 bytes
-rw-r--r--public/-/emojis/1/arrow_up.png (renamed from app/assets/images/emoji/arrow_up.png)bin507 -> 507 bytes
-rw-r--r--public/-/emojis/1/arrow_up_down.png (renamed from app/assets/images/emoji/arrow_up_down.png)bin474 -> 474 bytes
-rw-r--r--public/-/emojis/1/arrow_up_small.png (renamed from app/assets/images/emoji/arrow_up_small.png)bin454 -> 454 bytes
-rw-r--r--public/-/emojis/1/arrow_upper_left.png (renamed from app/assets/images/emoji/arrow_upper_left.png)bin521 -> 521 bytes
-rw-r--r--public/-/emojis/1/arrow_upper_right.png (renamed from app/assets/images/emoji/arrow_upper_right.png)bin524 -> 524 bytes
-rw-r--r--public/-/emojis/1/arrows_clockwise.png (renamed from app/assets/images/emoji/arrows_clockwise.png)bin519 -> 519 bytes
-rw-r--r--public/-/emojis/1/arrows_counterclockwise.png (renamed from app/assets/images/emoji/arrows_counterclockwise.png)bin693 -> 693 bytes
-rw-r--r--public/-/emojis/1/art.png (renamed from app/assets/images/emoji/art.png)bin1455 -> 1455 bytes
-rw-r--r--public/-/emojis/1/articulated_lorry.png (renamed from app/assets/images/emoji/articulated_lorry.png)bin1710 -> 1710 bytes
-rw-r--r--public/-/emojis/1/asterisk.png (renamed from app/assets/images/emoji/asterisk.png)bin627 -> 627 bytes
-rw-r--r--public/-/emojis/1/astonished.png (renamed from app/assets/images/emoji/astonished.png)bin862 -> 862 bytes
-rw-r--r--public/-/emojis/1/athletic_shoe.png (renamed from app/assets/images/emoji/athletic_shoe.png)bin1595 -> 1595 bytes
-rw-r--r--public/-/emojis/1/atm.png (renamed from app/assets/images/emoji/atm.png)bin1397 -> 1397 bytes
-rw-r--r--public/-/emojis/1/atom.png (renamed from app/assets/images/emoji/atom.png)bin912 -> 912 bytes
-rw-r--r--public/-/emojis/1/avocado.png (renamed from app/assets/images/emoji/avocado.png)bin1520 -> 1520 bytes
-rw-r--r--public/-/emojis/1/b.png (renamed from app/assets/images/emoji/b.png)bin391 -> 391 bytes
-rw-r--r--public/-/emojis/1/baby.png (renamed from app/assets/images/emoji/baby.png)bin1380 -> 1380 bytes
-rw-r--r--public/-/emojis/1/baby_bottle.png (renamed from app/assets/images/emoji/baby_bottle.png)bin818 -> 818 bytes
-rw-r--r--public/-/emojis/1/baby_chick.png (renamed from app/assets/images/emoji/baby_chick.png)bin1181 -> 1181 bytes
-rw-r--r--public/-/emojis/1/baby_symbol.png (renamed from app/assets/images/emoji/baby_symbol.png)bin665 -> 665 bytes
-rw-r--r--public/-/emojis/1/baby_tone1.png (renamed from app/assets/images/emoji/baby_tone1.png)bin1392 -> 1392 bytes
-rw-r--r--public/-/emojis/1/baby_tone2.png (renamed from app/assets/images/emoji/baby_tone2.png)bin1392 -> 1392 bytes
-rw-r--r--public/-/emojis/1/baby_tone3.png (renamed from app/assets/images/emoji/baby_tone3.png)bin1403 -> 1403 bytes
-rw-r--r--public/-/emojis/1/baby_tone4.png (renamed from app/assets/images/emoji/baby_tone4.png)bin1413 -> 1413 bytes
-rw-r--r--public/-/emojis/1/baby_tone5.png (renamed from app/assets/images/emoji/baby_tone5.png)bin1405 -> 1405 bytes
-rw-r--r--public/-/emojis/1/back.png (renamed from app/assets/images/emoji/back.png)bin562 -> 562 bytes
-rw-r--r--public/-/emojis/1/bacon.png (renamed from app/assets/images/emoji/bacon.png)bin2148 -> 2148 bytes
-rw-r--r--public/-/emojis/1/badminton.png (renamed from app/assets/images/emoji/badminton.png)bin1253 -> 1253 bytes
-rw-r--r--public/-/emojis/1/baggage_claim.png (renamed from app/assets/images/emoji/baggage_claim.png)bin490 -> 490 bytes
-rw-r--r--public/-/emojis/1/balloon.png (renamed from app/assets/images/emoji/balloon.png)bin501 -> 501 bytes
-rw-r--r--public/-/emojis/1/ballot_box.png (renamed from app/assets/images/emoji/ballot_box.png)bin1355 -> 1355 bytes
-rw-r--r--public/-/emojis/1/ballot_box_with_check.png (renamed from app/assets/images/emoji/ballot_box_with_check.png)bin639 -> 639 bytes
-rw-r--r--public/-/emojis/1/bamboo.png (renamed from app/assets/images/emoji/bamboo.png)bin1946 -> 1946 bytes
-rw-r--r--public/-/emojis/1/banana.png (renamed from app/assets/images/emoji/banana.png)bin1157 -> 1157 bytes
-rw-r--r--public/-/emojis/1/bangbang.png (renamed from app/assets/images/emoji/bangbang.png)bin390 -> 390 bytes
-rw-r--r--public/-/emojis/1/bank.png (renamed from app/assets/images/emoji/bank.png)bin1358 -> 1358 bytes
-rw-r--r--public/-/emojis/1/bar_chart.png (renamed from app/assets/images/emoji/bar_chart.png)bin408 -> 408 bytes
-rw-r--r--public/-/emojis/1/barber.png (renamed from app/assets/images/emoji/barber.png)bin820 -> 820 bytes
-rw-r--r--public/-/emojis/1/baseball.png (renamed from app/assets/images/emoji/baseball.png)bin1185 -> 1185 bytes
-rw-r--r--public/-/emojis/1/basketball.png (renamed from app/assets/images/emoji/basketball.png)bin1546 -> 1546 bytes
-rw-r--r--public/-/emojis/1/basketball_player.png (renamed from app/assets/images/emoji/basketball_player.png)bin1491 -> 1491 bytes
-rw-r--r--public/-/emojis/1/basketball_player_tone1.png (renamed from app/assets/images/emoji/basketball_player_tone1.png)bin1492 -> 1492 bytes
-rw-r--r--public/-/emojis/1/basketball_player_tone2.png (renamed from app/assets/images/emoji/basketball_player_tone2.png)bin1493 -> 1493 bytes
-rw-r--r--public/-/emojis/1/basketball_player_tone3.png (renamed from app/assets/images/emoji/basketball_player_tone3.png)bin1492 -> 1492 bytes
-rw-r--r--public/-/emojis/1/basketball_player_tone4.png (renamed from app/assets/images/emoji/basketball_player_tone4.png)bin1491 -> 1491 bytes
-rw-r--r--public/-/emojis/1/basketball_player_tone5.png (renamed from app/assets/images/emoji/basketball_player_tone5.png)bin1474 -> 1474 bytes
-rw-r--r--public/-/emojis/1/bat.png (renamed from app/assets/images/emoji/bat.png)bin1190 -> 1190 bytes
-rw-r--r--public/-/emojis/1/bath.png (renamed from app/assets/images/emoji/bath.png)bin1238 -> 1238 bytes
-rw-r--r--public/-/emojis/1/bath_tone1.png (renamed from app/assets/images/emoji/bath_tone1.png)bin1235 -> 1235 bytes
-rw-r--r--public/-/emojis/1/bath_tone2.png (renamed from app/assets/images/emoji/bath_tone2.png)bin1231 -> 1231 bytes
-rw-r--r--public/-/emojis/1/bath_tone3.png (renamed from app/assets/images/emoji/bath_tone3.png)bin1236 -> 1236 bytes
-rw-r--r--public/-/emojis/1/bath_tone4.png (renamed from app/assets/images/emoji/bath_tone4.png)bin1252 -> 1252 bytes
-rw-r--r--public/-/emojis/1/bath_tone5.png (renamed from app/assets/images/emoji/bath_tone5.png)bin1239 -> 1239 bytes
-rw-r--r--public/-/emojis/1/bathtub.png (renamed from app/assets/images/emoji/bathtub.png)bin767 -> 767 bytes
-rw-r--r--public/-/emojis/1/battery.png (renamed from app/assets/images/emoji/battery.png)bin228 -> 228 bytes
-rw-r--r--public/-/emojis/1/beach.png (renamed from app/assets/images/emoji/beach.png)bin942 -> 942 bytes
-rw-r--r--public/-/emojis/1/beach_umbrella.png (renamed from app/assets/images/emoji/beach_umbrella.png)bin1486 -> 1486 bytes
-rw-r--r--public/-/emojis/1/bear.png (renamed from app/assets/images/emoji/bear.png)bin1023 -> 1023 bytes
-rw-r--r--public/-/emojis/1/bed.png (renamed from app/assets/images/emoji/bed.png)bin1572 -> 1572 bytes
-rw-r--r--public/-/emojis/1/bee.png (renamed from app/assets/images/emoji/bee.png)bin1378 -> 1378 bytes
-rw-r--r--public/-/emojis/1/beer.png (renamed from app/assets/images/emoji/beer.png)bin1338 -> 1338 bytes
-rw-r--r--public/-/emojis/1/beers.png (renamed from app/assets/images/emoji/beers.png)bin2100 -> 2100 bytes
-rw-r--r--public/-/emojis/1/beetle.png (renamed from app/assets/images/emoji/beetle.png)bin1288 -> 1288 bytes
-rw-r--r--public/-/emojis/1/beginner.png (renamed from app/assets/images/emoji/beginner.png)bin545 -> 545 bytes
-rw-r--r--public/-/emojis/1/bell.png (renamed from app/assets/images/emoji/bell.png)bin1496 -> 1496 bytes
-rw-r--r--public/-/emojis/1/bellhop.png (renamed from app/assets/images/emoji/bellhop.png)bin891 -> 891 bytes
-rw-r--r--public/-/emojis/1/bento.png (renamed from app/assets/images/emoji/bento.png)bin1127 -> 1127 bytes
-rw-r--r--public/-/emojis/1/bicyclist.png (renamed from app/assets/images/emoji/bicyclist.png)bin1911 -> 1911 bytes
-rw-r--r--public/-/emojis/1/bicyclist_tone1.png (renamed from app/assets/images/emoji/bicyclist_tone1.png)bin1860 -> 1860 bytes
-rw-r--r--public/-/emojis/1/bicyclist_tone2.png (renamed from app/assets/images/emoji/bicyclist_tone2.png)bin1866 -> 1866 bytes
-rw-r--r--public/-/emojis/1/bicyclist_tone3.png (renamed from app/assets/images/emoji/bicyclist_tone3.png)bin1851 -> 1851 bytes
-rw-r--r--public/-/emojis/1/bicyclist_tone4.png (renamed from app/assets/images/emoji/bicyclist_tone4.png)bin1852 -> 1852 bytes
-rw-r--r--public/-/emojis/1/bicyclist_tone5.png (renamed from app/assets/images/emoji/bicyclist_tone5.png)bin1840 -> 1840 bytes
-rw-r--r--public/-/emojis/1/bike.png (renamed from app/assets/images/emoji/bike.png)bin1505 -> 1505 bytes
-rw-r--r--public/-/emojis/1/bikini.png (renamed from app/assets/images/emoji/bikini.png)bin613 -> 613 bytes
-rw-r--r--public/-/emojis/1/biohazard.png (renamed from app/assets/images/emoji/biohazard.png)bin794 -> 794 bytes
-rw-r--r--public/-/emojis/1/bird.png (renamed from app/assets/images/emoji/bird.png)bin1068 -> 1068 bytes
-rw-r--r--public/-/emojis/1/birthday.png (renamed from app/assets/images/emoji/birthday.png)bin2219 -> 2219 bytes
-rw-r--r--public/-/emojis/1/black_circle.png (renamed from app/assets/images/emoji/black_circle.png)bin374 -> 374 bytes
-rw-r--r--public/-/emojis/1/black_heart.png (renamed from app/assets/images/emoji/black_heart.png)bin435 -> 435 bytes
-rw-r--r--public/-/emojis/1/black_joker.png (renamed from app/assets/images/emoji/black_joker.png)bin1091 -> 1091 bytes
-rw-r--r--public/-/emojis/1/black_large_square.png (renamed from app/assets/images/emoji/black_large_square.png)bin110 -> 110 bytes
-rw-r--r--public/-/emojis/1/black_medium_small_square.png (renamed from app/assets/images/emoji/black_medium_small_square.png)bin110 -> 110 bytes
-rw-r--r--public/-/emojis/1/black_medium_square.png (renamed from app/assets/images/emoji/black_medium_square.png)bin108 -> 108 bytes
-rw-r--r--public/-/emojis/1/black_nib.png (renamed from app/assets/images/emoji/black_nib.png)bin620 -> 620 bytes
-rw-r--r--public/-/emojis/1/black_small_square.png (renamed from app/assets/images/emoji/black_small_square.png)bin108 -> 108 bytes
-rw-r--r--public/-/emojis/1/black_square_button.png (renamed from app/assets/images/emoji/black_square_button.png)bin122 -> 122 bytes
-rw-r--r--public/-/emojis/1/blossom.png (renamed from app/assets/images/emoji/blossom.png)bin867 -> 867 bytes
-rw-r--r--public/-/emojis/1/blowfish.png (renamed from app/assets/images/emoji/blowfish.png)bin1620 -> 1620 bytes
-rw-r--r--public/-/emojis/1/blue_book.png (renamed from app/assets/images/emoji/blue_book.png)bin1347 -> 1347 bytes
-rw-r--r--public/-/emojis/1/blue_car.png (renamed from app/assets/images/emoji/blue_car.png)bin1275 -> 1275 bytes
-rw-r--r--public/-/emojis/1/blue_heart.png (renamed from app/assets/images/emoji/blue_heart.png)bin435 -> 435 bytes
-rw-r--r--public/-/emojis/1/blush.png (renamed from app/assets/images/emoji/blush.png)bin812 -> 812 bytes
-rw-r--r--public/-/emojis/1/boar.png (renamed from app/assets/images/emoji/boar.png)bin1366 -> 1366 bytes
-rw-r--r--public/-/emojis/1/bomb.png (renamed from app/assets/images/emoji/bomb.png)bin702 -> 702 bytes
-rw-r--r--public/-/emojis/1/book.png (renamed from app/assets/images/emoji/book.png)bin1716 -> 1716 bytes
-rw-r--r--public/-/emojis/1/bookmark.png (renamed from app/assets/images/emoji/bookmark.png)bin747 -> 747 bytes
-rw-r--r--public/-/emojis/1/bookmark_tabs.png (renamed from app/assets/images/emoji/bookmark_tabs.png)bin1395 -> 1395 bytes
-rw-r--r--public/-/emojis/1/books.png (renamed from app/assets/images/emoji/books.png)bin2474 -> 2474 bytes
-rw-r--r--public/-/emojis/1/boom.png (renamed from app/assets/images/emoji/boom.png)bin1110 -> 1110 bytes
-rw-r--r--public/-/emojis/1/boot.png (renamed from app/assets/images/emoji/boot.png)bin662 -> 662 bytes
-rw-r--r--public/-/emojis/1/bouquet.png (renamed from app/assets/images/emoji/bouquet.png)bin1662 -> 1662 bytes
-rw-r--r--public/-/emojis/1/bow.png (renamed from app/assets/images/emoji/bow.png)bin1394 -> 1394 bytes
-rw-r--r--public/-/emojis/1/bow_and_arrow.png (renamed from app/assets/images/emoji/bow_and_arrow.png)bin1402 -> 1402 bytes
-rw-r--r--public/-/emojis/1/bow_tone1.png (renamed from app/assets/images/emoji/bow_tone1.png)bin1394 -> 1394 bytes
-rw-r--r--public/-/emojis/1/bow_tone2.png (renamed from app/assets/images/emoji/bow_tone2.png)bin1394 -> 1394 bytes
-rw-r--r--public/-/emojis/1/bow_tone3.png (renamed from app/assets/images/emoji/bow_tone3.png)bin1394 -> 1394 bytes
-rw-r--r--public/-/emojis/1/bow_tone4.png (renamed from app/assets/images/emoji/bow_tone4.png)bin1394 -> 1394 bytes
-rw-r--r--public/-/emojis/1/bow_tone5.png (renamed from app/assets/images/emoji/bow_tone5.png)bin1394 -> 1394 bytes
-rw-r--r--public/-/emojis/1/bowling.png (renamed from app/assets/images/emoji/bowling.png)bin1426 -> 1426 bytes
-rw-r--r--public/-/emojis/1/boxing_glove.png (renamed from app/assets/images/emoji/boxing_glove.png)bin1575 -> 1575 bytes
-rw-r--r--public/-/emojis/1/boy.png (renamed from app/assets/images/emoji/boy.png)bin881 -> 881 bytes
-rw-r--r--public/-/emojis/1/boy_tone1.png (renamed from app/assets/images/emoji/boy_tone1.png)bin876 -> 876 bytes
-rw-r--r--public/-/emojis/1/boy_tone2.png (renamed from app/assets/images/emoji/boy_tone2.png)bin876 -> 876 bytes
-rw-r--r--public/-/emojis/1/boy_tone3.png (renamed from app/assets/images/emoji/boy_tone3.png)bin876 -> 876 bytes
-rw-r--r--public/-/emojis/1/boy_tone4.png (renamed from app/assets/images/emoji/boy_tone4.png)bin870 -> 870 bytes
-rw-r--r--public/-/emojis/1/boy_tone5.png (renamed from app/assets/images/emoji/boy_tone5.png)bin873 -> 873 bytes
-rw-r--r--public/-/emojis/1/bread.png (renamed from app/assets/images/emoji/bread.png)bin1419 -> 1419 bytes
-rw-r--r--public/-/emojis/1/bride_with_veil.png (renamed from app/assets/images/emoji/bride_with_veil.png)bin2452 -> 2452 bytes
-rw-r--r--public/-/emojis/1/bride_with_veil_tone1.png (renamed from app/assets/images/emoji/bride_with_veil_tone1.png)bin2464 -> 2464 bytes
-rw-r--r--public/-/emojis/1/bride_with_veil_tone2.png (renamed from app/assets/images/emoji/bride_with_veil_tone2.png)bin2457 -> 2457 bytes
-rw-r--r--public/-/emojis/1/bride_with_veil_tone3.png (renamed from app/assets/images/emoji/bride_with_veil_tone3.png)bin2463 -> 2463 bytes
-rw-r--r--public/-/emojis/1/bride_with_veil_tone4.png (renamed from app/assets/images/emoji/bride_with_veil_tone4.png)bin2463 -> 2463 bytes
-rw-r--r--public/-/emojis/1/bride_with_veil_tone5.png (renamed from app/assets/images/emoji/bride_with_veil_tone5.png)bin2462 -> 2462 bytes
-rw-r--r--public/-/emojis/1/bridge_at_night.png (renamed from app/assets/images/emoji/bridge_at_night.png)bin637 -> 637 bytes
-rw-r--r--public/-/emojis/1/briefcase.png (renamed from app/assets/images/emoji/briefcase.png)bin1275 -> 1275 bytes
-rw-r--r--public/-/emojis/1/broken_heart.png (renamed from app/assets/images/emoji/broken_heart.png)bin556 -> 556 bytes
-rw-r--r--public/-/emojis/1/bug.png (renamed from app/assets/images/emoji/bug.png)bin1599 -> 1599 bytes
-rw-r--r--public/-/emojis/1/bulb.png (renamed from app/assets/images/emoji/bulb.png)bin805 -> 805 bytes
-rw-r--r--public/-/emojis/1/bullettrain_front.png (renamed from app/assets/images/emoji/bullettrain_front.png)bin1450 -> 1450 bytes
-rw-r--r--public/-/emojis/1/bullettrain_side.png (renamed from app/assets/images/emoji/bullettrain_side.png)bin1538 -> 1538 bytes
-rw-r--r--public/-/emojis/1/burrito.png (renamed from app/assets/images/emoji/burrito.png)bin2938 -> 2938 bytes
-rw-r--r--public/-/emojis/1/bus.png (renamed from app/assets/images/emoji/bus.png)bin1086 -> 1086 bytes
-rw-r--r--public/-/emojis/1/busstop.png (renamed from app/assets/images/emoji/busstop.png)bin626 -> 626 bytes
-rw-r--r--public/-/emojis/1/bust_in_silhouette.png (renamed from app/assets/images/emoji/bust_in_silhouette.png)bin426 -> 426 bytes
-rw-r--r--public/-/emojis/1/busts_in_silhouette.png (renamed from app/assets/images/emoji/busts_in_silhouette.png)bin526 -> 526 bytes
-rw-r--r--public/-/emojis/1/butterfly.png (renamed from app/assets/images/emoji/butterfly.png)bin1981 -> 1981 bytes
-rw-r--r--public/-/emojis/1/cactus.png (renamed from app/assets/images/emoji/cactus.png)bin628 -> 628 bytes
-rw-r--r--public/-/emojis/1/cake.png (renamed from app/assets/images/emoji/cake.png)bin2266 -> 2266 bytes
-rw-r--r--public/-/emojis/1/calendar.png (renamed from app/assets/images/emoji/calendar.png)bin2077 -> 2077 bytes
-rw-r--r--public/-/emojis/1/calendar_spiral.png (renamed from app/assets/images/emoji/calendar_spiral.png)bin1491 -> 1491 bytes
-rw-r--r--public/-/emojis/1/call_me.png (renamed from app/assets/images/emoji/call_me.png)bin894 -> 894 bytes
-rw-r--r--public/-/emojis/1/call_me_tone1.png (renamed from app/assets/images/emoji/call_me_tone1.png)bin893 -> 893 bytes
-rw-r--r--public/-/emojis/1/call_me_tone2.png (renamed from app/assets/images/emoji/call_me_tone2.png)bin891 -> 891 bytes
-rw-r--r--public/-/emojis/1/call_me_tone3.png (renamed from app/assets/images/emoji/call_me_tone3.png)bin891 -> 891 bytes
-rw-r--r--public/-/emojis/1/call_me_tone4.png (renamed from app/assets/images/emoji/call_me_tone4.png)bin891 -> 891 bytes
-rw-r--r--public/-/emojis/1/call_me_tone5.png (renamed from app/assets/images/emoji/call_me_tone5.png)bin893 -> 893 bytes
-rw-r--r--public/-/emojis/1/calling.png (renamed from app/assets/images/emoji/calling.png)bin815 -> 815 bytes
-rw-r--r--public/-/emojis/1/camel.png (renamed from app/assets/images/emoji/camel.png)bin1190 -> 1190 bytes
-rw-r--r--public/-/emojis/1/camera.png (renamed from app/assets/images/emoji/camera.png)bin1783 -> 1783 bytes
-rw-r--r--public/-/emojis/1/camera_with_flash.png (renamed from app/assets/images/emoji/camera_with_flash.png)bin2097 -> 2097 bytes
-rw-r--r--public/-/emojis/1/camping.png (renamed from app/assets/images/emoji/camping.png)bin1513 -> 1513 bytes
-rw-r--r--public/-/emojis/1/cancer.png (renamed from app/assets/images/emoji/cancer.png)bin729 -> 729 bytes
-rw-r--r--public/-/emojis/1/candle.png (renamed from app/assets/images/emoji/candle.png)bin1250 -> 1250 bytes
-rw-r--r--public/-/emojis/1/candy.png (renamed from app/assets/images/emoji/candy.png)bin1054 -> 1054 bytes
-rw-r--r--public/-/emojis/1/canoe.png (renamed from app/assets/images/emoji/canoe.png)bin1244 -> 1244 bytes
-rw-r--r--public/-/emojis/1/capital_abcd.png (renamed from app/assets/images/emoji/capital_abcd.png)bin805 -> 805 bytes
-rw-r--r--public/-/emojis/1/capricorn.png (renamed from app/assets/images/emoji/capricorn.png)bin688 -> 688 bytes
-rw-r--r--public/-/emojis/1/card_box.png (renamed from app/assets/images/emoji/card_box.png)bin1523 -> 1523 bytes
-rw-r--r--public/-/emojis/1/card_index.png (renamed from app/assets/images/emoji/card_index.png)bin1929 -> 1929 bytes
-rw-r--r--public/-/emojis/1/carousel_horse.png (renamed from app/assets/images/emoji/carousel_horse.png)bin1739 -> 1739 bytes
-rw-r--r--public/-/emojis/1/carrot.png (renamed from app/assets/images/emoji/carrot.png)bin1236 -> 1236 bytes
-rw-r--r--public/-/emojis/1/cartwheel.png (renamed from app/assets/images/emoji/cartwheel.png)bin1233 -> 1233 bytes
-rw-r--r--public/-/emojis/1/cartwheel_tone1.png (renamed from app/assets/images/emoji/cartwheel_tone1.png)bin1234 -> 1234 bytes
-rw-r--r--public/-/emojis/1/cartwheel_tone2.png (renamed from app/assets/images/emoji/cartwheel_tone2.png)bin1235 -> 1235 bytes
-rw-r--r--public/-/emojis/1/cartwheel_tone3.png (renamed from app/assets/images/emoji/cartwheel_tone3.png)bin1229 -> 1229 bytes
-rw-r--r--public/-/emojis/1/cartwheel_tone4.png (renamed from app/assets/images/emoji/cartwheel_tone4.png)bin1227 -> 1227 bytes
-rw-r--r--public/-/emojis/1/cartwheel_tone5.png (renamed from app/assets/images/emoji/cartwheel_tone5.png)bin1214 -> 1214 bytes
-rw-r--r--public/-/emojis/1/cat.png (renamed from app/assets/images/emoji/cat.png)bin1354 -> 1354 bytes
-rw-r--r--public/-/emojis/1/cat2.png (renamed from app/assets/images/emoji/cat2.png)bin1781 -> 1781 bytes
-rw-r--r--public/-/emojis/1/cd.png (renamed from app/assets/images/emoji/cd.png)bin908 -> 908 bytes
-rw-r--r--public/-/emojis/1/chains.png (renamed from app/assets/images/emoji/chains.png)bin708 -> 708 bytes
-rw-r--r--public/-/emojis/1/champagne.png (renamed from app/assets/images/emoji/champagne.png)bin1205 -> 1205 bytes
-rw-r--r--public/-/emojis/1/champagne_glass.png (renamed from app/assets/images/emoji/champagne_glass.png)bin1984 -> 1984 bytes
-rw-r--r--public/-/emojis/1/chart.png (renamed from app/assets/images/emoji/chart.png)bin724 -> 724 bytes
-rw-r--r--public/-/emojis/1/chart_with_downwards_trend.png (renamed from app/assets/images/emoji/chart_with_downwards_trend.png)bin709 -> 709 bytes
-rw-r--r--public/-/emojis/1/chart_with_upwards_trend.png (renamed from app/assets/images/emoji/chart_with_upwards_trend.png)bin688 -> 688 bytes
-rw-r--r--public/-/emojis/1/checkered_flag.png (renamed from app/assets/images/emoji/checkered_flag.png)bin787 -> 787 bytes
-rw-r--r--public/-/emojis/1/cheese.png (renamed from app/assets/images/emoji/cheese.png)bin1697 -> 1697 bytes
-rw-r--r--public/-/emojis/1/cherries.png (renamed from app/assets/images/emoji/cherries.png)bin1211 -> 1211 bytes
-rw-r--r--public/-/emojis/1/cherry_blossom.png (renamed from app/assets/images/emoji/cherry_blossom.png)bin1129 -> 1129 bytes
-rw-r--r--public/-/emojis/1/chestnut.png (renamed from app/assets/images/emoji/chestnut.png)bin1337 -> 1337 bytes
-rw-r--r--public/-/emojis/1/chicken.png (renamed from app/assets/images/emoji/chicken.png)bin1267 -> 1267 bytes
-rw-r--r--public/-/emojis/1/children_crossing.png (renamed from app/assets/images/emoji/children_crossing.png)bin778 -> 778 bytes
-rw-r--r--public/-/emojis/1/chipmunk.png (renamed from app/assets/images/emoji/chipmunk.png)bin1454 -> 1454 bytes
-rw-r--r--public/-/emojis/1/chocolate_bar.png (renamed from app/assets/images/emoji/chocolate_bar.png)bin771 -> 771 bytes
-rw-r--r--public/-/emojis/1/christmas_tree.png (renamed from app/assets/images/emoji/christmas_tree.png)bin1542 -> 1542 bytes
-rw-r--r--public/-/emojis/1/church.png (renamed from app/assets/images/emoji/church.png)bin1298 -> 1298 bytes
-rw-r--r--public/-/emojis/1/cinema.png (renamed from app/assets/images/emoji/cinema.png)bin585 -> 585 bytes
-rw-r--r--public/-/emojis/1/circus_tent.png (renamed from app/assets/images/emoji/circus_tent.png)bin1369 -> 1369 bytes
-rw-r--r--public/-/emojis/1/city_dusk.png (renamed from app/assets/images/emoji/city_dusk.png)bin431 -> 431 bytes
-rw-r--r--public/-/emojis/1/city_sunset.png (renamed from app/assets/images/emoji/city_sunset.png)bin997 -> 997 bytes
-rw-r--r--public/-/emojis/1/cityscape.png (renamed from app/assets/images/emoji/cityscape.png)bin599 -> 599 bytes
-rw-r--r--public/-/emojis/1/cl.png (renamed from app/assets/images/emoji/cl.png)bin393 -> 393 bytes
-rw-r--r--public/-/emojis/1/clap.png (renamed from app/assets/images/emoji/clap.png)bin1456 -> 1456 bytes
-rw-r--r--public/-/emojis/1/clap_tone1.png (renamed from app/assets/images/emoji/clap_tone1.png)bin1458 -> 1458 bytes
-rw-r--r--public/-/emojis/1/clap_tone2.png (renamed from app/assets/images/emoji/clap_tone2.png)bin1458 -> 1458 bytes
-rw-r--r--public/-/emojis/1/clap_tone3.png (renamed from app/assets/images/emoji/clap_tone3.png)bin1458 -> 1458 bytes
-rw-r--r--public/-/emojis/1/clap_tone4.png (renamed from app/assets/images/emoji/clap_tone4.png)bin1458 -> 1458 bytes
-rw-r--r--public/-/emojis/1/clap_tone5.png (renamed from app/assets/images/emoji/clap_tone5.png)bin1444 -> 1444 bytes
-rw-r--r--public/-/emojis/1/clapper.png (renamed from app/assets/images/emoji/clapper.png)bin1535 -> 1535 bytes
-rw-r--r--public/-/emojis/1/classical_building.png (renamed from app/assets/images/emoji/classical_building.png)bin1006 -> 1006 bytes
-rw-r--r--public/-/emojis/1/clipboard.png (renamed from app/assets/images/emoji/clipboard.png)bin1345 -> 1345 bytes
-rw-r--r--public/-/emojis/1/clock.png (renamed from app/assets/images/emoji/clock.png)bin592 -> 592 bytes
-rw-r--r--public/-/emojis/1/clock1.png (renamed from app/assets/images/emoji/clock1.png)bin586 -> 586 bytes
-rw-r--r--public/-/emojis/1/clock10.png (renamed from app/assets/images/emoji/clock10.png)bin593 -> 593 bytes
-rw-r--r--public/-/emojis/1/clock1030.png (renamed from app/assets/images/emoji/clock1030.png)bin530 -> 530 bytes
-rw-r--r--public/-/emojis/1/clock11.png (renamed from app/assets/images/emoji/clock11.png)bin590 -> 590 bytes
-rw-r--r--public/-/emojis/1/clock1130.png (renamed from app/assets/images/emoji/clock1130.png)bin583 -> 583 bytes
-rw-r--r--public/-/emojis/1/clock12.png (renamed from app/assets/images/emoji/clock12.png)bin480 -> 480 bytes
-rw-r--r--public/-/emojis/1/clock1230.png (renamed from app/assets/images/emoji/clock1230.png)bin579 -> 579 bytes
-rw-r--r--public/-/emojis/1/clock130.png (renamed from app/assets/images/emoji/clock130.png)bin526 -> 526 bytes
-rw-r--r--public/-/emojis/1/clock2.png (renamed from app/assets/images/emoji/clock2.png)bin591 -> 591 bytes
-rw-r--r--public/-/emojis/1/clock230.png (renamed from app/assets/images/emoji/clock230.png)bin576 -> 576 bytes
-rw-r--r--public/-/emojis/1/clock3.png (renamed from app/assets/images/emoji/clock3.png)bin482 -> 482 bytes
-rw-r--r--public/-/emojis/1/clock330.png (renamed from app/assets/images/emoji/clock330.png)bin568 -> 568 bytes
-rw-r--r--public/-/emojis/1/clock4.png (renamed from app/assets/images/emoji/clock4.png)bin592 -> 592 bytes
-rw-r--r--public/-/emojis/1/clock430.png (renamed from app/assets/images/emoji/clock430.png)bin531 -> 531 bytes
-rw-r--r--public/-/emojis/1/clock5.png (renamed from app/assets/images/emoji/clock5.png)bin585 -> 585 bytes
-rw-r--r--public/-/emojis/1/clock530.png (renamed from app/assets/images/emoji/clock530.png)bin552 -> 552 bytes
-rw-r--r--public/-/emojis/1/clock6.png (renamed from app/assets/images/emoji/clock6.png)bin466 -> 466 bytes
-rw-r--r--public/-/emojis/1/clock630.png (renamed from app/assets/images/emoji/clock630.png)bin536 -> 536 bytes
-rw-r--r--public/-/emojis/1/clock7.png (renamed from app/assets/images/emoji/clock7.png)bin581 -> 581 bytes
-rw-r--r--public/-/emojis/1/clock730.png (renamed from app/assets/images/emoji/clock730.png)bin531 -> 531 bytes
-rw-r--r--public/-/emojis/1/clock8.png (renamed from app/assets/images/emoji/clock8.png)bin590 -> 590 bytes
-rw-r--r--public/-/emojis/1/clock830.png (renamed from app/assets/images/emoji/clock830.png)bin570 -> 570 bytes
-rw-r--r--public/-/emojis/1/clock9.png (renamed from app/assets/images/emoji/clock9.png)bin484 -> 484 bytes
-rw-r--r--public/-/emojis/1/clock930.png (renamed from app/assets/images/emoji/clock930.png)bin576 -> 576 bytes
-rw-r--r--public/-/emojis/1/closed_book.png (renamed from app/assets/images/emoji/closed_book.png)bin1359 -> 1359 bytes
-rw-r--r--public/-/emojis/1/closed_lock_with_key.png (renamed from app/assets/images/emoji/closed_lock_with_key.png)bin1250 -> 1250 bytes
-rw-r--r--public/-/emojis/1/closed_umbrella.png (renamed from app/assets/images/emoji/closed_umbrella.png)bin1002 -> 1002 bytes
-rw-r--r--public/-/emojis/1/cloud.png (renamed from app/assets/images/emoji/cloud.png)bin626 -> 626 bytes
-rw-r--r--public/-/emojis/1/cloud_lightning.png (renamed from app/assets/images/emoji/cloud_lightning.png)bin767 -> 767 bytes
-rw-r--r--public/-/emojis/1/cloud_rain.png (renamed from app/assets/images/emoji/cloud_rain.png)bin876 -> 876 bytes
-rw-r--r--public/-/emojis/1/cloud_snow.png (renamed from app/assets/images/emoji/cloud_snow.png)bin823 -> 823 bytes
-rw-r--r--public/-/emojis/1/cloud_tornado.png (renamed from app/assets/images/emoji/cloud_tornado.png)bin1519 -> 1519 bytes
-rw-r--r--public/-/emojis/1/clown.png (renamed from app/assets/images/emoji/clown.png)bin1818 -> 1818 bytes
-rw-r--r--public/-/emojis/1/clubs.png (renamed from app/assets/images/emoji/clubs.png)bin458 -> 458 bytes
-rw-r--r--public/-/emojis/1/cocktail.png (renamed from app/assets/images/emoji/cocktail.png)bin1027 -> 1027 bytes
-rw-r--r--public/-/emojis/1/coffee.png (renamed from app/assets/images/emoji/coffee.png)bin1679 -> 1679 bytes
-rw-r--r--public/-/emojis/1/coffin.png (renamed from app/assets/images/emoji/coffin.png)bin2195 -> 2195 bytes
-rw-r--r--public/-/emojis/1/cold_sweat.png (renamed from app/assets/images/emoji/cold_sweat.png)bin971 -> 971 bytes
-rw-r--r--public/-/emojis/1/comet.png (renamed from app/assets/images/emoji/comet.png)bin1819 -> 1819 bytes
-rw-r--r--public/-/emojis/1/compression.png (renamed from app/assets/images/emoji/compression.png)bin1612 -> 1612 bytes
-rw-r--r--public/-/emojis/1/computer.png (renamed from app/assets/images/emoji/computer.png)bin369 -> 369 bytes
-rw-r--r--public/-/emojis/1/confetti_ball.png (renamed from app/assets/images/emoji/confetti_ball.png)bin1703 -> 1703 bytes
-rw-r--r--public/-/emojis/1/confounded.png (renamed from app/assets/images/emoji/confounded.png)bin844 -> 844 bytes
-rw-r--r--public/-/emojis/1/confused.png (renamed from app/assets/images/emoji/confused.png)bin647 -> 647 bytes
-rw-r--r--public/-/emojis/1/congratulations.png (renamed from app/assets/images/emoji/congratulations.png)bin729 -> 729 bytes
-rw-r--r--public/-/emojis/1/construction.png (renamed from app/assets/images/emoji/construction.png)bin1083 -> 1083 bytes
-rw-r--r--public/-/emojis/1/construction_site.png (renamed from app/assets/images/emoji/construction_site.png)bin668 -> 668 bytes
-rw-r--r--public/-/emojis/1/construction_worker.png (renamed from app/assets/images/emoji/construction_worker.png)bin1126 -> 1126 bytes
-rw-r--r--public/-/emojis/1/construction_worker_tone1.png (renamed from app/assets/images/emoji/construction_worker_tone1.png)bin1102 -> 1102 bytes
-rw-r--r--public/-/emojis/1/construction_worker_tone2.png (renamed from app/assets/images/emoji/construction_worker_tone2.png)bin1102 -> 1102 bytes
-rw-r--r--public/-/emojis/1/construction_worker_tone3.png (renamed from app/assets/images/emoji/construction_worker_tone3.png)bin1102 -> 1102 bytes
-rw-r--r--public/-/emojis/1/construction_worker_tone4.png (renamed from app/assets/images/emoji/construction_worker_tone4.png)bin1095 -> 1095 bytes
-rw-r--r--public/-/emojis/1/construction_worker_tone5.png (renamed from app/assets/images/emoji/construction_worker_tone5.png)bin1119 -> 1119 bytes
-rw-r--r--public/-/emojis/1/control_knobs.png (renamed from app/assets/images/emoji/control_knobs.png)bin1104 -> 1104 bytes
-rw-r--r--public/-/emojis/1/convenience_store.png (renamed from app/assets/images/emoji/convenience_store.png)bin528 -> 528 bytes
-rw-r--r--public/-/emojis/1/cookie.png (renamed from app/assets/images/emoji/cookie.png)bin1351 -> 1351 bytes
-rw-r--r--public/-/emojis/1/cooking.png (renamed from app/assets/images/emoji/cooking.png)bin764 -> 764 bytes
-rw-r--r--public/-/emojis/1/cool.png (renamed from app/assets/images/emoji/cool.png)bin396 -> 396 bytes
-rw-r--r--public/-/emojis/1/cop.png (renamed from app/assets/images/emoji/cop.png)bin1440 -> 1440 bytes
-rw-r--r--public/-/emojis/1/cop_tone1.png (renamed from app/assets/images/emoji/cop_tone1.png)bin1421 -> 1421 bytes
-rw-r--r--public/-/emojis/1/cop_tone2.png (renamed from app/assets/images/emoji/cop_tone2.png)bin1424 -> 1424 bytes
-rw-r--r--public/-/emojis/1/cop_tone3.png (renamed from app/assets/images/emoji/cop_tone3.png)bin1419 -> 1419 bytes
-rw-r--r--public/-/emojis/1/cop_tone4.png (renamed from app/assets/images/emoji/cop_tone4.png)bin1417 -> 1417 bytes
-rw-r--r--public/-/emojis/1/cop_tone5.png (renamed from app/assets/images/emoji/cop_tone5.png)bin1433 -> 1433 bytes
-rw-r--r--public/-/emojis/1/copyright.png (renamed from app/assets/images/emoji/copyright.png)bin530 -> 530 bytes
-rw-r--r--public/-/emojis/1/corn.png (renamed from app/assets/images/emoji/corn.png)bin1547 -> 1547 bytes
-rw-r--r--public/-/emojis/1/couch.png (renamed from app/assets/images/emoji/couch.png)bin1362 -> 1362 bytes
-rw-r--r--public/-/emojis/1/couple.png (renamed from app/assets/images/emoji/couple.png)bin1537 -> 1537 bytes
-rw-r--r--public/-/emojis/1/couple_mm.png (renamed from app/assets/images/emoji/couple_mm.png)bin1091 -> 1091 bytes
-rw-r--r--public/-/emojis/1/couple_with_heart.png (renamed from app/assets/images/emoji/couple_with_heart.png)bin1285 -> 1285 bytes
-rw-r--r--public/-/emojis/1/couple_ww.png (renamed from app/assets/images/emoji/couple_ww.png)bin1034 -> 1034 bytes
-rw-r--r--public/-/emojis/1/couplekiss.png (renamed from app/assets/images/emoji/couplekiss.png)bin1380 -> 1380 bytes
-rw-r--r--public/-/emojis/1/cow.png (renamed from app/assets/images/emoji/cow.png)bin1640 -> 1640 bytes
-rw-r--r--public/-/emojis/1/cow2.png (renamed from app/assets/images/emoji/cow2.png)bin1810 -> 1810 bytes
-rw-r--r--public/-/emojis/1/cowboy.png (renamed from app/assets/images/emoji/cowboy.png)bin1353 -> 1353 bytes
-rw-r--r--public/-/emojis/1/crab.png (renamed from app/assets/images/emoji/crab.png)bin1475 -> 1475 bytes
-rw-r--r--public/-/emojis/1/crayon.png (renamed from app/assets/images/emoji/crayon.png)bin633 -> 633 bytes
-rw-r--r--public/-/emojis/1/credit_card.png (renamed from app/assets/images/emoji/credit_card.png)bin1012 -> 1012 bytes
-rw-r--r--public/-/emojis/1/crescent_moon.png (renamed from app/assets/images/emoji/crescent_moon.png)bin446 -> 446 bytes
-rw-r--r--public/-/emojis/1/cricket.png (renamed from app/assets/images/emoji/cricket.png)bin1060 -> 1060 bytes
-rw-r--r--public/-/emojis/1/crocodile.png (renamed from app/assets/images/emoji/crocodile.png)bin2408 -> 2408 bytes
-rw-r--r--public/-/emojis/1/croissant.png (renamed from app/assets/images/emoji/croissant.png)bin1313 -> 1313 bytes
-rw-r--r--public/-/emojis/1/cross.png (renamed from app/assets/images/emoji/cross.png)bin408 -> 408 bytes
-rw-r--r--public/-/emojis/1/crossed_flags.png (renamed from app/assets/images/emoji/crossed_flags.png)bin1239 -> 1239 bytes
-rw-r--r--public/-/emojis/1/crossed_swords.png (renamed from app/assets/images/emoji/crossed_swords.png)bin1591 -> 1591 bytes
-rw-r--r--public/-/emojis/1/crown.png (renamed from app/assets/images/emoji/crown.png)bin1534 -> 1534 bytes
-rw-r--r--public/-/emojis/1/cruise_ship.png (renamed from app/assets/images/emoji/cruise_ship.png)bin2272 -> 2272 bytes
-rw-r--r--public/-/emojis/1/cry.png (renamed from app/assets/images/emoji/cry.png)bin1123 -> 1123 bytes
-rw-r--r--public/-/emojis/1/crying_cat_face.png (renamed from app/assets/images/emoji/crying_cat_face.png)bin1875 -> 1875 bytes
-rw-r--r--public/-/emojis/1/crystal_ball.png (renamed from app/assets/images/emoji/crystal_ball.png)bin1913 -> 1913 bytes
-rw-r--r--public/-/emojis/1/cucumber.png (renamed from app/assets/images/emoji/cucumber.png)bin1357 -> 1357 bytes
-rw-r--r--public/-/emojis/1/cupid.png (renamed from app/assets/images/emoji/cupid.png)bin846 -> 846 bytes
-rw-r--r--public/-/emojis/1/curly_loop.png (renamed from app/assets/images/emoji/curly_loop.png)bin545 -> 545 bytes
-rw-r--r--public/-/emojis/1/currency_exchange.png (renamed from app/assets/images/emoji/currency_exchange.png)bin576 -> 576 bytes
-rw-r--r--public/-/emojis/1/curry.png (renamed from app/assets/images/emoji/curry.png)bin1754 -> 1754 bytes
-rw-r--r--public/-/emojis/1/custard.png (renamed from app/assets/images/emoji/custard.png)bin1273 -> 1273 bytes
-rw-r--r--public/-/emojis/1/customs.png (renamed from app/assets/images/emoji/customs.png)bin648 -> 648 bytes
-rw-r--r--public/-/emojis/1/cyclone.png (renamed from app/assets/images/emoji/cyclone.png)bin797 -> 797 bytes
-rw-r--r--public/-/emojis/1/dagger.png (renamed from app/assets/images/emoji/dagger.png)bin916 -> 916 bytes
-rw-r--r--public/-/emojis/1/dancer.png (renamed from app/assets/images/emoji/dancer.png)bin1405 -> 1405 bytes
-rw-r--r--public/-/emojis/1/dancer_tone1.png (renamed from app/assets/images/emoji/dancer_tone1.png)bin1420 -> 1420 bytes
-rw-r--r--public/-/emojis/1/dancer_tone2.png (renamed from app/assets/images/emoji/dancer_tone2.png)bin1423 -> 1423 bytes
-rw-r--r--public/-/emojis/1/dancer_tone3.png (renamed from app/assets/images/emoji/dancer_tone3.png)bin1429 -> 1429 bytes
-rw-r--r--public/-/emojis/1/dancer_tone4.png (renamed from app/assets/images/emoji/dancer_tone4.png)bin1428 -> 1428 bytes
-rw-r--r--public/-/emojis/1/dancer_tone5.png (renamed from app/assets/images/emoji/dancer_tone5.png)bin1418 -> 1418 bytes
-rw-r--r--public/-/emojis/1/dancers.png (renamed from app/assets/images/emoji/dancers.png)bin1872 -> 1872 bytes
-rw-r--r--public/-/emojis/1/dango.png (renamed from app/assets/images/emoji/dango.png)bin802 -> 802 bytes
-rw-r--r--public/-/emojis/1/dark_sunglasses.png (renamed from app/assets/images/emoji/dark_sunglasses.png)bin829 -> 829 bytes
-rw-r--r--public/-/emojis/1/dart.png (renamed from app/assets/images/emoji/dart.png)bin1374 -> 1374 bytes
-rw-r--r--public/-/emojis/1/dash.png (renamed from app/assets/images/emoji/dash.png)bin840 -> 840 bytes
-rw-r--r--public/-/emojis/1/date.png (renamed from app/assets/images/emoji/date.png)bin788 -> 788 bytes
-rw-r--r--public/-/emojis/1/deciduous_tree.png (renamed from app/assets/images/emoji/deciduous_tree.png)bin1267 -> 1267 bytes
-rw-r--r--public/-/emojis/1/deer.png (renamed from app/assets/images/emoji/deer.png)bin1606 -> 1606 bytes
-rw-r--r--public/-/emojis/1/department_store.png (renamed from app/assets/images/emoji/department_store.png)bin673 -> 673 bytes
-rw-r--r--public/-/emojis/1/desert.png (renamed from app/assets/images/emoji/desert.png)bin1443 -> 1443 bytes
-rw-r--r--public/-/emojis/1/desktop.png (renamed from app/assets/images/emoji/desktop.png)bin311 -> 311 bytes
-rw-r--r--public/-/emojis/1/diamond_shape_with_a_dot_inside.png (renamed from app/assets/images/emoji/diamond_shape_with_a_dot_inside.png)bin693 -> 693 bytes
-rw-r--r--public/-/emojis/1/diamonds.png (renamed from app/assets/images/emoji/diamonds.png)bin247 -> 247 bytes
-rw-r--r--public/-/emojis/1/disappointed.png (renamed from app/assets/images/emoji/disappointed.png)bin757 -> 757 bytes
-rw-r--r--public/-/emojis/1/disappointed_relieved.png (renamed from app/assets/images/emoji/disappointed_relieved.png)bin835 -> 835 bytes
-rw-r--r--public/-/emojis/1/dividers.png (renamed from app/assets/images/emoji/dividers.png)bin810 -> 810 bytes
-rw-r--r--public/-/emojis/1/dizzy.png (renamed from app/assets/images/emoji/dizzy.png)bin795 -> 795 bytes
-rw-r--r--public/-/emojis/1/dizzy_face.png (renamed from app/assets/images/emoji/dizzy_face.png)bin710 -> 710 bytes
-rw-r--r--public/-/emojis/1/do_not_litter.png (renamed from app/assets/images/emoji/do_not_litter.png)bin1010 -> 1010 bytes
-rw-r--r--public/-/emojis/1/dog.png (renamed from app/assets/images/emoji/dog.png)bin1674 -> 1674 bytes
-rw-r--r--public/-/emojis/1/dog2.png (renamed from app/assets/images/emoji/dog2.png)bin2085 -> 2085 bytes
-rw-r--r--public/-/emojis/1/dollar.png (renamed from app/assets/images/emoji/dollar.png)bin405 -> 405 bytes
-rw-r--r--public/-/emojis/1/dolls.png (renamed from app/assets/images/emoji/dolls.png)bin2249 -> 2249 bytes
-rw-r--r--public/-/emojis/1/dolphin.png (renamed from app/assets/images/emoji/dolphin.png)bin1697 -> 1697 bytes
-rw-r--r--public/-/emojis/1/door.png (renamed from app/assets/images/emoji/door.png)bin1105 -> 1105 bytes
-rw-r--r--public/-/emojis/1/doughnut.png (renamed from app/assets/images/emoji/doughnut.png)bin1322 -> 1322 bytes
-rw-r--r--public/-/emojis/1/dove.png (renamed from app/assets/images/emoji/dove.png)bin967 -> 967 bytes
-rw-r--r--public/-/emojis/1/dragon.png (renamed from app/assets/images/emoji/dragon.png)bin1574 -> 1574 bytes
-rw-r--r--public/-/emojis/1/dragon_face.png (renamed from app/assets/images/emoji/dragon_face.png)bin1769 -> 1769 bytes
-rw-r--r--public/-/emojis/1/dress.png (renamed from app/assets/images/emoji/dress.png)bin1001 -> 1001 bytes
-rw-r--r--public/-/emojis/1/dromedary_camel.png (renamed from app/assets/images/emoji/dromedary_camel.png)bin1515 -> 1515 bytes
-rw-r--r--public/-/emojis/1/drooling_face.png (renamed from app/assets/images/emoji/drooling_face.png)bin1049 -> 1049 bytes
-rw-r--r--public/-/emojis/1/droplet.png (renamed from app/assets/images/emoji/droplet.png)bin411 -> 411 bytes
-rw-r--r--public/-/emojis/1/drum.png (renamed from app/assets/images/emoji/drum.png)bin1870 -> 1870 bytes
-rw-r--r--public/-/emojis/1/duck.png (renamed from app/assets/images/emoji/duck.png)bin1729 -> 1729 bytes
-rw-r--r--public/-/emojis/1/dvd.png (renamed from app/assets/images/emoji/dvd.png)bin933 -> 933 bytes
-rw-r--r--public/-/emojis/1/e-mail.png (renamed from app/assets/images/emoji/e-mail.png)bin1196 -> 1196 bytes
-rw-r--r--public/-/emojis/1/eagle.png (renamed from app/assets/images/emoji/eagle.png)bin2222 -> 2222 bytes
-rw-r--r--public/-/emojis/1/ear.png (renamed from app/assets/images/emoji/ear.png)bin860 -> 860 bytes
-rw-r--r--public/-/emojis/1/ear_of_rice.png (renamed from app/assets/images/emoji/ear_of_rice.png)bin1422 -> 1422 bytes
-rw-r--r--public/-/emojis/1/ear_tone1.png (renamed from app/assets/images/emoji/ear_tone1.png)bin860 -> 860 bytes
-rw-r--r--public/-/emojis/1/ear_tone2.png (renamed from app/assets/images/emoji/ear_tone2.png)bin860 -> 860 bytes
-rw-r--r--public/-/emojis/1/ear_tone3.png (renamed from app/assets/images/emoji/ear_tone3.png)bin860 -> 860 bytes
-rw-r--r--public/-/emojis/1/ear_tone4.png (renamed from app/assets/images/emoji/ear_tone4.png)bin860 -> 860 bytes
-rw-r--r--public/-/emojis/1/ear_tone5.png (renamed from app/assets/images/emoji/ear_tone5.png)bin860 -> 860 bytes
-rw-r--r--public/-/emojis/1/earth_africa.png (renamed from app/assets/images/emoji/earth_africa.png)bin978 -> 978 bytes
-rw-r--r--public/-/emojis/1/earth_americas.png (renamed from app/assets/images/emoji/earth_americas.png)bin1031 -> 1031 bytes
-rw-r--r--public/-/emojis/1/earth_asia.png (renamed from app/assets/images/emoji/earth_asia.png)bin966 -> 966 bytes
-rw-r--r--public/-/emojis/1/egg.png (renamed from app/assets/images/emoji/egg.png)bin710 -> 710 bytes
-rw-r--r--public/-/emojis/1/eggplant.png (renamed from app/assets/images/emoji/eggplant.png)bin773 -> 773 bytes
-rw-r--r--public/-/emojis/1/eight.png (renamed from app/assets/images/emoji/eight.png)bin608 -> 608 bytes
-rw-r--r--public/-/emojis/1/eight_pointed_black_star.png (renamed from app/assets/images/emoji/eight_pointed_black_star.png)bin493 -> 493 bytes
-rw-r--r--public/-/emojis/1/eight_spoked_asterisk.png (renamed from app/assets/images/emoji/eight_spoked_asterisk.png)bin493 -> 493 bytes
-rw-r--r--public/-/emojis/1/eject.png (renamed from app/assets/images/emoji/eject.png)bin548 -> 548 bytes
-rw-r--r--public/-/emojis/1/electric_plug.png (renamed from app/assets/images/emoji/electric_plug.png)bin548 -> 548 bytes
-rw-r--r--public/-/emojis/1/elephant.png (renamed from app/assets/images/emoji/elephant.png)bin1293 -> 1293 bytes
-rw-r--r--public/-/emojis/1/emojis.json10760
-rw-r--r--public/-/emojis/1/end.png (renamed from app/assets/images/emoji/end.png)bin393 -> 393 bytes
-rw-r--r--public/-/emojis/1/envelope.png (renamed from app/assets/images/emoji/envelope.png)bin916 -> 916 bytes
-rw-r--r--public/-/emojis/1/envelope_with_arrow.png (renamed from app/assets/images/emoji/envelope_with_arrow.png)bin1062 -> 1062 bytes
-rw-r--r--public/-/emojis/1/euro.png (renamed from app/assets/images/emoji/euro.png)bin460 -> 460 bytes
-rw-r--r--public/-/emojis/1/european_castle.png (renamed from app/assets/images/emoji/european_castle.png)bin965 -> 965 bytes
-rw-r--r--public/-/emojis/1/european_post_office.png (renamed from app/assets/images/emoji/european_post_office.png)bin551 -> 551 bytes
-rw-r--r--public/-/emojis/1/evergreen_tree.png (renamed from app/assets/images/emoji/evergreen_tree.png)bin719 -> 719 bytes
-rw-r--r--public/-/emojis/1/exclamation.png (renamed from app/assets/images/emoji/exclamation.png)bin354 -> 354 bytes
-rw-r--r--public/-/emojis/1/expressionless.png (renamed from app/assets/images/emoji/expressionless.png)bin438 -> 438 bytes
-rw-r--r--public/-/emojis/1/eye.png (renamed from app/assets/images/emoji/eye.png)bin664 -> 664 bytes
-rw-r--r--public/-/emojis/1/eye_in_speech_bubble.png (renamed from app/assets/images/emoji/eye_in_speech_bubble.png)bin698 -> 698 bytes
-rw-r--r--public/-/emojis/1/eyeglasses.png (renamed from app/assets/images/emoji/eyeglasses.png)bin577 -> 577 bytes
-rw-r--r--public/-/emojis/1/eyes.png (renamed from app/assets/images/emoji/eyes.png)bin791 -> 791 bytes
-rw-r--r--public/-/emojis/1/face_palm.png (renamed from app/assets/images/emoji/face_palm.png)bin1523 -> 1523 bytes
-rw-r--r--public/-/emojis/1/face_palm_tone1.png (renamed from app/assets/images/emoji/face_palm_tone1.png)bin1563 -> 1563 bytes
-rw-r--r--public/-/emojis/1/face_palm_tone2.png (renamed from app/assets/images/emoji/face_palm_tone2.png)bin1547 -> 1547 bytes
-rw-r--r--public/-/emojis/1/face_palm_tone3.png (renamed from app/assets/images/emoji/face_palm_tone3.png)bin1550 -> 1550 bytes
-rw-r--r--public/-/emojis/1/face_palm_tone4.png (renamed from app/assets/images/emoji/face_palm_tone4.png)bin1553 -> 1553 bytes
-rw-r--r--public/-/emojis/1/face_palm_tone5.png (renamed from app/assets/images/emoji/face_palm_tone5.png)bin1532 -> 1532 bytes
-rw-r--r--public/-/emojis/1/factory.png (renamed from app/assets/images/emoji/factory.png)bin936 -> 936 bytes
-rw-r--r--public/-/emojis/1/fallen_leaf.png (renamed from app/assets/images/emoji/fallen_leaf.png)bin951 -> 951 bytes
-rw-r--r--public/-/emojis/1/family.png (renamed from app/assets/images/emoji/family.png)bin1433 -> 1433 bytes
-rw-r--r--public/-/emojis/1/family_mmb.png (renamed from app/assets/images/emoji/family_mmb.png)bin1206 -> 1206 bytes
-rw-r--r--public/-/emojis/1/family_mmbb.png (renamed from app/assets/images/emoji/family_mmbb.png)bin1349 -> 1349 bytes
-rw-r--r--public/-/emojis/1/family_mmg.png (renamed from app/assets/images/emoji/family_mmg.png)bin1361 -> 1361 bytes
-rw-r--r--public/-/emojis/1/family_mmgb.png (renamed from app/assets/images/emoji/family_mmgb.png)bin1626 -> 1626 bytes
-rw-r--r--public/-/emojis/1/family_mmgg.png (renamed from app/assets/images/emoji/family_mmgg.png)bin1448 -> 1448 bytes
-rw-r--r--public/-/emojis/1/family_mwbb.png (renamed from app/assets/images/emoji/family_mwbb.png)bin1638 -> 1638 bytes
-rw-r--r--public/-/emojis/1/family_mwg.png (renamed from app/assets/images/emoji/family_mwg.png)bin1554 -> 1554 bytes
-rw-r--r--public/-/emojis/1/family_mwgb.png (renamed from app/assets/images/emoji/family_mwgb.png)bin1837 -> 1837 bytes
-rw-r--r--public/-/emojis/1/family_mwgg.png (renamed from app/assets/images/emoji/family_mwgg.png)bin1738 -> 1738 bytes
-rw-r--r--public/-/emojis/1/family_wwb.png (renamed from app/assets/images/emoji/family_wwb.png)bin1155 -> 1155 bytes
-rw-r--r--public/-/emojis/1/family_wwbb.png (renamed from app/assets/images/emoji/family_wwbb.png)bin1289 -> 1289 bytes
-rw-r--r--public/-/emojis/1/family_wwg.png (renamed from app/assets/images/emoji/family_wwg.png)bin1286 -> 1286 bytes
-rw-r--r--public/-/emojis/1/family_wwgb.png (renamed from app/assets/images/emoji/family_wwgb.png)bin1550 -> 1550 bytes
-rw-r--r--public/-/emojis/1/family_wwgg.png (renamed from app/assets/images/emoji/family_wwgg.png)bin1374 -> 1374 bytes
-rw-r--r--public/-/emojis/1/fast_forward.png (renamed from app/assets/images/emoji/fast_forward.png)bin523 -> 523 bytes
-rw-r--r--public/-/emojis/1/fax.png (renamed from app/assets/images/emoji/fax.png)bin1188 -> 1188 bytes
-rw-r--r--public/-/emojis/1/fearful.png (renamed from app/assets/images/emoji/fearful.png)bin1002 -> 1002 bytes
-rw-r--r--public/-/emojis/1/feet.png (renamed from app/assets/images/emoji/feet.png)bin603 -> 603 bytes
-rw-r--r--public/-/emojis/1/fencer.png (renamed from app/assets/images/emoji/fencer.png)bin1342 -> 1342 bytes
-rw-r--r--public/-/emojis/1/ferris_wheel.png (renamed from app/assets/images/emoji/ferris_wheel.png)bin2185 -> 2185 bytes
-rw-r--r--public/-/emojis/1/ferry.png (renamed from app/assets/images/emoji/ferry.png)bin528 -> 528 bytes
-rw-r--r--public/-/emojis/1/field_hockey.png (renamed from app/assets/images/emoji/field_hockey.png)bin947 -> 947 bytes
-rw-r--r--public/-/emojis/1/file_cabinet.png (renamed from app/assets/images/emoji/file_cabinet.png)bin1420 -> 1420 bytes
-rw-r--r--public/-/emojis/1/file_folder.png (renamed from app/assets/images/emoji/file_folder.png)bin1445 -> 1445 bytes
-rw-r--r--public/-/emojis/1/film_frames.png (renamed from app/assets/images/emoji/film_frames.png)bin560 -> 560 bytes
-rw-r--r--public/-/emojis/1/fingers_crossed.png (renamed from app/assets/images/emoji/fingers_crossed.png)bin1050 -> 1050 bytes
-rw-r--r--public/-/emojis/1/fingers_crossed_tone1.png (renamed from app/assets/images/emoji/fingers_crossed_tone1.png)bin1047 -> 1047 bytes
-rw-r--r--public/-/emojis/1/fingers_crossed_tone2.png (renamed from app/assets/images/emoji/fingers_crossed_tone2.png)bin1050 -> 1050 bytes
-rw-r--r--public/-/emojis/1/fingers_crossed_tone3.png (renamed from app/assets/images/emoji/fingers_crossed_tone3.png)bin1050 -> 1050 bytes
-rw-r--r--public/-/emojis/1/fingers_crossed_tone4.png (renamed from app/assets/images/emoji/fingers_crossed_tone4.png)bin1046 -> 1046 bytes
-rw-r--r--public/-/emojis/1/fingers_crossed_tone5.png (renamed from app/assets/images/emoji/fingers_crossed_tone5.png)bin1050 -> 1050 bytes
-rw-r--r--public/-/emojis/1/fire.png (renamed from app/assets/images/emoji/fire.png)bin1020 -> 1020 bytes
-rw-r--r--public/-/emojis/1/fire_engine.png (renamed from app/assets/images/emoji/fire_engine.png)bin1656 -> 1656 bytes
-rw-r--r--public/-/emojis/1/fireworks.png (renamed from app/assets/images/emoji/fireworks.png)bin1364 -> 1364 bytes
-rw-r--r--public/-/emojis/1/first_place.png (renamed from app/assets/images/emoji/first_place.png)bin1419 -> 1419 bytes
-rw-r--r--public/-/emojis/1/first_quarter_moon.png (renamed from app/assets/images/emoji/first_quarter_moon.png)bin1152 -> 1152 bytes
-rw-r--r--public/-/emojis/1/first_quarter_moon_with_face.png (renamed from app/assets/images/emoji/first_quarter_moon_with_face.png)bin1068 -> 1068 bytes
-rw-r--r--public/-/emojis/1/fish.png (renamed from app/assets/images/emoji/fish.png)bin1080 -> 1080 bytes
-rw-r--r--public/-/emojis/1/fish_cake.png (renamed from app/assets/images/emoji/fish_cake.png)bin1245 -> 1245 bytes
-rw-r--r--public/-/emojis/1/fishing_pole_and_fish.png (renamed from app/assets/images/emoji/fishing_pole_and_fish.png)bin1442 -> 1442 bytes
-rw-r--r--public/-/emojis/1/fist.png (renamed from app/assets/images/emoji/fist.png)bin1014 -> 1014 bytes
-rw-r--r--public/-/emojis/1/fist_tone1.png (renamed from app/assets/images/emoji/fist_tone1.png)bin1014 -> 1014 bytes
-rw-r--r--public/-/emojis/1/fist_tone2.png (renamed from app/assets/images/emoji/fist_tone2.png)bin1014 -> 1014 bytes
-rw-r--r--public/-/emojis/1/fist_tone3.png (renamed from app/assets/images/emoji/fist_tone3.png)bin1014 -> 1014 bytes
-rw-r--r--public/-/emojis/1/fist_tone4.png (renamed from app/assets/images/emoji/fist_tone4.png)bin1014 -> 1014 bytes
-rw-r--r--public/-/emojis/1/fist_tone5.png (renamed from app/assets/images/emoji/fist_tone5.png)bin1014 -> 1014 bytes
-rw-r--r--public/-/emojis/1/five.png (renamed from app/assets/images/emoji/five.png)bin577 -> 577 bytes
-rw-r--r--public/-/emojis/1/flag_ac.png (renamed from app/assets/images/emoji/flag_ac.png)bin1934 -> 1934 bytes
-rw-r--r--public/-/emojis/1/flag_ad.png (renamed from app/assets/images/emoji/flag_ad.png)bin1285 -> 1285 bytes
-rw-r--r--public/-/emojis/1/flag_ae.png (renamed from app/assets/images/emoji/flag_ae.png)bin544 -> 544 bytes
-rw-r--r--public/-/emojis/1/flag_af.png (renamed from app/assets/images/emoji/flag_af.png)bin942 -> 942 bytes
-rw-r--r--public/-/emojis/1/flag_ag.png (renamed from app/assets/images/emoji/flag_ag.png)bin913 -> 913 bytes
-rw-r--r--public/-/emojis/1/flag_ai.png (renamed from app/assets/images/emoji/flag_ai.png)bin1056 -> 1056 bytes
-rw-r--r--public/-/emojis/1/flag_al.png (renamed from app/assets/images/emoji/flag_al.png)bin905 -> 905 bytes
-rw-r--r--public/-/emojis/1/flag_am.png (renamed from app/assets/images/emoji/flag_am.png)bin514 -> 514 bytes
-rw-r--r--public/-/emojis/1/flag_ao.png (renamed from app/assets/images/emoji/flag_ao.png)bin997 -> 997 bytes
-rw-r--r--public/-/emojis/1/flag_aq.png (renamed from app/assets/images/emoji/flag_aq.png)bin657 -> 657 bytes
-rw-r--r--public/-/emojis/1/flag_ar.png (renamed from app/assets/images/emoji/flag_ar.png)bin975 -> 975 bytes
-rw-r--r--public/-/emojis/1/flag_as.png (renamed from app/assets/images/emoji/flag_as.png)bin1489 -> 1489 bytes
-rw-r--r--public/-/emojis/1/flag_at.png (renamed from app/assets/images/emoji/flag_at.png)bin430 -> 430 bytes
-rw-r--r--public/-/emojis/1/flag_au.png (renamed from app/assets/images/emoji/flag_au.png)bin962 -> 962 bytes
-rw-r--r--public/-/emojis/1/flag_aw.png (renamed from app/assets/images/emoji/flag_aw.png)bin709 -> 709 bytes
-rw-r--r--public/-/emojis/1/flag_ax.png (renamed from app/assets/images/emoji/flag_ax.png)bin496 -> 496 bytes
-rw-r--r--public/-/emojis/1/flag_az.png (renamed from app/assets/images/emoji/flag_az.png)bin709 -> 709 bytes
-rw-r--r--public/-/emojis/1/flag_ba.png (renamed from app/assets/images/emoji/flag_ba.png)bin848 -> 848 bytes
-rw-r--r--public/-/emojis/1/flag_bb.png (renamed from app/assets/images/emoji/flag_bb.png)bin789 -> 789 bytes
-rw-r--r--public/-/emojis/1/flag_bd.png (renamed from app/assets/images/emoji/flag_bd.png)bin490 -> 490 bytes
-rw-r--r--public/-/emojis/1/flag_be.png (renamed from app/assets/images/emoji/flag_be.png)bin444 -> 444 bytes
-rw-r--r--public/-/emojis/1/flag_bf.png (renamed from app/assets/images/emoji/flag_bf.png)bin717 -> 717 bytes
-rw-r--r--public/-/emojis/1/flag_bg.png (renamed from app/assets/images/emoji/flag_bg.png)bin513 -> 513 bytes
-rw-r--r--public/-/emojis/1/flag_bh.png (renamed from app/assets/images/emoji/flag_bh.png)bin593 -> 593 bytes
-rw-r--r--public/-/emojis/1/flag_bi.png (renamed from app/assets/images/emoji/flag_bi.png)bin795 -> 795 bytes
-rw-r--r--public/-/emojis/1/flag_bj.png (renamed from app/assets/images/emoji/flag_bj.png)bin554 -> 554 bytes
-rw-r--r--public/-/emojis/1/flag_bl.png (renamed from app/assets/images/emoji/flag_bl.png)bin1691 -> 1691 bytes
-rw-r--r--public/-/emojis/1/flag_black.png (renamed from app/assets/images/emoji/flag_black.png)bin702 -> 702 bytes
-rw-r--r--public/-/emojis/1/flag_bm.png (renamed from app/assets/images/emoji/flag_bm.png)bin1374 -> 1374 bytes
-rw-r--r--public/-/emojis/1/flag_bn.png (renamed from app/assets/images/emoji/flag_bn.png)bin1355 -> 1355 bytes
-rw-r--r--public/-/emojis/1/flag_bo.png (renamed from app/assets/images/emoji/flag_bo.png)bin1132 -> 1132 bytes
-rw-r--r--public/-/emojis/1/flag_bq.png (renamed from app/assets/images/emoji/flag_bq.png)bin1144 -> 1144 bytes
-rw-r--r--public/-/emojis/1/flag_br.png (renamed from app/assets/images/emoji/flag_br.png)bin819 -> 819 bytes
-rw-r--r--public/-/emojis/1/flag_bs.png (renamed from app/assets/images/emoji/flag_bs.png)bin448 -> 448 bytes
-rw-r--r--public/-/emojis/1/flag_bt.png (renamed from app/assets/images/emoji/flag_bt.png)bin1213 -> 1213 bytes
-rw-r--r--public/-/emojis/1/flag_bv.png (renamed from app/assets/images/emoji/flag_bv.png)bin495 -> 495 bytes
-rw-r--r--public/-/emojis/1/flag_bw.png (renamed from app/assets/images/emoji/flag_bw.png)bin391 -> 391 bytes
-rw-r--r--public/-/emojis/1/flag_by.png (renamed from app/assets/images/emoji/flag_by.png)bin1120 -> 1120 bytes
-rw-r--r--public/-/emojis/1/flag_bz.png (renamed from app/assets/images/emoji/flag_bz.png)bin1595 -> 1595 bytes
-rw-r--r--public/-/emojis/1/flag_ca.png (renamed from app/assets/images/emoji/flag_ca.png)bin755 -> 755 bytes
-rw-r--r--public/-/emojis/1/flag_cc.png (renamed from app/assets/images/emoji/flag_cc.png)bin851 -> 851 bytes
-rw-r--r--public/-/emojis/1/flag_cd.png (renamed from app/assets/images/emoji/flag_cd.png)bin707 -> 707 bytes
-rw-r--r--public/-/emojis/1/flag_cf.png (renamed from app/assets/images/emoji/flag_cf.png)bin673 -> 673 bytes
-rw-r--r--public/-/emojis/1/flag_cg.png (renamed from app/assets/images/emoji/flag_cg.png)bin586 -> 586 bytes
-rw-r--r--public/-/emojis/1/flag_ch.png (renamed from app/assets/images/emoji/flag_ch.png)bin390 -> 390 bytes
-rw-r--r--public/-/emojis/1/flag_ci.png (renamed from app/assets/images/emoji/flag_ci.png)bin440 -> 440 bytes
-rw-r--r--public/-/emojis/1/flag_ck.png (renamed from app/assets/images/emoji/flag_ck.png)bin1083 -> 1083 bytes
-rw-r--r--public/-/emojis/1/flag_cl.png (renamed from app/assets/images/emoji/flag_cl.png)bin748 -> 748 bytes
-rw-r--r--public/-/emojis/1/flag_cm.png (renamed from app/assets/images/emoji/flag_cm.png)bin627 -> 627 bytes
-rw-r--r--public/-/emojis/1/flag_cn.png (renamed from app/assets/images/emoji/flag_cn.png)bin676 -> 676 bytes
-rw-r--r--public/-/emojis/1/flag_co.png (renamed from app/assets/images/emoji/flag_co.png)bin524 -> 524 bytes
-rw-r--r--public/-/emojis/1/flag_cp.png (renamed from app/assets/images/emoji/flag_cp.png)bin443 -> 443 bytes
-rw-r--r--public/-/emojis/1/flag_cr.png (renamed from app/assets/images/emoji/flag_cr.png)bin419 -> 419 bytes
-rw-r--r--public/-/emojis/1/flag_cu.png (renamed from app/assets/images/emoji/flag_cu.png)bin586 -> 586 bytes
-rw-r--r--public/-/emojis/1/flag_cv.png (renamed from app/assets/images/emoji/flag_cv.png)bin642 -> 642 bytes
-rw-r--r--public/-/emojis/1/flag_cw.png (renamed from app/assets/images/emoji/flag_cw.png)bin665 -> 665 bytes
-rw-r--r--public/-/emojis/1/flag_cx.png (renamed from app/assets/images/emoji/flag_cx.png)bin1142 -> 1142 bytes
-rw-r--r--public/-/emojis/1/flag_cy.png (renamed from app/assets/images/emoji/flag_cy.png)bin830 -> 830 bytes
-rw-r--r--public/-/emojis/1/flag_cz.png (renamed from app/assets/images/emoji/flag_cz.png)bin600 -> 600 bytes
-rw-r--r--public/-/emojis/1/flag_de.png (renamed from app/assets/images/emoji/flag_de.png)bin502 -> 502 bytes
-rw-r--r--public/-/emojis/1/flag_dg.png (renamed from app/assets/images/emoji/flag_dg.png)bin1911 -> 1911 bytes
-rw-r--r--public/-/emojis/1/flag_dj.png (renamed from app/assets/images/emoji/flag_dj.png)bin753 -> 753 bytes
-rw-r--r--public/-/emojis/1/flag_dk.png (renamed from app/assets/images/emoji/flag_dk.png)bin450 -> 450 bytes
-rw-r--r--public/-/emojis/1/flag_dm.png (renamed from app/assets/images/emoji/flag_dm.png)bin1075 -> 1075 bytes
-rw-r--r--public/-/emojis/1/flag_do.png (renamed from app/assets/images/emoji/flag_do.png)bin1135 -> 1135 bytes
-rw-r--r--public/-/emojis/1/flag_dz.png (renamed from app/assets/images/emoji/flag_dz.png)bin734 -> 734 bytes
-rw-r--r--public/-/emojis/1/flag_ea.png (renamed from app/assets/images/emoji/flag_ea.png)bin1337 -> 1337 bytes
-rw-r--r--public/-/emojis/1/flag_ec.png (renamed from app/assets/images/emoji/flag_ec.png)bin1431 -> 1431 bytes
-rw-r--r--public/-/emojis/1/flag_ee.png (renamed from app/assets/images/emoji/flag_ee.png)bin512 -> 512 bytes
-rw-r--r--public/-/emojis/1/flag_eg.png (renamed from app/assets/images/emoji/flag_eg.png)bin818 -> 818 bytes
-rw-r--r--public/-/emojis/1/flag_eh.png (renamed from app/assets/images/emoji/flag_eh.png)bin742 -> 742 bytes
-rw-r--r--public/-/emojis/1/flag_er.png (renamed from app/assets/images/emoji/flag_er.png)bin1218 -> 1218 bytes
-rw-r--r--public/-/emojis/1/flag_es.png (renamed from app/assets/images/emoji/flag_es.png)bin1337 -> 1337 bytes
-rw-r--r--public/-/emojis/1/flag_et.png (renamed from app/assets/images/emoji/flag_et.png)bin947 -> 947 bytes
-rw-r--r--public/-/emojis/1/flag_eu.png (renamed from app/assets/images/emoji/flag_eu.png)bin760 -> 760 bytes
-rw-r--r--public/-/emojis/1/flag_fi.png (renamed from app/assets/images/emoji/flag_fi.png)bin487 -> 487 bytes
-rw-r--r--public/-/emojis/1/flag_fj.png (renamed from app/assets/images/emoji/flag_fj.png)bin1381 -> 1381 bytes
-rw-r--r--public/-/emojis/1/flag_fk.png (renamed from app/assets/images/emoji/flag_fk.png)bin1558 -> 1558 bytes
-rw-r--r--public/-/emojis/1/flag_fm.png (renamed from app/assets/images/emoji/flag_fm.png)bin554 -> 554 bytes
-rw-r--r--public/-/emojis/1/flag_fo.png (renamed from app/assets/images/emoji/flag_fo.png)bin495 -> 495 bytes
-rw-r--r--public/-/emojis/1/flag_fr.png (renamed from app/assets/images/emoji/flag_fr.png)bin443 -> 443 bytes
-rw-r--r--public/-/emojis/1/flag_ga.png (renamed from app/assets/images/emoji/flag_ga.png)bin512 -> 512 bytes
-rw-r--r--public/-/emojis/1/flag_gb.png (renamed from app/assets/images/emoji/flag_gb.png)bin919 -> 919 bytes
-rw-r--r--public/-/emojis/1/flag_gd.png (renamed from app/assets/images/emoji/flag_gd.png)bin1017 -> 1017 bytes
-rw-r--r--public/-/emojis/1/flag_ge.png (renamed from app/assets/images/emoji/flag_ge.png)bin583 -> 583 bytes
-rw-r--r--public/-/emojis/1/flag_gf.png (renamed from app/assets/images/emoji/flag_gf.png)bin865 -> 865 bytes
-rw-r--r--public/-/emojis/1/flag_gg.png (renamed from app/assets/images/emoji/flag_gg.png)bin521 -> 521 bytes
-rw-r--r--public/-/emojis/1/flag_gh.png (renamed from app/assets/images/emoji/flag_gh.png)bin723 -> 723 bytes
-rw-r--r--public/-/emojis/1/flag_gi.png (renamed from app/assets/images/emoji/flag_gi.png)bin1053 -> 1053 bytes
-rw-r--r--public/-/emojis/1/flag_gl.png (renamed from app/assets/images/emoji/flag_gl.png)bin700 -> 700 bytes
-rw-r--r--public/-/emojis/1/flag_gm.png (renamed from app/assets/images/emoji/flag_gm.png)bin501 -> 501 bytes
-rw-r--r--public/-/emojis/1/flag_gn.png (renamed from app/assets/images/emoji/flag_gn.png)bin434 -> 434 bytes
-rw-r--r--public/-/emojis/1/flag_gp.png (renamed from app/assets/images/emoji/flag_gp.png)bin1587 -> 1587 bytes
-rw-r--r--public/-/emojis/1/flag_gq.png (renamed from app/assets/images/emoji/flag_gq.png)bin1132 -> 1132 bytes
-rw-r--r--public/-/emojis/1/flag_gr.png (renamed from app/assets/images/emoji/flag_gr.png)bin549 -> 549 bytes
-rw-r--r--public/-/emojis/1/flag_gs.png (renamed from app/assets/images/emoji/flag_gs.png)bin2115 -> 2115 bytes
-rw-r--r--public/-/emojis/1/flag_gt.png (renamed from app/assets/images/emoji/flag_gt.png)bin1087 -> 1087 bytes
-rw-r--r--public/-/emojis/1/flag_gu.png (renamed from app/assets/images/emoji/flag_gu.png)bin1045 -> 1045 bytes
-rw-r--r--public/-/emojis/1/flag_gw.png (renamed from app/assets/images/emoji/flag_gw.png)bin705 -> 705 bytes
-rw-r--r--public/-/emojis/1/flag_gy.png (renamed from app/assets/images/emoji/flag_gy.png)bin690 -> 690 bytes
-rw-r--r--public/-/emojis/1/flag_hk.png (renamed from app/assets/images/emoji/flag_hk.png)bin759 -> 759 bytes
-rw-r--r--public/-/emojis/1/flag_hm.png (renamed from app/assets/images/emoji/flag_hm.png)bin1036 -> 1036 bytes
-rw-r--r--public/-/emojis/1/flag_hn.png (renamed from app/assets/images/emoji/flag_hn.png)bin513 -> 513 bytes
-rw-r--r--public/-/emojis/1/flag_hr.png (renamed from app/assets/images/emoji/flag_hr.png)bin1411 -> 1411 bytes
-rw-r--r--public/-/emojis/1/flag_ht.png (renamed from app/assets/images/emoji/flag_ht.png)bin1205 -> 1205 bytes
-rw-r--r--public/-/emojis/1/flag_hu.png (renamed from app/assets/images/emoji/flag_hu.png)bin513 -> 513 bytes
-rw-r--r--public/-/emojis/1/flag_ic.png (renamed from app/assets/images/emoji/flag_ic.png)bin1330 -> 1330 bytes
-rw-r--r--public/-/emojis/1/flag_id.png (renamed from app/assets/images/emoji/flag_id.png)bin498 -> 498 bytes
-rw-r--r--public/-/emojis/1/flag_ie.png (renamed from app/assets/images/emoji/flag_ie.png)bin478 -> 478 bytes
-rw-r--r--public/-/emojis/1/flag_il.png (renamed from app/assets/images/emoji/flag_il.png)bin658 -> 658 bytes
-rw-r--r--public/-/emojis/1/flag_im.png (renamed from app/assets/images/emoji/flag_im.png)bin976 -> 976 bytes
-rw-r--r--public/-/emojis/1/flag_in.png (renamed from app/assets/images/emoji/flag_in.png)bin773 -> 773 bytes
-rw-r--r--public/-/emojis/1/flag_io.png (renamed from app/assets/images/emoji/flag_io.png)bin1911 -> 1911 bytes
-rw-r--r--public/-/emojis/1/flag_iq.png (renamed from app/assets/images/emoji/flag_iq.png)bin811 -> 811 bytes
-rw-r--r--public/-/emojis/1/flag_ir.png (renamed from app/assets/images/emoji/flag_ir.png)bin1036 -> 1036 bytes
-rw-r--r--public/-/emojis/1/flag_is.png (renamed from app/assets/images/emoji/flag_is.png)bin491 -> 491 bytes
-rw-r--r--public/-/emojis/1/flag_it.png (renamed from app/assets/images/emoji/flag_it.png)bin472 -> 472 bytes
-rw-r--r--public/-/emojis/1/flag_je.png (renamed from app/assets/images/emoji/flag_je.png)bin956 -> 956 bytes
-rw-r--r--public/-/emojis/1/flag_jm.png (renamed from app/assets/images/emoji/flag_jm.png)bin837 -> 837 bytes
-rw-r--r--public/-/emojis/1/flag_jo.png (renamed from app/assets/images/emoji/flag_jo.png)bin740 -> 740 bytes
-rw-r--r--public/-/emojis/1/flag_jp.png (renamed from app/assets/images/emoji/flag_jp.png)bin455 -> 455 bytes
-rw-r--r--public/-/emojis/1/flag_ke.png (renamed from app/assets/images/emoji/flag_ke.png)bin1160 -> 1160 bytes
-rw-r--r--public/-/emojis/1/flag_kg.png (renamed from app/assets/images/emoji/flag_kg.png)bin1080 -> 1080 bytes
-rw-r--r--public/-/emojis/1/flag_kh.png (renamed from app/assets/images/emoji/flag_kh.png)bin872 -> 872 bytes
-rw-r--r--public/-/emojis/1/flag_ki.png (renamed from app/assets/images/emoji/flag_ki.png)bin1369 -> 1369 bytes
-rw-r--r--public/-/emojis/1/flag_km.png (renamed from app/assets/images/emoji/flag_km.png)bin783 -> 783 bytes
-rw-r--r--public/-/emojis/1/flag_kn.png (renamed from app/assets/images/emoji/flag_kn.png)bin1316 -> 1316 bytes
-rw-r--r--public/-/emojis/1/flag_kp.png (renamed from app/assets/images/emoji/flag_kp.png)bin696 -> 696 bytes
-rw-r--r--public/-/emojis/1/flag_kr.png (renamed from app/assets/images/emoji/flag_kr.png)bin967 -> 967 bytes
-rw-r--r--public/-/emojis/1/flag_kw.png (renamed from app/assets/images/emoji/flag_kw.png)bin560 -> 560 bytes
-rw-r--r--public/-/emojis/1/flag_ky.png (renamed from app/assets/images/emoji/flag_ky.png)bin1671 -> 1671 bytes
-rw-r--r--public/-/emojis/1/flag_kz.png (renamed from app/assets/images/emoji/flag_kz.png)bin1136 -> 1136 bytes
-rw-r--r--public/-/emojis/1/flag_la.png (renamed from app/assets/images/emoji/flag_la.png)bin479 -> 479 bytes
-rw-r--r--public/-/emojis/1/flag_lb.png (renamed from app/assets/images/emoji/flag_lb.png)bin740 -> 740 bytes
-rw-r--r--public/-/emojis/1/flag_lc.png (renamed from app/assets/images/emoji/flag_lc.png)bin561 -> 561 bytes
-rw-r--r--public/-/emojis/1/flag_li.png (renamed from app/assets/images/emoji/flag_li.png)bin946 -> 946 bytes
-rw-r--r--public/-/emojis/1/flag_lk.png (renamed from app/assets/images/emoji/flag_lk.png)bin974 -> 974 bytes
-rw-r--r--public/-/emojis/1/flag_lr.png (renamed from app/assets/images/emoji/flag_lr.png)bin772 -> 772 bytes
-rw-r--r--public/-/emojis/1/flag_ls.png (renamed from app/assets/images/emoji/flag_ls.png)bin775 -> 775 bytes
-rw-r--r--public/-/emojis/1/flag_lt.png (renamed from app/assets/images/emoji/flag_lt.png)bin510 -> 510 bytes
-rw-r--r--public/-/emojis/1/flag_lu.png (renamed from app/assets/images/emoji/flag_lu.png)bin512 -> 512 bytes
-rw-r--r--public/-/emojis/1/flag_lv.png (renamed from app/assets/images/emoji/flag_lv.png)bin388 -> 388 bytes
-rw-r--r--public/-/emojis/1/flag_ly.png (renamed from app/assets/images/emoji/flag_ly.png)bin685 -> 685 bytes
-rw-r--r--public/-/emojis/1/flag_ma.png (renamed from app/assets/images/emoji/flag_ma.png)bin626 -> 626 bytes
-rw-r--r--public/-/emojis/1/flag_mc.png (renamed from app/assets/images/emoji/flag_mc.png)bin528 -> 528 bytes
-rw-r--r--public/-/emojis/1/flag_md.png (renamed from app/assets/images/emoji/flag_md.png)bin1170 -> 1170 bytes
-rw-r--r--public/-/emojis/1/flag_me.png (renamed from app/assets/images/emoji/flag_me.png)bin1074 -> 1074 bytes
-rw-r--r--public/-/emojis/1/flag_mf.png (renamed from app/assets/images/emoji/flag_mf.png)bin443 -> 443 bytes
-rw-r--r--public/-/emojis/1/flag_mg.png (renamed from app/assets/images/emoji/flag_mg.png)bin556 -> 556 bytes
-rw-r--r--public/-/emojis/1/flag_mh.png (renamed from app/assets/images/emoji/flag_mh.png)bin1138 -> 1138 bytes
-rw-r--r--public/-/emojis/1/flag_mk.png (renamed from app/assets/images/emoji/flag_mk.png)bin1023 -> 1023 bytes
-rw-r--r--public/-/emojis/1/flag_ml.png (renamed from app/assets/images/emoji/flag_ml.png)bin440 -> 440 bytes
-rw-r--r--public/-/emojis/1/flag_mm.png (renamed from app/assets/images/emoji/flag_mm.png)bin937 -> 937 bytes
-rw-r--r--public/-/emojis/1/flag_mn.png (renamed from app/assets/images/emoji/flag_mn.png)bin698 -> 698 bytes
-rw-r--r--public/-/emojis/1/flag_mo.png (renamed from app/assets/images/emoji/flag_mo.png)bin792 -> 792 bytes
-rw-r--r--public/-/emojis/1/flag_mp.png (renamed from app/assets/images/emoji/flag_mp.png)bin1797 -> 1797 bytes
-rw-r--r--public/-/emojis/1/flag_mq.png (renamed from app/assets/images/emoji/flag_mq.png)bin780 -> 780 bytes
-rw-r--r--public/-/emojis/1/flag_mr.png (renamed from app/assets/images/emoji/flag_mr.png)bin657 -> 657 bytes
-rw-r--r--public/-/emojis/1/flag_ms.png (renamed from app/assets/images/emoji/flag_ms.png)bin1477 -> 1477 bytes
-rw-r--r--public/-/emojis/1/flag_mt.png (renamed from app/assets/images/emoji/flag_mt.png)bin799 -> 799 bytes
-rw-r--r--public/-/emojis/1/flag_mu.png (renamed from app/assets/images/emoji/flag_mu.png)bin544 -> 544 bytes
-rw-r--r--public/-/emojis/1/flag_mv.png (renamed from app/assets/images/emoji/flag_mv.png)bin598 -> 598 bytes
-rw-r--r--public/-/emojis/1/flag_mw.png (renamed from app/assets/images/emoji/flag_mw.png)bin825 -> 825 bytes
-rw-r--r--public/-/emojis/1/flag_mx.png (renamed from app/assets/images/emoji/flag_mx.png)bin951 -> 951 bytes
-rw-r--r--public/-/emojis/1/flag_my.png (renamed from app/assets/images/emoji/flag_my.png)bin775 -> 775 bytes
-rw-r--r--public/-/emojis/1/flag_mz.png (renamed from app/assets/images/emoji/flag_mz.png)bin1159 -> 1159 bytes
-rw-r--r--public/-/emojis/1/flag_na.png (renamed from app/assets/images/emoji/flag_na.png)bin1249 -> 1249 bytes
-rw-r--r--public/-/emojis/1/flag_nc.png (renamed from app/assets/images/emoji/flag_nc.png)bin1148 -> 1148 bytes
-rw-r--r--public/-/emojis/1/flag_ne.png (renamed from app/assets/images/emoji/flag_ne.png)bin593 -> 593 bytes
-rw-r--r--public/-/emojis/1/flag_nf.png (renamed from app/assets/images/emoji/flag_nf.png)bin877 -> 877 bytes
-rw-r--r--public/-/emojis/1/flag_ng.png (renamed from app/assets/images/emoji/flag_ng.png)bin438 -> 438 bytes
-rw-r--r--public/-/emojis/1/flag_ni.png (renamed from app/assets/images/emoji/flag_ni.png)bin823 -> 823 bytes
-rw-r--r--public/-/emojis/1/flag_nl.png (renamed from app/assets/images/emoji/flag_nl.png)bin499 -> 499 bytes
-rw-r--r--public/-/emojis/1/flag_no.png (renamed from app/assets/images/emoji/flag_no.png)bin484 -> 484 bytes
-rw-r--r--public/-/emojis/1/flag_np.png (renamed from app/assets/images/emoji/flag_np.png)bin802 -> 802 bytes
-rw-r--r--public/-/emojis/1/flag_nr.png (renamed from app/assets/images/emoji/flag_nr.png)bin529 -> 529 bytes
-rw-r--r--public/-/emojis/1/flag_nu.png (renamed from app/assets/images/emoji/flag_nu.png)bin1128 -> 1128 bytes
-rw-r--r--public/-/emojis/1/flag_nz.png (renamed from app/assets/images/emoji/flag_nz.png)bin1099 -> 1099 bytes
-rw-r--r--public/-/emojis/1/flag_om.png (renamed from app/assets/images/emoji/flag_om.png)bin754 -> 754 bytes
-rw-r--r--public/-/emojis/1/flag_pa.png (renamed from app/assets/images/emoji/flag_pa.png)bin830 -> 830 bytes
-rw-r--r--public/-/emojis/1/flag_pe.png (renamed from app/assets/images/emoji/flag_pe.png)bin439 -> 439 bytes
-rw-r--r--public/-/emojis/1/flag_pf.png (renamed from app/assets/images/emoji/flag_pf.png)bin1091 -> 1091 bytes
-rw-r--r--public/-/emojis/1/flag_pg.png (renamed from app/assets/images/emoji/flag_pg.png)bin1076 -> 1076 bytes
-rw-r--r--public/-/emojis/1/flag_ph.png (renamed from app/assets/images/emoji/flag_ph.png)bin867 -> 867 bytes
-rw-r--r--public/-/emojis/1/flag_pk.png (renamed from app/assets/images/emoji/flag_pk.png)bin753 -> 753 bytes
-rw-r--r--public/-/emojis/1/flag_pl.png (renamed from app/assets/images/emoji/flag_pl.png)bin522 -> 522 bytes
-rw-r--r--public/-/emojis/1/flag_pm.png (renamed from app/assets/images/emoji/flag_pm.png)bin2314 -> 2314 bytes
-rw-r--r--public/-/emojis/1/flag_pn.png (renamed from app/assets/images/emoji/flag_pn.png)bin1895 -> 1895 bytes
-rw-r--r--public/-/emojis/1/flag_pr.png (renamed from app/assets/images/emoji/flag_pr.png)bin605 -> 605 bytes
-rw-r--r--public/-/emojis/1/flag_ps.png (renamed from app/assets/images/emoji/flag_ps.png)bin574 -> 574 bytes
-rw-r--r--public/-/emojis/1/flag_pt.png (renamed from app/assets/images/emoji/flag_pt.png)bin1055 -> 1055 bytes
-rw-r--r--public/-/emojis/1/flag_pw.png (renamed from app/assets/images/emoji/flag_pw.png)bin475 -> 475 bytes
-rw-r--r--public/-/emojis/1/flag_py.png (renamed from app/assets/images/emoji/flag_py.png)bin1085 -> 1085 bytes
-rw-r--r--public/-/emojis/1/flag_qa.png (renamed from app/assets/images/emoji/flag_qa.png)bin657 -> 657 bytes
-rw-r--r--public/-/emojis/1/flag_re.png (renamed from app/assets/images/emoji/flag_re.png)bin837 -> 837 bytes
-rw-r--r--public/-/emojis/1/flag_ro.png (renamed from app/assets/images/emoji/flag_ro.png)bin441 -> 441 bytes
-rw-r--r--public/-/emojis/1/flag_rs.png (renamed from app/assets/images/emoji/flag_rs.png)bin1237 -> 1237 bytes
-rw-r--r--public/-/emojis/1/flag_ru.png (renamed from app/assets/images/emoji/flag_ru.png)bin496 -> 496 bytes
-rw-r--r--public/-/emojis/1/flag_rw.png (renamed from app/assets/images/emoji/flag_rw.png)bin940 -> 940 bytes
-rw-r--r--public/-/emojis/1/flag_sa.png (renamed from app/assets/images/emoji/flag_sa.png)bin781 -> 781 bytes
-rw-r--r--public/-/emojis/1/flag_sb.png (renamed from app/assets/images/emoji/flag_sb.png)bin1102 -> 1102 bytes
-rw-r--r--public/-/emojis/1/flag_sc.png (renamed from app/assets/images/emoji/flag_sc.png)bin1073 -> 1073 bytes
-rw-r--r--public/-/emojis/1/flag_sd.png (renamed from app/assets/images/emoji/flag_sd.png)bin578 -> 578 bytes
-rw-r--r--public/-/emojis/1/flag_se.png (renamed from app/assets/images/emoji/flag_se.png)bin455 -> 455 bytes
-rw-r--r--public/-/emojis/1/flag_sg.png (renamed from app/assets/images/emoji/flag_sg.png)bin730 -> 730 bytes
-rw-r--r--public/-/emojis/1/flag_sh.png (renamed from app/assets/images/emoji/flag_sh.png)bin1369 -> 1369 bytes
-rw-r--r--public/-/emojis/1/flag_si.png (renamed from app/assets/images/emoji/flag_si.png)bin1030 -> 1030 bytes
-rw-r--r--public/-/emojis/1/flag_sj.png (renamed from app/assets/images/emoji/flag_sj.png)bin495 -> 495 bytes
-rw-r--r--public/-/emojis/1/flag_sk.png (renamed from app/assets/images/emoji/flag_sk.png)bin780 -> 780 bytes
-rw-r--r--public/-/emojis/1/flag_sl.png (renamed from app/assets/images/emoji/flag_sl.png)bin510 -> 510 bytes
-rw-r--r--public/-/emojis/1/flag_sm.png (renamed from app/assets/images/emoji/flag_sm.png)bin2000 -> 2000 bytes
-rw-r--r--public/-/emojis/1/flag_sn.png (renamed from app/assets/images/emoji/flag_sn.png)bin621 -> 621 bytes
-rw-r--r--public/-/emojis/1/flag_so.png (renamed from app/assets/images/emoji/flag_so.png)bin609 -> 609 bytes
-rw-r--r--public/-/emojis/1/flag_sr.png (renamed from app/assets/images/emoji/flag_sr.png)bin650 -> 650 bytes
-rw-r--r--public/-/emojis/1/flag_ss.png (renamed from app/assets/images/emoji/flag_ss.png)bin722 -> 722 bytes
-rw-r--r--public/-/emojis/1/flag_st.png (renamed from app/assets/images/emoji/flag_st.png)bin562 -> 562 bytes
-rw-r--r--public/-/emojis/1/flag_sv.png (renamed from app/assets/images/emoji/flag_sv.png)bin1125 -> 1125 bytes
-rw-r--r--public/-/emojis/1/flag_sx.png (renamed from app/assets/images/emoji/flag_sx.png)bin1195 -> 1195 bytes
-rw-r--r--public/-/emojis/1/flag_sy.png (renamed from app/assets/images/emoji/flag_sy.png)bin696 -> 696 bytes
-rw-r--r--public/-/emojis/1/flag_sz.png (renamed from app/assets/images/emoji/flag_sz.png)bin1102 -> 1102 bytes
-rw-r--r--public/-/emojis/1/flag_ta.png (renamed from app/assets/images/emoji/flag_ta.png)bin1907 -> 1907 bytes
-rw-r--r--public/-/emojis/1/flag_tc.png (renamed from app/assets/images/emoji/flag_tc.png)bin1538 -> 1538 bytes
-rw-r--r--public/-/emojis/1/flag_td.png (renamed from app/assets/images/emoji/flag_td.png)bin443 -> 443 bytes
-rw-r--r--public/-/emojis/1/flag_tf.png (renamed from app/assets/images/emoji/flag_tf.png)bin857 -> 857 bytes
-rw-r--r--public/-/emojis/1/flag_tg.png (renamed from app/assets/images/emoji/flag_tg.png)bin790 -> 790 bytes
-rw-r--r--public/-/emojis/1/flag_th.png (renamed from app/assets/images/emoji/flag_th.png)bin421 -> 421 bytes
-rw-r--r--public/-/emojis/1/flag_tj.png (renamed from app/assets/images/emoji/flag_tj.png)bin906 -> 906 bytes
-rw-r--r--public/-/emojis/1/flag_tk.png (renamed from app/assets/images/emoji/flag_tk.png)bin835 -> 835 bytes
-rw-r--r--public/-/emojis/1/flag_tl.png (renamed from app/assets/images/emoji/flag_tl.png)bin849 -> 849 bytes
-rw-r--r--public/-/emojis/1/flag_tm.png (renamed from app/assets/images/emoji/flag_tm.png)bin1178 -> 1178 bytes
-rw-r--r--public/-/emojis/1/flag_tn.png (renamed from app/assets/images/emoji/flag_tn.png)bin625 -> 625 bytes
-rw-r--r--public/-/emojis/1/flag_to.png (renamed from app/assets/images/emoji/flag_to.png)bin553 -> 553 bytes
-rw-r--r--public/-/emojis/1/flag_tr.png (renamed from app/assets/images/emoji/flag_tr.png)bin576 -> 576 bytes
-rw-r--r--public/-/emojis/1/flag_tt.png (renamed from app/assets/images/emoji/flag_tt.png)bin604 -> 604 bytes
-rw-r--r--public/-/emojis/1/flag_tv.png (renamed from app/assets/images/emoji/flag_tv.png)bin1120 -> 1120 bytes
-rw-r--r--public/-/emojis/1/flag_tw.png (renamed from app/assets/images/emoji/flag_tw.png)bin761 -> 761 bytes
-rw-r--r--public/-/emojis/1/flag_tz.png (renamed from app/assets/images/emoji/flag_tz.png)bin1061 -> 1061 bytes
-rw-r--r--public/-/emojis/1/flag_ua.png (renamed from app/assets/images/emoji/flag_ua.png)bin528 -> 528 bytes
-rw-r--r--public/-/emojis/1/flag_ug.png (renamed from app/assets/images/emoji/flag_ug.png)bin887 -> 887 bytes
-rw-r--r--public/-/emojis/1/flag_um.png (renamed from app/assets/images/emoji/flag_um.png)bin776 -> 776 bytes
-rw-r--r--public/-/emojis/1/flag_us.png (renamed from app/assets/images/emoji/flag_us.png)bin776 -> 776 bytes
-rw-r--r--public/-/emojis/1/flag_uy.png (renamed from app/assets/images/emoji/flag_uy.png)bin966 -> 966 bytes
-rw-r--r--public/-/emojis/1/flag_uz.png (renamed from app/assets/images/emoji/flag_uz.png)bin750 -> 750 bytes
-rw-r--r--public/-/emojis/1/flag_va.png (renamed from app/assets/images/emoji/flag_va.png)bin1331 -> 1331 bytes
-rw-r--r--public/-/emojis/1/flag_vc.png (renamed from app/assets/images/emoji/flag_vc.png)bin897 -> 897 bytes
-rw-r--r--public/-/emojis/1/flag_ve.png (renamed from app/assets/images/emoji/flag_ve.png)bin748 -> 748 bytes
-rw-r--r--public/-/emojis/1/flag_vg.png (renamed from app/assets/images/emoji/flag_vg.png)bin1789 -> 1789 bytes
-rw-r--r--public/-/emojis/1/flag_vi.png (renamed from app/assets/images/emoji/flag_vi.png)bin1378 -> 1378 bytes
-rw-r--r--public/-/emojis/1/flag_vn.png (renamed from app/assets/images/emoji/flag_vn.png)bin583 -> 583 bytes
-rw-r--r--public/-/emojis/1/flag_vu.png (renamed from app/assets/images/emoji/flag_vu.png)bin844 -> 844 bytes
-rw-r--r--public/-/emojis/1/flag_wf.png (renamed from app/assets/images/emoji/flag_wf.png)bin443 -> 443 bytes
-rw-r--r--public/-/emojis/1/flag_white.png (renamed from app/assets/images/emoji/flag_white.png)bin699 -> 699 bytes
-rw-r--r--public/-/emojis/1/flag_ws.png (renamed from app/assets/images/emoji/flag_ws.png)bin634 -> 634 bytes
-rw-r--r--public/-/emojis/1/flag_xk.png (renamed from app/assets/images/emoji/flag_xk.png)bin722 -> 722 bytes
-rw-r--r--public/-/emojis/1/flag_ye.png (renamed from app/assets/images/emoji/flag_ye.png)bin507 -> 507 bytes
-rw-r--r--public/-/emojis/1/flag_yt.png (renamed from app/assets/images/emoji/flag_yt.png)bin1623 -> 1623 bytes
-rw-r--r--public/-/emojis/1/flag_za.png (renamed from app/assets/images/emoji/flag_za.png)bin676 -> 676 bytes
-rw-r--r--public/-/emojis/1/flag_zm.png (renamed from app/assets/images/emoji/flag_zm.png)bin881 -> 881 bytes
-rw-r--r--public/-/emojis/1/flag_zw.png (renamed from app/assets/images/emoji/flag_zw.png)bin993 -> 993 bytes
-rw-r--r--public/-/emojis/1/flags.png (renamed from app/assets/images/emoji/flags.png)bin1722 -> 1722 bytes
-rw-r--r--public/-/emojis/1/flashlight.png (renamed from app/assets/images/emoji/flashlight.png)bin964 -> 964 bytes
-rw-r--r--public/-/emojis/1/fleur-de-lis.png (renamed from app/assets/images/emoji/fleur-de-lis.png)bin632 -> 632 bytes
-rw-r--r--public/-/emojis/1/floppy_disk.png (renamed from app/assets/images/emoji/floppy_disk.png)bin258 -> 258 bytes
-rw-r--r--public/-/emojis/1/flower_playing_cards.png (renamed from app/assets/images/emoji/flower_playing_cards.png)bin449 -> 449 bytes
-rw-r--r--public/-/emojis/1/flushed.png (renamed from app/assets/images/emoji/flushed.png)bin1127 -> 1127 bytes
-rw-r--r--public/-/emojis/1/fog.png (renamed from app/assets/images/emoji/fog.png)bin713 -> 713 bytes
-rw-r--r--public/-/emojis/1/foggy.png (renamed from app/assets/images/emoji/foggy.png)bin1069 -> 1069 bytes
-rw-r--r--public/-/emojis/1/football.png (renamed from app/assets/images/emoji/football.png)bin956 -> 956 bytes
-rw-r--r--public/-/emojis/1/footprints.png (renamed from app/assets/images/emoji/footprints.png)bin621 -> 621 bytes
-rw-r--r--public/-/emojis/1/fork_and_knife.png (renamed from app/assets/images/emoji/fork_and_knife.png)bin668 -> 668 bytes
-rw-r--r--public/-/emojis/1/fork_knife_plate.png (renamed from app/assets/images/emoji/fork_knife_plate.png)bin976 -> 976 bytes
-rw-r--r--public/-/emojis/1/fountain.png (renamed from app/assets/images/emoji/fountain.png)bin1768 -> 1768 bytes
-rw-r--r--public/-/emojis/1/four.png (renamed from app/assets/images/emoji/four.png)bin497 -> 497 bytes
-rw-r--r--public/-/emojis/1/four_leaf_clover.png (renamed from app/assets/images/emoji/four_leaf_clover.png)bin1156 -> 1156 bytes
-rw-r--r--public/-/emojis/1/fox.png (renamed from app/assets/images/emoji/fox.png)bin1556 -> 1556 bytes
-rw-r--r--public/-/emojis/1/frame_photo.png (renamed from app/assets/images/emoji/frame_photo.png)bin514 -> 514 bytes
-rw-r--r--public/-/emojis/1/free.png (renamed from app/assets/images/emoji/free.png)bin370 -> 370 bytes
-rw-r--r--public/-/emojis/1/french_bread.png (renamed from app/assets/images/emoji/french_bread.png)bin1551 -> 1551 bytes
-rw-r--r--public/-/emojis/1/fried_shrimp.png (renamed from app/assets/images/emoji/fried_shrimp.png)bin1241 -> 1241 bytes
-rw-r--r--public/-/emojis/1/fries.png (renamed from app/assets/images/emoji/fries.png)bin1873 -> 1873 bytes
-rw-r--r--public/-/emojis/1/frog.png (renamed from app/assets/images/emoji/frog.png)bin897 -> 897 bytes
-rw-r--r--public/-/emojis/1/frowning.png (renamed from app/assets/images/emoji/frowning.png)bin633 -> 633 bytes
-rw-r--r--public/-/emojis/1/frowning2.png (renamed from app/assets/images/emoji/frowning2.png)bin589 -> 589 bytes
-rw-r--r--public/-/emojis/1/fuelpump.png (renamed from app/assets/images/emoji/fuelpump.png)bin864 -> 864 bytes
-rw-r--r--public/-/emojis/1/full_moon.png (renamed from app/assets/images/emoji/full_moon.png)bin841 -> 841 bytes
-rw-r--r--public/-/emojis/1/full_moon_with_face.png (renamed from app/assets/images/emoji/full_moon_with_face.png)bin1186 -> 1186 bytes
-rw-r--r--public/-/emojis/1/game_die.png (renamed from app/assets/images/emoji/game_die.png)bin1136 -> 1136 bytes
-rw-r--r--public/-/emojis/1/gay_pride_flag.png (renamed from app/assets/images/emoji/gay_pride_flag.png)bin2340 -> 2340 bytes
-rw-r--r--public/-/emojis/1/gear.png (renamed from app/assets/images/emoji/gear.png)bin747 -> 747 bytes
-rw-r--r--public/-/emojis/1/gem.png (renamed from app/assets/images/emoji/gem.png)bin715 -> 715 bytes
-rw-r--r--public/-/emojis/1/gemini.png (renamed from app/assets/images/emoji/gemini.png)bin547 -> 547 bytes
-rw-r--r--public/-/emojis/1/ghost.png (renamed from app/assets/images/emoji/ghost.png)bin1465 -> 1465 bytes
-rw-r--r--public/-/emojis/1/gift.png (renamed from app/assets/images/emoji/gift.png)bin1966 -> 1966 bytes
-rw-r--r--public/-/emojis/1/gift_heart.png (renamed from app/assets/images/emoji/gift_heart.png)bin1141 -> 1141 bytes
-rw-r--r--public/-/emojis/1/girl.png (renamed from app/assets/images/emoji/girl.png)bin1261 -> 1261 bytes
-rw-r--r--public/-/emojis/1/girl_tone1.png (renamed from app/assets/images/emoji/girl_tone1.png)bin1259 -> 1259 bytes
-rw-r--r--public/-/emojis/1/girl_tone2.png (renamed from app/assets/images/emoji/girl_tone2.png)bin1255 -> 1255 bytes
-rw-r--r--public/-/emojis/1/girl_tone3.png (renamed from app/assets/images/emoji/girl_tone3.png)bin1255 -> 1255 bytes
-rw-r--r--public/-/emojis/1/girl_tone4.png (renamed from app/assets/images/emoji/girl_tone4.png)bin1241 -> 1241 bytes
-rw-r--r--public/-/emojis/1/girl_tone5.png (renamed from app/assets/images/emoji/girl_tone5.png)bin1245 -> 1245 bytes
-rw-r--r--public/-/emojis/1/globe_with_meridians.png (renamed from app/assets/images/emoji/globe_with_meridians.png)bin796 -> 796 bytes
-rw-r--r--public/-/emojis/1/goal.png (renamed from app/assets/images/emoji/goal.png)bin1242 -> 1242 bytes
-rw-r--r--public/-/emojis/1/goat.png (renamed from app/assets/images/emoji/goat.png)bin981 -> 981 bytes
-rw-r--r--public/-/emojis/1/golf.png (renamed from app/assets/images/emoji/golf.png)bin823 -> 823 bytes
-rw-r--r--public/-/emojis/1/golfer.png (renamed from app/assets/images/emoji/golfer.png)bin1189 -> 1189 bytes
-rw-r--r--public/-/emojis/1/gorilla.png (renamed from app/assets/images/emoji/gorilla.png)bin1090 -> 1090 bytes
-rw-r--r--public/-/emojis/1/grapes.png (renamed from app/assets/images/emoji/grapes.png)bin1552 -> 1552 bytes
-rw-r--r--public/-/emojis/1/green_apple.png (renamed from app/assets/images/emoji/green_apple.png)bin656 -> 656 bytes
-rw-r--r--public/-/emojis/1/green_book.png (renamed from app/assets/images/emoji/green_book.png)bin1366 -> 1366 bytes
-rw-r--r--public/-/emojis/1/green_heart.png (renamed from app/assets/images/emoji/green_heart.png)bin435 -> 435 bytes
-rw-r--r--public/-/emojis/1/grey_exclamation.png (renamed from app/assets/images/emoji/grey_exclamation.png)bin354 -> 354 bytes
-rw-r--r--public/-/emojis/1/grey_question.png (renamed from app/assets/images/emoji/grey_question.png)bin449 -> 449 bytes
-rw-r--r--public/-/emojis/1/grimacing.png (renamed from app/assets/images/emoji/grimacing.png)bin694 -> 694 bytes
-rw-r--r--public/-/emojis/1/grin.png (renamed from app/assets/images/emoji/grin.png)bin767 -> 767 bytes
-rw-r--r--public/-/emojis/1/grinning.png (renamed from app/assets/images/emoji/grinning.png)bin810 -> 810 bytes
-rw-r--r--public/-/emojis/1/guardsman.png (renamed from app/assets/images/emoji/guardsman.png)bin1140 -> 1140 bytes
-rw-r--r--public/-/emojis/1/guardsman_tone1.png (renamed from app/assets/images/emoji/guardsman_tone1.png)bin1122 -> 1122 bytes
-rw-r--r--public/-/emojis/1/guardsman_tone2.png (renamed from app/assets/images/emoji/guardsman_tone2.png)bin1160 -> 1160 bytes
-rw-r--r--public/-/emojis/1/guardsman_tone3.png (renamed from app/assets/images/emoji/guardsman_tone3.png)bin1160 -> 1160 bytes
-rw-r--r--public/-/emojis/1/guardsman_tone4.png (renamed from app/assets/images/emoji/guardsman_tone4.png)bin1157 -> 1157 bytes
-rw-r--r--public/-/emojis/1/guardsman_tone5.png (renamed from app/assets/images/emoji/guardsman_tone5.png)bin1165 -> 1165 bytes
-rw-r--r--public/-/emojis/1/guitar.png (renamed from app/assets/images/emoji/guitar.png)bin1056 -> 1056 bytes
-rw-r--r--public/-/emojis/1/gun.png (renamed from app/assets/images/emoji/gun.png)bin1859 -> 1859 bytes
-rw-r--r--public/-/emojis/1/haircut.png (renamed from app/assets/images/emoji/haircut.png)bin1935 -> 1935 bytes
-rw-r--r--public/-/emojis/1/haircut_tone1.png (renamed from app/assets/images/emoji/haircut_tone1.png)bin1945 -> 1945 bytes
-rw-r--r--public/-/emojis/1/haircut_tone2.png (renamed from app/assets/images/emoji/haircut_tone2.png)bin1935 -> 1935 bytes
-rw-r--r--public/-/emojis/1/haircut_tone3.png (renamed from app/assets/images/emoji/haircut_tone3.png)bin1923 -> 1923 bytes
-rw-r--r--public/-/emojis/1/haircut_tone4.png (renamed from app/assets/images/emoji/haircut_tone4.png)bin1904 -> 1904 bytes
-rw-r--r--public/-/emojis/1/haircut_tone5.png (renamed from app/assets/images/emoji/haircut_tone5.png)bin1920 -> 1920 bytes
-rw-r--r--public/-/emojis/1/hamburger.png (renamed from app/assets/images/emoji/hamburger.png)bin1973 -> 1973 bytes
-rw-r--r--public/-/emojis/1/hammer.png (renamed from app/assets/images/emoji/hammer.png)bin834 -> 834 bytes
-rw-r--r--public/-/emojis/1/hammer_pick.png (renamed from app/assets/images/emoji/hammer_pick.png)bin1068 -> 1068 bytes
-rw-r--r--public/-/emojis/1/hamster.png (renamed from app/assets/images/emoji/hamster.png)bin1279 -> 1279 bytes
-rw-r--r--public/-/emojis/1/hand_splayed.png (renamed from app/assets/images/emoji/hand_splayed.png)bin1081 -> 1081 bytes
-rw-r--r--public/-/emojis/1/hand_splayed_tone1.png (renamed from app/assets/images/emoji/hand_splayed_tone1.png)bin1081 -> 1081 bytes
-rw-r--r--public/-/emojis/1/hand_splayed_tone2.png (renamed from app/assets/images/emoji/hand_splayed_tone2.png)bin1081 -> 1081 bytes
-rw-r--r--public/-/emojis/1/hand_splayed_tone3.png (renamed from app/assets/images/emoji/hand_splayed_tone3.png)bin1081 -> 1081 bytes
-rw-r--r--public/-/emojis/1/hand_splayed_tone4.png (renamed from app/assets/images/emoji/hand_splayed_tone4.png)bin1081 -> 1081 bytes
-rw-r--r--public/-/emojis/1/hand_splayed_tone5.png (renamed from app/assets/images/emoji/hand_splayed_tone5.png)bin1081 -> 1081 bytes
-rw-r--r--public/-/emojis/1/handbag.png (renamed from app/assets/images/emoji/handbag.png)bin1285 -> 1285 bytes
-rw-r--r--public/-/emojis/1/handball.png (renamed from app/assets/images/emoji/handball.png)bin1634 -> 1634 bytes
-rw-r--r--public/-/emojis/1/handball_tone1.png (renamed from app/assets/images/emoji/handball_tone1.png)bin1645 -> 1645 bytes
-rw-r--r--public/-/emojis/1/handball_tone2.png (renamed from app/assets/images/emoji/handball_tone2.png)bin1628 -> 1628 bytes
-rw-r--r--public/-/emojis/1/handball_tone3.png (renamed from app/assets/images/emoji/handball_tone3.png)bin1639 -> 1639 bytes
-rw-r--r--public/-/emojis/1/handball_tone4.png (renamed from app/assets/images/emoji/handball_tone4.png)bin1634 -> 1634 bytes
-rw-r--r--public/-/emojis/1/handball_tone5.png (renamed from app/assets/images/emoji/handball_tone5.png)bin1606 -> 1606 bytes
-rw-r--r--public/-/emojis/1/handshake.png (renamed from app/assets/images/emoji/handshake.png)bin1366 -> 1366 bytes
-rw-r--r--public/-/emojis/1/handshake_tone1.png (renamed from app/assets/images/emoji/handshake_tone1.png)bin1381 -> 1381 bytes
-rw-r--r--public/-/emojis/1/handshake_tone2.png (renamed from app/assets/images/emoji/handshake_tone2.png)bin1381 -> 1381 bytes
-rw-r--r--public/-/emojis/1/handshake_tone3.png (renamed from app/assets/images/emoji/handshake_tone3.png)bin1381 -> 1381 bytes
-rw-r--r--public/-/emojis/1/handshake_tone4.png (renamed from app/assets/images/emoji/handshake_tone4.png)bin1381 -> 1381 bytes
-rw-r--r--public/-/emojis/1/handshake_tone5.png (renamed from app/assets/images/emoji/handshake_tone5.png)bin1381 -> 1381 bytes
-rw-r--r--public/-/emojis/1/hash.png (renamed from app/assets/images/emoji/hash.png)bin604 -> 604 bytes
-rw-r--r--public/-/emojis/1/hatched_chick.png (renamed from app/assets/images/emoji/hatched_chick.png)bin1174 -> 1174 bytes
-rw-r--r--public/-/emojis/1/hatching_chick.png (renamed from app/assets/images/emoji/hatching_chick.png)bin1598 -> 1598 bytes
-rw-r--r--public/-/emojis/1/head_bandage.png (renamed from app/assets/images/emoji/head_bandage.png)bin1199 -> 1199 bytes
-rw-r--r--public/-/emojis/1/headphones.png (renamed from app/assets/images/emoji/headphones.png)bin1202 -> 1202 bytes
-rw-r--r--public/-/emojis/1/hear_no_evil.png (renamed from app/assets/images/emoji/hear_no_evil.png)bin1210 -> 1210 bytes
-rw-r--r--public/-/emojis/1/heart.png (renamed from app/assets/images/emoji/heart.png)bin435 -> 435 bytes
-rw-r--r--public/-/emojis/1/heart_decoration.png (renamed from app/assets/images/emoji/heart_decoration.png)bin557 -> 557 bytes
-rw-r--r--public/-/emojis/1/heart_exclamation.png (renamed from app/assets/images/emoji/heart_exclamation.png)bin471 -> 471 bytes
-rw-r--r--public/-/emojis/1/heart_eyes.png (renamed from app/assets/images/emoji/heart_eyes.png)bin1069 -> 1069 bytes
-rw-r--r--public/-/emojis/1/heart_eyes_cat.png (renamed from app/assets/images/emoji/heart_eyes_cat.png)bin1512 -> 1512 bytes
-rw-r--r--public/-/emojis/1/heartbeat.png (renamed from app/assets/images/emoji/heartbeat.png)bin699 -> 699 bytes
-rw-r--r--public/-/emojis/1/heartpulse.png (renamed from app/assets/images/emoji/heartpulse.png)bin675 -> 675 bytes
-rw-r--r--public/-/emojis/1/hearts.png (renamed from app/assets/images/emoji/hearts.png)bin449 -> 449 bytes
-rw-r--r--public/-/emojis/1/heavy_check_mark.png (renamed from app/assets/images/emoji/heavy_check_mark.png)bin438 -> 438 bytes
-rw-r--r--public/-/emojis/1/heavy_division_sign.png (renamed from app/assets/images/emoji/heavy_division_sign.png)bin204 -> 204 bytes
-rw-r--r--public/-/emojis/1/heavy_dollar_sign.png (renamed from app/assets/images/emoji/heavy_dollar_sign.png)bin429 -> 429 bytes
-rw-r--r--public/-/emojis/1/heavy_minus_sign.png (renamed from app/assets/images/emoji/heavy_minus_sign.png)bin108 -> 108 bytes
-rw-r--r--public/-/emojis/1/heavy_multiplication_x.png (renamed from app/assets/images/emoji/heavy_multiplication_x.png)bin298 -> 298 bytes
-rw-r--r--public/-/emojis/1/heavy_plus_sign.png (renamed from app/assets/images/emoji/heavy_plus_sign.png)bin115 -> 115 bytes
-rw-r--r--public/-/emojis/1/helicopter.png (renamed from app/assets/images/emoji/helicopter.png)bin1098 -> 1098 bytes
-rw-r--r--public/-/emojis/1/helmet_with_cross.png (renamed from app/assets/images/emoji/helmet_with_cross.png)bin1014 -> 1014 bytes
-rw-r--r--public/-/emojis/1/herb.png (renamed from app/assets/images/emoji/herb.png)bin886 -> 886 bytes
-rw-r--r--public/-/emojis/1/hibiscus.png (renamed from app/assets/images/emoji/hibiscus.png)bin1815 -> 1815 bytes
-rw-r--r--public/-/emojis/1/high_brightness.png (renamed from app/assets/images/emoji/high_brightness.png)bin474 -> 474 bytes
-rw-r--r--public/-/emojis/1/high_heel.png (renamed from app/assets/images/emoji/high_heel.png)bin1008 -> 1008 bytes
-rw-r--r--public/-/emojis/1/hockey.png (renamed from app/assets/images/emoji/hockey.png)bin1010 -> 1010 bytes
-rw-r--r--public/-/emojis/1/hole.png (renamed from app/assets/images/emoji/hole.png)bin1390 -> 1390 bytes
-rw-r--r--public/-/emojis/1/homes.png (renamed from app/assets/images/emoji/homes.png)bin981 -> 981 bytes
-rw-r--r--public/-/emojis/1/honey_pot.png (renamed from app/assets/images/emoji/honey_pot.png)bin1217 -> 1217 bytes
-rw-r--r--public/-/emojis/1/horse.png (renamed from app/assets/images/emoji/horse.png)bin1694 -> 1694 bytes
-rw-r--r--public/-/emojis/1/horse_racing.png (renamed from app/assets/images/emoji/horse_racing.png)bin2096 -> 2096 bytes
-rw-r--r--public/-/emojis/1/horse_racing_tone1.png (renamed from app/assets/images/emoji/horse_racing_tone1.png)bin2099 -> 2099 bytes
-rw-r--r--public/-/emojis/1/horse_racing_tone2.png (renamed from app/assets/images/emoji/horse_racing_tone2.png)bin2103 -> 2103 bytes
-rw-r--r--public/-/emojis/1/horse_racing_tone3.png (renamed from app/assets/images/emoji/horse_racing_tone3.png)bin2090 -> 2090 bytes
-rw-r--r--public/-/emojis/1/horse_racing_tone4.png (renamed from app/assets/images/emoji/horse_racing_tone4.png)bin2090 -> 2090 bytes
-rw-r--r--public/-/emojis/1/horse_racing_tone5.png (renamed from app/assets/images/emoji/horse_racing_tone5.png)bin2085 -> 2085 bytes
-rw-r--r--public/-/emojis/1/hospital.png (renamed from app/assets/images/emoji/hospital.png)bin530 -> 530 bytes
-rw-r--r--public/-/emojis/1/hot_pepper.png (renamed from app/assets/images/emoji/hot_pepper.png)bin677 -> 677 bytes
-rw-r--r--public/-/emojis/1/hotdog.png (renamed from app/assets/images/emoji/hotdog.png)bin1770 -> 1770 bytes
-rw-r--r--public/-/emojis/1/hotel.png (renamed from app/assets/images/emoji/hotel.png)bin1322 -> 1322 bytes
-rw-r--r--public/-/emojis/1/hotsprings.png (renamed from app/assets/images/emoji/hotsprings.png)bin733 -> 733 bytes
-rw-r--r--public/-/emojis/1/hourglass.png (renamed from app/assets/images/emoji/hourglass.png)bin800 -> 800 bytes
-rw-r--r--public/-/emojis/1/hourglass_flowing_sand.png (renamed from app/assets/images/emoji/hourglass_flowing_sand.png)bin847 -> 847 bytes
-rw-r--r--public/-/emojis/1/house.png (renamed from app/assets/images/emoji/house.png)bin863 -> 863 bytes
-rw-r--r--public/-/emojis/1/house_abandoned.png (renamed from app/assets/images/emoji/house_abandoned.png)bin1606 -> 1606 bytes
-rw-r--r--public/-/emojis/1/house_with_garden.png (renamed from app/assets/images/emoji/house_with_garden.png)bin1613 -> 1613 bytes
-rw-r--r--public/-/emojis/1/hugging.png (renamed from app/assets/images/emoji/hugging.png)bin1425 -> 1425 bytes
-rw-r--r--public/-/emojis/1/hushed.png (renamed from app/assets/images/emoji/hushed.png)bin634 -> 634 bytes
-rw-r--r--public/-/emojis/1/ice_cream.png (renamed from app/assets/images/emoji/ice_cream.png)bin1779 -> 1779 bytes
-rw-r--r--public/-/emojis/1/ice_skate.png (renamed from app/assets/images/emoji/ice_skate.png)bin1574 -> 1574 bytes
-rw-r--r--public/-/emojis/1/icecream.png (renamed from app/assets/images/emoji/icecream.png)bin1496 -> 1496 bytes
-rw-r--r--public/-/emojis/1/id.png (renamed from app/assets/images/emoji/id.png)bin348 -> 348 bytes
-rw-r--r--public/-/emojis/1/ideograph_advantage.png (renamed from app/assets/images/emoji/ideograph_advantage.png)bin716 -> 716 bytes
-rw-r--r--public/-/emojis/1/imp.png (renamed from app/assets/images/emoji/imp.png)bin1988 -> 1988 bytes
-rw-r--r--public/-/emojis/1/inbox_tray.png (renamed from app/assets/images/emoji/inbox_tray.png)bin1029 -> 1029 bytes
-rw-r--r--public/-/emojis/1/incoming_envelope.png (renamed from app/assets/images/emoji/incoming_envelope.png)bin1129 -> 1129 bytes
-rw-r--r--public/-/emojis/1/information_desk_person.png (renamed from app/assets/images/emoji/information_desk_person.png)bin1580 -> 1580 bytes
-rw-r--r--public/-/emojis/1/information_desk_person_tone1.png (renamed from app/assets/images/emoji/information_desk_person_tone1.png)bin1597 -> 1597 bytes
-rw-r--r--public/-/emojis/1/information_desk_person_tone2.png (renamed from app/assets/images/emoji/information_desk_person_tone2.png)bin1590 -> 1590 bytes
-rw-r--r--public/-/emojis/1/information_desk_person_tone3.png (renamed from app/assets/images/emoji/information_desk_person_tone3.png)bin1580 -> 1580 bytes
-rw-r--r--public/-/emojis/1/information_desk_person_tone4.png (renamed from app/assets/images/emoji/information_desk_person_tone4.png)bin1572 -> 1572 bytes
-rw-r--r--public/-/emojis/1/information_desk_person_tone5.png (renamed from app/assets/images/emoji/information_desk_person_tone5.png)bin1588 -> 1588 bytes
-rw-r--r--public/-/emojis/1/information_source.png (renamed from app/assets/images/emoji/information_source.png)bin506 -> 506 bytes
-rw-r--r--public/-/emojis/1/innocent.png (renamed from app/assets/images/emoji/innocent.png)bin935 -> 935 bytes
-rw-r--r--public/-/emojis/1/interrobang.png (renamed from app/assets/images/emoji/interrobang.png)bin601 -> 601 bytes
-rw-r--r--public/-/emojis/1/iphone.png (renamed from app/assets/images/emoji/iphone.png)bin695 -> 695 bytes
-rw-r--r--public/-/emojis/1/island.png (renamed from app/assets/images/emoji/island.png)bin1273 -> 1273 bytes
-rw-r--r--public/-/emojis/1/izakaya_lantern.png (renamed from app/assets/images/emoji/izakaya_lantern.png)bin1227 -> 1227 bytes
-rw-r--r--public/-/emojis/1/jack_o_lantern.png (renamed from app/assets/images/emoji/jack_o_lantern.png)bin2289 -> 2289 bytes
-rw-r--r--public/-/emojis/1/japan.png (renamed from app/assets/images/emoji/japan.png)bin539 -> 539 bytes
-rw-r--r--public/-/emojis/1/japanese_castle.png (renamed from app/assets/images/emoji/japanese_castle.png)bin1404 -> 1404 bytes
-rw-r--r--public/-/emojis/1/japanese_goblin.png (renamed from app/assets/images/emoji/japanese_goblin.png)bin1561 -> 1561 bytes
-rw-r--r--public/-/emojis/1/japanese_ogre.png (renamed from app/assets/images/emoji/japanese_ogre.png)bin1864 -> 1864 bytes
-rw-r--r--public/-/emojis/1/jeans.png (renamed from app/assets/images/emoji/jeans.png)bin1158 -> 1158 bytes
-rw-r--r--public/-/emojis/1/joy.png (renamed from app/assets/images/emoji/joy.png)bin1136 -> 1136 bytes
-rw-r--r--public/-/emojis/1/joy_cat.png (renamed from app/assets/images/emoji/joy_cat.png)bin1633 -> 1633 bytes
-rw-r--r--public/-/emojis/1/joystick.png (renamed from app/assets/images/emoji/joystick.png)bin1039 -> 1039 bytes
-rw-r--r--public/-/emojis/1/juggling.png (renamed from app/assets/images/emoji/juggling.png)bin1165 -> 1165 bytes
-rw-r--r--public/-/emojis/1/juggling_tone1.png (renamed from app/assets/images/emoji/juggling_tone1.png)bin1171 -> 1171 bytes
-rw-r--r--public/-/emojis/1/juggling_tone2.png (renamed from app/assets/images/emoji/juggling_tone2.png)bin1160 -> 1160 bytes
-rw-r--r--public/-/emojis/1/juggling_tone3.png (renamed from app/assets/images/emoji/juggling_tone3.png)bin1170 -> 1170 bytes
-rw-r--r--public/-/emojis/1/juggling_tone4.png (renamed from app/assets/images/emoji/juggling_tone4.png)bin1167 -> 1167 bytes
-rw-r--r--public/-/emojis/1/juggling_tone5.png (renamed from app/assets/images/emoji/juggling_tone5.png)bin1161 -> 1161 bytes
-rw-r--r--public/-/emojis/1/kaaba.png (renamed from app/assets/images/emoji/kaaba.png)bin1251 -> 1251 bytes
-rw-r--r--public/-/emojis/1/key.png (renamed from app/assets/images/emoji/key.png)bin770 -> 770 bytes
-rw-r--r--public/-/emojis/1/key2.png (renamed from app/assets/images/emoji/key2.png)bin593 -> 593 bytes
-rw-r--r--public/-/emojis/1/keyboard.png (renamed from app/assets/images/emoji/keyboard.png)bin429 -> 429 bytes
-rw-r--r--public/-/emojis/1/kimono.png (renamed from app/assets/images/emoji/kimono.png)bin1527 -> 1527 bytes
-rw-r--r--public/-/emojis/1/kiss.png (renamed from app/assets/images/emoji/kiss.png)bin842 -> 842 bytes
-rw-r--r--public/-/emojis/1/kiss_mm.png (renamed from app/assets/images/emoji/kiss_mm.png)bin1269 -> 1269 bytes
-rw-r--r--public/-/emojis/1/kiss_ww.png (renamed from app/assets/images/emoji/kiss_ww.png)bin1149 -> 1149 bytes
-rw-r--r--public/-/emojis/1/kissing.png (renamed from app/assets/images/emoji/kissing.png)bin738 -> 738 bytes
-rw-r--r--public/-/emojis/1/kissing_cat.png (renamed from app/assets/images/emoji/kissing_cat.png)bin1468 -> 1468 bytes
-rw-r--r--public/-/emojis/1/kissing_closed_eyes.png (renamed from app/assets/images/emoji/kissing_closed_eyes.png)bin888 -> 888 bytes
-rw-r--r--public/-/emojis/1/kissing_heart.png (renamed from app/assets/images/emoji/kissing_heart.png)bin843 -> 843 bytes
-rw-r--r--public/-/emojis/1/kissing_smiling_eyes.png (renamed from app/assets/images/emoji/kissing_smiling_eyes.png)bin648 -> 648 bytes
-rw-r--r--public/-/emojis/1/kiwi.png (renamed from app/assets/images/emoji/kiwi.png)bin1892 -> 1892 bytes
-rw-r--r--public/-/emojis/1/knife.png (renamed from app/assets/images/emoji/knife.png)bin616 -> 616 bytes
-rw-r--r--public/-/emojis/1/koala.png (renamed from app/assets/images/emoji/koala.png)bin1428 -> 1428 bytes
-rw-r--r--public/-/emojis/1/koko.png (renamed from app/assets/images/emoji/koko.png)bin266 -> 266 bytes
-rw-r--r--public/-/emojis/1/label.png (renamed from app/assets/images/emoji/label.png)bin669 -> 669 bytes
-rw-r--r--public/-/emojis/1/large_blue_circle.png (renamed from app/assets/images/emoji/large_blue_circle.png)bin371 -> 371 bytes
-rw-r--r--public/-/emojis/1/large_blue_diamond.png (renamed from app/assets/images/emoji/large_blue_diamond.png)bin245 -> 245 bytes
-rw-r--r--public/-/emojis/1/large_orange_diamond.png (renamed from app/assets/images/emoji/large_orange_diamond.png)bin248 -> 248 bytes
-rw-r--r--public/-/emojis/1/last_quarter_moon.png (renamed from app/assets/images/emoji/last_quarter_moon.png)bin1180 -> 1180 bytes
-rw-r--r--public/-/emojis/1/last_quarter_moon_with_face.png (renamed from app/assets/images/emoji/last_quarter_moon_with_face.png)bin1030 -> 1030 bytes
-rw-r--r--public/-/emojis/1/laughing.png (renamed from app/assets/images/emoji/laughing.png)bin901 -> 901 bytes
-rw-r--r--public/-/emojis/1/leaves.png (renamed from app/assets/images/emoji/leaves.png)bin993 -> 993 bytes
-rw-r--r--public/-/emojis/1/ledger.png (renamed from app/assets/images/emoji/ledger.png)bin1528 -> 1528 bytes
-rw-r--r--public/-/emojis/1/left_facing_fist.png (renamed from app/assets/images/emoji/left_facing_fist.png)bin972 -> 972 bytes
-rw-r--r--public/-/emojis/1/left_facing_fist_tone1.png (renamed from app/assets/images/emoji/left_facing_fist_tone1.png)bin960 -> 960 bytes
-rw-r--r--public/-/emojis/1/left_facing_fist_tone2.png (renamed from app/assets/images/emoji/left_facing_fist_tone2.png)bin972 -> 972 bytes
-rw-r--r--public/-/emojis/1/left_facing_fist_tone3.png (renamed from app/assets/images/emoji/left_facing_fist_tone3.png)bin960 -> 960 bytes
-rw-r--r--public/-/emojis/1/left_facing_fist_tone4.png (renamed from app/assets/images/emoji/left_facing_fist_tone4.png)bin960 -> 960 bytes
-rw-r--r--public/-/emojis/1/left_facing_fist_tone5.png (renamed from app/assets/images/emoji/left_facing_fist_tone5.png)bin976 -> 976 bytes
-rw-r--r--public/-/emojis/1/left_luggage.png (renamed from app/assets/images/emoji/left_luggage.png)bin576 -> 576 bytes
-rw-r--r--public/-/emojis/1/left_right_arrow.png (renamed from app/assets/images/emoji/left_right_arrow.png)bin495 -> 495 bytes
-rw-r--r--public/-/emojis/1/leftwards_arrow_with_hook.png (renamed from app/assets/images/emoji/leftwards_arrow_with_hook.png)bin643 -> 643 bytes
-rw-r--r--public/-/emojis/1/lemon.png (renamed from app/assets/images/emoji/lemon.png)bin1033 -> 1033 bytes
-rw-r--r--public/-/emojis/1/leo.png (renamed from app/assets/images/emoji/leo.png)bin745 -> 745 bytes
-rw-r--r--public/-/emojis/1/leopard.png (renamed from app/assets/images/emoji/leopard.png)bin2222 -> 2222 bytes
-rw-r--r--public/-/emojis/1/level_slider.png (renamed from app/assets/images/emoji/level_slider.png)bin454 -> 454 bytes
-rw-r--r--public/-/emojis/1/levitate.png (renamed from app/assets/images/emoji/levitate.png)bin914 -> 914 bytes
-rw-r--r--public/-/emojis/1/libra.png (renamed from app/assets/images/emoji/libra.png)bin657 -> 657 bytes
-rw-r--r--public/-/emojis/1/lifter.png (renamed from app/assets/images/emoji/lifter.png)bin1356 -> 1356 bytes
-rw-r--r--public/-/emojis/1/lifter_tone1.png (renamed from app/assets/images/emoji/lifter_tone1.png)bin1346 -> 1346 bytes
-rw-r--r--public/-/emojis/1/lifter_tone2.png (renamed from app/assets/images/emoji/lifter_tone2.png)bin1347 -> 1347 bytes
-rw-r--r--public/-/emojis/1/lifter_tone3.png (renamed from app/assets/images/emoji/lifter_tone3.png)bin1339 -> 1339 bytes
-rw-r--r--public/-/emojis/1/lifter_tone4.png (renamed from app/assets/images/emoji/lifter_tone4.png)bin1343 -> 1343 bytes
-rw-r--r--public/-/emojis/1/lifter_tone5.png (renamed from app/assets/images/emoji/lifter_tone5.png)bin1337 -> 1337 bytes
-rw-r--r--public/-/emojis/1/light_rail.png (renamed from app/assets/images/emoji/light_rail.png)bin902 -> 902 bytes
-rw-r--r--public/-/emojis/1/link.png (renamed from app/assets/images/emoji/link.png)bin477 -> 477 bytes
-rw-r--r--public/-/emojis/1/lion_face.png (renamed from app/assets/images/emoji/lion_face.png)bin1728 -> 1728 bytes
-rw-r--r--public/-/emojis/1/lips.png (renamed from app/assets/images/emoji/lips.png)bin599 -> 599 bytes
-rw-r--r--public/-/emojis/1/lipstick.png (renamed from app/assets/images/emoji/lipstick.png)bin549 -> 549 bytes
-rw-r--r--public/-/emojis/1/lizard.png (renamed from app/assets/images/emoji/lizard.png)bin1709 -> 1709 bytes
-rw-r--r--public/-/emojis/1/lock.png (renamed from app/assets/images/emoji/lock.png)bin986 -> 986 bytes
-rw-r--r--public/-/emojis/1/lock_with_ink_pen.png (renamed from app/assets/images/emoji/lock_with_ink_pen.png)bin1123 -> 1123 bytes
-rw-r--r--public/-/emojis/1/lollipop.png (renamed from app/assets/images/emoji/lollipop.png)bin2164 -> 2164 bytes
-rw-r--r--public/-/emojis/1/loop.png (renamed from app/assets/images/emoji/loop.png)bin550 -> 550 bytes
-rw-r--r--public/-/emojis/1/loud_sound.png (renamed from app/assets/images/emoji/loud_sound.png)bin977 -> 977 bytes
-rw-r--r--public/-/emojis/1/loudspeaker.png (renamed from app/assets/images/emoji/loudspeaker.png)bin1316 -> 1316 bytes
-rw-r--r--public/-/emojis/1/love_hotel.png (renamed from app/assets/images/emoji/love_hotel.png)bin372 -> 372 bytes
-rw-r--r--public/-/emojis/1/love_letter.png (renamed from app/assets/images/emoji/love_letter.png)bin923 -> 923 bytes
-rw-r--r--public/-/emojis/1/low_brightness.png (renamed from app/assets/images/emoji/low_brightness.png)bin431 -> 431 bytes
-rw-r--r--public/-/emojis/1/lying_face.png (renamed from app/assets/images/emoji/lying_face.png)bin1103 -> 1103 bytes
-rw-r--r--public/-/emojis/1/m.png (renamed from app/assets/images/emoji/m.png)bin500 -> 500 bytes
-rw-r--r--public/-/emojis/1/mag.png (renamed from app/assets/images/emoji/mag.png)bin1240 -> 1240 bytes
-rw-r--r--public/-/emojis/1/mag_right.png (renamed from app/assets/images/emoji/mag_right.png)bin1251 -> 1251 bytes
-rw-r--r--public/-/emojis/1/mahjong.png (renamed from app/assets/images/emoji/mahjong.png)bin951 -> 951 bytes
-rw-r--r--public/-/emojis/1/mailbox.png (renamed from app/assets/images/emoji/mailbox.png)bin1166 -> 1166 bytes
-rw-r--r--public/-/emojis/1/mailbox_closed.png (renamed from app/assets/images/emoji/mailbox_closed.png)bin1192 -> 1192 bytes
-rw-r--r--public/-/emojis/1/mailbox_with_mail.png (renamed from app/assets/images/emoji/mailbox_with_mail.png)bin1307 -> 1307 bytes
-rw-r--r--public/-/emojis/1/mailbox_with_no_mail.png (renamed from app/assets/images/emoji/mailbox_with_no_mail.png)bin960 -> 960 bytes
-rw-r--r--public/-/emojis/1/man.png (renamed from app/assets/images/emoji/man.png)bin1092 -> 1092 bytes
-rw-r--r--public/-/emojis/1/man_dancing.png (renamed from app/assets/images/emoji/man_dancing.png)bin1400 -> 1400 bytes
-rw-r--r--public/-/emojis/1/man_dancing_tone1.png (renamed from app/assets/images/emoji/man_dancing_tone1.png)bin1404 -> 1404 bytes
-rw-r--r--public/-/emojis/1/man_dancing_tone2.png (renamed from app/assets/images/emoji/man_dancing_tone2.png)bin1402 -> 1402 bytes
-rw-r--r--public/-/emojis/1/man_dancing_tone3.png (renamed from app/assets/images/emoji/man_dancing_tone3.png)bin1409 -> 1409 bytes
-rw-r--r--public/-/emojis/1/man_dancing_tone4.png (renamed from app/assets/images/emoji/man_dancing_tone4.png)bin1421 -> 1421 bytes
-rw-r--r--public/-/emojis/1/man_dancing_tone5.png (renamed from app/assets/images/emoji/man_dancing_tone5.png)bin1418 -> 1418 bytes
-rw-r--r--public/-/emojis/1/man_in_tuxedo.png (renamed from app/assets/images/emoji/man_in_tuxedo.png)bin1307 -> 1307 bytes
-rw-r--r--public/-/emojis/1/man_in_tuxedo_tone1.png (renamed from app/assets/images/emoji/man_in_tuxedo_tone1.png)bin1307 -> 1307 bytes
-rw-r--r--public/-/emojis/1/man_in_tuxedo_tone2.png (renamed from app/assets/images/emoji/man_in_tuxedo_tone2.png)bin1307 -> 1307 bytes
-rw-r--r--public/-/emojis/1/man_in_tuxedo_tone3.png (renamed from app/assets/images/emoji/man_in_tuxedo_tone3.png)bin1307 -> 1307 bytes
-rw-r--r--public/-/emojis/1/man_in_tuxedo_tone4.png (renamed from app/assets/images/emoji/man_in_tuxedo_tone4.png)bin1307 -> 1307 bytes
-rw-r--r--public/-/emojis/1/man_in_tuxedo_tone5.png (renamed from app/assets/images/emoji/man_in_tuxedo_tone5.png)bin1302 -> 1302 bytes
-rw-r--r--public/-/emojis/1/man_tone1.png (renamed from app/assets/images/emoji/man_tone1.png)bin1069 -> 1069 bytes
-rw-r--r--public/-/emojis/1/man_tone2.png (renamed from app/assets/images/emoji/man_tone2.png)bin1069 -> 1069 bytes
-rw-r--r--public/-/emojis/1/man_tone3.png (renamed from app/assets/images/emoji/man_tone3.png)bin1069 -> 1069 bytes
-rw-r--r--public/-/emojis/1/man_tone4.png (renamed from app/assets/images/emoji/man_tone4.png)bin1069 -> 1069 bytes
-rw-r--r--public/-/emojis/1/man_tone5.png (renamed from app/assets/images/emoji/man_tone5.png)bin1087 -> 1087 bytes
-rw-r--r--public/-/emojis/1/man_with_gua_pi_mao.png (renamed from app/assets/images/emoji/man_with_gua_pi_mao.png)bin1339 -> 1339 bytes
-rw-r--r--public/-/emojis/1/man_with_gua_pi_mao_tone1.png (renamed from app/assets/images/emoji/man_with_gua_pi_mao_tone1.png)bin1328 -> 1328 bytes
-rw-r--r--public/-/emojis/1/man_with_gua_pi_mao_tone2.png (renamed from app/assets/images/emoji/man_with_gua_pi_mao_tone2.png)bin1332 -> 1332 bytes
-rw-r--r--public/-/emojis/1/man_with_gua_pi_mao_tone3.png (renamed from app/assets/images/emoji/man_with_gua_pi_mao_tone3.png)bin1329 -> 1329 bytes
-rw-r--r--public/-/emojis/1/man_with_gua_pi_mao_tone4.png (renamed from app/assets/images/emoji/man_with_gua_pi_mao_tone4.png)bin1325 -> 1325 bytes
-rw-r--r--public/-/emojis/1/man_with_gua_pi_mao_tone5.png (renamed from app/assets/images/emoji/man_with_gua_pi_mao_tone5.png)bin1337 -> 1337 bytes
-rw-r--r--public/-/emojis/1/man_with_turban.png (renamed from app/assets/images/emoji/man_with_turban.png)bin1618 -> 1618 bytes
-rw-r--r--public/-/emojis/1/man_with_turban_tone1.png (renamed from app/assets/images/emoji/man_with_turban_tone1.png)bin1584 -> 1584 bytes
-rw-r--r--public/-/emojis/1/man_with_turban_tone2.png (renamed from app/assets/images/emoji/man_with_turban_tone2.png)bin1588 -> 1588 bytes
-rw-r--r--public/-/emojis/1/man_with_turban_tone3.png (renamed from app/assets/images/emoji/man_with_turban_tone3.png)bin1584 -> 1584 bytes
-rw-r--r--public/-/emojis/1/man_with_turban_tone4.png (renamed from app/assets/images/emoji/man_with_turban_tone4.png)bin1583 -> 1583 bytes
-rw-r--r--public/-/emojis/1/man_with_turban_tone5.png (renamed from app/assets/images/emoji/man_with_turban_tone5.png)bin1605 -> 1605 bytes
-rw-r--r--public/-/emojis/1/mans_shoe.png (renamed from app/assets/images/emoji/mans_shoe.png)bin1649 -> 1649 bytes
-rw-r--r--public/-/emojis/1/map.png (renamed from app/assets/images/emoji/map.png)bin2352 -> 2352 bytes
-rw-r--r--public/-/emojis/1/maple_leaf.png (renamed from app/assets/images/emoji/maple_leaf.png)bin1117 -> 1117 bytes
-rw-r--r--public/-/emojis/1/martial_arts_uniform.png (renamed from app/assets/images/emoji/martial_arts_uniform.png)bin1412 -> 1412 bytes
-rw-r--r--public/-/emojis/1/mask.png (renamed from app/assets/images/emoji/mask.png)bin1322 -> 1322 bytes
-rw-r--r--public/-/emojis/1/massage.png (renamed from app/assets/images/emoji/massage.png)bin1571 -> 1571 bytes
-rw-r--r--public/-/emojis/1/massage_tone1.png (renamed from app/assets/images/emoji/massage_tone1.png)bin1578 -> 1578 bytes
-rw-r--r--public/-/emojis/1/massage_tone2.png (renamed from app/assets/images/emoji/massage_tone2.png)bin1565 -> 1565 bytes
-rw-r--r--public/-/emojis/1/massage_tone3.png (renamed from app/assets/images/emoji/massage_tone3.png)bin1553 -> 1553 bytes
-rw-r--r--public/-/emojis/1/massage_tone4.png (renamed from app/assets/images/emoji/massage_tone4.png)bin1546 -> 1546 bytes
-rw-r--r--public/-/emojis/1/massage_tone5.png (renamed from app/assets/images/emoji/massage_tone5.png)bin1557 -> 1557 bytes
-rw-r--r--public/-/emojis/1/meat_on_bone.png (renamed from app/assets/images/emoji/meat_on_bone.png)bin1465 -> 1465 bytes
-rw-r--r--public/-/emojis/1/medal.png (renamed from app/assets/images/emoji/medal.png)bin1700 -> 1700 bytes
-rw-r--r--public/-/emojis/1/mega.png (renamed from app/assets/images/emoji/mega.png)bin1751 -> 1751 bytes
-rw-r--r--public/-/emojis/1/melon.png (renamed from app/assets/images/emoji/melon.png)bin2005 -> 2005 bytes
-rw-r--r--public/-/emojis/1/menorah.png (renamed from app/assets/images/emoji/menorah.png)bin1279 -> 1279 bytes
-rw-r--r--public/-/emojis/1/mens.png (renamed from app/assets/images/emoji/mens.png)bin561 -> 561 bytes
-rw-r--r--public/-/emojis/1/metal.png (renamed from app/assets/images/emoji/metal.png)bin894 -> 894 bytes
-rw-r--r--public/-/emojis/1/metal_tone1.png (renamed from app/assets/images/emoji/metal_tone1.png)bin894 -> 894 bytes
-rw-r--r--public/-/emojis/1/metal_tone2.png (renamed from app/assets/images/emoji/metal_tone2.png)bin888 -> 888 bytes
-rw-r--r--public/-/emojis/1/metal_tone3.png (renamed from app/assets/images/emoji/metal_tone3.png)bin894 -> 894 bytes
-rw-r--r--public/-/emojis/1/metal_tone4.png (renamed from app/assets/images/emoji/metal_tone4.png)bin888 -> 888 bytes
-rw-r--r--public/-/emojis/1/metal_tone5.png (renamed from app/assets/images/emoji/metal_tone5.png)bin894 -> 894 bytes
-rw-r--r--public/-/emojis/1/metro.png (renamed from app/assets/images/emoji/metro.png)bin1020 -> 1020 bytes
-rw-r--r--public/-/emojis/1/microphone.png (renamed from app/assets/images/emoji/microphone.png)bin1165 -> 1165 bytes
-rw-r--r--public/-/emojis/1/microphone2.png (renamed from app/assets/images/emoji/microphone2.png)bin839 -> 839 bytes
-rw-r--r--public/-/emojis/1/microscope.png (renamed from app/assets/images/emoji/microscope.png)bin1113 -> 1113 bytes
-rw-r--r--public/-/emojis/1/middle_finger.png (renamed from app/assets/images/emoji/middle_finger.png)bin893 -> 893 bytes
-rw-r--r--public/-/emojis/1/middle_finger_tone1.png (renamed from app/assets/images/emoji/middle_finger_tone1.png)bin892 -> 892 bytes
-rw-r--r--public/-/emojis/1/middle_finger_tone2.png (renamed from app/assets/images/emoji/middle_finger_tone2.png)bin892 -> 892 bytes
-rw-r--r--public/-/emojis/1/middle_finger_tone3.png (renamed from app/assets/images/emoji/middle_finger_tone3.png)bin892 -> 892 bytes
-rw-r--r--public/-/emojis/1/middle_finger_tone4.png (renamed from app/assets/images/emoji/middle_finger_tone4.png)bin892 -> 892 bytes
-rw-r--r--public/-/emojis/1/middle_finger_tone5.png (renamed from app/assets/images/emoji/middle_finger_tone5.png)bin892 -> 892 bytes
-rw-r--r--public/-/emojis/1/military_medal.png (renamed from app/assets/images/emoji/military_medal.png)bin949 -> 949 bytes
-rw-r--r--public/-/emojis/1/milk.png (renamed from app/assets/images/emoji/milk.png)bin1224 -> 1224 bytes
-rw-r--r--public/-/emojis/1/milky_way.png (renamed from app/assets/images/emoji/milky_way.png)bin622 -> 622 bytes
-rw-r--r--public/-/emojis/1/minibus.png (renamed from app/assets/images/emoji/minibus.png)bin1256 -> 1256 bytes
-rw-r--r--public/-/emojis/1/minidisc.png (renamed from app/assets/images/emoji/minidisc.png)bin522 -> 522 bytes
-rw-r--r--public/-/emojis/1/mobile_phone_off.png (renamed from app/assets/images/emoji/mobile_phone_off.png)bin621 -> 621 bytes
-rw-r--r--public/-/emojis/1/money_mouth.png (renamed from app/assets/images/emoji/money_mouth.png)bin967 -> 967 bytes
-rw-r--r--public/-/emojis/1/money_with_wings.png (renamed from app/assets/images/emoji/money_with_wings.png)bin2327 -> 2327 bytes
-rw-r--r--public/-/emojis/1/moneybag.png (renamed from app/assets/images/emoji/moneybag.png)bin2310 -> 2310 bytes
-rw-r--r--public/-/emojis/1/monkey.png (renamed from app/assets/images/emoji/monkey.png)bin1348 -> 1348 bytes
-rw-r--r--public/-/emojis/1/monkey_face.png (renamed from app/assets/images/emoji/monkey_face.png)bin1022 -> 1022 bytes
-rw-r--r--public/-/emojis/1/monorail.png (renamed from app/assets/images/emoji/monorail.png)bin1068 -> 1068 bytes
-rw-r--r--public/-/emojis/1/mortar_board.png (renamed from app/assets/images/emoji/mortar_board.png)bin710 -> 710 bytes
-rw-r--r--public/-/emojis/1/mosque.png (renamed from app/assets/images/emoji/mosque.png)bin984 -> 984 bytes
-rw-r--r--public/-/emojis/1/motor_scooter.png (renamed from app/assets/images/emoji/motor_scooter.png)bin1207 -> 1207 bytes
-rw-r--r--public/-/emojis/1/motorboat.png (renamed from app/assets/images/emoji/motorboat.png)bin990 -> 990 bytes
-rw-r--r--public/-/emojis/1/motorcycle.png (renamed from app/assets/images/emoji/motorcycle.png)bin2081 -> 2081 bytes
-rw-r--r--public/-/emojis/1/motorway.png (renamed from app/assets/images/emoji/motorway.png)bin1102 -> 1102 bytes
-rw-r--r--public/-/emojis/1/mount_fuji.png (renamed from app/assets/images/emoji/mount_fuji.png)bin881 -> 881 bytes
-rw-r--r--public/-/emojis/1/mountain.png (renamed from app/assets/images/emoji/mountain.png)bin1409 -> 1409 bytes
-rw-r--r--public/-/emojis/1/mountain_bicyclist.png (renamed from app/assets/images/emoji/mountain_bicyclist.png)bin2288 -> 2288 bytes
-rw-r--r--public/-/emojis/1/mountain_bicyclist_tone1.png (renamed from app/assets/images/emoji/mountain_bicyclist_tone1.png)bin2294 -> 2294 bytes
-rw-r--r--public/-/emojis/1/mountain_bicyclist_tone2.png (renamed from app/assets/images/emoji/mountain_bicyclist_tone2.png)bin2298 -> 2298 bytes
-rw-r--r--public/-/emojis/1/mountain_bicyclist_tone3.png (renamed from app/assets/images/emoji/mountain_bicyclist_tone3.png)bin2284 -> 2284 bytes
-rw-r--r--public/-/emojis/1/mountain_bicyclist_tone4.png (renamed from app/assets/images/emoji/mountain_bicyclist_tone4.png)bin2288 -> 2288 bytes
-rw-r--r--public/-/emojis/1/mountain_bicyclist_tone5.png (renamed from app/assets/images/emoji/mountain_bicyclist_tone5.png)bin2281 -> 2281 bytes
-rw-r--r--public/-/emojis/1/mountain_cableway.png (renamed from app/assets/images/emoji/mountain_cableway.png)bin811 -> 811 bytes
-rw-r--r--public/-/emojis/1/mountain_railway.png (renamed from app/assets/images/emoji/mountain_railway.png)bin1317 -> 1317 bytes
-rw-r--r--public/-/emojis/1/mountain_snow.png (renamed from app/assets/images/emoji/mountain_snow.png)bin1193 -> 1193 bytes
-rw-r--r--public/-/emojis/1/mouse.png (renamed from app/assets/images/emoji/mouse.png)bin1245 -> 1245 bytes
-rw-r--r--public/-/emojis/1/mouse2.png (renamed from app/assets/images/emoji/mouse2.png)bin1324 -> 1324 bytes
-rw-r--r--public/-/emojis/1/mouse_three_button.png (renamed from app/assets/images/emoji/mouse_three_button.png)bin934 -> 934 bytes
-rw-r--r--public/-/emojis/1/movie_camera.png (renamed from app/assets/images/emoji/movie_camera.png)bin576 -> 576 bytes
-rw-r--r--public/-/emojis/1/moyai.png (renamed from app/assets/images/emoji/moyai.png)bin1593 -> 1593 bytes
-rw-r--r--public/-/emojis/1/mrs_claus.png (renamed from app/assets/images/emoji/mrs_claus.png)bin3338 -> 3338 bytes
-rw-r--r--public/-/emojis/1/mrs_claus_tone1.png (renamed from app/assets/images/emoji/mrs_claus_tone1.png)bin1999 -> 1999 bytes
-rw-r--r--public/-/emojis/1/mrs_claus_tone2.png (renamed from app/assets/images/emoji/mrs_claus_tone2.png)bin2006 -> 2006 bytes
-rw-r--r--public/-/emojis/1/mrs_claus_tone3.png (renamed from app/assets/images/emoji/mrs_claus_tone3.png)bin2017 -> 2017 bytes
-rw-r--r--public/-/emojis/1/mrs_claus_tone4.png (renamed from app/assets/images/emoji/mrs_claus_tone4.png)bin2016 -> 2016 bytes
-rw-r--r--public/-/emojis/1/mrs_claus_tone5.png (renamed from app/assets/images/emoji/mrs_claus_tone5.png)bin2016 -> 2016 bytes
-rw-r--r--public/-/emojis/1/muscle.png (renamed from app/assets/images/emoji/muscle.png)bin1012 -> 1012 bytes
-rw-r--r--public/-/emojis/1/muscle_tone1.png (renamed from app/assets/images/emoji/muscle_tone1.png)bin1012 -> 1012 bytes
-rw-r--r--public/-/emojis/1/muscle_tone2.png (renamed from app/assets/images/emoji/muscle_tone2.png)bin1012 -> 1012 bytes
-rw-r--r--public/-/emojis/1/muscle_tone3.png (renamed from app/assets/images/emoji/muscle_tone3.png)bin1012 -> 1012 bytes
-rw-r--r--public/-/emojis/1/muscle_tone4.png (renamed from app/assets/images/emoji/muscle_tone4.png)bin1012 -> 1012 bytes
-rw-r--r--public/-/emojis/1/muscle_tone5.png (renamed from app/assets/images/emoji/muscle_tone5.png)bin1012 -> 1012 bytes
-rw-r--r--public/-/emojis/1/mushroom.png (renamed from app/assets/images/emoji/mushroom.png)bin1024 -> 1024 bytes
-rw-r--r--public/-/emojis/1/musical_keyboard.png (renamed from app/assets/images/emoji/musical_keyboard.png)bin1695 -> 1695 bytes
-rw-r--r--public/-/emojis/1/musical_note.png (renamed from app/assets/images/emoji/musical_note.png)bin419 -> 419 bytes
-rw-r--r--public/-/emojis/1/musical_score.png (renamed from app/assets/images/emoji/musical_score.png)bin1289 -> 1289 bytes
-rw-r--r--public/-/emojis/1/mute.png (renamed from app/assets/images/emoji/mute.png)bin823 -> 823 bytes
-rw-r--r--public/-/emojis/1/nail_care.png (renamed from app/assets/images/emoji/nail_care.png)bin1639 -> 1639 bytes
-rw-r--r--public/-/emojis/1/nail_care_tone1.png (renamed from app/assets/images/emoji/nail_care_tone1.png)bin1712 -> 1712 bytes
-rw-r--r--public/-/emojis/1/nail_care_tone2.png (renamed from app/assets/images/emoji/nail_care_tone2.png)bin1711 -> 1711 bytes
-rw-r--r--public/-/emojis/1/nail_care_tone3.png (renamed from app/assets/images/emoji/nail_care_tone3.png)bin1727 -> 1727 bytes
-rw-r--r--public/-/emojis/1/nail_care_tone4.png (renamed from app/assets/images/emoji/nail_care_tone4.png)bin1728 -> 1728 bytes
-rw-r--r--public/-/emojis/1/nail_care_tone5.png (renamed from app/assets/images/emoji/nail_care_tone5.png)bin1716 -> 1716 bytes
-rw-r--r--public/-/emojis/1/name_badge.png (renamed from app/assets/images/emoji/name_badge.png)bin632 -> 632 bytes
-rw-r--r--public/-/emojis/1/nauseated_face.png (renamed from app/assets/images/emoji/nauseated_face.png)bin965 -> 965 bytes
-rw-r--r--public/-/emojis/1/necktie.png (renamed from app/assets/images/emoji/necktie.png)bin995 -> 995 bytes
-rw-r--r--public/-/emojis/1/negative_squared_cross_mark.png (renamed from app/assets/images/emoji/negative_squared_cross_mark.png)bin370 -> 370 bytes
-rw-r--r--public/-/emojis/1/nerd.png (renamed from app/assets/images/emoji/nerd.png)bin975 -> 975 bytes
-rw-r--r--public/-/emojis/1/neutral_face.png (renamed from app/assets/images/emoji/neutral_face.png)bin517 -> 517 bytes
-rw-r--r--public/-/emojis/1/new.png (renamed from app/assets/images/emoji/new.png)bin486 -> 486 bytes
-rw-r--r--public/-/emojis/1/new_moon.png (renamed from app/assets/images/emoji/new_moon.png)bin829 -> 829 bytes
-rw-r--r--public/-/emojis/1/new_moon_with_face.png (renamed from app/assets/images/emoji/new_moon_with_face.png)bin975 -> 975 bytes
-rw-r--r--public/-/emojis/1/newspaper.png (renamed from app/assets/images/emoji/newspaper.png)bin1178 -> 1178 bytes
-rw-r--r--public/-/emojis/1/newspaper2.png (renamed from app/assets/images/emoji/newspaper2.png)bin1046 -> 1046 bytes
-rw-r--r--public/-/emojis/1/ng.png (renamed from app/assets/images/emoji/ng.png)bin445 -> 445 bytes
-rw-r--r--public/-/emojis/1/night_with_stars.png (renamed from app/assets/images/emoji/night_with_stars.png)bin835 -> 835 bytes
-rw-r--r--public/-/emojis/1/nine.png (renamed from app/assets/images/emoji/nine.png)bin607 -> 607 bytes
-rw-r--r--public/-/emojis/1/no_bell.png (renamed from app/assets/images/emoji/no_bell.png)bin823 -> 823 bytes
-rw-r--r--public/-/emojis/1/no_bicycles.png (renamed from app/assets/images/emoji/no_bicycles.png)bin998 -> 998 bytes
-rw-r--r--public/-/emojis/1/no_entry.png (renamed from app/assets/images/emoji/no_entry.png)bin377 -> 377 bytes
-rw-r--r--public/-/emojis/1/no_entry_sign.png (renamed from app/assets/images/emoji/no_entry_sign.png)bin555 -> 555 bytes
-rw-r--r--public/-/emojis/1/no_good.png (renamed from app/assets/images/emoji/no_good.png)bin1750 -> 1750 bytes
-rw-r--r--public/-/emojis/1/no_good_tone1.png (renamed from app/assets/images/emoji/no_good_tone1.png)bin1767 -> 1767 bytes
-rw-r--r--public/-/emojis/1/no_good_tone2.png (renamed from app/assets/images/emoji/no_good_tone2.png)bin1756 -> 1756 bytes
-rw-r--r--public/-/emojis/1/no_good_tone3.png (renamed from app/assets/images/emoji/no_good_tone3.png)bin1766 -> 1766 bytes
-rw-r--r--public/-/emojis/1/no_good_tone4.png (renamed from app/assets/images/emoji/no_good_tone4.png)bin1782 -> 1782 bytes
-rw-r--r--public/-/emojis/1/no_good_tone5.png (renamed from app/assets/images/emoji/no_good_tone5.png)bin1784 -> 1784 bytes
-rw-r--r--public/-/emojis/1/no_mobile_phones.png (renamed from app/assets/images/emoji/no_mobile_phones.png)bin790 -> 790 bytes
-rw-r--r--public/-/emojis/1/no_mouth.png (renamed from app/assets/images/emoji/no_mouth.png)bin465 -> 465 bytes
-rw-r--r--public/-/emojis/1/no_pedestrians.png (renamed from app/assets/images/emoji/no_pedestrians.png)bin875 -> 875 bytes
-rw-r--r--public/-/emojis/1/no_smoking.png (renamed from app/assets/images/emoji/no_smoking.png)bin1136 -> 1136 bytes
-rw-r--r--public/-/emojis/1/non-potable_water.png (renamed from app/assets/images/emoji/non-potable_water.png)bin827 -> 827 bytes
-rw-r--r--public/-/emojis/1/nose.png (renamed from app/assets/images/emoji/nose.png)bin703 -> 703 bytes
-rw-r--r--public/-/emojis/1/nose_tone1.png (renamed from app/assets/images/emoji/nose_tone1.png)bin703 -> 703 bytes
-rw-r--r--public/-/emojis/1/nose_tone2.png (renamed from app/assets/images/emoji/nose_tone2.png)bin703 -> 703 bytes
-rw-r--r--public/-/emojis/1/nose_tone3.png (renamed from app/assets/images/emoji/nose_tone3.png)bin703 -> 703 bytes
-rw-r--r--public/-/emojis/1/nose_tone4.png (renamed from app/assets/images/emoji/nose_tone4.png)bin703 -> 703 bytes
-rw-r--r--public/-/emojis/1/nose_tone5.png (renamed from app/assets/images/emoji/nose_tone5.png)bin703 -> 703 bytes
-rw-r--r--public/-/emojis/1/notebook.png (renamed from app/assets/images/emoji/notebook.png)bin1215 -> 1215 bytes
-rw-r--r--public/-/emojis/1/notebook_with_decorative_cover.png (renamed from app/assets/images/emoji/notebook_with_decorative_cover.png)bin1782 -> 1782 bytes
-rw-r--r--public/-/emojis/1/notepad_spiral.png (renamed from app/assets/images/emoji/notepad_spiral.png)bin1377 -> 1377 bytes
-rw-r--r--public/-/emojis/1/notes.png (renamed from app/assets/images/emoji/notes.png)bin501 -> 501 bytes
-rw-r--r--public/-/emojis/1/nut_and_bolt.png (renamed from app/assets/images/emoji/nut_and_bolt.png)bin899 -> 899 bytes
-rw-r--r--public/-/emojis/1/o.png (renamed from app/assets/images/emoji/o.png)bin475 -> 475 bytes
-rw-r--r--public/-/emojis/1/o2.png (renamed from app/assets/images/emoji/o2.png)bin425 -> 425 bytes
-rw-r--r--public/-/emojis/1/ocean.png (renamed from app/assets/images/emoji/ocean.png)bin1018 -> 1018 bytes
-rw-r--r--public/-/emojis/1/octagonal_sign.png (renamed from app/assets/images/emoji/octagonal_sign.png)bin260 -> 260 bytes
-rw-r--r--public/-/emojis/1/octopus.png (renamed from app/assets/images/emoji/octopus.png)bin1188 -> 1188 bytes
-rw-r--r--public/-/emojis/1/oden.png (renamed from app/assets/images/emoji/oden.png)bin794 -> 794 bytes
-rw-r--r--public/-/emojis/1/office.png (renamed from app/assets/images/emoji/office.png)bin524 -> 524 bytes
-rw-r--r--public/-/emojis/1/oil.png (renamed from app/assets/images/emoji/oil.png)bin674 -> 674 bytes
-rw-r--r--public/-/emojis/1/ok.png (renamed from app/assets/images/emoji/ok.png)bin511 -> 511 bytes
-rw-r--r--public/-/emojis/1/ok_hand.png (renamed from app/assets/images/emoji/ok_hand.png)bin979 -> 979 bytes
-rw-r--r--public/-/emojis/1/ok_hand_tone1.png (renamed from app/assets/images/emoji/ok_hand_tone1.png)bin979 -> 979 bytes
-rw-r--r--public/-/emojis/1/ok_hand_tone2.png (renamed from app/assets/images/emoji/ok_hand_tone2.png)bin979 -> 979 bytes
-rw-r--r--public/-/emojis/1/ok_hand_tone3.png (renamed from app/assets/images/emoji/ok_hand_tone3.png)bin979 -> 979 bytes
-rw-r--r--public/-/emojis/1/ok_hand_tone4.png (renamed from app/assets/images/emoji/ok_hand_tone4.png)bin979 -> 979 bytes
-rw-r--r--public/-/emojis/1/ok_hand_tone5.png (renamed from app/assets/images/emoji/ok_hand_tone5.png)bin979 -> 979 bytes
-rw-r--r--public/-/emojis/1/ok_woman.png (renamed from app/assets/images/emoji/ok_woman.png)bin1696 -> 1696 bytes
-rw-r--r--public/-/emojis/1/ok_woman_tone1.png (renamed from app/assets/images/emoji/ok_woman_tone1.png)bin1696 -> 1696 bytes
-rw-r--r--public/-/emojis/1/ok_woman_tone2.png (renamed from app/assets/images/emoji/ok_woman_tone2.png)bin1694 -> 1694 bytes
-rw-r--r--public/-/emojis/1/ok_woman_tone3.png (renamed from app/assets/images/emoji/ok_woman_tone3.png)bin1675 -> 1675 bytes
-rw-r--r--public/-/emojis/1/ok_woman_tone4.png (renamed from app/assets/images/emoji/ok_woman_tone4.png)bin1684 -> 1684 bytes
-rw-r--r--public/-/emojis/1/ok_woman_tone5.png (renamed from app/assets/images/emoji/ok_woman_tone5.png)bin1696 -> 1696 bytes
-rw-r--r--public/-/emojis/1/older_man.png (renamed from app/assets/images/emoji/older_man.png)bin1253 -> 1253 bytes
-rw-r--r--public/-/emojis/1/older_man_tone1.png (renamed from app/assets/images/emoji/older_man_tone1.png)bin1253 -> 1253 bytes
-rw-r--r--public/-/emojis/1/older_man_tone2.png (renamed from app/assets/images/emoji/older_man_tone2.png)bin1253 -> 1253 bytes
-rw-r--r--public/-/emojis/1/older_man_tone3.png (renamed from app/assets/images/emoji/older_man_tone3.png)bin1253 -> 1253 bytes
-rw-r--r--public/-/emojis/1/older_man_tone4.png (renamed from app/assets/images/emoji/older_man_tone4.png)bin1254 -> 1254 bytes
-rw-r--r--public/-/emojis/1/older_man_tone5.png (renamed from app/assets/images/emoji/older_man_tone5.png)bin1254 -> 1254 bytes
-rw-r--r--public/-/emojis/1/older_woman.png (renamed from app/assets/images/emoji/older_woman.png)bin1472 -> 1472 bytes
-rw-r--r--public/-/emojis/1/older_woman_tone1.png (renamed from app/assets/images/emoji/older_woman_tone1.png)bin1562 -> 1562 bytes
-rw-r--r--public/-/emojis/1/older_woman_tone2.png (renamed from app/assets/images/emoji/older_woman_tone2.png)bin1564 -> 1564 bytes
-rw-r--r--public/-/emojis/1/older_woman_tone3.png (renamed from app/assets/images/emoji/older_woman_tone3.png)bin1555 -> 1555 bytes
-rw-r--r--public/-/emojis/1/older_woman_tone4.png (renamed from app/assets/images/emoji/older_woman_tone4.png)bin1562 -> 1562 bytes
-rw-r--r--public/-/emojis/1/older_woman_tone5.png (renamed from app/assets/images/emoji/older_woman_tone5.png)bin1544 -> 1544 bytes
-rw-r--r--public/-/emojis/1/om_symbol.png (renamed from app/assets/images/emoji/om_symbol.png)bin773 -> 773 bytes
-rw-r--r--public/-/emojis/1/on.png (renamed from app/assets/images/emoji/on.png)bin459 -> 459 bytes
-rw-r--r--public/-/emojis/1/oncoming_automobile.png (renamed from app/assets/images/emoji/oncoming_automobile.png)bin1238 -> 1238 bytes
-rw-r--r--public/-/emojis/1/oncoming_bus.png (renamed from app/assets/images/emoji/oncoming_bus.png)bin964 -> 964 bytes
-rw-r--r--public/-/emojis/1/oncoming_police_car.png (renamed from app/assets/images/emoji/oncoming_police_car.png)bin1547 -> 1547 bytes
-rw-r--r--public/-/emojis/1/oncoming_taxi.png (renamed from app/assets/images/emoji/oncoming_taxi.png)bin1405 -> 1405 bytes
-rw-r--r--public/-/emojis/1/one.png (renamed from app/assets/images/emoji/one.png)bin442 -> 442 bytes
-rw-r--r--public/-/emojis/1/open_file_folder.png (renamed from app/assets/images/emoji/open_file_folder.png)bin755 -> 755 bytes
-rw-r--r--public/-/emojis/1/open_hands.png (renamed from app/assets/images/emoji/open_hands.png)bin1053 -> 1053 bytes
-rw-r--r--public/-/emojis/1/open_hands_tone1.png (renamed from app/assets/images/emoji/open_hands_tone1.png)bin1053 -> 1053 bytes
-rw-r--r--public/-/emojis/1/open_hands_tone2.png (renamed from app/assets/images/emoji/open_hands_tone2.png)bin1053 -> 1053 bytes
-rw-r--r--public/-/emojis/1/open_hands_tone3.png (renamed from app/assets/images/emoji/open_hands_tone3.png)bin1053 -> 1053 bytes
-rw-r--r--public/-/emojis/1/open_hands_tone4.png (renamed from app/assets/images/emoji/open_hands_tone4.png)bin1053 -> 1053 bytes
-rw-r--r--public/-/emojis/1/open_hands_tone5.png (renamed from app/assets/images/emoji/open_hands_tone5.png)bin1053 -> 1053 bytes
-rw-r--r--public/-/emojis/1/open_mouth.png (renamed from app/assets/images/emoji/open_mouth.png)bin575 -> 575 bytes
-rw-r--r--public/-/emojis/1/ophiuchus.png (renamed from app/assets/images/emoji/ophiuchus.png)bin723 -> 723 bytes
-rw-r--r--public/-/emojis/1/orange_book.png (renamed from app/assets/images/emoji/orange_book.png)bin1329 -> 1329 bytes
-rw-r--r--public/-/emojis/1/orthodox_cross.png (renamed from app/assets/images/emoji/orthodox_cross.png)bin239 -> 239 bytes
-rw-r--r--public/-/emojis/1/outbox_tray.png (renamed from app/assets/images/emoji/outbox_tray.png)bin1002 -> 1002 bytes
-rw-r--r--public/-/emojis/1/owl.png (renamed from app/assets/images/emoji/owl.png)bin2045 -> 2045 bytes
-rw-r--r--public/-/emojis/1/ox.png (renamed from app/assets/images/emoji/ox.png)bin1436 -> 1436 bytes
-rw-r--r--public/-/emojis/1/package.png (renamed from app/assets/images/emoji/package.png)bin950 -> 950 bytes
-rw-r--r--public/-/emojis/1/page_facing_up.png (renamed from app/assets/images/emoji/page_facing_up.png)bin1110 -> 1110 bytes
-rw-r--r--public/-/emojis/1/page_with_curl.png (renamed from app/assets/images/emoji/page_with_curl.png)bin1157 -> 1157 bytes
-rw-r--r--public/-/emojis/1/pager.png (renamed from app/assets/images/emoji/pager.png)bin553 -> 553 bytes
-rw-r--r--public/-/emojis/1/paintbrush.png (renamed from app/assets/images/emoji/paintbrush.png)bin950 -> 950 bytes
-rw-r--r--public/-/emojis/1/palm_tree.png (renamed from app/assets/images/emoji/palm_tree.png)bin1450 -> 1450 bytes
-rw-r--r--public/-/emojis/1/pancakes.png (renamed from app/assets/images/emoji/pancakes.png)bin3661 -> 3661 bytes
-rw-r--r--public/-/emojis/1/panda_face.png (renamed from app/assets/images/emoji/panda_face.png)bin1478 -> 1478 bytes
-rw-r--r--public/-/emojis/1/paperclip.png (renamed from app/assets/images/emoji/paperclip.png)bin439 -> 439 bytes
-rw-r--r--public/-/emojis/1/paperclips.png (renamed from app/assets/images/emoji/paperclips.png)bin642 -> 642 bytes
-rw-r--r--public/-/emojis/1/park.png (renamed from app/assets/images/emoji/park.png)bin929 -> 929 bytes
-rw-r--r--public/-/emojis/1/parking.png (renamed from app/assets/images/emoji/parking.png)bin385 -> 385 bytes
-rw-r--r--public/-/emojis/1/part_alternation_mark.png (renamed from app/assets/images/emoji/part_alternation_mark.png)bin521 -> 521 bytes
-rw-r--r--public/-/emojis/1/partly_sunny.png (renamed from app/assets/images/emoji/partly_sunny.png)bin977 -> 977 bytes
-rw-r--r--public/-/emojis/1/passport_control.png (renamed from app/assets/images/emoji/passport_control.png)bin683 -> 683 bytes
-rw-r--r--public/-/emojis/1/pause_button.png (renamed from app/assets/images/emoji/pause_button.png)bin395 -> 395 bytes
-rw-r--r--public/-/emojis/1/peace.png (renamed from app/assets/images/emoji/peace.png)bin933 -> 933 bytes
-rw-r--r--public/-/emojis/1/peach.png (renamed from app/assets/images/emoji/peach.png)bin1189 -> 1189 bytes
-rw-r--r--public/-/emojis/1/peanuts.png (renamed from app/assets/images/emoji/peanuts.png)bin3266 -> 3266 bytes
-rw-r--r--public/-/emojis/1/pear.png (renamed from app/assets/images/emoji/pear.png)bin747 -> 747 bytes
-rw-r--r--public/-/emojis/1/pen_ballpoint.png (renamed from app/assets/images/emoji/pen_ballpoint.png)bin696 -> 696 bytes
-rw-r--r--public/-/emojis/1/pen_fountain.png (renamed from app/assets/images/emoji/pen_fountain.png)bin623 -> 623 bytes
-rw-r--r--public/-/emojis/1/pencil.png (renamed from app/assets/images/emoji/pencil.png)bin1624 -> 1624 bytes
-rw-r--r--public/-/emojis/1/pencil2.png (renamed from app/assets/images/emoji/pencil2.png)bin654 -> 654 bytes
-rw-r--r--public/-/emojis/1/penguin.png (renamed from app/assets/images/emoji/penguin.png)bin1034 -> 1034 bytes
-rw-r--r--public/-/emojis/1/pensive.png (renamed from app/assets/images/emoji/pensive.png)bin718 -> 718 bytes
-rw-r--r--public/-/emojis/1/performing_arts.png (renamed from app/assets/images/emoji/performing_arts.png)bin1971 -> 1971 bytes
-rw-r--r--public/-/emojis/1/persevere.png (renamed from app/assets/images/emoji/persevere.png)bin891 -> 891 bytes
-rw-r--r--public/-/emojis/1/person_frowning.png (renamed from app/assets/images/emoji/person_frowning.png)bin1148 -> 1148 bytes
-rw-r--r--public/-/emojis/1/person_frowning_tone1.png (renamed from app/assets/images/emoji/person_frowning_tone1.png)bin1141 -> 1141 bytes
-rw-r--r--public/-/emojis/1/person_frowning_tone2.png (renamed from app/assets/images/emoji/person_frowning_tone2.png)bin1141 -> 1141 bytes
-rw-r--r--public/-/emojis/1/person_frowning_tone3.png (renamed from app/assets/images/emoji/person_frowning_tone3.png)bin1141 -> 1141 bytes
-rw-r--r--public/-/emojis/1/person_frowning_tone4.png (renamed from app/assets/images/emoji/person_frowning_tone4.png)bin1109 -> 1109 bytes
-rw-r--r--public/-/emojis/1/person_frowning_tone5.png (renamed from app/assets/images/emoji/person_frowning_tone5.png)bin1114 -> 1114 bytes
-rw-r--r--public/-/emojis/1/person_with_blond_hair.png (renamed from app/assets/images/emoji/person_with_blond_hair.png)bin1205 -> 1205 bytes
-rw-r--r--public/-/emojis/1/person_with_blond_hair_tone1.png (renamed from app/assets/images/emoji/person_with_blond_hair_tone1.png)bin1181 -> 1181 bytes
-rw-r--r--public/-/emojis/1/person_with_blond_hair_tone2.png (renamed from app/assets/images/emoji/person_with_blond_hair_tone2.png)bin1181 -> 1181 bytes
-rw-r--r--public/-/emojis/1/person_with_blond_hair_tone3.png (renamed from app/assets/images/emoji/person_with_blond_hair_tone3.png)bin1181 -> 1181 bytes
-rw-r--r--public/-/emojis/1/person_with_blond_hair_tone4.png (renamed from app/assets/images/emoji/person_with_blond_hair_tone4.png)bin1189 -> 1189 bytes
-rw-r--r--public/-/emojis/1/person_with_blond_hair_tone5.png (renamed from app/assets/images/emoji/person_with_blond_hair_tone5.png)bin1214 -> 1214 bytes
-rw-r--r--public/-/emojis/1/person_with_pouting_face.png (renamed from app/assets/images/emoji/person_with_pouting_face.png)bin1297 -> 1297 bytes
-rw-r--r--public/-/emojis/1/person_with_pouting_face_tone1.png (renamed from app/assets/images/emoji/person_with_pouting_face_tone1.png)bin1309 -> 1309 bytes
-rw-r--r--public/-/emojis/1/person_with_pouting_face_tone2.png (renamed from app/assets/images/emoji/person_with_pouting_face_tone2.png)bin1292 -> 1292 bytes
-rw-r--r--public/-/emojis/1/person_with_pouting_face_tone3.png (renamed from app/assets/images/emoji/person_with_pouting_face_tone3.png)bin1305 -> 1305 bytes
-rw-r--r--public/-/emojis/1/person_with_pouting_face_tone4.png (renamed from app/assets/images/emoji/person_with_pouting_face_tone4.png)bin1296 -> 1296 bytes
-rw-r--r--public/-/emojis/1/person_with_pouting_face_tone5.png (renamed from app/assets/images/emoji/person_with_pouting_face_tone5.png)bin1303 -> 1303 bytes
-rw-r--r--public/-/emojis/1/pick.png (renamed from app/assets/images/emoji/pick.png)bin1023 -> 1023 bytes
-rw-r--r--public/-/emojis/1/pig.png (renamed from app/assets/images/emoji/pig.png)bin1138 -> 1138 bytes
-rw-r--r--public/-/emojis/1/pig2.png (renamed from app/assets/images/emoji/pig2.png)bin1548 -> 1548 bytes
-rw-r--r--public/-/emojis/1/pig_nose.png (renamed from app/assets/images/emoji/pig_nose.png)bin820 -> 820 bytes
-rw-r--r--public/-/emojis/1/pill.png (renamed from app/assets/images/emoji/pill.png)bin442 -> 442 bytes
-rw-r--r--public/-/emojis/1/pineapple.png (renamed from app/assets/images/emoji/pineapple.png)bin1642 -> 1642 bytes
-rw-r--r--public/-/emojis/1/ping_pong.png (renamed from app/assets/images/emoji/ping_pong.png)bin823 -> 823 bytes
-rw-r--r--public/-/emojis/1/pisces.png (renamed from app/assets/images/emoji/pisces.png)bin678 -> 678 bytes
-rw-r--r--public/-/emojis/1/pizza.png (renamed from app/assets/images/emoji/pizza.png)bin2008 -> 2008 bytes
-rw-r--r--public/-/emojis/1/place_of_worship.png (renamed from app/assets/images/emoji/place_of_worship.png)bin487 -> 487 bytes
-rw-r--r--public/-/emojis/1/play_pause.png (renamed from app/assets/images/emoji/play_pause.png)bin509 -> 509 bytes
-rw-r--r--public/-/emojis/1/point_down.png (renamed from app/assets/images/emoji/point_down.png)bin853 -> 853 bytes
-rw-r--r--public/-/emojis/1/point_down_tone1.png (renamed from app/assets/images/emoji/point_down_tone1.png)bin856 -> 856 bytes
-rw-r--r--public/-/emojis/1/point_down_tone2.png (renamed from app/assets/images/emoji/point_down_tone2.png)bin856 -> 856 bytes
-rw-r--r--public/-/emojis/1/point_down_tone3.png (renamed from app/assets/images/emoji/point_down_tone3.png)bin858 -> 858 bytes
-rw-r--r--public/-/emojis/1/point_down_tone4.png (renamed from app/assets/images/emoji/point_down_tone4.png)bin856 -> 856 bytes
-rw-r--r--public/-/emojis/1/point_down_tone5.png (renamed from app/assets/images/emoji/point_down_tone5.png)bin856 -> 856 bytes
-rw-r--r--public/-/emojis/1/point_left.png (renamed from app/assets/images/emoji/point_left.png)bin825 -> 825 bytes
-rw-r--r--public/-/emojis/1/point_left_tone1.png (renamed from app/assets/images/emoji/point_left_tone1.png)bin832 -> 832 bytes
-rw-r--r--public/-/emojis/1/point_left_tone2.png (renamed from app/assets/images/emoji/point_left_tone2.png)bin830 -> 830 bytes
-rw-r--r--public/-/emojis/1/point_left_tone3.png (renamed from app/assets/images/emoji/point_left_tone3.png)bin830 -> 830 bytes
-rw-r--r--public/-/emojis/1/point_left_tone4.png (renamed from app/assets/images/emoji/point_left_tone4.png)bin830 -> 830 bytes
-rw-r--r--public/-/emojis/1/point_left_tone5.png (renamed from app/assets/images/emoji/point_left_tone5.png)bin832 -> 832 bytes
-rw-r--r--public/-/emojis/1/point_right.png (renamed from app/assets/images/emoji/point_right.png)bin805 -> 805 bytes
-rw-r--r--public/-/emojis/1/point_right_tone1.png (renamed from app/assets/images/emoji/point_right_tone1.png)bin805 -> 805 bytes
-rw-r--r--public/-/emojis/1/point_right_tone2.png (renamed from app/assets/images/emoji/point_right_tone2.png)bin805 -> 805 bytes
-rw-r--r--public/-/emojis/1/point_right_tone3.png (renamed from app/assets/images/emoji/point_right_tone3.png)bin805 -> 805 bytes
-rw-r--r--public/-/emojis/1/point_right_tone4.png (renamed from app/assets/images/emoji/point_right_tone4.png)bin805 -> 805 bytes
-rw-r--r--public/-/emojis/1/point_right_tone5.png (renamed from app/assets/images/emoji/point_right_tone5.png)bin805 -> 805 bytes
-rw-r--r--public/-/emojis/1/point_up.png (renamed from app/assets/images/emoji/point_up.png)bin819 -> 819 bytes
-rw-r--r--public/-/emojis/1/point_up_2.png (renamed from app/assets/images/emoji/point_up_2.png)bin822 -> 822 bytes
-rw-r--r--public/-/emojis/1/point_up_2_tone1.png (renamed from app/assets/images/emoji/point_up_2_tone1.png)bin822 -> 822 bytes
-rw-r--r--public/-/emojis/1/point_up_2_tone2.png (renamed from app/assets/images/emoji/point_up_2_tone2.png)bin822 -> 822 bytes
-rw-r--r--public/-/emojis/1/point_up_2_tone3.png (renamed from app/assets/images/emoji/point_up_2_tone3.png)bin871 -> 871 bytes
-rw-r--r--public/-/emojis/1/point_up_2_tone4.png (renamed from app/assets/images/emoji/point_up_2_tone4.png)bin822 -> 822 bytes
-rw-r--r--public/-/emojis/1/point_up_2_tone5.png (renamed from app/assets/images/emoji/point_up_2_tone5.png)bin822 -> 822 bytes
-rw-r--r--public/-/emojis/1/point_up_tone1.png (renamed from app/assets/images/emoji/point_up_tone1.png)bin820 -> 820 bytes
-rw-r--r--public/-/emojis/1/point_up_tone2.png (renamed from app/assets/images/emoji/point_up_tone2.png)bin820 -> 820 bytes
-rw-r--r--public/-/emojis/1/point_up_tone3.png (renamed from app/assets/images/emoji/point_up_tone3.png)bin820 -> 820 bytes
-rw-r--r--public/-/emojis/1/point_up_tone4.png (renamed from app/assets/images/emoji/point_up_tone4.png)bin820 -> 820 bytes
-rw-r--r--public/-/emojis/1/point_up_tone5.png (renamed from app/assets/images/emoji/point_up_tone5.png)bin820 -> 820 bytes
-rw-r--r--public/-/emojis/1/police_car.png (renamed from app/assets/images/emoji/police_car.png)bin1431 -> 1431 bytes
-rw-r--r--public/-/emojis/1/poodle.png (renamed from app/assets/images/emoji/poodle.png)bin1531 -> 1531 bytes
-rw-r--r--public/-/emojis/1/poop.png (renamed from app/assets/images/emoji/poop.png)bin1273 -> 1273 bytes
-rw-r--r--public/-/emojis/1/popcorn.png (renamed from app/assets/images/emoji/popcorn.png)bin1843 -> 1843 bytes
-rw-r--r--public/-/emojis/1/post_office.png (renamed from app/assets/images/emoji/post_office.png)bin676 -> 676 bytes
-rw-r--r--public/-/emojis/1/postal_horn.png (renamed from app/assets/images/emoji/postal_horn.png)bin809 -> 809 bytes
-rw-r--r--public/-/emojis/1/postbox.png (renamed from app/assets/images/emoji/postbox.png)bin1077 -> 1077 bytes
-rw-r--r--public/-/emojis/1/potable_water.png (renamed from app/assets/images/emoji/potable_water.png)bin633 -> 633 bytes
-rw-r--r--public/-/emojis/1/potato.png (renamed from app/assets/images/emoji/potato.png)bin1246 -> 1246 bytes
-rw-r--r--public/-/emojis/1/pouch.png (renamed from app/assets/images/emoji/pouch.png)bin1259 -> 1259 bytes
-rw-r--r--public/-/emojis/1/poultry_leg.png (renamed from app/assets/images/emoji/poultry_leg.png)bin925 -> 925 bytes
-rw-r--r--public/-/emojis/1/pound.png (renamed from app/assets/images/emoji/pound.png)bin452 -> 452 bytes
-rw-r--r--public/-/emojis/1/pouting_cat.png (renamed from app/assets/images/emoji/pouting_cat.png)bin1675 -> 1675 bytes
-rw-r--r--public/-/emojis/1/pray.png (renamed from app/assets/images/emoji/pray.png)bin1122 -> 1122 bytes
-rw-r--r--public/-/emojis/1/pray_tone1.png (renamed from app/assets/images/emoji/pray_tone1.png)bin1131 -> 1131 bytes
-rw-r--r--public/-/emojis/1/pray_tone2.png (renamed from app/assets/images/emoji/pray_tone2.png)bin1134 -> 1134 bytes
-rw-r--r--public/-/emojis/1/pray_tone3.png (renamed from app/assets/images/emoji/pray_tone3.png)bin1137 -> 1137 bytes
-rw-r--r--public/-/emojis/1/pray_tone4.png (renamed from app/assets/images/emoji/pray_tone4.png)bin1126 -> 1126 bytes
-rw-r--r--public/-/emojis/1/pray_tone5.png (renamed from app/assets/images/emoji/pray_tone5.png)bin1117 -> 1117 bytes
-rw-r--r--public/-/emojis/1/prayer_beads.png (renamed from app/assets/images/emoji/prayer_beads.png)bin1059 -> 1059 bytes
-rw-r--r--public/-/emojis/1/pregnant_woman.png (renamed from app/assets/images/emoji/pregnant_woman.png)bin1252 -> 1252 bytes
-rw-r--r--public/-/emojis/1/pregnant_woman_tone1.png (renamed from app/assets/images/emoji/pregnant_woman_tone1.png)bin1255 -> 1255 bytes
-rw-r--r--public/-/emojis/1/pregnant_woman_tone2.png (renamed from app/assets/images/emoji/pregnant_woman_tone2.png)bin1246 -> 1246 bytes
-rw-r--r--public/-/emojis/1/pregnant_woman_tone3.png (renamed from app/assets/images/emoji/pregnant_woman_tone3.png)bin1237 -> 1237 bytes
-rw-r--r--public/-/emojis/1/pregnant_woman_tone4.png (renamed from app/assets/images/emoji/pregnant_woman_tone4.png)bin1246 -> 1246 bytes
-rw-r--r--public/-/emojis/1/pregnant_woman_tone5.png (renamed from app/assets/images/emoji/pregnant_woman_tone5.png)bin1235 -> 1235 bytes
-rw-r--r--public/-/emojis/1/prince.png (renamed from app/assets/images/emoji/prince.png)bin1616 -> 1616 bytes
-rw-r--r--public/-/emojis/1/prince_tone1.png (renamed from app/assets/images/emoji/prince_tone1.png)bin1618 -> 1618 bytes
-rw-r--r--public/-/emojis/1/prince_tone2.png (renamed from app/assets/images/emoji/prince_tone2.png)bin1621 -> 1621 bytes
-rw-r--r--public/-/emojis/1/prince_tone3.png (renamed from app/assets/images/emoji/prince_tone3.png)bin1619 -> 1619 bytes
-rw-r--r--public/-/emojis/1/prince_tone4.png (renamed from app/assets/images/emoji/prince_tone4.png)bin1619 -> 1619 bytes
-rw-r--r--public/-/emojis/1/prince_tone5.png (renamed from app/assets/images/emoji/prince_tone5.png)bin1616 -> 1616 bytes
-rw-r--r--public/-/emojis/1/princess.png (renamed from app/assets/images/emoji/princess.png)bin1812 -> 1812 bytes
-rw-r--r--public/-/emojis/1/princess_tone1.png (renamed from app/assets/images/emoji/princess_tone1.png)bin1812 -> 1812 bytes
-rw-r--r--public/-/emojis/1/princess_tone2.png (renamed from app/assets/images/emoji/princess_tone2.png)bin1805 -> 1805 bytes
-rw-r--r--public/-/emojis/1/princess_tone3.png (renamed from app/assets/images/emoji/princess_tone3.png)bin1805 -> 1805 bytes
-rw-r--r--public/-/emojis/1/princess_tone4.png (renamed from app/assets/images/emoji/princess_tone4.png)bin1813 -> 1813 bytes
-rw-r--r--public/-/emojis/1/princess_tone5.png (renamed from app/assets/images/emoji/princess_tone5.png)bin1812 -> 1812 bytes
-rw-r--r--public/-/emojis/1/printer.png (renamed from app/assets/images/emoji/printer.png)bin926 -> 926 bytes
-rw-r--r--public/-/emojis/1/projector.png (renamed from app/assets/images/emoji/projector.png)bin943 -> 943 bytes
-rw-r--r--public/-/emojis/1/punch.png (renamed from app/assets/images/emoji/punch.png)bin838 -> 838 bytes
-rw-r--r--public/-/emojis/1/punch_tone1.png (renamed from app/assets/images/emoji/punch_tone1.png)bin838 -> 838 bytes
-rw-r--r--public/-/emojis/1/punch_tone2.png (renamed from app/assets/images/emoji/punch_tone2.png)bin838 -> 838 bytes
-rw-r--r--public/-/emojis/1/punch_tone3.png (renamed from app/assets/images/emoji/punch_tone3.png)bin838 -> 838 bytes
-rw-r--r--public/-/emojis/1/punch_tone4.png (renamed from app/assets/images/emoji/punch_tone4.png)bin838 -> 838 bytes
-rw-r--r--public/-/emojis/1/punch_tone5.png (renamed from app/assets/images/emoji/punch_tone5.png)bin838 -> 838 bytes
-rw-r--r--public/-/emojis/1/purple_heart.png (renamed from app/assets/images/emoji/purple_heart.png)bin435 -> 435 bytes
-rw-r--r--public/-/emojis/1/purse.png (renamed from app/assets/images/emoji/purse.png)bin1558 -> 1558 bytes
-rw-r--r--public/-/emojis/1/pushpin.png (renamed from app/assets/images/emoji/pushpin.png)bin640 -> 640 bytes
-rw-r--r--public/-/emojis/1/put_litter_in_its_place.png (renamed from app/assets/images/emoji/put_litter_in_its_place.png)bin650 -> 650 bytes
-rw-r--r--public/-/emojis/1/question.png (renamed from app/assets/images/emoji/question.png)bin449 -> 449 bytes
-rw-r--r--public/-/emojis/1/rabbit.png (renamed from app/assets/images/emoji/rabbit.png)bin1660 -> 1660 bytes
-rw-r--r--public/-/emojis/1/rabbit2.png (renamed from app/assets/images/emoji/rabbit2.png)bin1805 -> 1805 bytes
-rw-r--r--public/-/emojis/1/race_car.png (renamed from app/assets/images/emoji/race_car.png)bin2140 -> 2140 bytes
-rw-r--r--public/-/emojis/1/racehorse.png (renamed from app/assets/images/emoji/racehorse.png)bin1401 -> 1401 bytes
-rw-r--r--public/-/emojis/1/radio.png (renamed from app/assets/images/emoji/radio.png)bin851 -> 851 bytes
-rw-r--r--public/-/emojis/1/radio_button.png (renamed from app/assets/images/emoji/radio_button.png)bin674 -> 674 bytes
-rw-r--r--public/-/emojis/1/radioactive.png (renamed from app/assets/images/emoji/radioactive.png)bin858 -> 858 bytes
-rw-r--r--public/-/emojis/1/rage.png (renamed from app/assets/images/emoji/rage.png)bin845 -> 845 bytes
-rw-r--r--public/-/emojis/1/railway_car.png (renamed from app/assets/images/emoji/railway_car.png)bin847 -> 847 bytes
-rw-r--r--public/-/emojis/1/railway_track.png (renamed from app/assets/images/emoji/railway_track.png)bin1550 -> 1550 bytes
-rw-r--r--public/-/emojis/1/rainbow.png (renamed from app/assets/images/emoji/rainbow.png)bin1299 -> 1299 bytes
-rw-r--r--public/-/emojis/1/raised_back_of_hand.png (renamed from app/assets/images/emoji/raised_back_of_hand.png)bin848 -> 848 bytes
-rw-r--r--public/-/emojis/1/raised_back_of_hand_tone1.png (renamed from app/assets/images/emoji/raised_back_of_hand_tone1.png)bin848 -> 848 bytes
-rw-r--r--public/-/emojis/1/raised_back_of_hand_tone2.png (renamed from app/assets/images/emoji/raised_back_of_hand_tone2.png)bin848 -> 848 bytes
-rw-r--r--public/-/emojis/1/raised_back_of_hand_tone3.png (renamed from app/assets/images/emoji/raised_back_of_hand_tone3.png)bin848 -> 848 bytes
-rw-r--r--public/-/emojis/1/raised_back_of_hand_tone4.png (renamed from app/assets/images/emoji/raised_back_of_hand_tone4.png)bin848 -> 848 bytes
-rw-r--r--public/-/emojis/1/raised_back_of_hand_tone5.png (renamed from app/assets/images/emoji/raised_back_of_hand_tone5.png)bin848 -> 848 bytes
-rw-r--r--public/-/emojis/1/raised_hand.png (renamed from app/assets/images/emoji/raised_hand.png)bin791 -> 791 bytes
-rw-r--r--public/-/emojis/1/raised_hand_tone1.png (renamed from app/assets/images/emoji/raised_hand_tone1.png)bin791 -> 791 bytes
-rw-r--r--public/-/emojis/1/raised_hand_tone2.png (renamed from app/assets/images/emoji/raised_hand_tone2.png)bin791 -> 791 bytes
-rw-r--r--public/-/emojis/1/raised_hand_tone3.png (renamed from app/assets/images/emoji/raised_hand_tone3.png)bin791 -> 791 bytes
-rw-r--r--public/-/emojis/1/raised_hand_tone4.png (renamed from app/assets/images/emoji/raised_hand_tone4.png)bin791 -> 791 bytes
-rw-r--r--public/-/emojis/1/raised_hand_tone5.png (renamed from app/assets/images/emoji/raised_hand_tone5.png)bin791 -> 791 bytes
-rw-r--r--public/-/emojis/1/raised_hands.png (renamed from app/assets/images/emoji/raised_hands.png)bin1098 -> 1098 bytes
-rw-r--r--public/-/emojis/1/raised_hands_tone1.png (renamed from app/assets/images/emoji/raised_hands_tone1.png)bin1098 -> 1098 bytes
-rw-r--r--public/-/emojis/1/raised_hands_tone2.png (renamed from app/assets/images/emoji/raised_hands_tone2.png)bin1098 -> 1098 bytes
-rw-r--r--public/-/emojis/1/raised_hands_tone3.png (renamed from app/assets/images/emoji/raised_hands_tone3.png)bin1098 -> 1098 bytes
-rw-r--r--public/-/emojis/1/raised_hands_tone4.png (renamed from app/assets/images/emoji/raised_hands_tone4.png)bin1098 -> 1098 bytes
-rw-r--r--public/-/emojis/1/raised_hands_tone5.png (renamed from app/assets/images/emoji/raised_hands_tone5.png)bin1098 -> 1098 bytes
-rw-r--r--public/-/emojis/1/raising_hand.png (renamed from app/assets/images/emoji/raising_hand.png)bin1664 -> 1664 bytes
-rw-r--r--public/-/emojis/1/raising_hand_tone1.png (renamed from app/assets/images/emoji/raising_hand_tone1.png)bin1678 -> 1678 bytes
-rw-r--r--public/-/emojis/1/raising_hand_tone2.png (renamed from app/assets/images/emoji/raising_hand_tone2.png)bin1665 -> 1665 bytes
-rw-r--r--public/-/emojis/1/raising_hand_tone3.png (renamed from app/assets/images/emoji/raising_hand_tone3.png)bin1657 -> 1657 bytes
-rw-r--r--public/-/emojis/1/raising_hand_tone4.png (renamed from app/assets/images/emoji/raising_hand_tone4.png)bin1657 -> 1657 bytes
-rw-r--r--public/-/emojis/1/raising_hand_tone5.png (renamed from app/assets/images/emoji/raising_hand_tone5.png)bin1661 -> 1661 bytes
-rw-r--r--public/-/emojis/1/ram.png (renamed from app/assets/images/emoji/ram.png)bin1951 -> 1951 bytes
-rw-r--r--public/-/emojis/1/ramen.png (renamed from app/assets/images/emoji/ramen.png)bin1992 -> 1992 bytes
-rw-r--r--public/-/emojis/1/rat.png (renamed from app/assets/images/emoji/rat.png)bin1193 -> 1193 bytes
-rw-r--r--public/-/emojis/1/record_button.png (renamed from app/assets/images/emoji/record_button.png)bin475 -> 475 bytes
-rw-r--r--public/-/emojis/1/recycle.png (renamed from app/assets/images/emoji/recycle.png)bin914 -> 914 bytes
-rw-r--r--public/-/emojis/1/red_car.png (renamed from app/assets/images/emoji/red_car.png)bin1065 -> 1065 bytes
-rw-r--r--public/-/emojis/1/red_circle.png (renamed from app/assets/images/emoji/red_circle.png)bin374 -> 374 bytes
-rw-r--r--public/-/emojis/1/registered.png (renamed from app/assets/images/emoji/registered.png)bin547 -> 547 bytes
-rw-r--r--public/-/emojis/1/relaxed.png (renamed from app/assets/images/emoji/relaxed.png)bin636 -> 636 bytes
-rw-r--r--public/-/emojis/1/relieved.png (renamed from app/assets/images/emoji/relieved.png)bin785 -> 785 bytes
-rw-r--r--public/-/emojis/1/reminder_ribbon.png (renamed from app/assets/images/emoji/reminder_ribbon.png)bin921 -> 921 bytes
-rw-r--r--public/-/emojis/1/repeat.png (renamed from app/assets/images/emoji/repeat.png)bin644 -> 644 bytes
-rw-r--r--public/-/emojis/1/repeat_one.png (renamed from app/assets/images/emoji/repeat_one.png)bin688 -> 688 bytes
-rw-r--r--public/-/emojis/1/restroom.png (renamed from app/assets/images/emoji/restroom.png)bin676 -> 676 bytes
-rw-r--r--public/-/emojis/1/revolving_hearts.png (renamed from app/assets/images/emoji/revolving_hearts.png)bin920 -> 920 bytes
-rw-r--r--public/-/emojis/1/rewind.png (renamed from app/assets/images/emoji/rewind.png)bin523 -> 523 bytes
-rw-r--r--public/-/emojis/1/rhino.png (renamed from app/assets/images/emoji/rhino.png)bin1558 -> 1558 bytes
-rw-r--r--public/-/emojis/1/ribbon.png (renamed from app/assets/images/emoji/ribbon.png)bin968 -> 968 bytes
-rw-r--r--public/-/emojis/1/rice.png (renamed from app/assets/images/emoji/rice.png)bin1195 -> 1195 bytes
-rw-r--r--public/-/emojis/1/rice_ball.png (renamed from app/assets/images/emoji/rice_ball.png)bin1091 -> 1091 bytes
-rw-r--r--public/-/emojis/1/rice_cracker.png (renamed from app/assets/images/emoji/rice_cracker.png)bin1443 -> 1443 bytes
-rw-r--r--public/-/emojis/1/rice_scene.png (renamed from app/assets/images/emoji/rice_scene.png)bin1349 -> 1349 bytes
-rw-r--r--public/-/emojis/1/right_facing_fist.png (renamed from app/assets/images/emoji/right_facing_fist.png)bin975 -> 975 bytes
-rw-r--r--public/-/emojis/1/right_facing_fist_tone1.png (renamed from app/assets/images/emoji/right_facing_fist_tone1.png)bin964 -> 964 bytes
-rw-r--r--public/-/emojis/1/right_facing_fist_tone2.png (renamed from app/assets/images/emoji/right_facing_fist_tone2.png)bin964 -> 964 bytes
-rw-r--r--public/-/emojis/1/right_facing_fist_tone3.png (renamed from app/assets/images/emoji/right_facing_fist_tone3.png)bin964 -> 964 bytes
-rw-r--r--public/-/emojis/1/right_facing_fist_tone4.png (renamed from app/assets/images/emoji/right_facing_fist_tone4.png)bin964 -> 964 bytes
-rw-r--r--public/-/emojis/1/right_facing_fist_tone5.png (renamed from app/assets/images/emoji/right_facing_fist_tone5.png)bin964 -> 964 bytes
-rw-r--r--public/-/emojis/1/ring.png (renamed from app/assets/images/emoji/ring.png)bin1113 -> 1113 bytes
-rw-r--r--public/-/emojis/1/robot.png (renamed from app/assets/images/emoji/robot.png)bin1228 -> 1228 bytes
-rw-r--r--public/-/emojis/1/rocket.png (renamed from app/assets/images/emoji/rocket.png)bin1639 -> 1639 bytes
-rw-r--r--public/-/emojis/1/rofl.png (renamed from app/assets/images/emoji/rofl.png)bin1760 -> 1760 bytes
-rw-r--r--public/-/emojis/1/roller_coaster.png (renamed from app/assets/images/emoji/roller_coaster.png)bin1723 -> 1723 bytes
-rw-r--r--public/-/emojis/1/rolling_eyes.png (renamed from app/assets/images/emoji/rolling_eyes.png)bin743 -> 743 bytes
-rw-r--r--public/-/emojis/1/rooster.png (renamed from app/assets/images/emoji/rooster.png)bin1333 -> 1333 bytes
-rw-r--r--public/-/emojis/1/rose.png (renamed from app/assets/images/emoji/rose.png)bin1182 -> 1182 bytes
-rw-r--r--public/-/emojis/1/rosette.png (renamed from app/assets/images/emoji/rosette.png)bin1023 -> 1023 bytes
-rw-r--r--public/-/emojis/1/rotating_light.png (renamed from app/assets/images/emoji/rotating_light.png)bin1969 -> 1969 bytes
-rw-r--r--public/-/emojis/1/round_pushpin.png (renamed from app/assets/images/emoji/round_pushpin.png)bin455 -> 455 bytes
-rw-r--r--public/-/emojis/1/rowboat.png (renamed from app/assets/images/emoji/rowboat.png)bin1963 -> 1963 bytes
-rw-r--r--public/-/emojis/1/rowboat_tone1.png (renamed from app/assets/images/emoji/rowboat_tone1.png)bin1971 -> 1971 bytes
-rw-r--r--public/-/emojis/1/rowboat_tone2.png (renamed from app/assets/images/emoji/rowboat_tone2.png)bin1972 -> 1972 bytes
-rw-r--r--public/-/emojis/1/rowboat_tone3.png (renamed from app/assets/images/emoji/rowboat_tone3.png)bin1967 -> 1967 bytes
-rw-r--r--public/-/emojis/1/rowboat_tone4.png (renamed from app/assets/images/emoji/rowboat_tone4.png)bin1974 -> 1974 bytes
-rw-r--r--public/-/emojis/1/rowboat_tone5.png (renamed from app/assets/images/emoji/rowboat_tone5.png)bin1971 -> 1971 bytes
-rw-r--r--public/-/emojis/1/rugby_football.png (renamed from app/assets/images/emoji/rugby_football.png)bin1618 -> 1618 bytes
-rw-r--r--public/-/emojis/1/runner.png (renamed from app/assets/images/emoji/runner.png)bin1161 -> 1161 bytes
-rw-r--r--public/-/emojis/1/runner_tone1.png (renamed from app/assets/images/emoji/runner_tone1.png)bin1163 -> 1163 bytes
-rw-r--r--public/-/emojis/1/runner_tone2.png (renamed from app/assets/images/emoji/runner_tone2.png)bin1162 -> 1162 bytes
-rw-r--r--public/-/emojis/1/runner_tone3.png (renamed from app/assets/images/emoji/runner_tone3.png)bin1151 -> 1151 bytes
-rw-r--r--public/-/emojis/1/runner_tone4.png (renamed from app/assets/images/emoji/runner_tone4.png)bin1156 -> 1156 bytes
-rw-r--r--public/-/emojis/1/runner_tone5.png (renamed from app/assets/images/emoji/runner_tone5.png)bin1145 -> 1145 bytes
-rw-r--r--public/-/emojis/1/running_shirt_with_sash.png (renamed from app/assets/images/emoji/running_shirt_with_sash.png)bin784 -> 784 bytes
-rw-r--r--public/-/emojis/1/sa.png (renamed from app/assets/images/emoji/sa.png)bin420 -> 420 bytes
-rw-r--r--public/-/emojis/1/sagittarius.png (renamed from app/assets/images/emoji/sagittarius.png)bin602 -> 602 bytes
-rw-r--r--public/-/emojis/1/sailboat.png (renamed from app/assets/images/emoji/sailboat.png)bin1274 -> 1274 bytes
-rw-r--r--public/-/emojis/1/sake.png (renamed from app/assets/images/emoji/sake.png)bin826 -> 826 bytes
-rw-r--r--public/-/emojis/1/salad.png (renamed from app/assets/images/emoji/salad.png)bin2398 -> 2398 bytes
-rw-r--r--public/-/emojis/1/sandal.png (renamed from app/assets/images/emoji/sandal.png)bin1180 -> 1180 bytes
-rw-r--r--public/-/emojis/1/santa.png (renamed from app/assets/images/emoji/santa.png)bin1585 -> 1585 bytes
-rw-r--r--public/-/emojis/1/santa_tone1.png (renamed from app/assets/images/emoji/santa_tone1.png)bin1585 -> 1585 bytes
-rw-r--r--public/-/emojis/1/santa_tone2.png (renamed from app/assets/images/emoji/santa_tone2.png)bin1578 -> 1578 bytes
-rw-r--r--public/-/emojis/1/santa_tone3.png (renamed from app/assets/images/emoji/santa_tone3.png)bin1578 -> 1578 bytes
-rw-r--r--public/-/emojis/1/santa_tone4.png (renamed from app/assets/images/emoji/santa_tone4.png)bin1578 -> 1578 bytes
-rw-r--r--public/-/emojis/1/santa_tone5.png (renamed from app/assets/images/emoji/santa_tone5.png)bin1578 -> 1578 bytes
-rw-r--r--public/-/emojis/1/satellite.png (renamed from app/assets/images/emoji/satellite.png)bin1173 -> 1173 bytes
-rw-r--r--public/-/emojis/1/satellite_orbital.png (renamed from app/assets/images/emoji/satellite_orbital.png)bin762 -> 762 bytes
-rw-r--r--public/-/emojis/1/saxophone.png (renamed from app/assets/images/emoji/saxophone.png)bin1442 -> 1442 bytes
-rw-r--r--public/-/emojis/1/scales.png (renamed from app/assets/images/emoji/scales.png)bin1181 -> 1181 bytes
-rw-r--r--public/-/emojis/1/school.png (renamed from app/assets/images/emoji/school.png)bin1234 -> 1234 bytes
-rw-r--r--public/-/emojis/1/school_satchel.png (renamed from app/assets/images/emoji/school_satchel.png)bin1490 -> 1490 bytes
-rw-r--r--public/-/emojis/1/scissors.png (renamed from app/assets/images/emoji/scissors.png)bin937 -> 937 bytes
-rw-r--r--public/-/emojis/1/scooter.png (renamed from app/assets/images/emoji/scooter.png)bin1228 -> 1228 bytes
-rw-r--r--public/-/emojis/1/scorpion.png (renamed from app/assets/images/emoji/scorpion.png)bin1503 -> 1503 bytes
-rw-r--r--public/-/emojis/1/scorpius.png (renamed from app/assets/images/emoji/scorpius.png)bin612 -> 612 bytes
-rw-r--r--public/-/emojis/1/scream.png (renamed from app/assets/images/emoji/scream.png)bin1583 -> 1583 bytes
-rw-r--r--public/-/emojis/1/scream_cat.png (renamed from app/assets/images/emoji/scream_cat.png)bin2120 -> 2120 bytes
-rw-r--r--public/-/emojis/1/scroll.png (renamed from app/assets/images/emoji/scroll.png)bin989 -> 989 bytes
-rw-r--r--public/-/emojis/1/seat.png (renamed from app/assets/images/emoji/seat.png)bin884 -> 884 bytes
-rw-r--r--public/-/emojis/1/second_place.png (renamed from app/assets/images/emoji/second_place.png)bin1511 -> 1511 bytes
-rw-r--r--public/-/emojis/1/secret.png (renamed from app/assets/images/emoji/secret.png)bin857 -> 857 bytes
-rw-r--r--public/-/emojis/1/see_no_evil.png (renamed from app/assets/images/emoji/see_no_evil.png)bin1227 -> 1227 bytes
-rw-r--r--public/-/emojis/1/seedling.png (renamed from app/assets/images/emoji/seedling.png)bin749 -> 749 bytes
-rw-r--r--public/-/emojis/1/selfie.png (renamed from app/assets/images/emoji/selfie.png)bin1160 -> 1160 bytes
-rw-r--r--public/-/emojis/1/selfie_tone1.png (renamed from app/assets/images/emoji/selfie_tone1.png)bin1166 -> 1166 bytes
-rw-r--r--public/-/emojis/1/selfie_tone2.png (renamed from app/assets/images/emoji/selfie_tone2.png)bin1167 -> 1167 bytes
-rw-r--r--public/-/emojis/1/selfie_tone3.png (renamed from app/assets/images/emoji/selfie_tone3.png)bin1154 -> 1154 bytes
-rw-r--r--public/-/emojis/1/selfie_tone4.png (renamed from app/assets/images/emoji/selfie_tone4.png)bin1153 -> 1153 bytes
-rw-r--r--public/-/emojis/1/selfie_tone5.png (renamed from app/assets/images/emoji/selfie_tone5.png)bin1148 -> 1148 bytes
-rw-r--r--public/-/emojis/1/seven.png (renamed from app/assets/images/emoji/seven.png)bin522 -> 522 bytes
-rw-r--r--public/-/emojis/1/shallow_pan_of_food.png (renamed from app/assets/images/emoji/shallow_pan_of_food.png)bin1738 -> 1738 bytes
-rw-r--r--public/-/emojis/1/shamrock.png (renamed from app/assets/images/emoji/shamrock.png)bin1023 -> 1023 bytes
-rw-r--r--public/-/emojis/1/shark.png (renamed from app/assets/images/emoji/shark.png)bin1811 -> 1811 bytes
-rw-r--r--public/-/emojis/1/shaved_ice.png (renamed from app/assets/images/emoji/shaved_ice.png)bin997 -> 997 bytes
-rw-r--r--public/-/emojis/1/sheep.png (renamed from app/assets/images/emoji/sheep.png)bin1372 -> 1372 bytes
-rw-r--r--public/-/emojis/1/shell.png (renamed from app/assets/images/emoji/shell.png)bin1497 -> 1497 bytes
-rw-r--r--public/-/emojis/1/shield.png (renamed from app/assets/images/emoji/shield.png)bin1602 -> 1602 bytes
-rw-r--r--public/-/emojis/1/shinto_shrine.png (renamed from app/assets/images/emoji/shinto_shrine.png)bin579 -> 579 bytes
-rw-r--r--public/-/emojis/1/ship.png (renamed from app/assets/images/emoji/ship.png)bin1405 -> 1405 bytes
-rw-r--r--public/-/emojis/1/shirt.png (renamed from app/assets/images/emoji/shirt.png)bin670 -> 670 bytes
-rw-r--r--public/-/emojis/1/shopping_bags.png (renamed from app/assets/images/emoji/shopping_bags.png)bin1234 -> 1234 bytes
-rw-r--r--public/-/emojis/1/shopping_cart.png (renamed from app/assets/images/emoji/shopping_cart.png)bin1072 -> 1072 bytes
-rw-r--r--public/-/emojis/1/shower.png (renamed from app/assets/images/emoji/shower.png)bin2537 -> 2537 bytes
-rw-r--r--public/-/emojis/1/shrimp.png (renamed from app/assets/images/emoji/shrimp.png)bin1376 -> 1376 bytes
-rw-r--r--public/-/emojis/1/shrug.png (renamed from app/assets/images/emoji/shrug.png)bin1671 -> 1671 bytes
-rw-r--r--public/-/emojis/1/shrug_tone1.png (renamed from app/assets/images/emoji/shrug_tone1.png)bin1676 -> 1676 bytes
-rw-r--r--public/-/emojis/1/shrug_tone2.png (renamed from app/assets/images/emoji/shrug_tone2.png)bin1671 -> 1671 bytes
-rw-r--r--public/-/emojis/1/shrug_tone3.png (renamed from app/assets/images/emoji/shrug_tone3.png)bin1675 -> 1675 bytes
-rw-r--r--public/-/emojis/1/shrug_tone4.png (renamed from app/assets/images/emoji/shrug_tone4.png)bin1641 -> 1641 bytes
-rw-r--r--public/-/emojis/1/shrug_tone5.png (renamed from app/assets/images/emoji/shrug_tone5.png)bin1634 -> 1634 bytes
-rw-r--r--public/-/emojis/1/signal_strength.png (renamed from app/assets/images/emoji/signal_strength.png)bin445 -> 445 bytes
-rw-r--r--public/-/emojis/1/six.png (renamed from app/assets/images/emoji/six.png)bin612 -> 612 bytes
-rw-r--r--public/-/emojis/1/six_pointed_star.png (renamed from app/assets/images/emoji/six_pointed_star.png)bin540 -> 540 bytes
-rw-r--r--public/-/emojis/1/ski.png (renamed from app/assets/images/emoji/ski.png)bin1762 -> 1762 bytes
-rw-r--r--public/-/emojis/1/skier.png (renamed from app/assets/images/emoji/skier.png)bin1539 -> 1539 bytes
-rw-r--r--public/-/emojis/1/skull.png (renamed from app/assets/images/emoji/skull.png)bin628 -> 628 bytes
-rw-r--r--public/-/emojis/1/skull_crossbones.png (renamed from app/assets/images/emoji/skull_crossbones.png)bin726 -> 726 bytes
-rw-r--r--public/-/emojis/1/sleeping.png (renamed from app/assets/images/emoji/sleeping.png)bin1075 -> 1075 bytes
-rw-r--r--public/-/emojis/1/sleeping_accommodation.png (renamed from app/assets/images/emoji/sleeping_accommodation.png)bin926 -> 926 bytes
-rw-r--r--public/-/emojis/1/sleepy.png (renamed from app/assets/images/emoji/sleepy.png)bin1185 -> 1185 bytes
-rw-r--r--public/-/emojis/1/slight_frown.png (renamed from app/assets/images/emoji/slight_frown.png)bin580 -> 580 bytes
-rw-r--r--public/-/emojis/1/slight_smile.png (renamed from app/assets/images/emoji/slight_smile.png)bin600 -> 600 bytes
-rw-r--r--public/-/emojis/1/slot_machine.png (renamed from app/assets/images/emoji/slot_machine.png)bin1648 -> 1648 bytes
-rw-r--r--public/-/emojis/1/small_blue_diamond.png (renamed from app/assets/images/emoji/small_blue_diamond.png)bin191 -> 191 bytes
-rw-r--r--public/-/emojis/1/small_orange_diamond.png (renamed from app/assets/images/emoji/small_orange_diamond.png)bin194 -> 194 bytes
-rw-r--r--public/-/emojis/1/small_red_triangle.png (renamed from app/assets/images/emoji/small_red_triangle.png)bin273 -> 273 bytes
-rw-r--r--public/-/emojis/1/small_red_triangle_down.png (renamed from app/assets/images/emoji/small_red_triangle_down.png)bin291 -> 291 bytes
-rw-r--r--public/-/emojis/1/smile.png (renamed from app/assets/images/emoji/smile.png)bin737 -> 737 bytes
-rw-r--r--public/-/emojis/1/smile_cat.png (renamed from app/assets/images/emoji/smile_cat.png)bin1405 -> 1405 bytes
-rw-r--r--public/-/emojis/1/smiley.png (renamed from app/assets/images/emoji/smiley.png)bin686 -> 686 bytes
-rw-r--r--public/-/emojis/1/smiley_cat.png (renamed from app/assets/images/emoji/smiley_cat.png)bin1669 -> 1669 bytes
-rw-r--r--public/-/emojis/1/smiling_imp.png (renamed from app/assets/images/emoji/smiling_imp.png)bin1078 -> 1078 bytes
-rw-r--r--public/-/emojis/1/smirk.png (renamed from app/assets/images/emoji/smirk.png)bin775 -> 775 bytes
-rw-r--r--public/-/emojis/1/smirk_cat.png (renamed from app/assets/images/emoji/smirk_cat.png)bin1663 -> 1663 bytes
-rw-r--r--public/-/emojis/1/smoking.png (renamed from app/assets/images/emoji/smoking.png)bin417 -> 417 bytes
-rw-r--r--public/-/emojis/1/snail.png (renamed from app/assets/images/emoji/snail.png)bin1731 -> 1731 bytes
-rw-r--r--public/-/emojis/1/snake.png (renamed from app/assets/images/emoji/snake.png)bin1575 -> 1575 bytes
-rw-r--r--public/-/emojis/1/sneezing_face.png (renamed from app/assets/images/emoji/sneezing_face.png)bin1289 -> 1289 bytes
-rw-r--r--public/-/emojis/1/snowboarder.png (renamed from app/assets/images/emoji/snowboarder.png)bin2020 -> 2020 bytes
-rw-r--r--public/-/emojis/1/snowflake.png (renamed from app/assets/images/emoji/snowflake.png)bin691 -> 691 bytes
-rw-r--r--public/-/emojis/1/snowman.png (renamed from app/assets/images/emoji/snowman.png)bin1481 -> 1481 bytes
-rw-r--r--public/-/emojis/1/snowman2.png (renamed from app/assets/images/emoji/snowman2.png)bin2176 -> 2176 bytes
-rw-r--r--public/-/emojis/1/sob.png (renamed from app/assets/images/emoji/sob.png)bin1236 -> 1236 bytes
-rw-r--r--public/-/emojis/1/soccer.png (renamed from app/assets/images/emoji/soccer.png)bin1034 -> 1034 bytes
-rw-r--r--public/-/emojis/1/soon.png (renamed from app/assets/images/emoji/soon.png)bin483 -> 483 bytes
-rw-r--r--public/-/emojis/1/sos.png (renamed from app/assets/images/emoji/sos.png)bin604 -> 604 bytes
-rw-r--r--public/-/emojis/1/sound.png (renamed from app/assets/images/emoji/sound.png)bin690 -> 690 bytes
-rw-r--r--public/-/emojis/1/space_invader.png (renamed from app/assets/images/emoji/space_invader.png)bin1325 -> 1325 bytes
-rw-r--r--public/-/emojis/1/spades.png (renamed from app/assets/images/emoji/spades.png)bin454 -> 454 bytes
-rw-r--r--public/-/emojis/1/spaghetti.png (renamed from app/assets/images/emoji/spaghetti.png)bin1796 -> 1796 bytes
-rw-r--r--public/-/emojis/1/sparkle.png (renamed from app/assets/images/emoji/sparkle.png)bin663 -> 663 bytes
-rw-r--r--public/-/emojis/1/sparkler.png (renamed from app/assets/images/emoji/sparkler.png)bin910 -> 910 bytes
-rw-r--r--public/-/emojis/1/sparkles.png (renamed from app/assets/images/emoji/sparkles.png)bin651 -> 651 bytes
-rw-r--r--public/-/emojis/1/sparkling_heart.png (renamed from app/assets/images/emoji/sparkling_heart.png)bin821 -> 821 bytes
-rw-r--r--public/-/emojis/1/speak_no_evil.png (renamed from app/assets/images/emoji/speak_no_evil.png)bin1497 -> 1497 bytes
-rw-r--r--public/-/emojis/1/speaker.png (renamed from app/assets/images/emoji/speaker.png)bin575 -> 575 bytes
-rw-r--r--public/-/emojis/1/speaking_head.png (renamed from app/assets/images/emoji/speaking_head.png)bin531 -> 531 bytes
-rw-r--r--public/-/emojis/1/speech_balloon.png (renamed from app/assets/images/emoji/speech_balloon.png)bin384 -> 384 bytes
-rw-r--r--public/-/emojis/1/speech_left.png (renamed from app/assets/images/emoji/speech_left.png)bin390 -> 390 bytes
-rw-r--r--public/-/emojis/1/speedboat.png (renamed from app/assets/images/emoji/speedboat.png)bin1255 -> 1255 bytes
-rw-r--r--public/-/emojis/1/spider.png (renamed from app/assets/images/emoji/spider.png)bin1724 -> 1724 bytes
-rw-r--r--public/-/emojis/1/spider_web.png (renamed from app/assets/images/emoji/spider_web.png)bin929 -> 929 bytes
-rw-r--r--public/-/emojis/1/spoon.png (renamed from app/assets/images/emoji/spoon.png)bin700 -> 700 bytes
-rw-r--r--public/-/emojis/1/spy.png (renamed from app/assets/images/emoji/spy.png)bin1650 -> 1650 bytes
-rw-r--r--public/-/emojis/1/spy_tone1.png (renamed from app/assets/images/emoji/spy_tone1.png)bin1639 -> 1639 bytes
-rw-r--r--public/-/emojis/1/spy_tone2.png (renamed from app/assets/images/emoji/spy_tone2.png)bin1632 -> 1632 bytes
-rw-r--r--public/-/emojis/1/spy_tone3.png (renamed from app/assets/images/emoji/spy_tone3.png)bin1645 -> 1645 bytes
-rw-r--r--public/-/emojis/1/spy_tone4.png (renamed from app/assets/images/emoji/spy_tone4.png)bin1639 -> 1639 bytes
-rw-r--r--public/-/emojis/1/spy_tone5.png (renamed from app/assets/images/emoji/spy_tone5.png)bin1639 -> 1639 bytes
-rw-r--r--public/-/emojis/1/squid.png (renamed from app/assets/images/emoji/squid.png)bin1394 -> 1394 bytes
-rw-r--r--public/-/emojis/1/stadium.png (renamed from app/assets/images/emoji/stadium.png)bin1515 -> 1515 bytes
-rw-r--r--public/-/emojis/1/star.png (renamed from app/assets/images/emoji/star.png)bin456 -> 456 bytes
-rw-r--r--public/-/emojis/1/star2.png (renamed from app/assets/images/emoji/star2.png)bin732 -> 732 bytes
-rw-r--r--public/-/emojis/1/star_and_crescent.png (renamed from app/assets/images/emoji/star_and_crescent.png)bin490 -> 490 bytes
-rw-r--r--public/-/emojis/1/star_of_david.png (renamed from app/assets/images/emoji/star_of_david.png)bin491 -> 491 bytes
-rw-r--r--public/-/emojis/1/stars.png (renamed from app/assets/images/emoji/stars.png)bin1048 -> 1048 bytes
-rw-r--r--public/-/emojis/1/station.png (renamed from app/assets/images/emoji/station.png)bin1336 -> 1336 bytes
-rw-r--r--public/-/emojis/1/statue_of_liberty.png (renamed from app/assets/images/emoji/statue_of_liberty.png)bin1145 -> 1145 bytes
-rw-r--r--public/-/emojis/1/steam_locomotive.png (renamed from app/assets/images/emoji/steam_locomotive.png)bin1736 -> 1736 bytes
-rw-r--r--public/-/emojis/1/stew.png (renamed from app/assets/images/emoji/stew.png)bin1960 -> 1960 bytes
-rw-r--r--public/-/emojis/1/stop_button.png (renamed from app/assets/images/emoji/stop_button.png)bin385 -> 385 bytes
-rw-r--r--public/-/emojis/1/stopwatch.png (renamed from app/assets/images/emoji/stopwatch.png)bin1329 -> 1329 bytes
-rw-r--r--public/-/emojis/1/straight_ruler.png (renamed from app/assets/images/emoji/straight_ruler.png)bin1406 -> 1406 bytes
-rw-r--r--public/-/emojis/1/strawberry.png (renamed from app/assets/images/emoji/strawberry.png)bin1206 -> 1206 bytes
-rw-r--r--public/-/emojis/1/stuck_out_tongue.png (renamed from app/assets/images/emoji/stuck_out_tongue.png)bin752 -> 752 bytes
-rw-r--r--public/-/emojis/1/stuck_out_tongue_closed_eyes.png (renamed from app/assets/images/emoji/stuck_out_tongue_closed_eyes.png)bin867 -> 867 bytes
-rw-r--r--public/-/emojis/1/stuck_out_tongue_winking_eye.png (renamed from app/assets/images/emoji/stuck_out_tongue_winking_eye.png)bin1061 -> 1061 bytes
-rw-r--r--public/-/emojis/1/stuffed_flatbread.png (renamed from app/assets/images/emoji/stuffed_flatbread.png)bin2160 -> 2160 bytes
-rw-r--r--public/-/emojis/1/sun_with_face.png (renamed from app/assets/images/emoji/sun_with_face.png)bin741 -> 741 bytes
-rw-r--r--public/-/emojis/1/sunflower.png (renamed from app/assets/images/emoji/sunflower.png)bin1915 -> 1915 bytes
-rw-r--r--public/-/emojis/1/sunglasses.png (renamed from app/assets/images/emoji/sunglasses.png)bin824 -> 824 bytes
-rw-r--r--public/-/emojis/1/sunny.png (renamed from app/assets/images/emoji/sunny.png)bin746 -> 746 bytes
-rw-r--r--public/-/emojis/1/sunrise.png (renamed from app/assets/images/emoji/sunrise.png)bin812 -> 812 bytes
-rw-r--r--public/-/emojis/1/sunrise_over_mountains.png (renamed from app/assets/images/emoji/sunrise_over_mountains.png)bin1576 -> 1576 bytes
-rw-r--r--public/-/emojis/1/surfer.png (renamed from app/assets/images/emoji/surfer.png)bin1777 -> 1777 bytes
-rw-r--r--public/-/emojis/1/surfer_tone1.png (renamed from app/assets/images/emoji/surfer_tone1.png)bin1781 -> 1781 bytes
-rw-r--r--public/-/emojis/1/surfer_tone2.png (renamed from app/assets/images/emoji/surfer_tone2.png)bin1769 -> 1769 bytes
-rw-r--r--public/-/emojis/1/surfer_tone3.png (renamed from app/assets/images/emoji/surfer_tone3.png)bin1777 -> 1777 bytes
-rw-r--r--public/-/emojis/1/surfer_tone4.png (renamed from app/assets/images/emoji/surfer_tone4.png)bin1784 -> 1784 bytes
-rw-r--r--public/-/emojis/1/surfer_tone5.png (renamed from app/assets/images/emoji/surfer_tone5.png)bin1782 -> 1782 bytes
-rw-r--r--public/-/emojis/1/sushi.png (renamed from app/assets/images/emoji/sushi.png)bin2101 -> 2101 bytes
-rw-r--r--public/-/emojis/1/suspension_railway.png (renamed from app/assets/images/emoji/suspension_railway.png)bin927 -> 927 bytes
-rw-r--r--public/-/emojis/1/sweat.png (renamed from app/assets/images/emoji/sweat.png)bin861 -> 861 bytes
-rw-r--r--public/-/emojis/1/sweat_drops.png (renamed from app/assets/images/emoji/sweat_drops.png)bin549 -> 549 bytes
-rw-r--r--public/-/emojis/1/sweat_smile.png (renamed from app/assets/images/emoji/sweat_smile.png)bin851 -> 851 bytes
-rw-r--r--public/-/emojis/1/sweet_potato.png (renamed from app/assets/images/emoji/sweet_potato.png)bin951 -> 951 bytes
-rw-r--r--public/-/emojis/1/swimmer.png (renamed from app/assets/images/emoji/swimmer.png)bin1184 -> 1184 bytes
-rw-r--r--public/-/emojis/1/swimmer_tone1.png (renamed from app/assets/images/emoji/swimmer_tone1.png)bin1184 -> 1184 bytes
-rw-r--r--public/-/emojis/1/swimmer_tone2.png (renamed from app/assets/images/emoji/swimmer_tone2.png)bin1184 -> 1184 bytes
-rw-r--r--public/-/emojis/1/swimmer_tone3.png (renamed from app/assets/images/emoji/swimmer_tone3.png)bin1184 -> 1184 bytes
-rw-r--r--public/-/emojis/1/swimmer_tone4.png (renamed from app/assets/images/emoji/swimmer_tone4.png)bin1184 -> 1184 bytes
-rw-r--r--public/-/emojis/1/swimmer_tone5.png (renamed from app/assets/images/emoji/swimmer_tone5.png)bin1184 -> 1184 bytes
-rw-r--r--public/-/emojis/1/symbols.png (renamed from app/assets/images/emoji/symbols.png)bin746 -> 746 bytes
-rw-r--r--public/-/emojis/1/synagogue.png (renamed from app/assets/images/emoji/synagogue.png)bin1309 -> 1309 bytes
-rw-r--r--public/-/emojis/1/syringe.png (renamed from app/assets/images/emoji/syringe.png)bin737 -> 737 bytes
-rw-r--r--public/-/emojis/1/taco.png (renamed from app/assets/images/emoji/taco.png)bin3045 -> 3045 bytes
-rw-r--r--public/-/emojis/1/tada.png (renamed from app/assets/images/emoji/tada.png)bin1778 -> 1778 bytes
-rw-r--r--public/-/emojis/1/tanabata_tree.png (renamed from app/assets/images/emoji/tanabata_tree.png)bin1479 -> 1479 bytes
-rw-r--r--public/-/emojis/1/tangerine.png (renamed from app/assets/images/emoji/tangerine.png)bin1184 -> 1184 bytes
-rw-r--r--public/-/emojis/1/taurus.png (renamed from app/assets/images/emoji/taurus.png)bin701 -> 701 bytes
-rw-r--r--public/-/emojis/1/taxi.png (renamed from app/assets/images/emoji/taxi.png)bin1230 -> 1230 bytes
-rw-r--r--public/-/emojis/1/tea.png (renamed from app/assets/images/emoji/tea.png)bin1297 -> 1297 bytes
-rw-r--r--public/-/emojis/1/telephone.png (renamed from app/assets/images/emoji/telephone.png)bin1760 -> 1760 bytes
-rw-r--r--public/-/emojis/1/telephone_receiver.png (renamed from app/assets/images/emoji/telephone_receiver.png)bin941 -> 941 bytes
-rw-r--r--public/-/emojis/1/telescope.png (renamed from app/assets/images/emoji/telescope.png)bin1256 -> 1256 bytes
-rw-r--r--public/-/emojis/1/ten.png (renamed from app/assets/images/emoji/ten.png)bin621 -> 621 bytes
-rw-r--r--public/-/emojis/1/tennis.png (renamed from app/assets/images/emoji/tennis.png)bin1561 -> 1561 bytes
-rw-r--r--public/-/emojis/1/tent.png (renamed from app/assets/images/emoji/tent.png)bin1684 -> 1684 bytes
-rw-r--r--public/-/emojis/1/thermometer.png (renamed from app/assets/images/emoji/thermometer.png)bin759 -> 759 bytes
-rw-r--r--public/-/emojis/1/thermometer_face.png (renamed from app/assets/images/emoji/thermometer_face.png)bin1503 -> 1503 bytes
-rw-r--r--public/-/emojis/1/thinking.png (renamed from app/assets/images/emoji/thinking.png)bin1345 -> 1345 bytes
-rw-r--r--public/-/emojis/1/third_place.png (renamed from app/assets/images/emoji/third_place.png)bin1529 -> 1529 bytes
-rw-r--r--public/-/emojis/1/thought_balloon.png (renamed from app/assets/images/emoji/thought_balloon.png)bin489 -> 489 bytes
-rw-r--r--public/-/emojis/1/three.png (renamed from app/assets/images/emoji/three.png)bin602 -> 602 bytes
-rw-r--r--public/-/emojis/1/thumbsdown.png (renamed from app/assets/images/emoji/thumbsdown.png)bin815 -> 815 bytes
-rw-r--r--public/-/emojis/1/thumbsdown_tone1.png (renamed from app/assets/images/emoji/thumbsdown_tone1.png)bin815 -> 815 bytes
-rw-r--r--public/-/emojis/1/thumbsdown_tone2.png (renamed from app/assets/images/emoji/thumbsdown_tone2.png)bin815 -> 815 bytes
-rw-r--r--public/-/emojis/1/thumbsdown_tone3.png (renamed from app/assets/images/emoji/thumbsdown_tone3.png)bin815 -> 815 bytes
-rw-r--r--public/-/emojis/1/thumbsdown_tone4.png (renamed from app/assets/images/emoji/thumbsdown_tone4.png)bin815 -> 815 bytes
-rw-r--r--public/-/emojis/1/thumbsdown_tone5.png (renamed from app/assets/images/emoji/thumbsdown_tone5.png)bin815 -> 815 bytes
-rw-r--r--public/-/emojis/1/thumbsup.png (renamed from app/assets/images/emoji/thumbsup.png)bin814 -> 814 bytes
-rw-r--r--public/-/emojis/1/thumbsup_tone1.png (renamed from app/assets/images/emoji/thumbsup_tone1.png)bin814 -> 814 bytes
-rw-r--r--public/-/emojis/1/thumbsup_tone2.png (renamed from app/assets/images/emoji/thumbsup_tone2.png)bin814 -> 814 bytes
-rw-r--r--public/-/emojis/1/thumbsup_tone3.png (renamed from app/assets/images/emoji/thumbsup_tone3.png)bin814 -> 814 bytes
-rw-r--r--public/-/emojis/1/thumbsup_tone4.png (renamed from app/assets/images/emoji/thumbsup_tone4.png)bin814 -> 814 bytes
-rw-r--r--public/-/emojis/1/thumbsup_tone5.png (renamed from app/assets/images/emoji/thumbsup_tone5.png)bin814 -> 814 bytes
-rw-r--r--public/-/emojis/1/thunder_cloud_rain.png (renamed from app/assets/images/emoji/thunder_cloud_rain.png)bin1020 -> 1020 bytes
-rw-r--r--public/-/emojis/1/ticket.png (renamed from app/assets/images/emoji/ticket.png)bin763 -> 763 bytes
-rw-r--r--public/-/emojis/1/tickets.png (renamed from app/assets/images/emoji/tickets.png)bin1750 -> 1750 bytes
-rw-r--r--public/-/emojis/1/tiger.png (renamed from app/assets/images/emoji/tiger.png)bin2104 -> 2104 bytes
-rw-r--r--public/-/emojis/1/tiger2.png (renamed from app/assets/images/emoji/tiger2.png)bin2623 -> 2623 bytes
-rw-r--r--public/-/emojis/1/timer.png (renamed from app/assets/images/emoji/timer.png)bin1897 -> 1897 bytes
-rw-r--r--public/-/emojis/1/tired_face.png (renamed from app/assets/images/emoji/tired_face.png)bin1126 -> 1126 bytes
-rw-r--r--public/-/emojis/1/tm.png (renamed from app/assets/images/emoji/tm.png)bin300 -> 300 bytes
-rw-r--r--public/-/emojis/1/toilet.png (renamed from app/assets/images/emoji/toilet.png)bin726 -> 726 bytes
-rw-r--r--public/-/emojis/1/tokyo_tower.png (renamed from app/assets/images/emoji/tokyo_tower.png)bin765 -> 765 bytes
-rw-r--r--public/-/emojis/1/tomato.png (renamed from app/assets/images/emoji/tomato.png)bin1055 -> 1055 bytes
-rw-r--r--public/-/emojis/1/tone1.png (renamed from app/assets/images/emoji/tone1.png)bin372 -> 372 bytes
-rw-r--r--public/-/emojis/1/tone2.png (renamed from app/assets/images/emoji/tone2.png)bin372 -> 372 bytes
-rw-r--r--public/-/emojis/1/tone3.png (renamed from app/assets/images/emoji/tone3.png)bin375 -> 375 bytes
-rw-r--r--public/-/emojis/1/tone4.png (renamed from app/assets/images/emoji/tone4.png)bin374 -> 374 bytes
-rw-r--r--public/-/emojis/1/tone5.png (renamed from app/assets/images/emoji/tone5.png)bin374 -> 374 bytes
-rw-r--r--public/-/emojis/1/tongue.png (renamed from app/assets/images/emoji/tongue.png)bin599 -> 599 bytes
-rw-r--r--public/-/emojis/1/tools.png (renamed from app/assets/images/emoji/tools.png)bin1225 -> 1225 bytes
-rw-r--r--public/-/emojis/1/top.png (renamed from app/assets/images/emoji/top.png)bin389 -> 389 bytes
-rw-r--r--public/-/emojis/1/tophat.png (renamed from app/assets/images/emoji/tophat.png)bin845 -> 845 bytes
-rw-r--r--public/-/emojis/1/track_next.png (renamed from app/assets/images/emoji/track_next.png)bin551 -> 551 bytes
-rw-r--r--public/-/emojis/1/track_previous.png (renamed from app/assets/images/emoji/track_previous.png)bin549 -> 549 bytes
-rw-r--r--public/-/emojis/1/trackball.png (renamed from app/assets/images/emoji/trackball.png)bin892 -> 892 bytes
-rw-r--r--public/-/emojis/1/tractor.png (renamed from app/assets/images/emoji/tractor.png)bin1192 -> 1192 bytes
-rw-r--r--public/-/emojis/1/traffic_light.png (renamed from app/assets/images/emoji/traffic_light.png)bin590 -> 590 bytes
-rw-r--r--public/-/emojis/1/train.png (renamed from app/assets/images/emoji/train.png)bin1031 -> 1031 bytes
-rw-r--r--public/-/emojis/1/train2.png (renamed from app/assets/images/emoji/train2.png)bin1499 -> 1499 bytes
-rw-r--r--public/-/emojis/1/tram.png (renamed from app/assets/images/emoji/tram.png)bin1065 -> 1065 bytes
-rw-r--r--public/-/emojis/1/triangular_flag_on_post.png (renamed from app/assets/images/emoji/triangular_flag_on_post.png)bin415 -> 415 bytes
-rw-r--r--public/-/emojis/1/triangular_ruler.png (renamed from app/assets/images/emoji/triangular_ruler.png)bin369 -> 369 bytes
-rw-r--r--public/-/emojis/1/trident.png (renamed from app/assets/images/emoji/trident.png)bin668 -> 668 bytes
-rw-r--r--public/-/emojis/1/triumph.png (renamed from app/assets/images/emoji/triumph.png)bin1529 -> 1529 bytes
-rw-r--r--public/-/emojis/1/trolleybus.png (renamed from app/assets/images/emoji/trolleybus.png)bin1168 -> 1168 bytes
-rw-r--r--public/-/emojis/1/trophy.png (renamed from app/assets/images/emoji/trophy.png)bin863 -> 863 bytes
-rw-r--r--public/-/emojis/1/tropical_drink.png (renamed from app/assets/images/emoji/tropical_drink.png)bin1428 -> 1428 bytes
-rw-r--r--public/-/emojis/1/tropical_fish.png (renamed from app/assets/images/emoji/tropical_fish.png)bin1676 -> 1676 bytes
-rw-r--r--public/-/emojis/1/truck.png (renamed from app/assets/images/emoji/truck.png)bin1366 -> 1366 bytes
-rw-r--r--public/-/emojis/1/trumpet.png (renamed from app/assets/images/emoji/trumpet.png)bin1281 -> 1281 bytes
-rw-r--r--public/-/emojis/1/tulip.png (renamed from app/assets/images/emoji/tulip.png)bin1065 -> 1065 bytes
-rw-r--r--public/-/emojis/1/tumbler_glass.png (renamed from app/assets/images/emoji/tumbler_glass.png)bin2312 -> 2312 bytes
-rw-r--r--public/-/emojis/1/turkey.png (renamed from app/assets/images/emoji/turkey.png)bin1240 -> 1240 bytes
-rw-r--r--public/-/emojis/1/turtle.png (renamed from app/assets/images/emoji/turtle.png)bin1515 -> 1515 bytes
-rw-r--r--public/-/emojis/1/tv.png (renamed from app/assets/images/emoji/tv.png)bin776 -> 776 bytes
-rw-r--r--public/-/emojis/1/twisted_rightwards_arrows.png (renamed from app/assets/images/emoji/twisted_rightwards_arrows.png)bin574 -> 574 bytes
-rw-r--r--public/-/emojis/1/two.png (renamed from app/assets/images/emoji/two.png)bin567 -> 567 bytes
-rw-r--r--public/-/emojis/1/two_hearts.png (renamed from app/assets/images/emoji/two_hearts.png)bin493 -> 493 bytes
-rw-r--r--public/-/emojis/1/two_men_holding_hands.png (renamed from app/assets/images/emoji/two_men_holding_hands.png)bin1347 -> 1347 bytes
-rw-r--r--public/-/emojis/1/two_women_holding_hands.png (renamed from app/assets/images/emoji/two_women_holding_hands.png)bin1544 -> 1544 bytes
-rw-r--r--public/-/emojis/1/u5272.png (renamed from app/assets/images/emoji/u5272.png)bin411 -> 411 bytes
-rw-r--r--public/-/emojis/1/u5408.png (renamed from app/assets/images/emoji/u5408.png)bin484 -> 484 bytes
-rw-r--r--public/-/emojis/1/u55b6.png (renamed from app/assets/images/emoji/u55b6.png)bin460 -> 460 bytes
-rw-r--r--public/-/emojis/1/u6307.png (renamed from app/assets/images/emoji/u6307.png)bin504 -> 504 bytes
-rw-r--r--public/-/emojis/1/u6708.png (renamed from app/assets/images/emoji/u6708.png)bin409 -> 409 bytes
-rw-r--r--public/-/emojis/1/u6709.png (renamed from app/assets/images/emoji/u6709.png)bin434 -> 434 bytes
-rw-r--r--public/-/emojis/1/u6e80.png (renamed from app/assets/images/emoji/u6e80.png)bin564 -> 564 bytes
-rw-r--r--public/-/emojis/1/u7121.png (renamed from app/assets/images/emoji/u7121.png)bin534 -> 534 bytes
-rw-r--r--public/-/emojis/1/u7533.png (renamed from app/assets/images/emoji/u7533.png)bin306 -> 306 bytes
-rw-r--r--public/-/emojis/1/u7981.png (renamed from app/assets/images/emoji/u7981.png)bin584 -> 584 bytes
-rw-r--r--public/-/emojis/1/u7a7a.png (renamed from app/assets/images/emoji/u7a7a.png)bin456 -> 456 bytes
-rw-r--r--public/-/emojis/1/umbrella.png (renamed from app/assets/images/emoji/umbrella.png)bin1229 -> 1229 bytes
-rw-r--r--public/-/emojis/1/umbrella2.png (renamed from app/assets/images/emoji/umbrella2.png)bin897 -> 897 bytes
-rw-r--r--public/-/emojis/1/unamused.png (renamed from app/assets/images/emoji/unamused.png)bin632 -> 632 bytes
-rw-r--r--public/-/emojis/1/underage.png (renamed from app/assets/images/emoji/underage.png)bin863 -> 863 bytes
-rw-r--r--public/-/emojis/1/unicorn.png (renamed from app/assets/images/emoji/unicorn.png)bin2107 -> 2107 bytes
-rw-r--r--public/-/emojis/1/unlock.png (renamed from app/assets/images/emoji/unlock.png)bin856 -> 856 bytes
-rw-r--r--public/-/emojis/1/up.png (renamed from app/assets/images/emoji/up.png)bin405 -> 405 bytes
-rw-r--r--public/-/emojis/1/upside_down.png (renamed from app/assets/images/emoji/upside_down.png)bin602 -> 602 bytes
-rw-r--r--public/-/emojis/1/urn.png (renamed from app/assets/images/emoji/urn.png)bin742 -> 742 bytes
-rw-r--r--public/-/emojis/1/v.png (renamed from app/assets/images/emoji/v.png)bin1009 -> 1009 bytes
-rw-r--r--public/-/emojis/1/v_tone1.png (renamed from app/assets/images/emoji/v_tone1.png)bin1009 -> 1009 bytes
-rw-r--r--public/-/emojis/1/v_tone2.png (renamed from app/assets/images/emoji/v_tone2.png)bin1009 -> 1009 bytes
-rw-r--r--public/-/emojis/1/v_tone3.png (renamed from app/assets/images/emoji/v_tone3.png)bin1009 -> 1009 bytes
-rw-r--r--public/-/emojis/1/v_tone4.png (renamed from app/assets/images/emoji/v_tone4.png)bin1009 -> 1009 bytes
-rw-r--r--public/-/emojis/1/v_tone5.png (renamed from app/assets/images/emoji/v_tone5.png)bin1009 -> 1009 bytes
-rw-r--r--public/-/emojis/1/vertical_traffic_light.png (renamed from app/assets/images/emoji/vertical_traffic_light.png)bin752 -> 752 bytes
-rw-r--r--public/-/emojis/1/vhs.png (renamed from app/assets/images/emoji/vhs.png)bin632 -> 632 bytes
-rw-r--r--public/-/emojis/1/vibration_mode.png (renamed from app/assets/images/emoji/vibration_mode.png)bin683 -> 683 bytes
-rw-r--r--public/-/emojis/1/video_camera.png (renamed from app/assets/images/emoji/video_camera.png)bin1611 -> 1611 bytes
-rw-r--r--public/-/emojis/1/video_game.png (renamed from app/assets/images/emoji/video_game.png)bin765 -> 765 bytes
-rw-r--r--public/-/emojis/1/violin.png (renamed from app/assets/images/emoji/violin.png)bin1156 -> 1156 bytes
-rw-r--r--public/-/emojis/1/virgo.png (renamed from app/assets/images/emoji/virgo.png)bin618 -> 618 bytes
-rw-r--r--public/-/emojis/1/volcano.png (renamed from app/assets/images/emoji/volcano.png)bin1257 -> 1257 bytes
-rw-r--r--public/-/emojis/1/volleyball.png (renamed from app/assets/images/emoji/volleyball.png)bin1202 -> 1202 bytes
-rw-r--r--public/-/emojis/1/vs.png (renamed from app/assets/images/emoji/vs.png)bin604 -> 604 bytes
-rw-r--r--public/-/emojis/1/vulcan.png (renamed from app/assets/images/emoji/vulcan.png)bin1083 -> 1083 bytes
-rw-r--r--public/-/emojis/1/vulcan_tone1.png (renamed from app/assets/images/emoji/vulcan_tone1.png)bin1083 -> 1083 bytes
-rw-r--r--public/-/emojis/1/vulcan_tone2.png (renamed from app/assets/images/emoji/vulcan_tone2.png)bin1083 -> 1083 bytes
-rw-r--r--public/-/emojis/1/vulcan_tone3.png (renamed from app/assets/images/emoji/vulcan_tone3.png)bin1083 -> 1083 bytes
-rw-r--r--public/-/emojis/1/vulcan_tone4.png (renamed from app/assets/images/emoji/vulcan_tone4.png)bin1083 -> 1083 bytes
-rw-r--r--public/-/emojis/1/vulcan_tone5.png (renamed from app/assets/images/emoji/vulcan_tone5.png)bin1083 -> 1083 bytes
-rw-r--r--public/-/emojis/1/walking.png (renamed from app/assets/images/emoji/walking.png)bin1082 -> 1082 bytes
-rw-r--r--public/-/emojis/1/walking_tone1.png (renamed from app/assets/images/emoji/walking_tone1.png)bin1084 -> 1084 bytes
-rw-r--r--public/-/emojis/1/walking_tone2.png (renamed from app/assets/images/emoji/walking_tone2.png)bin1084 -> 1084 bytes
-rw-r--r--public/-/emojis/1/walking_tone3.png (renamed from app/assets/images/emoji/walking_tone3.png)bin1066 -> 1066 bytes
-rw-r--r--public/-/emojis/1/walking_tone4.png (renamed from app/assets/images/emoji/walking_tone4.png)bin1075 -> 1075 bytes
-rw-r--r--public/-/emojis/1/walking_tone5.png (renamed from app/assets/images/emoji/walking_tone5.png)bin1065 -> 1065 bytes
-rw-r--r--public/-/emojis/1/waning_crescent_moon.png (renamed from app/assets/images/emoji/waning_crescent_moon.png)bin1213 -> 1213 bytes
-rw-r--r--public/-/emojis/1/waning_gibbous_moon.png (renamed from app/assets/images/emoji/waning_gibbous_moon.png)bin1208 -> 1208 bytes
-rw-r--r--public/-/emojis/1/warning.png (renamed from app/assets/images/emoji/warning.png)bin565 -> 565 bytes
-rw-r--r--public/-/emojis/1/wastebasket.png (renamed from app/assets/images/emoji/wastebasket.png)bin2414 -> 2414 bytes
-rw-r--r--public/-/emojis/1/watch.png (renamed from app/assets/images/emoji/watch.png)bin785 -> 785 bytes
-rw-r--r--public/-/emojis/1/water_buffalo.png (renamed from app/assets/images/emoji/water_buffalo.png)bin1536 -> 1536 bytes
-rw-r--r--public/-/emojis/1/water_polo.png (renamed from app/assets/images/emoji/water_polo.png)bin1755 -> 1755 bytes
-rw-r--r--public/-/emojis/1/water_polo_tone1.png (renamed from app/assets/images/emoji/water_polo_tone1.png)bin1758 -> 1758 bytes
-rw-r--r--public/-/emojis/1/water_polo_tone2.png (renamed from app/assets/images/emoji/water_polo_tone2.png)bin1756 -> 1756 bytes
-rw-r--r--public/-/emojis/1/water_polo_tone3.png (renamed from app/assets/images/emoji/water_polo_tone3.png)bin1760 -> 1760 bytes
-rw-r--r--public/-/emojis/1/water_polo_tone4.png (renamed from app/assets/images/emoji/water_polo_tone4.png)bin1749 -> 1749 bytes
-rw-r--r--public/-/emojis/1/water_polo_tone5.png (renamed from app/assets/images/emoji/water_polo_tone5.png)bin1748 -> 1748 bytes
-rw-r--r--public/-/emojis/1/watermelon.png (renamed from app/assets/images/emoji/watermelon.png)bin1275 -> 1275 bytes
-rw-r--r--public/-/emojis/1/wave.png (renamed from app/assets/images/emoji/wave.png)bin1300 -> 1300 bytes
-rw-r--r--public/-/emojis/1/wave_tone1.png (renamed from app/assets/images/emoji/wave_tone1.png)bin1300 -> 1300 bytes
-rw-r--r--public/-/emojis/1/wave_tone2.png (renamed from app/assets/images/emoji/wave_tone2.png)bin1300 -> 1300 bytes
-rw-r--r--public/-/emojis/1/wave_tone3.png (renamed from app/assets/images/emoji/wave_tone3.png)bin1295 -> 1295 bytes
-rw-r--r--public/-/emojis/1/wave_tone4.png (renamed from app/assets/images/emoji/wave_tone4.png)bin1300 -> 1300 bytes
-rw-r--r--public/-/emojis/1/wave_tone5.png (renamed from app/assets/images/emoji/wave_tone5.png)bin1300 -> 1300 bytes
-rw-r--r--public/-/emojis/1/wavy_dash.png (renamed from app/assets/images/emoji/wavy_dash.png)bin359 -> 359 bytes
-rw-r--r--public/-/emojis/1/waxing_crescent_moon.png (renamed from app/assets/images/emoji/waxing_crescent_moon.png)bin1199 -> 1199 bytes
-rw-r--r--public/-/emojis/1/waxing_gibbous_moon.png (renamed from app/assets/images/emoji/waxing_gibbous_moon.png)bin1229 -> 1229 bytes
-rw-r--r--public/-/emojis/1/wc.png (renamed from app/assets/images/emoji/wc.png)bin752 -> 752 bytes
-rw-r--r--public/-/emojis/1/weary.png (renamed from app/assets/images/emoji/weary.png)bin871 -> 871 bytes
-rw-r--r--public/-/emojis/1/wedding.png (renamed from app/assets/images/emoji/wedding.png)bin1260 -> 1260 bytes
-rw-r--r--public/-/emojis/1/whale.png (renamed from app/assets/images/emoji/whale.png)bin1572 -> 1572 bytes
-rw-r--r--public/-/emojis/1/whale2.png (renamed from app/assets/images/emoji/whale2.png)bin1196 -> 1196 bytes
-rw-r--r--public/-/emojis/1/wheel_of_dharma.png (renamed from app/assets/images/emoji/wheel_of_dharma.png)bin666 -> 666 bytes
-rw-r--r--public/-/emojis/1/wheelchair.png (renamed from app/assets/images/emoji/wheelchair.png)bin683 -> 683 bytes
-rw-r--r--public/-/emojis/1/white_check_mark.png (renamed from app/assets/images/emoji/white_check_mark.png)bin547 -> 547 bytes
-rw-r--r--public/-/emojis/1/white_circle.png (renamed from app/assets/images/emoji/white_circle.png)bin351 -> 351 bytes
-rw-r--r--public/-/emojis/1/white_flower.png (renamed from app/assets/images/emoji/white_flower.png)bin941 -> 941 bytes
-rw-r--r--public/-/emojis/1/white_large_square.png (renamed from app/assets/images/emoji/white_large_square.png)bin110 -> 110 bytes
-rw-r--r--public/-/emojis/1/white_medium_small_square.png (renamed from app/assets/images/emoji/white_medium_small_square.png)bin110 -> 110 bytes
-rw-r--r--public/-/emojis/1/white_medium_square.png (renamed from app/assets/images/emoji/white_medium_square.png)bin108 -> 108 bytes
-rw-r--r--public/-/emojis/1/white_small_square.png (renamed from app/assets/images/emoji/white_small_square.png)bin108 -> 108 bytes
-rw-r--r--public/-/emojis/1/white_square_button.png (renamed from app/assets/images/emoji/white_square_button.png)bin122 -> 122 bytes
-rw-r--r--public/-/emojis/1/white_sun_cloud.png (renamed from app/assets/images/emoji/white_sun_cloud.png)bin968 -> 968 bytes
-rw-r--r--public/-/emojis/1/white_sun_rain_cloud.png (renamed from app/assets/images/emoji/white_sun_rain_cloud.png)bin1161 -> 1161 bytes
-rw-r--r--public/-/emojis/1/white_sun_small_cloud.png (renamed from app/assets/images/emoji/white_sun_small_cloud.png)bin989 -> 989 bytes
-rw-r--r--public/-/emojis/1/wilted_rose.png (renamed from app/assets/images/emoji/wilted_rose.png)bin1349 -> 1349 bytes
-rw-r--r--public/-/emojis/1/wind_blowing_face.png (renamed from app/assets/images/emoji/wind_blowing_face.png)bin1827 -> 1827 bytes
-rw-r--r--public/-/emojis/1/wind_chime.png (renamed from app/assets/images/emoji/wind_chime.png)bin1046 -> 1046 bytes
-rw-r--r--public/-/emojis/1/wine_glass.png (renamed from app/assets/images/emoji/wine_glass.png)bin655 -> 655 bytes
-rw-r--r--public/-/emojis/1/wink.png (renamed from app/assets/images/emoji/wink.png)bin746 -> 746 bytes
-rw-r--r--public/-/emojis/1/wolf.png (renamed from app/assets/images/emoji/wolf.png)bin1528 -> 1528 bytes
-rw-r--r--public/-/emojis/1/woman.png (renamed from app/assets/images/emoji/woman.png)bin1212 -> 1212 bytes
-rw-r--r--public/-/emojis/1/woman_tone1.png (renamed from app/assets/images/emoji/woman_tone1.png)bin1212 -> 1212 bytes
-rw-r--r--public/-/emojis/1/woman_tone2.png (renamed from app/assets/images/emoji/woman_tone2.png)bin1212 -> 1212 bytes
-rw-r--r--public/-/emojis/1/woman_tone3.png (renamed from app/assets/images/emoji/woman_tone3.png)bin1202 -> 1202 bytes
-rw-r--r--public/-/emojis/1/woman_tone4.png (renamed from app/assets/images/emoji/woman_tone4.png)bin1195 -> 1195 bytes
-rw-r--r--public/-/emojis/1/woman_tone5.png (renamed from app/assets/images/emoji/woman_tone5.png)bin1202 -> 1202 bytes
-rw-r--r--public/-/emojis/1/womans_clothes.png (renamed from app/assets/images/emoji/womans_clothes.png)bin1042 -> 1042 bytes
-rw-r--r--public/-/emojis/1/womans_hat.png (renamed from app/assets/images/emoji/womans_hat.png)bin1553 -> 1553 bytes
-rw-r--r--public/-/emojis/1/womens.png (renamed from app/assets/images/emoji/womens.png)bin577 -> 577 bytes
-rw-r--r--public/-/emojis/1/worried.png (renamed from app/assets/images/emoji/worried.png)bin715 -> 715 bytes
-rw-r--r--public/-/emojis/1/wrench.png (renamed from app/assets/images/emoji/wrench.png)bin418 -> 418 bytes
-rw-r--r--public/-/emojis/1/wrestlers.png (renamed from app/assets/images/emoji/wrestlers.png)bin2556 -> 2556 bytes
-rw-r--r--public/-/emojis/1/wrestlers_tone1.png (renamed from app/assets/images/emoji/wrestlers_tone1.png)bin2563 -> 2563 bytes
-rw-r--r--public/-/emojis/1/wrestlers_tone2.png (renamed from app/assets/images/emoji/wrestlers_tone2.png)bin2553 -> 2553 bytes
-rw-r--r--public/-/emojis/1/wrestlers_tone3.png (renamed from app/assets/images/emoji/wrestlers_tone3.png)bin2541 -> 2541 bytes
-rw-r--r--public/-/emojis/1/wrestlers_tone4.png (renamed from app/assets/images/emoji/wrestlers_tone4.png)bin2553 -> 2553 bytes
-rw-r--r--public/-/emojis/1/wrestlers_tone5.png (renamed from app/assets/images/emoji/wrestlers_tone5.png)bin2542 -> 2542 bytes
-rw-r--r--public/-/emojis/1/writing_hand.png (renamed from app/assets/images/emoji/writing_hand.png)bin1001 -> 1001 bytes
-rw-r--r--public/-/emojis/1/writing_hand_tone1.png (renamed from app/assets/images/emoji/writing_hand_tone1.png)bin988 -> 988 bytes
-rw-r--r--public/-/emojis/1/writing_hand_tone2.png (renamed from app/assets/images/emoji/writing_hand_tone2.png)bin987 -> 987 bytes
-rw-r--r--public/-/emojis/1/writing_hand_tone3.png (renamed from app/assets/images/emoji/writing_hand_tone3.png)bin977 -> 977 bytes
-rw-r--r--public/-/emojis/1/writing_hand_tone4.png (renamed from app/assets/images/emoji/writing_hand_tone4.png)bin973 -> 973 bytes
-rw-r--r--public/-/emojis/1/writing_hand_tone5.png (renamed from app/assets/images/emoji/writing_hand_tone5.png)bin970 -> 970 bytes
-rw-r--r--public/-/emojis/1/x.png (renamed from app/assets/images/emoji/x.png)bin298 -> 298 bytes
-rw-r--r--public/-/emojis/1/yellow_heart.png (renamed from app/assets/images/emoji/yellow_heart.png)bin435 -> 435 bytes
-rw-r--r--public/-/emojis/1/yen.png (renamed from app/assets/images/emoji/yen.png)bin421 -> 421 bytes
-rw-r--r--public/-/emojis/1/yin_yang.png (renamed from app/assets/images/emoji/yin_yang.png)bin776 -> 776 bytes
-rw-r--r--public/-/emojis/1/yum.png (renamed from app/assets/images/emoji/yum.png)bin896 -> 896 bytes
-rw-r--r--public/-/emojis/1/zap.png (renamed from app/assets/images/emoji/zap.png)bin413 -> 413 bytes
-rw-r--r--public/-/emojis/1/zero.png (renamed from app/assets/images/emoji/zero.png)bin560 -> 560 bytes
-rw-r--r--public/-/emojis/1/zipper_mouth.png (renamed from app/assets/images/emoji/zipper_mouth.png)bin722 -> 722 bytes
-rw-r--r--public/-/emojis/1/zzz.png (renamed from app/assets/images/emoji/zzz.png)bin540 -> 540 bytes
-rw-r--r--qa/.gitignore1
-rw-r--r--qa/Gemfile2
-rw-r--r--qa/Gemfile.lock6
-rw-r--r--qa/README.md17
-rw-r--r--qa/Rakefile28
-rw-r--r--qa/load/artillery.yml25
-rw-r--r--qa/qa.rb15
-rw-r--r--qa/qa/git/repository.rb5
-rw-r--r--qa/qa/page/alert/auto_devops_alert.rb13
-rw-r--r--qa/qa/page/base.rb12
-rw-r--r--qa/qa/page/component/select2.rb2
-rw-r--r--qa/qa/page/dashboard/projects.rb2
-rw-r--r--qa/qa/page/dashboard/snippet/index.rb21
-rw-r--r--qa/qa/page/dashboard/snippet/new.rb53
-rw-r--r--qa/qa/page/dashboard/snippet/show.rb63
-rw-r--r--qa/qa/page/group/show.rb2
-rw-r--r--qa/qa/page/main/menu.rb5
-rw-r--r--qa/qa/page/merge_request/show.rb16
-rw-r--r--qa/qa/page/project/activity.rb2
-rw-r--r--qa/qa/page/project/import/github.rb21
-rw-r--r--qa/qa/page/project/issue/show.rb12
-rw-r--r--qa/qa/page/project/job/show.rb31
-rw-r--r--qa/qa/page/project/menu.rb2
-rw-r--r--qa/qa/page/project/new.rb10
-rw-r--r--qa/qa/page/project/operations/kubernetes/show.rb17
-rw-r--r--qa/qa/page/project/pipeline/show.rb12
-rw-r--r--qa/qa/page/project/settings/deploy_keys.rb4
-rw-r--r--qa/qa/page/project/web_ide/edit.rb8
-rw-r--r--qa/qa/resource/kubernetes_cluster.rb10
-rw-r--r--qa/qa/resource/project_imported_from_github.rb2
-rw-r--r--qa/qa/resource/snippet.rb30
-rw-r--r--qa/qa/runtime/namespace.rb4
-rw-r--r--qa/qa/service/kubernetes_cluster.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb29
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb7
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb18
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb56
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb34
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb7
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb (renamed from qa/qa/specs/features/browser_ui/3_create/repository/user_views_raw_diff_patch_requests_spec.rb)0
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb31
-rw-r--r--qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb3
-rw-r--r--qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb244
-rw-r--r--qa/qa/specs/helpers/quarantine.rb68
-rw-r--r--qa/qa/support/api.rb10
-rw-r--r--qa/qa/support/page/logging.rb13
-rw-r--r--qa/qa/tools/generate_perf_testdata.rb143
-rw-r--r--qa/spec/git/repository_spec.rb4
-rw-r--r--qa/spec/page/logging_spec.rb15
-rw-r--r--qa/spec/spec_helper.rb53
-rw-r--r--qa/spec/spec_helper_spec.rb304
-rw-r--r--qa/spec/specs/helpers/quarantine_spec.rb271
-rwxr-xr-xscripts/build_assets_image19
-rw-r--r--scripts/frontend/postinstall.js2
-rw-r--r--scripts/frontend/prettier.js4
-rw-r--r--scripts/frontend/stylelint/stylelint-duplicate-selectors.js23
-rw-r--r--scripts/frontend/stylelint/stylelint-utility-classes.js24
-rw-r--r--scripts/frontend/stylelint/stylelint-utility-map.js54
-rw-r--r--scripts/frontend/stylelint/stylelint-utils.js77
-rw-r--r--scripts/frontend/stylelint/utility-classes-map.js216
-rw-r--r--scripts/gitaly_test.rb16
-rwxr-xr-xscripts/insert-rspec-profiling-data47
-rwxr-xr-xscripts/lint-doc.sh2
-rwxr-xr-xscripts/lint-rugged8
-rwxr-xr-xscripts/merge-simplecov8
-rwxr-xr-xscripts/review_apps/review-apps.sh10
-rwxr-xr-xscripts/trigger-build2
-rw-r--r--spec/controllers/admin/appearances_controller_spec.rb91
-rw-r--r--spec/controllers/admin/projects_controller_spec.rb10
-rw-r--r--spec/controllers/admin/runners_controller_spec.rb19
-rw-r--r--spec/controllers/admin/users_controller_spec.rb14
-rw-r--r--spec/controllers/application_controller_spec.rb8
-rw-r--r--spec/controllers/autocomplete_controller_spec.rb31
-rw-r--r--spec/controllers/concerns/issuable_collections_spec.rb4
-rw-r--r--spec/controllers/concerns/send_file_upload_spec.rb2
-rw-r--r--spec/controllers/dashboard/milestones_controller_spec.rb2
-rw-r--r--spec/controllers/dashboard/projects_controller_spec.rb26
-rw-r--r--spec/controllers/explore/projects_controller_spec.rb30
-rw-r--r--spec/controllers/google_api/authorizations_controller_spec.rb60
-rw-r--r--spec/controllers/graphql_controller_spec.rb113
-rw-r--r--spec/controllers/groups/boards_controller_spec.rb22
-rw-r--r--spec/controllers/groups/clusters/applications_controller_spec.rb100
-rw-r--r--spec/controllers/groups/settings/ci_cd_controller_spec.rb73
-rw-r--r--spec/controllers/groups/shared_projects_controller_spec.rb2
-rw-r--r--spec/controllers/groups_controller_spec.rb41
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb29
-rw-r--r--spec/controllers/profiles/preferences_controller_spec.rb3
-rw-r--r--spec/controllers/projects/autocomplete_sources_controller_spec.rb69
-rw-r--r--spec/controllers/projects/blob_controller_spec.rb58
-rw-r--r--spec/controllers/projects/boards_controller_spec.rb22
-rw-r--r--spec/controllers/projects/clusters/applications_controller_spec.rb97
-rw-r--r--spec/controllers/projects/environments_controller_spec.rb10
-rw-r--r--spec/controllers/projects/git_http_controller_spec.rb15
-rw-r--r--spec/controllers/projects/graphs_controller_spec.rb16
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb37
-rw-r--r--spec/controllers/projects/notes_controller_spec.rb51
-rw-r--r--spec/controllers/projects/pages_controller_spec.rb12
-rw-r--r--spec/controllers/projects/settings/operations_controller_spec.rb63
-rw-r--r--spec/controllers/registrations_controller_spec.rb6
-rw-r--r--spec/controllers/snippets_controller_spec.rb4
-rw-r--r--spec/controllers/users_controller_spec.rb34
-rw-r--r--spec/factories/ci/builds.rb4
-rw-r--r--spec/factories/ci/group_variables.rb1
-rw-r--r--spec/factories/ci/pipelines.rb14
-rw-r--r--spec/factories/ci/variables.rb1
-rw-r--r--spec/factories/clusters/clusters.rb4
-rw-r--r--spec/factories/clusters/providers/gcp.rb4
-rw-r--r--spec/factories/commit_statuses.rb4
-rw-r--r--spec/factories/groups.rb8
-rw-r--r--spec/factories/merge_requests.rb30
-rw-r--r--spec/factories/personal_access_tokens.rb5
-rw-r--r--spec/factories/project_daily_statistics.rb8
-rw-r--r--spec/factories/projects.rb18
-rw-r--r--spec/factories/suggestions.rb6
-rw-r--r--spec/features/admin/admin_appearance_spec.rb32
-rw-r--r--spec/features/admin/admin_runners_spec.rb50
-rw-r--r--spec/features/clusters/cluster_detail_page_spec.rb6
-rw-r--r--spec/features/cycle_analytics_spec.rb19
-rw-r--r--spec/features/dashboard/activity_spec.rb17
-rw-r--r--spec/features/dashboard/projects_spec.rb11
-rw-r--r--spec/features/dashboard/shortcuts_spec.rb2
-rw-r--r--spec/features/dashboard/snippets_spec.rb15
-rw-r--r--spec/features/display_system_header_and_footer_bar_spec.rb137
-rw-r--r--spec/features/group_variables_spec.rb2
-rw-r--r--spec/features/groups/settings/ci_cd_spec.rb45
-rw-r--r--spec/features/issuables/issuable_list_spec.rb24
-rw-r--r--spec/features/issues/filtered_search/dropdown_hint_spec.rb11
-rw-r--r--spec/features/issues/filtered_search/search_bar_spec.rb4
-rw-r--r--spec/features/issues/filtered_search/visual_tokens_spec.rb21
-rw-r--r--spec/features/issues/gfm_autocomplete_spec.rb84
-rw-r--r--spec/features/issues/user_creates_issue_spec.rb18
-rw-r--r--spec/features/issues/user_uses_quick_actions_spec.rb4
-rw-r--r--spec/features/issues_spec.rb15
-rw-r--r--spec/features/markdown/copy_as_gfm_spec.rb11
-rw-r--r--spec/features/merge_request/maintainer_edits_fork_spec.rb4
-rw-r--r--spec/features/merge_request/user_posts_diff_notes_spec.rb2
-rw-r--r--spec/features/merge_request/user_posts_notes_spec.rb5
-rw-r--r--spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb38
-rw-r--r--spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb86
-rw-r--r--spec/features/merge_request/user_sees_merge_widget_spec.rb113
-rw-r--r--spec/features/merge_request/user_sees_versions_spec.rb6
-rw-r--r--spec/features/merge_request/user_views_diffs_spec.rb10
-rw-r--r--spec/features/merge_request/user_views_open_merge_request_spec.rb2
-rw-r--r--spec/features/merge_requests/user_filters_by_target_branch_spec.rb45
-rw-r--r--spec/features/merge_requests/user_lists_merge_requests_spec.rb4
-rw-r--r--spec/features/milestone_spec.rb28
-rw-r--r--spec/features/profiles/active_sessions_spec.rb48
-rw-r--r--spec/features/profiles/user_visits_profile_preferences_page_spec.rb24
-rw-r--r--spec/features/project_variables_spec.rb2
-rw-r--r--spec/features/projects/artifacts/user_browses_artifacts_spec.rb6
-rw-r--r--spec/features/projects/badges/pipeline_badge_spec.rb19
-rw-r--r--spec/features/projects/blobs/blob_show_spec.rb5
-rw-r--r--spec/features/projects/blobs/edit_spec.rb15
-rw-r--r--spec/features/projects/branches_spec.rb32
-rw-r--r--spec/features/projects/clusters/applications_spec.rb92
-rw-r--r--spec/features/projects/environments/environment_spec.rb8
-rw-r--r--spec/features/projects/environments/environments_spec.rb25
-rw-r--r--spec/features/projects/files/user_creates_files_spec.rb2
-rw-r--r--spec/features/projects/files/user_edits_files_spec.rb2
-rw-r--r--spec/features/projects/jobs_spec.rb109
-rw-r--r--spec/features/projects/members/invite_group_spec.rb2
-rw-r--r--spec/features/projects/pages_spec.rb107
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb148
-rw-r--r--spec/features/projects/pipelines/pipelines_spec.rb121
-rw-r--r--spec/features/projects/services/user_activates_issue_tracker_spec.rb35
-rw-r--r--spec/features/projects/services/user_activates_youtrack_spec.rb89
-rw-r--r--spec/features/projects/settings/operations_settings_spec.rb77
-rw-r--r--spec/features/projects/settings/pipelines_settings_spec.rb31
-rw-r--r--spec/features/projects/settings/project_settings_spec.rb43
-rw-r--r--spec/features/projects/settings/user_manages_group_links_spec.rb1
-rw-r--r--spec/features/projects/show/user_sees_git_instructions_spec.rb2
-rw-r--r--spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb58
-rw-r--r--spec/features/projects/user_sees_sidebar_spec.rb102
-rw-r--r--spec/features/projects/wiki/markdown_preview_spec.rb2
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb2
-rw-r--r--spec/features/search/user_searches_for_users_spec.rb83
-rw-r--r--spec/features/security/group/private_access_spec.rb21
-rw-r--r--spec/features/snippets/notes_on_personal_snippets_spec.rb2
-rw-r--r--spec/features/snippets/user_creates_snippet_spec.rb2
-rw-r--r--spec/features/tags/master_deletes_tag_spec.rb2
-rw-r--r--spec/features/task_lists_spec.rb8
-rw-r--r--spec/features/user_opens_link_to_comment.rb33
-rw-r--r--spec/features/users/login_spec.rb29
-rw-r--r--spec/finders/admin/runners_finder_spec.rb8
-rw-r--r--spec/finders/autocomplete/acts_as_taggable_on/tags_finder_spec.rb66
-rw-r--r--spec/finders/concerns/finder_methods_spec.rb22
-rw-r--r--spec/finders/group_projects_finder_spec.rb39
-rw-r--r--spec/finders/issues_finder_spec.rb196
-rw-r--r--spec/finders/labels_finder_spec.rb6
-rw-r--r--spec/finders/merge_requests_finder_spec.rb491
-rw-r--r--spec/finders/snippets_finder_spec.rb221
-rw-r--r--spec/finders/users_finder_spec.rb31
-rw-r--r--spec/fixtures/api/schemas/board.json3
-rw-r--r--spec/fixtures/api/schemas/cluster_status.json1
-rw-r--r--spec/fixtures/api/schemas/entities/issue.json3
-rw-r--r--spec/fixtures/api/schemas/entities/issue_boards.json3
-rw-r--r--spec/fixtures/api/schemas/entities/merge_request_widget.json4
-rw-r--r--spec/fixtures/api/schemas/environment.json1
-rw-r--r--spec/fixtures/api/schemas/issue.json5
-rw-r--r--spec/fixtures/api/schemas/issues.json3
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/merge_request.json124
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/merge_requests.json122
-rw-r--r--spec/fixtures/security-reports/remediations/gl-dependency-scanning-report.json104
-rw-r--r--spec/fixtures/security-reports/remediations/remediation.patch180
-rw-r--r--spec/fixtures/security-reports/remediations/yarn.lock104
-rw-r--r--spec/fixtures/trace/sample_trace18
-rw-r--r--spec/frontend/__mocks__/file_mock.js1
-rw-r--r--spec/frontend/behaviors/secret_values_spec.js (renamed from spec/javascripts/behaviors/secret_values_spec.js)0
-rw-r--r--spec/frontend/blob/blob_fork_suggestion_spec.js (renamed from spec/javascripts/blob/blob_fork_suggestion_spec.js)0
-rw-r--r--spec/frontend/boards/modal_store_spec.js (renamed from spec/javascripts/boards/modal_store_spec.js)0
-rw-r--r--spec/frontend/cycle_analytics/limit_warning_component_spec.js (renamed from spec/javascripts/cycle_analytics/limit_warning_component_spec.js)0
-rw-r--r--spec/frontend/diffs/components/diff_stats_spec.js (renamed from spec/javascripts/diffs/components/diff_stats_spec.js)0
-rw-r--r--spec/frontend/diffs/components/edit_button_spec.js61
-rw-r--r--spec/frontend/diffs/components/hidden_files_warning_spec.js48
-rw-r--r--spec/frontend/diffs/components/no_changes_spec.js (renamed from spec/javascripts/diffs/components/no_changes_spec.js)0
-rw-r--r--spec/frontend/environment.js27
-rw-r--r--spec/frontend/error_tracking/components/error_tracking_list_spec.js118
-rw-r--r--spec/frontend/error_tracking/store/mutation_spec.js (renamed from spec/javascripts/error_tracking/store/mutation_spec.js)0
-rw-r--r--spec/frontend/filtered_search/filtered_search_token_keys_spec.js (renamed from spec/javascripts/filtered_search/filtered_search_token_keys_spec.js)0
-rw-r--r--spec/frontend/filtered_search/services/recent_searches_service_error_spec.js (renamed from spec/javascripts/filtered_search/services/recent_searches_service_error_spec.js)0
-rw-r--r--spec/frontend/filtered_search/stores/recent_searches_store_spec.js (renamed from spec/javascripts/filtered_search/stores/recent_searches_store_spec.js)0
-rw-r--r--spec/frontend/frequent_items/store/getters_spec.js (renamed from spec/javascripts/frequent_items/store/getters_spec.js)0
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js348
-rw-r--r--spec/frontend/helpers/fixtures.js22
-rw-r--r--spec/frontend/helpers/timeout.js24
-rw-r--r--spec/frontend/helpers/vue_mount_component_helper.js38
-rw-r--r--spec/frontend/ide/lib/common/disposable_spec.js (renamed from spec/javascripts/ide/lib/common/disposable_spec.js)0
-rw-r--r--spec/frontend/ide/lib/diff/diff_spec.js (renamed from spec/javascripts/ide/lib/diff/diff_spec.js)0
-rw-r--r--spec/frontend/ide/lib/editor_options_spec.js (renamed from spec/javascripts/ide/lib/editor_options_spec.js)0
-rw-r--r--spec/frontend/ide/lib/files_spec.js77
-rw-r--r--spec/frontend/ide/stores/modules/commit/mutations_spec.js (renamed from spec/javascripts/ide/stores/modules/commit/mutations_spec.js)0
-rw-r--r--spec/frontend/ide/stores/modules/file_templates/getters_spec.js (renamed from spec/javascripts/ide/stores/modules/file_templates/getters_spec.js)0
-rw-r--r--spec/frontend/ide/stores/modules/file_templates/mutations_spec.js (renamed from spec/javascripts/ide/stores/modules/file_templates/mutations_spec.js)0
-rw-r--r--spec/frontend/ide/stores/modules/pane/getters_spec.js (renamed from spec/javascripts/ide/stores/modules/pane/getters_spec.js)0
-rw-r--r--spec/frontend/ide/stores/modules/pane/mutations_spec.js (renamed from spec/javascripts/ide/stores/modules/pane/mutations_spec.js)0
-rw-r--r--spec/frontend/ide/stores/modules/pipelines/getters_spec.js (renamed from spec/javascripts/ide/stores/modules/pipelines/getters_spec.js)0
-rw-r--r--spec/frontend/ide/stores/mutations/branch_spec.js (renamed from spec/javascripts/ide/stores/mutations/branch_spec.js)0
-rw-r--r--spec/frontend/ide/stores/mutations/merge_request_spec.js (renamed from spec/javascripts/ide/stores/mutations/merge_request_spec.js)0
-rw-r--r--spec/frontend/image_diff/view_types_spec.js (renamed from spec/javascripts/image_diff/view_types_spec.js)0
-rw-r--r--spec/frontend/import_projects/store/getters_spec.js (renamed from spec/javascripts/import_projects/store/getters_spec.js)0
-rw-r--r--spec/frontend/import_projects/store/mutations_spec.js (renamed from spec/javascripts/import_projects/store/mutations_spec.js)0
-rw-r--r--spec/frontend/issuable_suggestions/components/app_spec.js (renamed from spec/javascripts/issuable_suggestions/components/app_spec.js)0
-rw-r--r--spec/frontend/issuable_suggestions/components/item_spec.js (renamed from spec/javascripts/issuable_suggestions/components/item_spec.js)0
-rw-r--r--spec/frontend/issuable_suggestions/mock_data.js (renamed from spec/javascripts/issuable_suggestions/mock_data.js)0
-rw-r--r--spec/frontend/jobs/components/empty_state_spec.js (renamed from spec/javascripts/jobs/components/empty_state_spec.js)0
-rw-r--r--spec/frontend/jobs/components/erased_block_spec.js (renamed from spec/javascripts/jobs/components/erased_block_spec.js)0
-rw-r--r--spec/frontend/jobs/components/sidebar_detail_row_spec.js (renamed from spec/javascripts/jobs/components/sidebar_detail_row_spec.js)0
-rw-r--r--spec/frontend/jobs/components/stuck_block_spec.js (renamed from spec/javascripts/jobs/components/stuck_block_spec.js)0
-rw-r--r--spec/frontend/jobs/store/getters_spec.js243
-rw-r--r--spec/frontend/jobs/store/mutations_spec.js (renamed from spec/javascripts/jobs/store/mutations_spec.js)0
-rw-r--r--spec/frontend/labels_select_spec.js (renamed from spec/javascripts/labels_select_spec.js)0
-rw-r--r--spec/frontend/lib/utils/ajax_cache_spec.js161
-rw-r--r--spec/frontend/lib/utils/autosave_spec.js64
-rw-r--r--spec/frontend/lib/utils/cache_spec.js (renamed from spec/javascripts/lib/utils/cache_spec.js)0
-rw-r--r--spec/frontend/lib/utils/grammar_spec.js (renamed from spec/javascripts/lib/utils/grammar_spec.js)0
-rw-r--r--spec/frontend/lib/utils/image_utility_spec.js (renamed from spec/javascripts/lib/utils/image_utility_spec.js)0
-rw-r--r--spec/frontend/lib/utils/number_utility_spec.js101
-rw-r--r--spec/frontend/lib/utils/text_utility_spec.js (renamed from spec/javascripts/lib/utils/text_utility_spec.js)0
-rw-r--r--spec/frontend/locale/ensure_single_line_spec.js (renamed from spec/javascripts/locale/ensure_single_line_spec.js)0
-rw-r--r--spec/frontend/locale/sprintf_spec.js (renamed from spec/javascripts/locale/sprintf_spec.js)0
-rw-r--r--spec/frontend/mr_popover/__snapshots__/mr_popover_spec.js.snap93
-rw-r--r--spec/frontend/mr_popover/mr_popover_spec.js61
-rw-r--r--spec/frontend/notebook/lib/highlight_spec.js (renamed from spec/javascripts/notebook/lib/highlight_spec.js)0
-rw-r--r--spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap20
-rw-r--r--spec/frontend/notes/components/discussion_jump_to_next_button_spec.js30
-rw-r--r--spec/frontend/notes/components/discussion_reply_placeholder_spec.js (renamed from spec/javascripts/notes/components/discussion_reply_placeholder_spec.js)0
-rw-r--r--spec/frontend/notes/components/discussion_resolve_button_spec.js (renamed from spec/javascripts/notes/components/discussion_resolve_button_spec.js)0
-rw-r--r--spec/frontend/notes/components/note_attachment_spec.js (renamed from spec/javascripts/notes/components/note_attachment_spec.js)0
-rw-r--r--spec/frontend/notes/components/note_edited_text_spec.js (renamed from spec/javascripts/notes/components/note_edited_text_spec.js)0
-rw-r--r--spec/frontend/performance_bar/services/performance_bar_service_spec.js (renamed from spec/javascripts/performance_bar/services/performance_bar_service_spec.js)0
-rw-r--r--spec/frontend/pipelines/blank_state_spec.js (renamed from spec/javascripts/pipelines/blank_state_spec.js)0
-rw-r--r--spec/frontend/pipelines/empty_state_spec.js (renamed from spec/javascripts/pipelines/empty_state_spec.js)0
-rw-r--r--spec/frontend/pipelines/pipeline_store_spec.js (renamed from spec/javascripts/pipelines/pipeline_store_spec.js)0
-rw-r--r--spec/frontend/pipelines/pipelines_store_spec.js (renamed from spec/javascripts/pipelines/pipelines_store_spec.js)0
-rw-r--r--spec/frontend/registry/getters_spec.js (renamed from spec/javascripts/registry/getters_spec.js)0
-rw-r--r--spec/frontend/reports/components/report_link_spec.js (renamed from spec/javascripts/reports/components/report_link_spec.js)0
-rw-r--r--spec/frontend/reports/store/utils_spec.js (renamed from spec/javascripts/reports/store/utils_spec.js)0
-rw-r--r--spec/frontend/sidebar/confidential_edit_buttons_spec.js (renamed from spec/javascripts/sidebar/confidential_edit_buttons_spec.js)0
-rw-r--r--spec/frontend/sidebar/confidential_edit_form_buttons_spec.js (renamed from spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js)0
-rw-r--r--spec/frontend/sidebar/lock/edit_form_spec.js (renamed from spec/javascripts/sidebar/lock/edit_form_spec.js)0
-rw-r--r--spec/frontend/test_setup.js33
-rw-r--r--spec/frontend/u2f/util_spec.js (renamed from spec/javascripts/u2f/util_spec.js)0
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_container_spec.js (renamed from spec/javascripts/vue_mr_widget/components/mr_widget_container_spec.js)0
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js (renamed from spec/javascripts/vue_mr_widget/components/mr_widget_icon_spec.js)0
-rw-r--r--spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js (renamed from spec/javascripts/vue_mr_widget/components/states/commit_edit_spec.js)0
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js (renamed from spec/javascripts/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js)0
-rw-r--r--spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js136
-rw-r--r--spec/frontend/vue_mr_widget/stores/get_state_key_spec.js (renamed from spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js)0
-rw-r--r--spec/frontend/vue_shared/components/callout_spec.js (renamed from spec/javascripts/vue_shared/components/callout_spec.js)0
-rw-r--r--spec/frontend/vue_shared/components/code_block_spec.js (renamed from spec/javascripts/vue_shared/components/code_block_spec.js)0
-rw-r--r--spec/frontend/vue_shared/components/diff_viewer/viewers/mode_changed_spec.js (renamed from spec/javascripts/vue_shared/components/diff_viewer/viewers/mode_changed_spec.js)0
-rw-r--r--spec/frontend/vue_shared/components/identicon_spec.js (renamed from spec/javascripts/vue_shared/components/identicon_spec.js)0
-rw-r--r--spec/frontend/vue_shared/components/lib/utils/dom_utils_spec.js (renamed from spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js)0
-rw-r--r--spec/frontend/vue_shared/components/pagination_links_spec.js (renamed from spec/javascripts/vue_shared/components/pagination_links_spec.js)0
-rw-r--r--spec/frontend/vue_shared/components/time_ago_tooltip_spec.js (renamed from spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js)0
-rw-r--r--spec/frontend/vuex_shared/modules/modal/mutations_spec.js (renamed from spec/javascripts/vuex_shared/modules/modal/mutations_spec.js)0
-rw-r--r--spec/graphql/features/authorization_spec.rb106
-rw-r--r--spec/graphql/resolvers/issues_resolver_spec.rb69
-rw-r--r--spec/graphql/resolvers/metadata_resolver_spec.rb11
-rw-r--r--spec/graphql/types/ci/detailed_status_type_spec.rb11
-rw-r--r--spec/graphql/types/issuable_state_enum_spec.rb9
-rw-r--r--spec/graphql/types/issue_state_enum_spec.rb9
-rw-r--r--spec/graphql/types/merge_request_state_enum_spec.rb13
-rw-r--r--spec/graphql/types/metadata_type_spec.rb5
-rw-r--r--spec/graphql/types/query_type_spec.rb15
-rw-r--r--spec/helpers/appearances_helper_spec.rb76
-rw-r--r--spec/helpers/auth_helper_spec.rb38
-rw-r--r--spec/helpers/auto_devops_helper_spec.rb138
-rw-r--r--spec/helpers/blob_helper_spec.rb8
-rw-r--r--spec/helpers/clusters_helper_spec.rb33
-rw-r--r--spec/helpers/emails_helper_spec.rb54
-rw-r--r--spec/helpers/labels_helper_spec.rb13
-rw-r--r--spec/helpers/preferences_helper_spec.rb23
-rw-r--r--spec/helpers/projects_helper_spec.rb50
-rw-r--r--spec/javascripts/activities_spec.js2
-rw-r--r--spec/javascripts/ajax_loading_spinner_spec.js2
-rw-r--r--spec/javascripts/api_spec.js34
-rw-r--r--spec/javascripts/awards_handler_spec.js15
-rw-r--r--spec/javascripts/badges/components/badge_list_spec.js2
-rw-r--r--spec/javascripts/badges/components/badge_spec.js2
-rw-r--r--spec/javascripts/badges/store/actions_spec.js4
-rw-r--r--spec/javascripts/behaviors/copy_as_gfm_spec.js4
-rw-r--r--spec/javascripts/behaviors/quick_submit_spec.js4
-rw-r--r--spec/javascripts/behaviors/requires_input_spec.js4
-rw-r--r--spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js2
-rw-r--r--spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js4
-rw-r--r--spec/javascripts/blob/blob_file_dropzone_spec.js4
-rw-r--r--spec/javascripts/blob/notebook/index_spec.js4
-rw-r--r--spec/javascripts/blob/pdf/index_spec.js4
-rw-r--r--spec/javascripts/blob/sketch/index_spec.js4
-rw-r--r--spec/javascripts/blob/viewer/index_spec.js4
-rw-r--r--spec/javascripts/boards/board_list_spec.js2
-rw-r--r--spec/javascripts/boards/components/board_spec.js2
-rw-r--r--spec/javascripts/boards/components/issue_due_date_spec.js4
-rw-r--r--spec/javascripts/bootstrap_linked_tabs_spec.js4
-rw-r--r--spec/javascripts/ci_variable_list/ajax_variable_list_spec.js8
-rw-r--r--spec/javascripts/ci_variable_list/ci_variable_list_spec.js16
-rw-r--r--spec/javascripts/ci_variable_list/native_form_variable_list_spec.js4
-rw-r--r--spec/javascripts/clusters/clusters_bundle_spec.js84
-rw-r--r--spec/javascripts/clusters/components/application_row_spec.js10
-rw-r--r--spec/javascripts/clusters/components/applications_spec.js195
-rw-r--r--spec/javascripts/clusters/services/mock_data.js14
-rw-r--r--spec/javascripts/clusters/stores/clusters_store_spec.js3
-rw-r--r--spec/javascripts/collapsed_sidebar_todo_spec.js2
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/create_item_dropdown_spec.js4
-rw-r--r--spec/javascripts/diffs/components/app_spec.js379
-rw-r--r--spec/javascripts/diffs/components/changed_files_dropdown_spec.js1
-rw-r--r--spec/javascripts/diffs/components/compare_versions_dropdown_spec.js153
-rw-r--r--spec/javascripts/diffs/components/diff_content_spec.js2
-rw-r--r--spec/javascripts/diffs/components/diff_file_header_spec.js148
-rw-r--r--spec/javascripts/diffs/components/diff_file_spec.js27
-rw-r--r--spec/javascripts/diffs/components/edit_button_spec.js1
-rw-r--r--spec/javascripts/diffs/components/hidden_files_warning_spec.js1
-rw-r--r--spec/javascripts/diffs/components/inline_diff_view_spec.js2
-rw-r--r--spec/javascripts/diffs/components/parallel_diff_view_spec.js6
-rw-r--r--spec/javascripts/diffs/mock_data/diff_discussions.js3
-rw-r--r--spec/javascripts/diffs/store/actions_spec.js171
-rw-r--r--spec/javascripts/diffs/store/getters_spec.js20
-rw-r--r--spec/javascripts/diffs/store/mutations_spec.js80
-rw-r--r--spec/javascripts/diffs/store/utils_spec.js45
-rw-r--r--spec/javascripts/dirty_submit/dirty_submit_form_spec.js2
-rw-r--r--spec/javascripts/emoji_spec.js189
-rw-r--r--spec/javascripts/environments/confirm_rollback_modal_spec.js70
-rw-r--r--spec/javascripts/environments/environment_item_spec.js20
-rw-r--r--spec/javascripts/environments/environment_rollback_spec.js32
-rw-r--r--spec/javascripts/environments/environment_table_spec.js234
-rw-r--r--spec/javascripts/environments/environments_app_spec.js7
-rw-r--r--spec/javascripts/environments/environments_store_spec.js74
-rw-r--r--spec/javascripts/environments/folder/environments_folder_view_spec.js16
-rw-r--r--spec/javascripts/error_tracking/components/error_tracking_list_spec.js100
-rw-r--r--spec/javascripts/error_tracking_settings/components/app_spec.js63
-rw-r--r--spec/javascripts/error_tracking_settings/components/error_tracking_form_spec.js91
-rw-r--r--spec/javascripts/error_tracking_settings/components/project_dropdown_spec.js109
-rw-r--r--spec/javascripts/error_tracking_settings/mock.js92
-rw-r--r--spec/javascripts/error_tracking_settings/store/actions_spec.js191
-rw-r--r--spec/javascripts/error_tracking_settings/store/getters_spec.js93
-rw-r--r--spec/javascripts/error_tracking_settings/store/mutation_spec.js82
-rw-r--r--spec/javascripts/error_tracking_settings/utils_spec.js29
-rw-r--r--spec/javascripts/filtered_search/dropdown_user_spec.js2
-rw-r--r--spec/javascripts/filtered_search/dropdown_utils_spec.js2
-rw-r--r--spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js339
-rw-r--r--spec/javascripts/filtered_search/visual_token_value_spec.js383
-rw-r--r--spec/javascripts/fixtures/.gitignore1
-rw-r--r--spec/javascripts/fixtures/abuse_reports.rb2
-rw-r--r--spec/javascripts/fixtures/admin_users.rb2
-rw-r--r--spec/javascripts/fixtures/ajax_loading_spinner.html.haml2
-rw-r--r--spec/javascripts/fixtures/application_settings.rb2
-rw-r--r--spec/javascripts/fixtures/autocomplete_sources.rb40
-rw-r--r--spec/javascripts/fixtures/balsamiq_viewer.html.haml1
-rw-r--r--spec/javascripts/fixtures/blob.rb3
-rw-r--r--spec/javascripts/fixtures/boards.rb2
-rw-r--r--spec/javascripts/fixtures/branches.rb2
-rw-r--r--spec/javascripts/fixtures/clusters.rb2
-rw-r--r--spec/javascripts/fixtures/commit.rb3
-rw-r--r--spec/javascripts/fixtures/create_item_dropdown.html.haml13
-rw-r--r--spec/javascripts/fixtures/deploy_keys.rb2
-rw-r--r--spec/javascripts/fixtures/event_filter.html.haml25
-rw-r--r--spec/javascripts/fixtures/gl_dropdown.html.haml17
-rw-r--r--spec/javascripts/fixtures/gl_field_errors.html.haml15
-rw-r--r--spec/javascripts/fixtures/groups.rb6
-rw-r--r--spec/javascripts/fixtures/issuable_filter.html.haml8
-rw-r--r--spec/javascripts/fixtures/issue_sidebar_label.html.haml16
-rw-r--r--spec/javascripts/fixtures/issues.rb12
-rw-r--r--spec/javascripts/fixtures/jobs.rb2
-rw-r--r--spec/javascripts/fixtures/line_highlighter.html.haml11
-rw-r--r--spec/javascripts/fixtures/linked_tabs.html.haml13
-rw-r--r--spec/javascripts/fixtures/merge_requests.rb17
-rw-r--r--spec/javascripts/fixtures/merge_requests_show.html.haml13
-rw-r--r--spec/javascripts/fixtures/mini_dropdown_graph.html.haml10
-rw-r--r--spec/javascripts/fixtures/notebook_viewer.html.haml1
-rw-r--r--spec/javascripts/fixtures/oauth_remember_me.html.haml6
-rw-r--r--spec/javascripts/fixtures/pdf_viewer.html.haml1
-rw-r--r--spec/javascripts/fixtures/pipeline_graph.html.haml14
-rw-r--r--spec/javascripts/fixtures/pipeline_schedules.rb4
-rw-r--r--spec/javascripts/fixtures/pipelines.html.haml12
-rw-r--r--spec/javascripts/fixtures/project_select_combo_button.html.haml6
-rw-r--r--spec/javascripts/fixtures/projects.rb22
-rw-r--r--spec/javascripts/fixtures/prometheus_service.rb2
-rw-r--r--spec/javascripts/fixtures/search.rb2
-rw-r--r--spec/javascripts/fixtures/search_autocomplete.html.haml9
-rw-r--r--spec/javascripts/fixtures/services.rb2
-rw-r--r--spec/javascripts/fixtures/sessions.rb2
-rw-r--r--spec/javascripts/fixtures/signin_tabs.html.haml5
-rw-r--r--spec/javascripts/fixtures/sketch_viewer.html.haml2
-rw-r--r--spec/javascripts/fixtures/snippet.rb6
-rw-r--r--spec/javascripts/fixtures/static/README.md3
-rw-r--r--spec/javascripts/fixtures/static/ajax_loading_spinner.html3
-rw-r--r--spec/javascripts/fixtures/static/balsamiq_viewer.html1
-rw-r--r--spec/javascripts/fixtures/static/create_item_dropdown.html11
-rw-r--r--spec/javascripts/fixtures/static/event_filter.html44
-rw-r--r--spec/javascripts/fixtures/static/gl_dropdown.html26
-rw-r--r--spec/javascripts/fixtures/static/gl_field_errors.html22
-rw-r--r--spec/javascripts/fixtures/static/issuable_filter.html9
-rw-r--r--spec/javascripts/fixtures/static/issue_sidebar_label.html26
-rw-r--r--spec/javascripts/fixtures/static/line_highlighter.html107
-rw-r--r--spec/javascripts/fixtures/static/linked_tabs.html20
-rw-r--r--spec/javascripts/fixtures/static/merge_requests_show.html15
-rw-r--r--spec/javascripts/fixtures/static/mini_dropdown_graph.html13
-rw-r--r--spec/javascripts/fixtures/static/notebook_viewer.html1
-rw-r--r--spec/javascripts/fixtures/static/oauth_remember_me.html6
-rw-r--r--spec/javascripts/fixtures/static/pdf_viewer.html1
-rw-r--r--spec/javascripts/fixtures/static/pipeline_graph.html24
-rw-r--r--spec/javascripts/fixtures/static/pipelines.html3
-rw-r--r--spec/javascripts/fixtures/static/project_select_combo_button.html9
-rw-r--r--spec/javascripts/fixtures/static/search_autocomplete.html15
-rw-r--r--spec/javascripts/fixtures/static/signin_tabs.html8
-rw-r--r--spec/javascripts/fixtures/static/sketch_viewer.html3
-rw-r--r--spec/javascripts/fixtures/static_fixtures.rb22
-rw-r--r--spec/javascripts/fixtures/todos.rb2
-rw-r--r--spec/javascripts/fixtures/u2f.rb7
-rw-r--r--spec/javascripts/frequent_items/components/app_spec.js2
-rw-r--r--spec/javascripts/gfm_auto_complete_spec.js244
-rw-r--r--spec/javascripts/gl_dropdown_spec.js4
-rw-r--r--spec/javascripts/gl_field_errors_spec.js4
-rw-r--r--spec/javascripts/groups/components/app_spec.js2
-rw-r--r--spec/javascripts/header_spec.js2
-rw-r--r--spec/javascripts/helpers/filtered_search_spec_helper.js2
-rw-r--r--spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js7
-rw-r--r--spec/javascripts/ide/components/new_dropdown/modal_spec.js67
-rw-r--r--spec/javascripts/ide/stores/actions/merge_request_spec.js93
-rw-r--r--spec/javascripts/ide/stores/actions/project_spec.js1
-rw-r--r--spec/javascripts/ide/stores/actions/tree_spec.js7
-rw-r--r--spec/javascripts/ide/stores/actions_spec.js28
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js2
-rw-r--r--spec/javascripts/ide/stores/modules/commit/getters_spec.js8
-rw-r--r--spec/javascripts/ide/stores/mutations_spec.js14
-rw-r--r--spec/javascripts/import_projects/components/import_projects_table_spec.js8
-rw-r--r--spec/javascripts/import_projects/components/imported_project_table_row_spec.js3
-rw-r--r--spec/javascripts/import_projects/components/provider_repo_table_row_spec.js20
-rw-r--r--spec/javascripts/integrations/integration_settings_form_spec.js2
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js4
-rw-r--r--spec/javascripts/issue_show/components/description_spec.js4
-rw-r--r--spec/javascripts/issue_spec.js10
-rw-r--r--spec/javascripts/jobs/components/commit_block_spec.js3
-rw-r--r--spec/javascripts/jobs/components/stages_dropdown_spec.js188
-rw-r--r--spec/javascripts/jobs/store/getters_spec.js188
-rw-r--r--spec/javascripts/labels_issue_sidebar_spec.js4
-rw-r--r--spec/javascripts/lazy_loader_spec.js6
-rw-r--r--spec/javascripts/lib/utils/ajax_cache_spec.js184
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js7
-rw-r--r--spec/javascripts/lib/utils/number_utility_spec.js90
-rw-r--r--spec/javascripts/lib/utils/poll_spec.js32
-rw-r--r--spec/javascripts/line_highlighter_spec.js4
-rw-r--r--spec/javascripts/merge_request_spec.js8
-rw-r--r--spec/javascripts/merge_request_tabs_spec.js6
-rw-r--r--spec/javascripts/mini_pipeline_graph_dropdown_spec.js4
-rw-r--r--spec/javascripts/monitoring/charts/area_spec.js66
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js24
-rw-r--r--spec/javascripts/new_branch_spec.js4
-rw-r--r--spec/javascripts/notes/components/discussion_filter_note_spec.js93
-rw-r--r--spec/javascripts/notes/components/discussion_filter_spec.js22
-rw-r--r--spec/javascripts/notes/components/discussion_jump_to_next_button_spec.js33
-rw-r--r--spec/javascripts/notes/components/discussion_resolve_with_issue_button_spec.js31
-rw-r--r--spec/javascripts/notes/components/note_app_spec.js17
-rw-r--r--spec/javascripts/notes/components/note_form_spec.js241
-rw-r--r--spec/javascripts/notes/components/noteable_discussion_spec.js53
-rw-r--r--spec/javascripts/notes/stores/getters_spec.js8
-rw-r--r--spec/javascripts/notes/stores/mutation_spec.js17
-rw-r--r--spec/javascripts/notes_spec.js2
-rw-r--r--spec/javascripts/oauth_remember_me_spec.js4
-rw-r--r--spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js2
-rw-r--r--spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js2
-rw-r--r--spec/javascripts/pages/admin/users/new/index_spec.js2
-rw-r--r--spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js4
-rw-r--r--spec/javascripts/persistent_user_callout_spec.js88
-rw-r--r--spec/javascripts/pipelines/graph/stage_column_component_spec.js4
-rw-r--r--spec/javascripts/pipelines/pipeline_url_spec.js12
-rw-r--r--spec/javascripts/pipelines/pipelines_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js4
-rw-r--r--spec/javascripts/pipelines_spec.js4
-rw-r--r--spec/javascripts/project_select_combo_button_spec.js2
-rw-r--r--spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js2
-rw-r--r--spec/javascripts/read_more_spec.js2
-rw-r--r--spec/javascripts/registry/components/app_spec.js2
-rw-r--r--spec/javascripts/reports/components/grouped_test_reports_app_spec.js10
-rw-r--r--spec/javascripts/right_sidebar_spec.js2
-rw-r--r--spec/javascripts/search_autocomplete_spec.js4
-rw-r--r--spec/javascripts/search_spec.js2
-rw-r--r--spec/javascripts/settings_panels_spec.js4
-rw-r--r--spec/javascripts/shortcuts_spec.js2
-rw-r--r--spec/javascripts/sidebar/assignees_spec.js13
-rw-r--r--spec/javascripts/sidebar/sidebar_assignees_spec.js4
-rw-r--r--spec/javascripts/signin_tabs_memoizer_spec.js2
-rw-r--r--spec/javascripts/test_bundle.js73
-rw-r--r--spec/javascripts/todos_spec.js4
-rw-r--r--spec/javascripts/u2f/authenticate_spec.js4
-rw-r--r--spec/javascripts/u2f/register_spec.js4
-rw-r--r--spec/javascripts/user_popovers_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/deployment_spec.js81
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js83
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_commits_header_spec.js110
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js2
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js163
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js11
-rw-r--r--spec/javascripts/vue_shared/components/commit_spec.js90
-rw-r--r--spec/javascripts/vue_shared/components/file_icon_spec.js7
-rw-r--r--spec/javascripts/vue_shared/components/header_ci_component_spec.js2
-rw-r--r--spec/javascripts/vue_shared/components/issue/related_issuable_item_spec.js194
-rw-r--r--spec/javascripts/vue_shared/components/issue/related_issuable_mock_data.js111
-rw-r--r--spec/javascripts/vue_shared/components/markdown/field_spec.js8
-rw-r--r--spec/javascripts/vue_shared/components/table_pagination_spec.js194
-rw-r--r--spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js2
-rw-r--r--spec/javascripts/zen_mode_spec.js2
-rw-r--r--spec/lib/api/helpers/custom_validators_spec.rb23
-rw-r--r--spec/lib/api/helpers/pagination_spec.rb155
-rw-r--r--spec/lib/backup/uploads_spec.rb18
-rw-r--r--spec/lib/banzai/filter/external_issue_reference_filter_spec.rb36
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb25
-rw-r--r--spec/lib/banzai/filter/output_safety_spec.rb29
-rw-r--r--spec/lib/banzai/filter/suggestion_filter_spec.rb33
-rw-r--r--spec/lib/banzai/filter/syntax_highlight_filter_spec.rb32
-rw-r--r--spec/lib/bitbucket_server/client_spec.rb4
-rw-r--r--spec/lib/constraints/project_url_constrainer_spec.rb4
-rw-r--r--spec/lib/event_filter_spec.rb8
-rw-r--r--spec/lib/gitlab/auth/ldap/config_spec.rb153
-rw-r--r--spec/lib/gitlab/auth/o_auth/user_spec.rb11
-rw-r--r--spec/lib/gitlab/authorized_keys_spec.rb194
-rw-r--r--spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb6
-rw-r--r--spec/lib/gitlab/background_migration/digest_column_spec.rb4
-rw-r--r--spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb56
-rw-r--r--spec/lib/gitlab/badge/pipeline/template_spec.rb10
-rw-r--r--spec/lib/gitlab/bitbucket_import/importer_spec.rb20
-rw-r--r--spec/lib/gitlab/chat/command_spec.rb77
-rw-r--r--spec/lib/gitlab/chat/output_spec.rb101
-rw-r--r--spec/lib/gitlab/chat/responder/base_spec.rb48
-rw-r--r--spec/lib/gitlab/chat/responder/slack_spec.rb77
-rw-r--r--spec/lib/gitlab/chat/responder_spec.rb32
-rw-r--r--spec/lib/gitlab/chat_spec.rb23
-rw-r--r--spec/lib/gitlab/checks/branch_check_spec.rb146
-rw-r--r--spec/lib/gitlab/ci/build/policy/changes_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/build/policy/refs_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/build/policy/variables_spec.rb21
-rw-r--r--spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb34
-rw-r--r--spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb85
-rw-r--r--spec/lib/gitlab/ci/config/entry/global_spec.rb6
-rw-r--r--spec/lib/gitlab/ci/config/external/file/base_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/config/external/file/local_spec.rb36
-rw-r--r--spec/lib/gitlab/ci/config/external/file/project_spec.rb47
-rw-r--r--spec/lib/gitlab/ci/config/external/file/remote_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/config/external/file/template_spec.rb35
-rw-r--r--spec/lib/gitlab/ci/config/external/mapper_spec.rb35
-rw-r--r--spec/lib/gitlab/ci/config/external/processor_spec.rb82
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/build_spec.rb11
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/command_spec.rb48
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb33
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/preparing_spec.rb33
-rw-r--r--spec/lib/gitlab/ci/status/preparing_spec.rb29
-rw-r--r--spec/lib/gitlab/ci/templates/templates_spec.rb37
-rw-r--r--spec/lib/gitlab/ci/variables/collection/item_spec.rb20
-rw-r--r--spec/lib/gitlab/ci/variables/collection_spec.rb12
-rw-r--r--spec/lib/gitlab/ci/yaml_processor_spec.rb79
-rw-r--r--spec/lib/gitlab/current_settings_spec.rb2
-rw-r--r--spec/lib/gitlab/danger/helper_spec.rb3
-rw-r--r--spec/lib/gitlab/danger/teammate_spec.rb39
-rw-r--r--spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb19
-rw-r--r--spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb16
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb2
-rw-r--r--spec/lib/gitlab/database_spec.rb52
-rw-r--r--spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb4
-rw-r--r--spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb9
-rw-r--r--spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb4
-rw-r--r--spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb18
-rw-r--r--spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb44
-rw-r--r--spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb5
-rw-r--r--spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb4
-rw-r--r--spec/lib/gitlab/diff/file_spec.rb208
-rw-r--r--spec/lib/gitlab/diff/suggestion_diff_spec.rb55
-rw-r--r--spec/lib/gitlab/diff/suggestion_spec.rb88
-rw-r--r--spec/lib/gitlab/diff/suggestions_parser_spec.rb73
-rw-r--r--spec/lib/gitlab/fake_application_settings_spec.rb31
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb16
-rw-r--r--spec/lib/gitlab/git/commit_spec.rb28
-rw-r--r--spec/lib/gitlab/git/pre_receive_error_spec.rb16
-rw-r--r--spec/lib/gitlab/git/repository_cleaner_spec.rb12
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb50
-rw-r--r--spec/lib/gitlab/git/tree_spec.rb74
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb10
-rw-r--r--spec/lib/gitlab/gitaly_client/ref_service_spec.rb10
-rw-r--r--spec/lib/gitlab/gitaly_client/storage_settings_spec.rb10
-rw-r--r--spec/lib/gitlab/gitaly_client_spec.rb38
-rw-r--r--spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb24
-rw-r--r--spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb75
-rw-r--r--spec/lib/gitlab/github_import/importer/repository_importer_spec.rb11
-rw-r--r--spec/lib/gitlab/gl_repository/repo_type_spec.rb64
-rw-r--r--spec/lib/gitlab/gl_repository_spec.rb4
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb18
-rw-r--r--spec/lib/gitlab/graphql/authorize_spec.rb20
-rw-r--r--spec/lib/gitlab/group_search_results_spec.rb69
-rw-r--r--spec/lib/gitlab/hashed_storage/migrator_spec.rb166
-rw-r--r--spec/lib/gitlab/import_export/all_models.yml8
-rw-r--r--spec/lib/gitlab/import_export/merge_request_parser_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/project.json20
-rw-r--r--spec/lib/gitlab/import_export/project_tree_saver_spec.rb8
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml13
-rw-r--r--spec/lib/gitlab/issuable_metadata_spec.rb8
-rw-r--r--spec/lib/gitlab/json_cache_spec.rb82
-rw-r--r--spec/lib/gitlab/kubernetes/helm/pod_spec.rb2
-rw-r--r--spec/lib/gitlab/kubernetes/kube_client_spec.rb30
-rw-r--r--spec/lib/gitlab/kubernetes_spec.rb34
-rw-r--r--spec/lib/gitlab/middleware/basic_health_check_spec.rb29
-rw-r--r--spec/lib/gitlab/path_regex_spec.rb2
-rw-r--r--spec/lib/gitlab/profiler_spec.rb8
-rw-r--r--spec/lib/gitlab/project_search_results_spec.rb32
-rw-r--r--spec/lib/gitlab/project_template_spec.rb11
-rw-r--r--spec/lib/gitlab/prometheus/queries/matched_metric_query_spec.rb4
-rw-r--r--spec/lib/gitlab/quick_actions/command_definition_spec.rb7
-rw-r--r--spec/lib/gitlab/reference_extractor_spec.rb2
-rw-r--r--spec/lib/gitlab/repo_path_spec.rb20
-rw-r--r--spec/lib/gitlab/request_context_spec.rb27
-rw-r--r--spec/lib/gitlab/search_results_spec.rb16
-rw-r--r--spec/lib/gitlab/serializer/pagination_spec.rb10
-rw-r--r--spec/lib/gitlab/shell_spec.rb585
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb74
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb88
-rw-r--r--spec/lib/gitlab/sidekiq_signals_spec.rb69
-rw-r--r--spec/lib/gitlab/slash_commands/application_help_spec.rb19
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/error_spec.rb15
-rw-r--r--spec/lib/gitlab/slash_commands/presenters/run_spec.rb79
-rw-r--r--spec/lib/gitlab/slash_commands/run_spec.rb123
-rw-r--r--spec/lib/gitlab/tracing_spec.rb15
-rw-r--r--spec/lib/gitlab/usage_data_spec.rb6
-rw-r--r--spec/lib/gitlab/user_extractor_spec.rb20
-rw-r--r--spec/lib/gitlab/utils_spec.rb18
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb10
-rw-r--r--spec/lib/gitlab_spec.rb14
-rw-r--r--spec/lib/google_api/cloud_platform/client_spec.rb12
-rw-r--r--spec/lib/object_storage/direct_upload_spec.rb2
-rw-r--r--spec/lib/sentry/client_spec.rb2
-rw-r--r--spec/mailers/abuse_report_mailer_spec.rb25
-rw-r--r--spec/mailers/email_rejection_mailer_spec.rb16
-rw-r--r--spec/mailers/emails/auto_devops_spec.rb3
-rw-r--r--spec/mailers/emails/issues_spec.rb9
-rw-r--r--spec/mailers/notify_spec.rb162
-rw-r--r--spec/mailers/repository_check_mailer_spec.rb7
-rw-r--r--spec/migrations/add_foreign_keys_to_todos_spec.rb2
-rw-r--r--spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb24
-rw-r--r--spec/migrations/calculate_conv_dev_index_percentages_spec.rb22
-rw-r--r--spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb19
-rw-r--r--spec/migrations/delete_inconsistent_internal_id_records_spec.rb83
-rw-r--r--spec/migrations/issues_moved_to_id_foreign_key_spec.rb15
-rw-r--r--spec/migrations/migrate_old_artifacts_spec.rb32
-rw-r--r--spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb6
-rw-r--r--spec/migrations/migrate_user_project_view_spec.rb6
-rw-r--r--spec/migrations/move_personal_snippets_files_spec.rb35
-rw-r--r--spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb47
-rw-r--r--spec/models/active_session_spec.rb5
-rw-r--r--spec/models/appearance_spec.rb33
-rw-r--r--spec/models/application_setting_spec.rb232
-rw-r--r--spec/models/board_group_recent_visit_spec.rb22
-rw-r--r--spec/models/board_project_recent_visit_spec.rb22
-rw-r--r--spec/models/broadcast_message_spec.rb6
-rw-r--r--spec/models/ci/bridge_spec.rb15
-rw-r--r--spec/models/ci/build_spec.rb380
-rw-r--r--spec/models/ci/build_trace_chunk_spec.rb4
-rw-r--r--spec/models/ci/group_variable_spec.rb1
-rw-r--r--spec/models/ci/pipeline_spec.rb541
-rw-r--r--spec/models/ci/variable_spec.rb1
-rw-r--r--spec/models/clusters/applications/ingress_spec.rb8
-rw-r--r--spec/models/clusters/applications/jupyter_spec.rb12
-rw-r--r--spec/models/clusters/applications/knative_spec.rb34
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb59
-rw-r--r--spec/models/clusters/applications/runner_spec.rb47
-rw-r--r--spec/models/clusters/cluster_spec.rb1
-rw-r--r--spec/models/clusters/kubernetes_namespace_spec.rb2
-rw-r--r--spec/models/clusters/platforms/kubernetes_spec.rb32
-rw-r--r--spec/models/commit_collection_spec.rb86
-rw-r--r--spec/models/commit_spec.rb16
-rw-r--r--spec/models/commit_status_spec.rb23
-rw-r--r--spec/models/concerns/cache_markdown_field_spec.rb14
-rw-r--r--spec/models/concerns/has_ref_spec.rb20
-rw-r--r--spec/models/concerns/has_status_spec.rb22
-rw-r--r--spec/models/concerns/has_variable_spec.rb2
-rw-r--r--spec/models/concerns/issuable_spec.rb97
-rw-r--r--spec/models/concerns/maskable_spec.rb76
-rw-r--r--spec/models/concerns/milestoneish_spec.rb142
-rw-r--r--spec/models/concerns/sortable_spec.rb2
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb235
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/base_spec.rb32
-rw-r--r--spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb28
-rw-r--r--spec/models/deployment_spec.rb28
-rw-r--r--spec/models/diff_note_spec.rb10
-rw-r--r--spec/models/environment_spec.rb22
-rw-r--r--spec/models/environment_status_spec.rb4
-rw-r--r--spec/models/error_tracking/project_error_tracking_setting_spec.rb53
-rw-r--r--spec/models/group_spec.rb121
-rw-r--r--spec/models/issue/metrics_spec.rb6
-rw-r--r--spec/models/issue_spec.rb9
-rw-r--r--spec/models/merge_request_diff_spec.rb35
-rw-r--r--spec/models/merge_request_spec.rb167
-rw-r--r--spec/models/milestone_spec.rb23
-rw-r--r--spec/models/namespace_spec.rb24
-rw-r--r--spec/models/note_spec.rb18
-rw-r--r--spec/models/project_daily_statistic_spec.rb7
-rw-r--r--spec/models/project_services/jira_service_spec.rb3
-rw-r--r--spec/models/project_services/kubernetes_service_spec.rb14
-rw-r--r--spec/models/project_services/prometheus_service_spec.rb61
-rw-r--r--spec/models/project_services/slack_slash_commands_service_spec.rb7
-rw-r--r--spec/models/project_services/youtrack_service_spec.rb38
-rw-r--r--spec/models/project_spec.rb341
-rw-r--r--spec/models/project_wiki_spec.rb8
-rw-r--r--spec/models/prometheus_metric_spec.rb13
-rw-r--r--spec/models/protected_branch_spec.rb28
-rw-r--r--spec/models/repository_spec.rb99
-rw-r--r--spec/models/user_spec.rb76
-rw-r--r--spec/policies/commit_policy_spec.rb55
-rw-r--r--spec/policies/global_policy_spec.rb12
-rw-r--r--spec/policies/group_policy_spec.rb31
-rw-r--r--spec/policies/identity_provider_policy_spec.rb30
-rw-r--r--spec/policies/issuable_policy_spec.rb6
-rw-r--r--spec/policies/merge_request_policy_spec.rb50
-rw-r--r--spec/policies/note_policy_spec.rb94
-rw-r--r--spec/policies/project_policy_spec.rb24
-rw-r--r--spec/presenters/blobs/unfold_presenter_spec.rb159
-rw-r--r--spec/presenters/ci/build_runner_presenter_spec.rb68
-rw-r--r--spec/presenters/ci/pipeline_presenter_spec.rb134
-rw-r--r--spec/presenters/group_clusterable_presenter_spec.rb8
-rw-r--r--spec/presenters/merge_request_presenter_spec.rb93
-rw-r--r--spec/presenters/project_clusterable_presenter_spec.rb8
-rw-r--r--spec/rack_servers/puma_spec.rb8
-rw-r--r--spec/requests/api/commits_spec.rb15
-rw-r--r--spec/requests/api/graphql/metadata_query_spec.rb32
-rw-r--r--spec/requests/api/graphql/project/merge_request_spec.rb4
-rw-r--r--spec/requests/api/graphql_spec.rb86
-rw-r--r--spec/requests/api/group_variables_spec.rb4
-rw-r--r--spec/requests/api/internal_spec.rb33
-rw-r--r--spec/requests/api/issues_spec.rb266
-rw-r--r--spec/requests/api/jobs_spec.rb43
-rw-r--r--spec/requests/api/keys_spec.rb2
-rw-r--r--spec/requests/api/merge_request_diffs_spec.rb4
-rw-r--r--spec/requests/api/merge_requests_spec.rb733
-rw-r--r--spec/requests/api/namespaces_spec.rb2
-rw-r--r--spec/requests/api/project_clusters_spec.rb1
-rw-r--r--spec/requests/api/project_milestones_spec.rb74
-rw-r--r--spec/requests/api/project_statistics_spec.rb62
-rw-r--r--spec/requests/api/project_templates_spec.rb28
-rw-r--r--spec/requests/api/projects_spec.rb161
-rw-r--r--spec/requests/api/release/links_spec.rb16
-rw-r--r--spec/requests/api/runner_spec.rb112
-rw-r--r--spec/requests/api/runners_spec.rb45
-rw-r--r--spec/requests/api/search_spec.rb83
-rw-r--r--spec/requests/api/snippets_spec.rb76
-rw-r--r--spec/requests/api/suggestions_spec.rb3
-rw-r--r--spec/requests/api/todos_spec.rb52
-rw-r--r--spec/requests/api/users_spec.rb48
-rw-r--r--spec/requests/api/variables_spec.rb4
-rw-r--r--spec/requests/api/version_spec.rb18
-rw-r--r--spec/requests/git_http_spec.rb134
-rw-r--r--spec/routing/api_routing_spec.rb14
-rw-r--r--spec/routing/group_routing_spec.rb4
-rw-r--r--spec/serializers/environment_serializer_spec.rb9
-rw-r--r--spec/serializers/merge_request_for_pipeline_entity_spec.rb29
-rw-r--r--spec/serializers/merge_request_widget_entity_spec.rb15
-rw-r--r--spec/serializers/pipeline_entity_spec.rb62
-rw-r--r--spec/serializers/pipeline_serializer_spec.rb54
-rw-r--r--spec/serializers/provider_repo_entity_spec.rb2
-rw-r--r--spec/serializers/suggestion_entity_spec.rb4
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb93
-rw-r--r--spec/services/boards/visits/latest_service_spec.rb12
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb38
-rw-r--r--spec/services/ci/destroy_pipeline_service_spec.rb15
-rw-r--r--spec/services/ci/prepare_build_service_spec.rb54
-rw-r--r--spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb10
-rw-r--r--spec/services/clusters/applications/create_service_spec.rb222
-rw-r--r--spec/services/clusters/applications/patch_service_spec.rb128
-rw-r--r--spec/services/clusters/applications/schedule_installation_service_spec.rb77
-rw-r--r--spec/services/clusters/applications/update_service_spec.rb72
-rw-r--r--spec/services/emails/create_service_spec.rb2
-rw-r--r--spec/services/emails/destroy_service_spec.rb2
-rw-r--r--spec/services/error_tracking/list_issues_service_spec.rb26
-rw-r--r--spec/services/error_tracking/list_projects_service_spec.rb8
-rw-r--r--spec/services/files/multi_service_spec.rb16
-rw-r--r--spec/services/git/branch_push_service_spec.rb837
-rw-r--r--spec/services/git/tag_push_service_spec.rb196
-rw-r--r--spec/services/git_push_service_spec.rb837
-rw-r--r--spec/services/git_tag_push_service_spec.rb196
-rw-r--r--spec/services/groups/auto_devops_service_spec.rb62
-rw-r--r--spec/services/groups/transfer_service_spec.rb29
-rw-r--r--spec/services/issuable/common_system_notes_service_spec.rb4
-rw-r--r--spec/services/issues/build_service_spec.rb76
-rw-r--r--spec/services/issues/update_service_spec.rb6
-rw-r--r--spec/services/merge_requests/build_service_spec.rb9
-rw-r--r--spec/services/merge_requests/create_service_spec.rb4
-rw-r--r--spec/services/merge_requests/ff_merge_service_spec.rb2
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb14
-rw-r--r--spec/services/merge_requests/merge_to_ref_service_spec.rb195
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb42
-rw-r--r--spec/services/merge_requests/update_service_spec.rb6
-rw-r--r--spec/services/notes/create_service_spec.rb13
-rw-r--r--spec/services/notes/quick_actions_service_spec.rb24
-rw-r--r--spec/services/notification_service_spec.rb2
-rw-r--r--spec/services/projects/create_service_spec.rb1
-rw-r--r--spec/services/projects/destroy_service_spec.rb6
-rw-r--r--spec/services/projects/fetch_statistics_increment_service_spec.rb36
-rw-r--r--spec/services/projects/group_links/create_service_spec.rb8
-rw-r--r--spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb12
-rw-r--r--spec/services/projects/hashed_storage/migrate_repository_service_spec.rb18
-rw-r--r--spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb106
-rw-r--r--spec/services/projects/hashed_storage/rollback_repository_service_spec.rb117
-rw-r--r--spec/services/projects/hashed_storage/rollback_service_spec.rb57
-rw-r--r--spec/services/projects/operations/update_service_spec.rb50
-rw-r--r--spec/services/projects/transfer_service_spec.rb1
-rw-r--r--spec/services/prometheus/adapter_service_spec.rb10
-rw-r--r--spec/services/quick_actions/interpret_service_spec.rb10
-rw-r--r--spec/services/releases/destroy_service_spec.rb8
-rw-r--r--spec/services/suggestions/apply_service_spec.rb102
-rw-r--r--spec/services/suggestions/create_service_spec.rb94
-rw-r--r--spec/services/suggestions/outdate_service_spec.rb102
-rw-r--r--spec/services/system_note_service_spec.rb9
-rw-r--r--spec/services/tags/create_service_spec.rb2
-rw-r--r--spec/services/tags/destroy_service_spec.rb16
-rw-r--r--spec/services/web_hook_service_spec.rb2
-rw-r--r--spec/spec_helper.rb9
-rw-r--r--spec/support/api/milestones_shared_examples.rb9
-rw-r--r--spec/support/api/schema_matcher.rb24
-rw-r--r--spec/support/api/time_tracking_shared_examples.rb2
-rw-r--r--spec/support/database_cleaner.rb56
-rw-r--r--spec/support/db_cleaner.rb50
-rw-r--r--spec/support/features/reportable_note_shared_examples.rb2
-rw-r--r--spec/support/features/variable_list_shared_examples.rb8
-rw-r--r--spec/support/helpers/cycle_analytics_helpers.rb2
-rw-r--r--spec/support/helpers/file_mover_helpers.rb12
-rw-r--r--spec/support/helpers/graphql_helpers.rb30
-rw-r--r--spec/support/helpers/javascript_fixtures_helpers.rb23
-rw-r--r--spec/support/helpers/kubernetes_helpers.rb31
-rw-r--r--spec/support/helpers/login_helpers.rb13
-rw-r--r--spec/support/helpers/repo_helpers.rb14
-rw-r--r--spec/support/helpers/stub_configuration.rb4
-rw-r--r--spec/support/helpers/stub_object_storage.rb12
-rw-r--r--spec/support/helpers/test_env.rb25
-rw-r--r--spec/support/import_export/export_file_helper.rb2
-rw-r--r--spec/support/matchers/access_matchers.rb35
-rw-r--r--spec/support/matchers/graphql_matchers.rb4
-rw-r--r--spec/support/matchers/issuable_matchers.rb2
-rw-r--r--spec/support/matchers/not_changed_matcher.rb3
-rw-r--r--spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb24
-rw-r--r--spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb44
-rw-r--r--spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb65
-rw-r--r--spec/support/shared_contexts/finders/users_finder_shared_contexts.rb8
-rw-r--r--spec/support/shared_contexts/services_shared_context.rb8
-rw-r--r--spec/support/shared_examples/application_setting_examples.rb252
-rw-r--r--spec/support/shared_examples/graphql/issuable_state_shared_examples.rb5
-rw-r--r--spec/support/shared_examples/issuable_shared_examples.rb2
-rw-r--r--spec/support/shared_examples/notify_shared_examples.rb28
-rw-r--r--spec/support/shared_examples/requests/api/discussions.rb31
-rw-r--r--spec/support/shared_examples/requests/api/merge_requests_list.rb335
-rw-r--r--spec/support/shared_examples/services/boards/issues_move_service.rb16
-rw-r--r--spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb8
-rw-r--r--spec/support/shared_examples/snippet_visibility.rb322
-rw-r--r--spec/support/shared_examples/snippet_visibility_shared_examples.rb306
-rw-r--r--spec/support/shared_examples/views/nav_sidebar.rb11
-rw-r--r--spec/support/webmock.rb10
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb4
-rw-r--r--spec/tasks/gitlab/shell_rake_spec.rb2
-rw-r--r--spec/tasks/gitlab/storage_rake_spec.rb108
-rw-r--r--spec/uploaders/file_mover_spec.rb33
-rw-r--r--spec/validators/devise_email_validator_spec.rb94
-rw-r--r--spec/validators/sha_validator_spec.rb47
-rw-r--r--spec/views/ci/status/_icon.html.haml_spec.rb89
-rw-r--r--spec/views/groups/_home_panel.html.haml_spec.rb15
-rw-r--r--spec/views/layouts/_head.html.haml_spec.rb8
-rw-r--r--spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb2
-rw-r--r--spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb13
-rw-r--r--spec/views/layouts/nav/sidebar/_instance_statistics.html.haml_spec.rb7
-rw-r--r--spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb13
-rw-r--r--spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb2
-rw-r--r--spec/views/projects/commits/_commit.html.haml_spec.rb59
-rw-r--r--spec/views/projects/deployments/_confirm_rollback_modal_spec.html.rb63
-rw-r--r--spec/views/projects/issues/_merge_requests_status.html.haml_spec.rb6
-rw-r--r--spec/views/projects/issues/show.html.haml_spec.rb20
-rw-r--r--spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb1
-rw-r--r--spec/views/projects/settings/operations/show.html.haml_spec.rb2
-rw-r--r--spec/workers/build_finished_worker_spec.rb19
-rw-r--r--spec/workers/chat_notification_worker_spec.rb91
-rw-r--r--spec/workers/ci/build_prepare_worker_spec.rb30
-rw-r--r--spec/workers/cluster_configure_worker_spec.rb16
-rw-r--r--spec/workers/cluster_project_configure_worker_spec.rb21
-rw-r--r--spec/workers/hashed_storage/project_migrate_worker_spec.rb48
-rw-r--r--spec/workers/hashed_storage/project_rollback_worker_spec.rb50
-rw-r--r--spec/workers/hashed_storage/rollbacker_worker_spec.rb27
-rw-r--r--spec/workers/post_receive_spec.rb26
-rw-r--r--spec/workers/project_daily_statistics_worker_spec.rb35
-rw-r--r--spec/workers/project_migrate_hashed_storage_worker_spec.rb48
-rw-r--r--spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb4
-rw-r--r--vendor/assets/javascripts/jquery.atwho.js1202
-rw-r--r--vendor/assets/javascripts/jquery.caret.js436
-rw-r--r--vendor/licenses.csv1
-rw-r--r--vendor/project_templates/android.tar.gzbin0 -> 132592 bytes
-rw-r--r--vendor/project_templates/dotnetcore.tar.gzbin0 -> 7027 bytes
-rw-r--r--vendor/project_templates/express.tar.gzbin4894 -> 16098 bytes
-rw-r--r--vendor/project_templates/gitbook.tar.gzbin13808 -> 13068 bytes
-rw-r--r--vendor/project_templates/gomicro.tar.gzbin0 -> 5388 bytes
-rw-r--r--vendor/project_templates/hexo.tar.gzbin548020 -> 547220 bytes
-rw-r--r--vendor/project_templates/hugo.tar.gzbin1048753 -> 1047952 bytes
-rw-r--r--vendor/project_templates/iosswift.tar.gzbin0 -> 2422200 bytes
-rw-r--r--vendor/project_templates/jekyll.tar.gzbin60703 -> 60086 bytes
-rw-r--r--vendor/project_templates/nfgitbook.tar.gzbin0 -> 122562 bytes
-rw-r--r--vendor/project_templates/nfhexo.tar.gzbin0 -> 655028 bytes
-rw-r--r--vendor/project_templates/nfhugo.tar.gzbin0 -> 1159251 bytes
-rw-r--r--vendor/project_templates/nfjekyll.tar.gzbin0 -> 132262 bytes
-rw-r--r--vendor/project_templates/nfplainhtml.tar.gzbin0 -> 122392 bytes
-rw-r--r--vendor/project_templates/plainhtml.tar.gzbin12079 -> 11389 bytes
-rw-r--r--vendor/project_templates/rails.tar.gzbin25182 -> 25490 bytes
-rw-r--r--vendor/project_templates/spring.tar.gzbin49476 -> 49518 bytes
-rw-r--r--yarn.lock2048
4727 files changed, 92031 insertions, 30866 deletions
diff --git a/.babelrc.js b/.babelrc.js
deleted file mode 100644
index 1b05a67354e..00000000000
--- a/.babelrc.js
+++ /dev/null
@@ -1,45 +0,0 @@
-const BABEL_ENV = process.env.BABEL_ENV || process.env.NODE_ENV || null;
-
-const presets = [
- [
- '@babel/preset-env',
- {
- modules: false,
- targets: {
- ie: '11',
- },
- },
- ],
-];
-
-// include stage 3 proposals
-const plugins = [
- '@babel/plugin-syntax-dynamic-import',
- '@babel/plugin-syntax-import-meta',
- '@babel/plugin-proposal-class-properties',
- '@babel/plugin-proposal-json-strings',
- '@babel/plugin-proposal-private-methods',
-];
-
-// add code coverage tooling if necessary
-if (BABEL_ENV === 'coverage') {
- plugins.push([
- 'babel-plugin-istanbul',
- {
- exclude: ['spec/javascripts/**/*', 'app/assets/javascripts/locale/**/app.js'],
- },
- ]);
-}
-
-// add rewire support when running tests
-if (BABEL_ENV === 'karma' || BABEL_ENV === 'coverage') {
- plugins.push('babel-plugin-rewire');
-}
-
-// Jest is running in node environment
-if (BABEL_ENV === 'jest') {
- plugins.push('transform-es2015-modules-commonjs');
- plugins.push('dynamic-import-node');
-}
-
-module.exports = { presets, plugins };
diff --git a/.eslintrc.yml b/.eslintrc.yml
index b0794bb7434..98a497aa12a 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -9,9 +9,6 @@ plugins:
- import
- html
settings:
- html/html-extensions:
- - '.html'
- - '.html.raw'
import/resolver:
webpack:
config: './config/webpack.config.js'
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c5cd006a289..33559812872 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,5 +1,8 @@
image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.5.3-golang-1.11-git-2.18-chrome-71.0-node-10.x-yarn-1.12-postgresql-9.6-graphicsmagick-1.3.29"
+include:
+ - local: /lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml
+
.dedicated-runner: &dedicated-runner
retry: 1
tags:
@@ -66,6 +69,7 @@ stages:
paths:
- knapsack/
- rspec_flaky/
+ - rspec_profiling/
.use-pg: &use-pg
services:
@@ -159,6 +163,7 @@ stages:
- coverage/
- knapsack/
- rspec_flaky/
+ - rspec_profiling/
- tmp/capybara/
reports:
junit: junit_rspec.xml
@@ -336,6 +341,7 @@ retrieve-tests-metadata:
- wget -O $KNAPSACK_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$KNAPSACK_RSPEC_SUITE_REPORT_PATH || rm $KNAPSACK_RSPEC_SUITE_REPORT_PATH
- '[[ -f $KNAPSACK_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${KNAPSACK_RSPEC_SUITE_REPORT_PATH}'
- mkdir -p rspec_flaky/
+ - mkdir -p rspec_profiling/
- wget -O $FLAKY_RSPEC_SUITE_REPORT_PATH http://${TESTS_METADATA_S3_BUCKET}.s3.amazonaws.com/$FLAKY_RSPEC_SUITE_REPORT_PATH || rm $FLAKY_RSPEC_SUITE_REPORT_PATH
- '[[ -f $FLAKY_RSPEC_SUITE_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_SUITE_REPORT_PATH}'
@@ -350,7 +356,7 @@ update-tests-metadata:
- rspec_flaky/
policy: push
script:
- - retry gem install fog-aws mime-types activesupport --no-document
+ - retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document
- scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json
- scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json
- FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH}
@@ -358,6 +364,7 @@ update-tests-metadata:
- '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $FLAKY_RSPEC_SUITE_REPORT_PATH'
- rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json
- rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json
+ - scripts/insert-rspec-profiling-data
flaky-examples-check:
<<: *dedicated-runner
@@ -444,6 +451,17 @@ setup-test-env:
- master
- /(^docs[\/-].*|.*-docs$)/
+.review-schedules-only: &review-schedules-only
+ only:
+ refs:
+ - schedules@gitlab-org/gitlab-ce
+ - schedules@gitlab-org/gitlab-ee
+ kubernetes: active
+ except:
+ refs:
+ - tags
+ - /(^docs[\/-].*|.*-docs$)/
+
.review-base: &review-base
<<: *dedicated-no-docs-no-db-pull-cache-job
<<: *review-only
@@ -473,6 +491,9 @@ setup-test-env:
build-qa-image:
<<: *review-docker
+ variables:
+ <<: *review-docker-variables
+ GIT_DEPTH: "20"
stage: prepare
script:
- time docker build --cache-from ${LATEST_QA_IMAGE} --tag ${QA_IMAGE} ./qa/
@@ -669,8 +690,38 @@ gitlab:assets:compile:
- //@gitlab/gitlabhq
- //@gitlab/gitlab-ee
tags:
- - gitlab-org-delivery
- - high-cpu
+ - docker
+ - gitlab-org
+
+gitlab:ui:visual:
+ tags:
+ - gitlab-org
+ before_script: []
+ allow_failure: true
+ dependencies:
+ - compile-assets
+ script:
+ # Remove node modules from GitLab that may conflict with gitlab-ui
+ - rm -r node_modules
+ - git clone https://gitlab.com/gitlab-org/gitlab-ui.git
+ - cp public/assets/application-*.css gitlab-ui/styles/application.css
+ - cd gitlab-ui
+ - yarn install
+ - CSS_URL=./application.css yarn test
+ only:
+ changes:
+ - app/assets/stylesheets/*.scss
+ - app/assets/stylesheets/**/*.scss
+ - app/assets/stylesheets/**/**/*.scss
+ except:
+ refs:
+ - /(^docs[\/-].*|.*-docs$)/
+ - master
+ variables:
+ - $CI_COMMIT_MESSAGE =~ /\[skip visual\]/i
+ artifacts:
+ paths:
+ - tests/__image_snapshots__/
karma:
<<: *dedicated-no-docs-pull-cache-job
@@ -726,31 +777,14 @@ jest:
code_quality:
<<: *dedicated-no-docs-no-db-pull-cache-job
- image: docker:stable
- allow_failure: true
# gitlab-org runners set `privileged: false` but we need to have it set to true
# since we're using Docker in Docker
tags: []
before_script: []
- services:
- - docker:stable-dind
- variables:
- SETUP_DB: "false"
- DOCKER_DRIVER: overlay2
cache: {}
dependencies: []
- script:
- # Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
- - 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:
- reports:
- codequality: gl-code-quality-report.json
- expire_in: 1 week
+ variables:
+ SETUP_DB: "false"
sast:
<<: *dedicated-no-docs-no-db-pull-cache-job
@@ -959,15 +993,13 @@ no_ee_check:
- //@gitlab-org/gitlab-ce
# GitLab Review apps
-review-build-cng:
- <<: *review-only
+.review-build-cng-base: &review-build-cng-base
image: ruby:2.5-alpine
stage: test
before_script: []
dependencies: []
cache: {}
variables:
- GIT_DEPTH: "1"
API_TOKEN: "${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}"
script:
- apk add --update openssl curl jq
@@ -976,7 +1008,15 @@ review-build-cng:
- wait_for_job_to_be_done "gitlab:assets:compile"
- BUILD_TRIGGER_TOKEN=$REVIEW_APPS_BUILD_TRIGGER_TOKEN ./scripts/trigger-build cng
-review-deploy:
+review-build-cng:
+ <<: *review-only
+ <<: *review-build-cng-base
+
+schedule:review-build-cng:
+ <<: *review-schedules-only
+ <<: *review-build-cng-base
+
+.review-deploy-base: &review-deploy-base
<<: *review-base
retry: 2
allow_failure: true
@@ -998,13 +1038,17 @@ review-deploy:
- source ./scripts/review_apps/review-apps.sh
script:
- wait_for_job_to_be_done "review-build-cng"
- - check_kube_domain
- - download_gitlab_chart
- - ensure_namespace
- - install_tiller
- - install_external_dns
- - time deploy
- - add_license
+ - perform_review_app_deployment
+
+review-deploy:
+ <<: *review-deploy-base
+
+schedule:review-deploy:
+ <<: *review-deploy-base
+ <<: *review-schedules-only
+ script:
+ - wait_for_job_to_be_done "schedule:review-build-cng"
+ - perform_review_app_deployment
.review-qa-base: &review-qa-base
<<: *review-docker
@@ -1032,19 +1076,39 @@ review-deploy:
- apk update && apk add curl jq
- source ./scripts/review_apps/review-apps.sh
- gem install gitlab-qa --no-document ${GITLAB_QA_VERSION:+ --version ${GITLAB_QA_VERSION}}
- - wait_for_job_to_be_done "review-deploy"
review-qa-smoke:
<<: *review-qa-base
script:
+ - wait_for_job_to_be_done "review-deploy"
- gitlab-qa Test::Instance::Smoke "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
review-qa-all:
<<: *review-qa-base
script:
+ - wait_for_job_to_be_done "review-deploy"
- gitlab-qa Test::Instance::Any "${QA_IMAGE}" "${CI_ENVIRONMENT_URL}"
when: manual
+
+.review-performance-base: &review-performance-base
+ <<: *review-qa-base
+ script:
+ - wait_for_job_to_be_done "review-deploy"
+ - mkdir -p gitlab-exporter
+ - wget -O ./gitlab-exporter/index.js https://gitlab.com/gitlab-org/gl-performance/raw/master/index.js
+ - mkdir sitespeed-results
+ - docker run --shm-size=1g --rm -v "$(pwd)":/sitespeed.io sitespeedio/sitespeed.io:6.3.1 --plugins.add ./gitlab-exporter --outputFolder sitespeed-results "$CI_ENVIRONMENT_URL"
+ - mv sitespeed-results/data/performance.json performance.json
+ artifacts:
+ paths:
+ - sitespeed-results/
+ reports:
+ performance: performance.json
+
+review-performance:
+ <<: *review-performance-base
+
review-stop:
<<: *review-base
<<: *single-script-job
@@ -1064,21 +1128,20 @@ review-stop:
schedule:review-cleanup:
<<: *review-base
+ <<: *review-schedules-only
stage: build
allow_failure: true
variables:
GIT_DEPTH: "1"
environment:
name: review/auto-cleanup
- only:
- refs:
- - schedules@gitlab-org/gitlab-ce
- - schedules@gitlab-org/gitlab-ee
- kubernetes: active
- except:
- - tags
- - /(^docs[\/-].*|.*-docs$)/
before_script:
- gem install gitlab --no-document
script:
- ruby -rrubygems scripts/review_apps/automated_cleanup.rb
+
+schedule:review-performance:
+ <<: *review-performance-base
+ <<: *review-schedules-only
+ script:
+ - wait_for_job_to_be_done "schedule:review-deploy"
diff --git a/.gitlab/issue_templates/Database Reviewer.md b/.gitlab/issue_templates/Database Reviewer.md
new file mode 100644
index 00000000000..a5e7e42fd14
--- /dev/null
+++ b/.gitlab/issue_templates/Database Reviewer.md
@@ -0,0 +1,32 @@
+#### Database Reviewer Checklist
+
+Thank you for becoming a ~database reviewer! Please work on the list below to complete your setup. For any question, reach out to #database an mention @abrandl.
+
+- [ ] Change issue title to include your name: `Database Reviewer Checklist: Your Name`
+- [ ] Review general [code review guide](https://docs.gitlab.com/ee/development/code_review.html)
+- [ ] Review [database review documentation](https://about.gitlab.com/handbook/engineering/workflow/code-review/database.html)
+- [ ] Familiarize with [migration helpers](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/database/migration_helpers.rb) and review usage in existing migrations
+- [ ] Read [database migration style guide](https://docs.gitlab.com/ee/development/migration_style_guide.html)
+- [ ] Familiarize with best practices in [database guides](https://docs.gitlab.com/ee/development/#database-guides)
+- [ ] Watch [Optimising Rails Database Queries: Episode 1](https://www.youtube.com/watch?v=79GurlaxhsI)
+- [ ] Read [Understanding EXPLAIN plans](https://docs.gitlab.com/ee/development/understanding_explain_plans.html)
+- [ ] Review [database best practices](https://docs.gitlab.com/ee/development/#best-practices)
+- [ ] Review how we use [database instances restored from a backup](https://ops.gitlab.net/gitlab-com/gl-infra/gitlab-restore/postgres-gprd) for testing and make sure you're set up to execute pipelines (check [README.md](https://ops.gitlab.net/gitlab-com/gl-infra/gitlab-restore/postgres-gprd/blob/master/README.md) and reach out to @abrandl since this is currently subject to being changed)
+- [ ] Get yourself added to [@gl-database](https://gitlab.com/groups/gl-database/-/group_members) group and respond to @-mentions to the group (reach out to any maintainer on the group to get added). You will get TODOs on gitlab.com for group mentions.
+- [ ] Make sure you have proper access to at least a read-only replica in staging and production
+- [ ] Indicate in `data/team.yml` your role as a database reviewer ([example MR](https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/19600/diffs)). Assign MR to your manager for merge.
+- [ ] Send one MR to improve the [review documentation](https://about.gitlab.com/handbook/engineering/workflow/code-review/database.html) or the [issue template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab/issue_templates/Database%20Reviewer.md)
+
+Note that *approving and accepting* merge requests is *restricted* to
+Database Maintainers only. As a reviewer, pass the MR to a maintainer
+for approval.
+
+You're all set! Watch out for TODOs on GitLab.com.
+
+###### Where to go for questions?
+
+Reach out to `#database` on Slack and mention @abrandl for any questions.
+
+cc @abrandl
+
+/label ~meta ~database
diff --git a/.gitlab/issue_templates/Doc Review.md b/.gitlab/issue_templates/Doc Review.md
new file mode 100644
index 00000000000..14ab0c82d59
--- /dev/null
+++ b/.gitlab/issue_templates/Doc Review.md
@@ -0,0 +1,20 @@
+<!-- This issue requests a technical writer review as required for documentation
+ content that was merged without one. -->
+
+<!-- NOTE: Please add a DevOps stage label (format `devops:<stage_name>`)
+ and assign the technical writer who is
+ [listed for that stage](https://about.gitlab.com/handbook/product/categories/#devops-stages). -->
+
+
+## References
+
+Merged MR that introduced documentation requiring review:
+
+Related issue(s):
+
+## Further Details
+
+<!-- Any additional context, questions, or notes for the technical writer. -->
+
+
+/label ~Documentation ~docs-review
diff --git a/.gitlab/issue_templates/Documentation.md b/.gitlab/issue_templates/Documentation.md
index b33ed5bcaa8..67602b7b2df 100644
--- a/.gitlab/issue_templates/Documentation.md
+++ b/.gitlab/issue_templates/Documentation.md
@@ -1,54 +1,53 @@
-<!--See the general documentation guidelines https://docs.gitlab.com/ee/development/documentation -->
+<!--
-<!-- Mention "documentation" or "docs" in the issue title -->
+* Use this issue template for suggesting new docs or updates to existing docs.
+ Note: Doc work as part of feature development is covered in the Feature Request template.
+
+* For issues related to features of the docs.gitlab.com site, see
+ https://gitlab.com/gitlab-com/gitlab-docs/issues/
-<!-- Use this description template for new docs or updates to existing docs. -->
+* For information about documentation content and process, see
+ https://docs.gitlab.com/ee/development/documentation/ -->
-<!-- Check the documentation structure guidelines for guidance: https://docs.gitlab.com/ee/development/documentation/structure.html-->
+<!-- Type of issue -->
-- [ ] Documents Feature A <!-- feature name -->
-- [ ] Follow-up from: #XXX, !YYY <!-- Mention related issues, MRs, and epics when available -->
+<!-- Un-comment the line for the applicable doc issue type to add its label.
+ Note that all text on that line is deleted upon issue creation. -->
+<!-- /label ~"docs:fix" - Correction or clarification needed. -->
+<!-- /label ~"docs:new" - New doc needed to cover a new topic or use case. -->
+<!-- /label ~"docs:improvement" - Improving an existing doc; e.g. adding a diagram, adding or rewording text, resolving redundancies, cross-linking, etc. -->
+<!-- /label ~"docs:revamp" - Review a page or group of pages in order to plan and implement major improvements/rewrites. -->
+<!-- /label ~"docs:other" - Anything else. -->
-## New doc or update?
+### Problem to solve
-<!-- Mark either of these boxes: -->
+<!-- Include the following detail as necessary:
+* What product or feature(s) affected?
+* What docs or doc section affected? Include links or paths.
+* Is there a problem with a specific document, or a feature/process that's not addressed sufficiently in docs?
+* Any other ideas or requests?
+-->
-- [ ] New documentation
-- [ ] Update existing documentation
+### Further details
-## Checklists
+<!--
+* Any concepts, procedures, reference info we could add to make it easier to successfully use GitLab?
+* Include use cases, benefits, and/or goals for this work.
+* If adding content: What audience is it intended for? (What roles and scenarios?)
+ For ideas, see personas at https://design.gitlab.com/research/personas or the persona labels at
+ https://gitlab.com/groups/gitlab-org/-/labels?utf8=%E2%9C%93&subscribed=&search=persona%3A
+-->
-### Product Manager
+### Proposal
-<!-- Reference: https://docs.gitlab.com/ee/development/documentation/workflow.html#1-product-manager-s-role-in-the-documentation-process -->
+<!-- Further specifics for how can we solve the problem. -->
-- [ ] Add the correct labels
-- [ ] Add the correct milestone
-- [ ] Indicate the correct document/directory for this feature <!-- (ping the tech writers for help if you're not sure) -->
-- [ ] Fill the doc blurb below
+### Who can address the issue
-#### Documentation blurb
+<!-- What if any special expertise is required to resolve this issue? -->
-<!-- Documentation template: https://docs.gitlab.com/ee/development/documentation/structure.html#documentation-template-for-new-docs -->
+### Other links/references
-- Doc **title**
-
- <!-- write the doc title here -->
-
-- Feature **overview/description**
-
- <!-- Write the feature overview here -->
-
-- Feature **use cases**
-
- <!-- Write the use cases here -->
-
-### Developer
-
-<!-- Reference: https://docs.gitlab.com/ee/development/documentation/workflow.html#2-developer-s-role-in-the-documentation-process -->
-
-- [ ] Copy the doc blurb above and paste it into the doc
-- [ ] Write the tutorial - explain how to use the feature
-- [ ] Submit the MR using the appropriate MR description template
+<!-- E.g. related GitLab issues/MRs -->
/label ~Documentation
diff --git a/.gitlab/issue_templates/Feature proposal.md b/.gitlab/issue_templates/Feature proposal.md
index 1bb8d33ff63..b4007c1ba7b 100644
--- a/.gitlab/issue_templates/Feature proposal.md
+++ b/.gitlab/issue_templates/Feature proposal.md
@@ -1,45 +1,32 @@
### Problem to solve
-<!--- What problem do we solve? -->
+<!-- What problem do we solve? -->
-### Target audience
+### Intended users
-<!--- For whom are we doing this? Include a [persona](https://design.gitlab.com/research/personas)
-listed below, if applicable, along with its [label](https://gitlab.com/groups/gitlab-org/-/labels?utf8=%E2%9C%93&subscribed=&search=persona%3A),
-or define a specific company role, e.g. "Release Manager".
+<!-- Who will use this feature? If known, include any of the following: types of users (e.g. Developer), personas, or specific company roles (e.g. Release Manager). It's okay to write "Unknown" and fill this field in later.
+Personas can be found at https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/ -->
-Existing personas are: (copy relevant personas out of this comment, and delete any persona that does not apply)
-
-- Parker, Product Manager, https://design.gitlab.com/research/personas#persona-parker
-/label ~"Persona: Product Manager"
-
-- Delaney, Development Team Lead, https://design.gitlab.com/research/personas#persona-delaney
-/label ~"Persona: Development Team Lead"
-
-- Sasha, Software Developer, https://design.gitlab.com/research/personas#persona-sasha
-/label ~"Persona: Software developer"
+### Further details
-- Devon, DevOps Engineer, https://design.gitlab.com/research/personas#persona-devon
-/label ~"Persona: DevOps Engineer"
+<!-- Include use cases, benefits, and/or goals (contributes to our vision?) -->
-- Sidney, Systems Administrator, https://design.gitlab.com/research/personas#persona-sidney
-/label ~"Persona: Systems Administrator"
+### Proposal
-- Sam, Security Analyst, https://design.gitlab.com/research/personas#persona-sam
-/label ~"Persona: Security Analyst"
--->
+<!-- How are we going to solve the problem? Try to include the user journey! https://about.gitlab.com/handbook/journeys/#user-journey -->
-### Further details
+### Permissions and Security
-<!--- Include use cases, benefits, and/or goals (contributes to our vision?) -->
+<!-- What permissions are required to perform the described actions? Are they consistent with the existing permissions as documented for users, groups, and projects as appropriate? Is the proposed behavior consistent between the UI, API, and other access methods (e.g. email replies)? -->
-### Proposal
+### Documentation
-<!--- How are we going to solve the problem? Try to include the user journey! -->
+<!-- See the Feature Change Documentation Workflow https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html
+Add all known Documentation Requirements here, per https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#documentation-requirements -->
### What does success look like, and how can we measure that?
-<!--- Define both the success metrics and acceptance criteria. Note that success metrics indicate the desired business outcomes, while acceptance criteria indicate when the solution is working correctly. If there is no way to measure success, link to an issue that will implement a way to measure this -->
+<!-- Define both the success metrics and acceptance criteria. Note that success metrics indicate the desired business outcomes, while acceptance criteria indicate when the solution is working correctly. If there is no way to measure success, link to an issue that will implement a way to measure this. -->
### Links / references
diff --git a/.gitlab/issue_templates/Security developer workflow.md b/.gitlab/issue_templates/Security developer workflow.md
index aaa16145399..9946651075f 100644
--- a/.gitlab/issue_templates/Security developer workflow.md
+++ b/.gitlab/issue_templates/Security developer workflow.md
@@ -30,6 +30,7 @@ Set the title to: `Description of the original issue`
#### 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)
+- [ ] Add links to this issue and your MRs in the description of the security release issue
- [ ] 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)
diff --git a/.gitlab/issue_templates/Test plan.md b/.gitlab/issue_templates/Test plan.md
index a3c3f4a6509..3aedd5859d3 100644
--- a/.gitlab/issue_templates/Test plan.md
+++ b/.gitlab/issue_templates/Test plan.md
@@ -93,4 +93,4 @@ When adding new automated tests, please keep [testing levels](https://docs.gitla
in mind.
-->
-/label ~Quality ~"test plan"
+/label ~Quality ~"test\-plan"
diff --git a/.gitlab/merge_request_templates/Documentation.md b/.gitlab/merge_request_templates/Documentation.md
index 4457821e23e..ba9624aeeab 100644
--- a/.gitlab/merge_request_templates/Documentation.md
+++ b/.gitlab/merge_request_templates/Documentation.md
@@ -1,33 +1,39 @@
-<!--See the general documentation guidelines https://docs.gitlab.com/ee/development/documentation -->
+<!-- Follow the documentation workflow https://docs.gitlab.com/ee/development/documentation/workflow.html -->
+<!-- Additional information is located at https://docs.gitlab.com/ee/development/documentation/ -->
<!-- Mention "documentation" or "docs" in the MR title -->
-
-<!-- Use this description template for new docs or updates to existing docs. For changing documentation location use the "Change documentation location" template -->
+<!-- For changing documentation location use the "Change documentation location" template -->
## What does this MR do?
-<!-- Briefly describe what this MR is about -->
+<!-- Briefly describe what this MR is about. -->
## Related issues
-<!-- Mention the issue(s) this MR closes or is related to -->
-
-Closes
+<!-- Link related issues below. Insert the issue link or reference after the word "Closes" if merging this should automatically close it. -->
## Author's checklist
-- [ ] [Apply the correct labels and milestone](https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#1-product-managers-role)
-- [ ] Crosslink the document from the higher-level index
-- [ ] Crosslink the document from other subject-related docs
-- [ ] Feature moving tiers? Make sure the change is also reflected in [`features.yml`](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml)
-- [ ] Correctly apply the product [badges](https://docs.gitlab.com/ee/development/documentation/styleguide.html#product-badges) and [tiers](https://docs.gitlab.com/ee/development/documentation/styleguide.html#gitlab-versions-and-tiers)
-- [ ] [Port the MR to EE (or backport from CE)](https://docs.gitlab.com/ee/development/documentation/index.html#cherry-picking-from-ce-to-ee): _always recommended, required when the `ee-compat-check` job fails_
+- [ ] Follow the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
+- [ ] Link docs to and from the higher-level index page, plus other related docs where helpful.
+- [ ] Apply the ~Documentation label.
## Review checklist
-- [ ] Your team's review (required)
-- [ ] PM's review (recommended, but not a blocker)
-- [ ] Technical writer's review (required)
-- [ ] Merge the EE-MR first, CE-MR afterwards
+All reviewers can help ensure accuracy, clarity, completeness, and adherence to the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
+
+**1. Primary Reviewer**
+
+* [ ] Review by a code reviewer or other selected colleague to confirm accuracy, clarity, and completeness. This can be skipped for minor fixes without substantive content changes.
+
+**2. Technical Writer**
+
+* [ ] Optional: Technical writer review. If not requested for this MR, must be scheduled post-merge. To request for this MR, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+
+**3. Maintainer**
+
+1. [ ] Review by assigned maintainer, who can always request/require the above reviews. Maintainer's review can occur before or after a technical writer review.
+1. [ ] Ensure a release milestone is set and that you merge the equivalent EE MR before the CE MR if both exist.
+1. [ ] If there has not been a technical writer review, [create an issue for one using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review).
/label ~Documentation
diff --git a/.gitlab/merge_request_templates/Security Release.md b/.gitlab/merge_request_templates/Security Release.md
index 246f2dae009..42314f9b2dd 100644
--- a/.gitlab/merge_request_templates/Security Release.md
+++ b/.gitlab/merge_request_templates/Security Release.md
@@ -7,6 +7,10 @@ See [the general developer security release guidelines](https://gitlab.com/gitla
This merge request _must not_ close the corresponding security issue _unless_ it
targets master.
+When submitting a merge request for CE, a corresponding EE merge request is
+always required. This makes it easier to merge security merge requests, as
+manually merging CE into EE is no longer required.
+
-->
## Related issues
@@ -20,8 +24,8 @@ targets master.
- [ ] Title of this MR is the same as for all backports
- [ ] A [CHANGELOG entry](https://docs.gitlab.com/ee/development/changelog.html) is added without a `merge_request` value, with `type` set to `security`
- [ ] Add a link to this MR in the `links` section of related issue
-- [ ] Add a link to an EE MR if required
-- [ ] Assign to a reviewer
+- [ ] Set up an EE MR (always required for CE merge requests): EE_MR_LINK_HERE
+- [ ] Assign to a reviewer (that is not a release manager)
## Reviewer checklist
diff --git a/.prettierrc b/.prettierrc
index 3384551aea5..5e2863a11f6 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,13 +1,5 @@
{
"printWidth": 100,
"singleQuote": true,
- "trailingComma": "es5",
- "overrides": [
- {
- "files": ["**/app/**/*", "**/spec/**/*"],
- "options": {
- "trailingComma": "all"
- }
- }
- ]
+ "trailingComma": "all"
}
diff --git a/.rubocop.yml b/.rubocop.yml
index bcff67ded8c..9143966b864 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -8,6 +8,7 @@ require:
- rubocop-rspec
AllCops:
+ TargetRubyVersion: 2.5
TargetRailsVersion: 5.0
Exclude:
- 'vendor/**/*'
@@ -181,3 +182,11 @@ Cop/InjectEnterpriseEditionModule:
Exclude:
- 'spec/**/*'
- 'ee/spec/**/*'
+
+Style/ReturnNil:
+ Enabled: true
+
+# It isn't always safe to replace `=~` with `.match?`, especially when there are
+# nil values on the left hand side
+Performance/RegexpMatch:
+ Enabled: false
diff --git a/.stylelintrc b/.stylelintrc
index 69de9a5dd13..241d2c94a88 100644
--- a/.stylelintrc
+++ b/.stylelintrc
@@ -1,30 +1,111 @@
{
- "extends": "stylelint-config-recommended",
- "plugins": [
- "stylelint-scss"
- ],
- "rules": {
- "no-descending-specificity": null,
- "font-family-no-missing-generic-family-keyword": null,
- "at-rule-no-unknown": [ true, {
- ignoreAtRules: ["include", "each", "mixin", "extend", "if", "function", "for", "else", "return"]
- }],
- "selector-type-no-unknown": [true, {
- "ignoreTypes": ["gl-emoji"]
- }],
- "unit-no-unknown" : [true, {
- "ignoreFunctions": ["-webkit-image-set"]
- }],
- "scss/at-extend-no-missing-placeholder": null,
- "scss/at-function-pattern": "^[a-z]+([a-z0-9-]+[a-z0-9]+)?$",
- "scss/at-import-no-partial-leading-underscore": true,
- "scss/at-import-partial-extension-blacklist": ["scss"],
- "scss/at-mixin-pattern": "^[a-z]+([a-z0-9-]+[a-z0-9]+)?$",
- "scss/at-rule-no-unknown": true,
- "scss/dollar-variable-colon-space-after": "always",
- "scss/dollar-variable-colon-space-before": "never",
- "scss/dollar-variable-pattern": "^[_]?[a-z]+([a-z0-9-]+[a-z0-9]+)?$",
- "scss/percent-placeholder-pattern": "^[a-z]+([a-z0-9-]+[a-z0-9]+)?$",
- "scss/selector-no-redundant-nesting-selector": true,
- }
+ "plugins":[
+ "./scripts/frontend/stylelint/stylelint-duplicate-selectors.js",
+ "./scripts/frontend/stylelint/stylelint-utility-classes.js",
+ "stylelint-scss",
+ ],
+ "rules":{
+ "at-rule-blacklist":[
+ "debug"
+ ],
+ "at-rule-no-unknown":null,
+ "at-rule-no-vendor-prefix":true,
+ "block-no-empty":true,
+ "block-opening-brace-space-before":"always",
+ "color-hex-case":"lower",
+ "color-hex-length":"short",
+ "color-named":"never",
+ "color-no-invalid-hex":true,
+ "declaration-bang-space-after":"never",
+ "declaration-bang-space-before":"always",
+ "declaration-block-semicolon-newline-after":"always",
+ "declaration-block-semicolon-space-before":"never",
+ "declaration-block-single-line-max-declarations":1,
+ "declaration-block-trailing-semicolon":"always",
+ "declaration-colon-space-after":"always-single-line",
+ "declaration-colon-space-before":"never",
+ "declaration-property-value-blacklist":{
+ "border":[
+ "none"
+ ],
+ "border-top":[
+ "none"
+ ],
+ "border-right":[
+ "none"
+ ],
+ "border-bottom":[
+ "none"
+ ],
+ "border-left":[
+ "none"
+ ]
+ },
+ "function-comma-space-after":"always-single-line",
+ "function-parentheses-space-inside":"never",
+ "function-url-quotes":"always",
+ "indentation":2,
+ "length-zero-no-unit":true,
+ "max-nesting-depth":[
+ 3,
+ {
+ "ignoreAtRules":[
+ "each",
+ "media",
+ "supports",
+ "include"
+ ],
+ "severity":"warning"
+ }
+ ],
+ "media-feature-name-no-vendor-prefix":true,
+ "media-feature-parentheses-space-inside":"never",
+ "no-missing-end-of-source-newline":true,
+ "number-leading-zero":"always",
+ "number-no-trailing-zeros":true,
+ "property-no-unknown":true,
+ "property-no-vendor-prefix":true,
+ "rule-empty-line-before":[
+ "always-multi-line",
+ {
+ "except":[
+ "first-nested"
+ ],
+ "ignore":[
+ "after-comment"
+ ]
+ }
+ ],
+ "scss/at-extend-no-missing-placeholder":[true,{ "severity": "warning" }],
+ "scss/at-function-pattern":"^[a-z]+([a-z0-9-]+[a-z0-9]+)?$",
+ "scss/at-import-no-partial-leading-underscore":true,
+ "scss/at-import-partial-extension-blacklist":[
+ "scss"
+ ],
+ "scss/at-mixin-pattern":"^[a-z]+([a-z0-9-]+[a-z0-9]+)?$",
+ "scss/at-rule-no-unknown":true,
+ "scss/dollar-variable-colon-space-after":"always",
+ "scss/dollar-variable-colon-space-before":"never",
+ "scss/dollar-variable-pattern":"^[_]?[a-z]+([a-z0-9-]+[a-z0-9]+)?$",
+ "scss/percent-placeholder-pattern":"^[a-z]+([a-z0-9-]+[a-z0-9]+)?$",
+ "scss/selector-no-redundant-nesting-selector":true,
+ "selector-class-pattern":[
+ "^[a-z0-9\\-]+$",
+ {
+ "message":"Selector should be written in lowercase with hyphens (selector-class-pattern)",
+ "severity": "warning"
+ },
+ ],
+ "selector-list-comma-newline-after":"always",
+ "selector-max-compound-selectors":[3, { "severity": "warning" }],
+ "selector-max-id":1,
+ "selector-no-vendor-prefix":true,
+ "selector-pseudo-element-colon-notation":"double",
+ "selector-pseudo-element-no-unknown":true,
+ "shorthand-property-no-redundant-values":true,
+ "string-quotes":"single",
+ "value-no-vendor-prefix":[true, { ignoreValues: ["sticky"] }],
+ "stylelint-gitlab/duplicate-selectors":[true,{ "severity": "warning" }],
+ "stylelint-gitlab/utility-classes":[true,{ "severity": "warning" }],
+ }
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e220d61b316..3d7e8e10280 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,560 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.9.1 (2019-03-25)
+
+### Fixed (7 changes)
+
+- Fix issue that caused the "Show all activity" button to appear on top of the mini pipeline status dropdown on the merge request page. !26274
+- Fix duplicated bottom match line on merge request parallel diff view. !26402
+- Allow users who can push to protected branches to create protected branches via CLI. !26413
+- Add missing .gitlab-ci.yml to Android template. !26415
+- Refresh commit count after repository head changes. !26473
+- Set proper default-branch for repository on GitHub Import. !26476
+- GitHub importer: Use the project creator to create branches from forks. !26510
+
+### Changed (1 change)
+
+- Upgrade to Gitaly v1.27.1. !26533
+
+
+## 11.9.0 (2019-03-22)
+
+### Security (24 changes)
+
+- Use encrypted runner tokens. !25532
+- Stop linking to unrecognized package sources. !55518
+- Disable issue boards API when issues are disabled.
+- Forbid creating discussions for users with restricted access.
+- Fix leaking private repository information in API.
+- Fixed ability to see private groups by users not belonging to given group.
+- Prevent releases links API to leak tag existance.
+- Display the correct number of MRs a user has access to.
+- Block local URLs for Kubernetes integration.
+- Fix arbitrary file read via diffs during import.
+- Check if desired milestone for an issue is available.
+- Don't allow non-members to see private related MRs.
+- Check snippet attached file to be moved is within designated directory.
+- Fix blind SSRF in Prometheus integration by checking URL before querying.
+- Fix git clone revealing private repo's presence.
+- Remove project serialization in quick actions response.
+- Don't show new issue link after move when a user does not have permissions.
+- Limit mermaid rendering to 5K characters.
+- Show only merge requests visible to user on milestone detail page.
+- Display only information visible to current user on the Milestone page.
+- Do not display impersonated sessions under active sessions and remove ability to revoke session.
+- Validate session key when authorizing with GCP to create a cluster.
+- Do not disclose milestone titles for unauthorized users.
+- Remove the possibility to share a project with a group that a user is not a member of.
+
+### Removed (1 change)
+
+- Remove HipChat integration from GitLab. !22223
+
+### Fixed (86 changes, 21 of them are from the community)
+
+- Fixes issue with AWS V4 signatures not working with some S3 providers. !21788
+- Validate 'include' keywords in gitlab-ci.yml configuration files. !24098 (Paul Bonaud)
+- Close More Actions tooltip when menu opens. !24285
+- API: Support Jira transition ID as string. !24400 (Robert Schilling)
+- Fixed navigation sidebar flashing open on page load. !24555
+- Fix username escaping when using assign to me for issues. !24673
+- commit page info-well overflow fix #56436. !24799 (Gokhan Apaydin)
+- Fix error tracking list page. !24806
+- Fix overlapping empty-header logo. !24868 (Jonas L.)
+- Resolve Jobs tab border top in pipeline's page is 1px off. !24878
+- Require maintainer access to show pages domain settings. !24926
+- Display error message when API call to list Sentry issues fails. !24936
+- Fix rollout status for statefulsets and daemonsets. !24972 (Sergej Nikolaev <kinolaev@gmail.com>)
+- Display job names consistently on pipelines and environments list. !24984
+- Update new password breadcrumb. !25037 (George Tsiolis)
+- Fixes functions finder for upgraded Knative app. !25067
+- Provide expires_in in LFS authentication payload. !25082
+- Fix validation of certain ed25519 keys. !25115 (Merlijn B. W. Wajer)
+- Timer and action name aligned vertically for delayed jobs in pipeline actions. !25117 (Gokhan Apaydin)
+- Fix the border style of CONTRIBUTING button when it exists. !25124 (Takuya Noguchi)
+- Change badges.svg example to pipeline.svg. !25157 (Aviad Levy)
+- API: Fix docs and parameters for hangouts-chat service. !25180 (Robert Schilling)
+- API: Expose full commit title. !25189 (Robert Schilling)
+- API: Require only one parameter when updating a wiki. !25191 (Robert Schilling)
+- Hide pipeline status when pipelines are disabled on project. !25204
+- Fix alignment of dropdown icon on issuable on mobile. !25205 (Takuya Noguchi)
+- Add left margin to 1st time contributor badge. !25216 (Gokhan Apaydin)
+- Use limited counter for runner build count in admin page. !25220
+- API: Ensure that related merge requests are referenced cross-project. !25222 (Robert Schilling)
+- Ensure the base pipeline of a Merge Request belongs to its target branch. !25226
+- Fix import_jid error on project import. !25239
+- Fix commenting on commits having SHA1 starting with a large number. !25278
+- Allow empty values such as [] to be stored in reactive cache. !25283
+- Remove vertical connecting line placeholder from diff discussion notes. !25292
+- Fix hover and active state colors of award emoji button. !25295
+- Fix author layouts in issuable meta line UIs on mobile. !25332 (Takuya Noguchi)
+- Fix bug where project topics truncate. !25398
+- Fix ETag caching not being used for AJAX requests. !25400
+- Doc - fix the url of pipeline status badge. !25404 (Aviad Levy)
+- Fix pipeline status icon mismatch. !25407
+- Allow users to compare branches on a read-only instance. !25414
+- Fix 404s when C++ .gitignore template selected. !25416
+- Always fetch MR latest version when creating suggestions. !25441
+- Only show borders for markdown images in notes. !25448
+- Bring back Rugged implementation of find_commit. !25477
+- Remove duplicate units from metrics graph. !25485
+- Fix project import error importing releases. !25495
+- Remove duplicate XHR request when requesting new pipeline page. !25506
+- Properly handle multiple X-Forwarded-For addresses in runner IP. !25511
+- Fix weekday shift in issue board cards for UTC+X timezones by removing local timezone to UTC conversion. !25512 (Elias Werberich)
+- Fix large table horizontal scroll and prevent side-by-side tables. !25520 (Dany Jupille)
+- Fix error when viewing group issue boards when user doesn't have explicit group permissions. !25524
+- Respect the should_remove_source_branch parameter to the merge API. !25525
+- Externalize markdown toolbar buttons tooltips. !25529
+- Fix method to mark a project repository as writable. !25546
+- fix group without owner after transfer. !25573 (Peter Marko)
+- Fix pagination and duplicate requests in environments page. !25582
+- Improve the JS pagination to handle the case when the `X-Total` and `X-Total-Pages` headers aren't present. !25601
+- Add right padding to the repository mirror action buttons. !25606
+- Use 'folder-open' from sprite icons for Browse Files button in Tag page. !25635
+- Make merge to refs/merge-requests/:iid/merge not raise when FF-only enabled. !25653
+- Fixed "Copying comment with ordered list includes extraneous newlines". !25695
+- Fix bridge jobs only/except variables policy. !25710
+- Allow GraphQL requests without CSRF token. !25719
+- Skip Project validation during Hashed Storage migration or rollback. !25753
+- Resolve showing squash commit edit issue when only single commit is present. !25807
+- Fix the last-ditch memory killer pgroup SIGKILL. !25940
+- Disable timeout on merge request merging poll. !25988
+- Allow modifying squash commit message for fast-forward only merge method. !26017
+- Fix bug in BitBucket imports with SHA shorter than 40 chars. !26050
+- Fix health checks not working behind load balancers. !26055
+- Fix 500 error caused by CODEOWNERS with no matches. !26072
+- Fix notes being marked as edited after resolving. !26143
+- Fix error creating a merge request when diff includes a null byte. !26190
+- Fix undefined variable error on json project views. !26297
+- GitHub import: Create new branches as project owner. !26335
+- Gracefully handles excluded fields from attributes during serialization on JsonCache. !26368
+- Admin section finds users case-insensitively.
+- Fixes not working dropdowns in pipelines page.
+- Do not show file templates when creating a new directory in WebIDE.
+- Allow project members to see private group if the project is in the group namespace.
+- Allow maintainers to remove pages.
+- Fix inconsistent pagination styles.
+- Fixed blob editor deleting file content for certain file paths.
+- Fix upcoming milestone when there are milestones with far-future due dates.
+- Fixed alignment of changed icon in Web IDE.
+
+### Changed (31 changes, 10 of them are from the community)
+
+- Improve snippets empty state. !18348 (George Tsiolis)
+- Remove second primary button on wiki edit. !19959 (George Tsiolis)
+- Allow raw `tls_options` to be passed in LDAP configuration. !20678
+- Remove undigested token column from personal_access_tokens table from the database. !22743
+- Update activity filter for issues. !23423 (George Tsiolis)
+- Use auto-build-image for build job in Auto-DevOps.gitlab-ci.yml. !24279
+- Error tracking configuration - add a Sentry project selection dropdown. !24701
+- Move ChatOps to Core. !24780
+- Implement new arguments `state`, `closed_before` and `closed_after` for `IssuesResolver` in GraphQL. !24910
+- Validate kubernetes cluster CA certificate. !24990
+- Review App Link to Changed Page if Only One Change Present. !25048
+- Show pipeline ID, commit, and branch name on modal while stopping pipeline. !25059
+- Improve empty state for starred projects. !25138
+- Capture due date when importing milestones from Github. !25182 (dstanley)
+- Add a spinner icon which is rendered using pure css. !25186
+- Make emoji picker bigger. !25187 (Jacopo Beschi @jacopo-beschi)
+- API: Sort tie breaker with id DESC. !25311 (Nermin Vehabovic)
+- Add iOS-fastlane template for .gitlab-ci.yml. !25395
+- Move language setting to preferences. !25427 (Fabian Schneider @fabsrc)
+- Resolve Create Project Template for Netlify. !25453
+- Sort labels alphabetically on issues and merge requests list. !25470
+- Add Project template for .NET Core. !25486
+- Update operations settings breadcrumb trail. !25539 (George Tsiolis)
+- Add Project template for go-micro. !25553
+- Jira: make issue links title compact. !25609 (Elan Ruusamäe @glensc)
+- Project level filtering for JupyterHub. !25684 (Amit Rathi (amit1rrr))
+- Clean up vendored templates. !25794
+- Mask all TOKEN and PASSWORD CI variables. !25868
+- Add project template for Android. !25870
+- Add iOS project template. !25872
+- Upgrade to Gitaly v1.26.0. !25890
+
+### Performance (11 changes)
+
+- Improve performance for diverging commit counts. !24287
+- Optimize Redis usage in User::ActivityService. !25005
+- Only load syntax highlight CSS of selected theme. !25232
+- Improve label select rendering. !25281
+- Enable persisted pipeline stages by default. !25347
+- Speed up group issue search counts. !25411
+- Load repository language from the database if detected before. !25518
+- Remove N+1 query for tags in /admin/runners page. !25572
+- Eliminate most N+1 queries loading UserController#calendar_activities. !25697
+- Improve Web IDE launch performance. !25700
+- Significantly reduce N+1 queries in /api/v4/todos endpoint. !25711
+
+### Added (55 changes, 18 of them are from the community)
+
+- Add a tag filter to the admin runners view. !19740 (Alexis Reigel)
+- Add project fetch statistics. !23596 (Jacopo Beschi @jacopo-beschi)
+- Hashed Storage rollback mechanism. !23955
+- Allow to recursively expand includes. !24356
+- Allow expanding a diff to display full file. !24406
+- Support `only: changes:` on MR pipelines. !24490 (Hiroyuki Sato)
+- Expose additional merge request pipeline variables. !24595 (Hiroyuki Sato)
+- Add metadata about the GitLab server to GraphQL. !24636
+- Support merge ref writing (without merging to target branch). !24692
+- Add field mergeRequests for project in GraphQL. !24805
+- API support for MR merge to temporary merge ref path. !24918
+- Ability to filter confidential issues. !24960 (Robert Schilling)
+- Allow creation of branches that match a wildcard protection, except directly through git. !24969
+- Add related merge request count to api response. !24974
+- Add realtime validation for user fullname and username on validation. !25017 (Ehsan Abdulqader @EhsanZ)
+- Allow setting feature flags per GitLab group through the API. !25022
+- Add API endpoint to get a commit's GPG signature. !25032
+- Add support for FTP assets for releases. !25071 (Robert Schilling)
+- Add Confirmation Modal to Rollback on Environment. !25110
+- add title attribute to display file name. !25154 (Satoshi Nakamatsu @satoshicano)
+- API: Expose text_color for project and group labels. !25172 (Robert Schilling)
+- Added support for ingress hostnames. !25181 (walkafwalka)
+- API: Promote project milestone to a group milestone. !25203 (Nermin Vehabovic)
+- API: Expose if the current user can merge a MR. !25207 (Robert Schilling)
+- add readme to changelogs directory. !25209 (@glensc)
+- API: Indicate if label is a project label. !25219 (Robert Schilling)
+- Expose refspecs and depth to runner. !25233
+- Port System Header and Footer feature to Core. !25241
+- Sort Environments by Last Updated. !25260
+- Accept force option to overwrite branch on commit via API. !25286
+- Add support for masking CI variables. !25293
+- Add Link from Closed (moved) Issues to Moved Issue. !25300
+- Next/previous navigation between files in MR review. !25355
+- Add YouTrack integration service. !25361 (Yauhen Kotau @bessorion)
+- Add ability to set path and name for project on fork using API. !25363
+- Add project level config for merge pipelines. !25385
+- Edit Knative domain after it has been deployed. !25386
+- Add zoom and scroll to metrics dashboard. !25388
+- Persist source sha and target sha for merge pipelines. !25417
+- Add support for toggling discussion filter from notes section. !25426
+- Resolve Move files in the Web IDE. !25431
+- Show header and footer system messages in email. !25474
+- Allow configuring POSTGRES_VERSION in Auto DevOps. !25500
+- Add Saturday to Localization first day of the week. !25509 (Ahmad Haghighi)
+- Extend the Gitlab API for deletion of job_artifacts of a single job. !25522 (rroger)
+- Simplify CI/CD configuration on serverless projects. !25523
+- Add button to start discussion from single comment. !25575
+- sidekiq: terminate child processes at shutdown. !25669
+- Expose merge request entity for pipelines. !25679
+- Link to most recent MR from a branch. !25689
+- Adds Auto DevOps build job for tags. !25718 (walkafwalka)
+- Allow all snippets to be accessed by API. !25772
+- Make file tree in merge requests resizable.
+- Make the Web IDE the default editor.
+- File uploads are deleted asynchronously when deleting a project or group.
+
+### Other (28 changes, 6 of them are from the community)
+
+- Improve GitHub and Gitea project import table UI. !24606
+- Externalize strings from `/app/views/projects/commit`. !24668 (George Tsiolis)
+- Correct non-standard unicode spaces to regular unicode. !24795 (Marcel Amirault)
+- Provide a performance bar link to the Jaeger UI. !24902
+- Remove BATCH_SIZE from WikiFileFinder. !24933
+- Use export-import svgs from gitlab-svgs. !24954
+- Fix N+1 query in Issues and MergeRequest API when issuable_metadata is present. !25042 (Alex Koval)
+- Directly inheriting from ActiveRecord::Migration is deprecated. !25066 (Jasper Maes)
+- Bump Helm and kubectl in Auto DevOps to 2.12.3 and 1.11.7 respectively. !25072
+- Log queue duration in production_json.log. !25075
+- Extracted ResolveWithIssueButton to its own component. !25093 (Martin Hobert)
+- Add rectangular project and group avatars. !25098
+- Include note in the Rails filter_parameters configuration. !25238
+- Bump Helm and kubectl used in Kubernetes integration to 2.12.3 and 1.11.7 respectively. !25268
+- Include gl_project_path in API /internal/allowed response. !25314
+- Fix incorrect Pages Domains checkbox description. !25392 (Anton Melser)
+- Update GitLab Runner Helm Chart to 0.2.0. !25493
+- Add suffix (`_event`) to merge request source. !25508
+- Creates a helper function to check if repo is EE. !25647
+- If chpst is available, make fron-source installations run sidekiq as a process group leader. !25654
+- Bring back Rugged implementation of GetTreeEntries. !25674
+- Moves EE util into the CE file. !25680
+- Bring back Rugged implementation of CommitIsAncestor. !25702
+- Bring back Rugged implementation of TreeEntry. !25706
+- Enable syntax highlighting to other supported markups. !25761
+- Update GitLab Shell to v8.7.1. !25801
+- Bring back Rugged implementation of commit_tree_entry. !25896
+- Removes EE differences for jobs/getters.js.
+
+
+## 11.8.3 (2019-03-19)
+
+### Security (1 change)
+
+- Remove project serialization in quick actions response.
+
+
+## 11.8.2 (2019-03-13)
+
+### Security (1 change)
+
+- Fixed ability to see private groups by users not belonging to given group.
+
+### Fixed (5 changes)
+
+- Fix import_jid error on project import. !25239
+- Properly handle multiple X-Forwarded-For addresses in runner IP. !25511
+- Fix error when viewing group issue boards when user doesn't have explicit group permissions. !25524
+- Fix method to mark a project repository as writable. !25546
+- Allow project members to see private group if the project is in the group namespace.
+
+
+## 11.8.0 (2019-02-22)
+
+### Security (7 changes, 1 of them is from the community)
+
+- Sanitize user full name to clean up any URL to prevent mail clients from auto-linking URLs. !2793
+- Update Helm to 2.12.2 to address Helm client vulnerability. !24418 (Takuya Noguchi)
+- Use sanitized user status message for user popover.
+- Validate bundle files before unpacking them.
+- Alias GitHub and BitBucket OAuth2 callback URLs.
+- Fixed XSS content in KaTex links.
+- Disallows unauthorized users from accessing the pipelines section.
+
+### Removed (2 changes, 1 of them is from the community)
+
+- Removed deprecated Redcarpet markdown engine.
+- Remove Cancel all jobs button in general jobs list view. (Jordi Llull)
+
+### Fixed (84 changes, 20 of them are from the community)
+
+- Fix ambiguous brackets in task lists. !18514 (Jared Deckard <jared.deckard@gmail.com>)
+- Fix lost line number when navigating to a specific line in a protected file before authenticating. !19165 (Scott Escue)
+- Fix suboptimal handling of checkbox and radio input events causing group general settings submit button to stay disabled after changing its visibility. !23022
+- Fix upcoming milestones filter not including group milestones. !23098 (Heinrich Lee Yu)
+- Update runner admin page to make description field larger. !23593 (Sascha Reynolds)
+- Fix Bitbucket Server import not allowing personal projects. !23601
+- Fix bug causing repository mirror settings UI to break. !23712
+- Fix foreground color for labels to ensure consistency of label appearance. !23873 (Nathan Friend)
+- Resolve In Merge Request diff screen, master is not a hyperlink. !23874
+- Show the correct error page when access is denied. !23932
+- Increase reliability and performance of toggling task items. !23938
+- Modify file restore to rectify tar issue. !24000
+- Fix default visibility_level for new projects. !24120 (Fabian Schneider @fabsrc)
+- Footnotes now render properly in markdown. !24168
+- Emoji and cancel button are taller than input in set user status modal. !24173 (Dhiraj Bodicherla)
+- Adjusts duplicated line when commenting on unfolded diff lines (in the bottom). !24201
+- Adjust height of "Add list" dropdown in issue boards. !24227
+- Improves restriction of multiple Kubernetes clusters through API. !24251
+- Fix files/blob api endpoints content disposition. !24267
+- Cleanup stale +deleted repo paths on project removal (adjusts project removal bug). !24269
+- Handle regular job dependencies next to parallelized job dependencies. !24273
+- Proper align Projects dropdown on issue boards page. !24277 (Johann Hubert Sonntagbauer)
+- Resolve When merging an MR, the squash checkbox isnt always supported. !24296
+- Fix Bitbucket Server importer error handling. !24343
+- Fix syntax highlighting for suggested changes preview. !24358
+- API: Support dots in wiki slugs. !24383 (Robert Schilling)
+- Show CI artifact file size with 3 significant digits on 'browse job artifacts' page. !24387
+- API: Support username with dots. !24395 (Robert Schilling)
+- API: Fix default_branch_protection admin setting. !24398 (Robert Schilling)
+- Remove unwanted margin above suggested changes. !24419
+- Prevent checking protected_ref? for ambiguous refs. !24437
+- Update metrics environment dropdown to show complete option set. !24441
+- Fix empty labels of CI builds for gitlab-pages on pipeline page. !24451
+- Do not run spam checks on confidential issues. !24453
+- Upgrade KaTeX to version 0.10.0. !24478 (Andrew Harmon)
+- Avoid overwriting default jaeger values with nil. !24482
+- Display SAML failure messages instead of expecting CSRF token. !24509
+- Adjust vertical alignment for project visibility icons. !24511 (Martin Hobert)
+- Load initUserInternalRegexPlaceholder only when required. !24522
+- Hashed Storage: `AfterRenameService` was receiving the wrong `old_path` under some circunstances. !24526
+- Resolve Runners IPv6 address overlaps other values. !24531
+- Fix 404s with snippet uploads in object storage. !24550
+- Fixed oversized custom project notification selector dropdown. !24557
+- Allow users with full private access to read private personal snippets. !24560
+- Resolve Pipeline stages job action button icon is not aligned. !24577
+- Fix cluster page non-interactive on form validation error. !24583
+- Fix 404s for snippet uploads when relative URL root used. !24588
+- Fix markdown table border. !24601
+- Fix CSS grid on a new Project/Group Milestone. !24614 (Takuya Noguchi)
+- Prevent unload when Recaptcha is open. !24625
+- Clean up unicorn sampler metric labels. !24626 (bjk-gitlab)
+- Support bamboo api polymorphism. !24680 (Alex Lossent)
+- Ensure Cert Manager works with Auto DevOps URLs greater than 64 bytes. !24683
+- Fix failed LDAP logins when nil user_id present. !24749
+- fix display comment avatars issue in IE 11. !24777 (Gokhan Apaydin)
+- Fix template labels not being created on new projects. !24803
+- Fix cluster installation processing spinner. !24814
+- Append prioritized label before pagination. !24815
+- Resolve UI bug adding group members with lower permissions. !24820
+- Make `ActionController::Parameters` serializable for sidekiq jobs. !24864
+- Fix Jira Service password validation on project integration services. !24896 (Daniel Juarez)
+- Fix potential Addressable::URI::InvalidURIError. !24908
+- Update Workhorse to v8.2.0. !24909
+- Encode Content-Disposition filenames. !24919
+- Avoid race conditions when creating GpgSignature. !24939
+- Create the source branch for a GitHub import. !25064
+- Fix suggested changes syntax highlighting. !25116
+- Fix counts in milestones dashboard. !25230
+- Fixes incorrect TLD validation errors for Kubernetes cluster domain. !25262
+- Fix 403 errors when adding an assignee list in project boards. !25263
+- Prevent Auto DevOps from trying to deploy without a domain name. !25308
+- Fix uninitialized constant with GitLab Pages.
+- Increase line height of project summaries. (gfyoung)
+- Remove extra space between MR tab bar and sticky file headers.
+- Correct spacing for comparison page.
+- Update CI YAML param table with include.
+- Return bottom border on MR Tabs.
+- Fixes z-index and margins of archived alert in job page.
+- Fixes archived sticky top bar without perfomance bar.
+- Fixed rebase button not showing in merge request widget.
+- Fixed double tooltips on note awards buttons.
+- Allow suggestions to be copied and pasted as GFM.
+- Fix bug that caused Suggestion Markdown toolbar button to insert snippet with leading +/-/<space>.
+- Moved primary button for labels to follow the design patterns used on rest of the site. (Martin Hobert)
+
+### Changed (37 changes, 11 of them are from the community)
+
+- Change spawning of tooltips to be top by default. !21223
+- Standardize filter value capitlization in filter bar in both issues and boards pages. !23846 (obahareth)
+- Refresh group overview to match project overview. !23866
+- Build number does not need to be tweaked anymore for the TeamCity integration to work properly. !23898
+- Added empty project illustration and updated text to user profile overview. !23973 (Fernando Arias)
+- Modified Knative list view to provide more details. !24072 (Chris Baumbauer)
+- Move cancel & new issue button on job page. !24074
+- Make issuable empty states actionable. !24077
+- Fix code search when text is larger than max gRPC message size. !24111
+- Update string structure for available group runners. !24187 (George Tsiolis)
+- Remove multilingual translation from the word "in" in the job details sidebar. !24192 (Nathan Friend)
+- Fix duplicate project disk path in BackfillLegacyProjectRepositories. !24213
+- Ensured links to a comment or system note anchor resolves to the right note if a user has a discussion filter. !24228
+- Remove expansion hover animation from pipeline status icon buttons. !24268 (Nathan Friend)
+- Redesigned related merge requests in issue page. !24270
+- Return the maximum group access level in the projects API. !24403
+- Update project topics styling to use badges design. !24415
+- Display "commented" only for commit discussions on merge requests. !24427
+- Upgrade js-regex gem to version 3.1. !24433 (rroger)
+- Prevent Sidekiq arguments over 10 KB in size from being logged to JSON. !24493
+- Added Avatar in the settings sidebar. !24515 (Yoginth)
+- Refresh empty states for profile page tabs. !24549
+- remove red/green colors from diff view of no-color syntax theme. !24582 (khm)
+- Get remote IP address of runner. !24624
+- Update last_activity_on for Users on some main GET endpoints. !24642
+- Update metrics dashboard graph design. !24653
+- Update to GitLab SVG icon from Font Awesome in profile for location and work. !24671 (Yoginth)
+- Add template for Android with Fastlane. !24722
+- Display timestamps to messages printed by gitlab:backup:restore rake tasks. (Will Chandler)
+- Show MR statistics in diff comparisons.
+- Make possible to toggle file tree while scrolling through diffs.
+- Use delete instead of remove when referring to `git branch -D`.
+- Add folder header to files in merge request tree list.
+- Added fuzzy file finder to merge requests.
+- Collapse directory structure in merge request file tree.
+- Adds skeleton loading to releases page.
+- Support multiple outputs in jupyter notebooks.
+
+### Performance (8 changes, 1 of them is from the community)
+
+- Remove unused button classes `btn-create` and `comment-btn`. !23232 (George Tsiolis)
+- [API] Omit `X-Total` and `X-Total-Pages` headers when items count is more than 10,000. !23931
+- Improve efficiency of GitHub importer by reducing amount of locks needed. !24102
+- Improve milestone queries using subqueries instead of separate queries for ids. !24325
+- Efficiently remove expired artifacts in `ExpireBuildArtifactsWorker`. !24450
+- Eliminate N+1 queries in /api/groups/:id. !24513
+- Use deployment relation to get an environment name. !24890
+- Do not reload daemon if configuration file of pages does not change.
+
+### Added (35 changes, 18 of them are from the community)
+
+- Add badge count to projects. !18425 (George Tsiolis)
+- API: Add support for group labels. !21368 (Robert Schilling)
+- Add setting for first day of the week. !22755 (Fabian Schneider @fabsrc)
+- Pages for subgroups. !23505
+- Add support for customer provided encryption keys for Amazon S3 remote backups. !23797 (Pepijn Van Eeckhoudt)
+- Add Knative detailed view. !23863 (Chris Baumbauer)
+- Add group full path to project's shared_with_groups. !24052 (Mathieu Parent)
+- Added feature to specify a custom Auto DevOps chart repository. !24162 (walkafwalka)
+- Add flat-square badge style. !24172 (Fabian Schneider @fabsrc)
+- Display last activity and created at datetimes for users. !24181
+- Allow setting of feature gates per project. !24184
+- Save issues/merge request sorting options to backend. !24198
+- Added support for custom hosts/domains to Auto DevOps. !24248 (walkafwalka)
+- Adds milestone search. !24265 (Jacopo Beschi @jacopo-beschi)
+- Allow merge request diffs to be placed into an object store. !24276
+- Add Container Registry API with cleanup function. !24303
+- GitLab now supports the profile and email scopes from OpenID Connect. !24335 (Goten Xiao)
+- Add 'in' filter that modifies scope of 'search' filter to issues and merge requests API. !24350 (Hiroyuki Sato)
+- Add `with_programming_language` filter for projects to API. !24377 (Dylan MacKenzie)
+- API: Support searching for tags. !24385 (Robert Schilling)
+- Document graphicsmagick installation for source installation. !24404 (Alexis Reigel)
+- Redirect GET projects/:id to project page. !24467
+- Indicate on Issue Status if an Issue was Moved. !24470
+- Redeploy Auto DevOps deployment on variable updates. !24498 (walkafwalka)
+- Don't create new merge request pipeline without commits. !24503 (Hiroyuki Sato)
+- Add GitLab Pages predefined CI variables 'CI_PAGES_DOMAIN' and 'CI_PAGES_URL'. !24504 (Adrian Moisey)
+- Moves domain setting from Auto DevOps to Cluster's page. !24580
+- API allows setting the squash commit message when squashing a merge request. !24784
+- Added ability to upgrade cluster applications. !24789
+- Add argument iids for issues in GraphQL. !24802
+- Add repositories count to usage ping data. !24823
+- Add support for extensionless pages URLs. !24876
+- Add templates for most popular Pages templates. !24906
+- Introduce Internal API for searching environment names. !24923
+- Allow admins to invalidate markdown texts by setting local markdown version.
+
+### Other (50 changes, 18 of them are from the community)
+
+- Externalize strings from `/app/views/projects/project_members`. !23227 (Tao Wang)
+- Add CSS & JS global flags to represent browser and platform. !24017
+- Fix deprecation: Passing an argument to force an association to reload is now deprecated. !24136 (Jasper Maes)
+- Cleanup legacy artifact background migration. !24144
+- Bump kubectl in Auto DevOps to 1.11.6. !24176
+- Conditionally initialize the global opentracing tracer. !24186
+- Remove horizontal whitespace on user profile overview on small breakpoints. !24189
+- Bump nginx-ingress chart to 1.1.2. !24203
+- Use monospace font for registry table tag id and tag name. !24205
+- Rename project tags to project topics. !24219
+- Add uniqueness validation to url column in Releases::Link model. !24223
+- Update sidekiq-cron to 1.0.4 and use fugit to replace rufus-scheduler to parse cron syntax. !24235
+- Adds inter-service OpenTracing propagation. !24239
+- Fixes Auto DevOps title on CI/CD admin settings. !24249
+- Upgrade kubeclient to 4.2.2 and swap out monkey-patch to disallow redirects. !24284
+- i18n: externalize strings from 'app/views/search'. !24297 (Tao Wang)
+- Fix several ActionController::Parameters deprecations. !24332 (Jasper Maes)
+- Remove all `$theme-gray-{weight}` variables in favor of `$gray-{weight}`. !24333 (George Tsiolis)
+- Update gitlab-styles to 2.5.1. !24336 (Jasper Maes)
+- Modifies environment scope UI on cluster page. !24376
+- Extract process_name from GitLab::Sentry. !24422
+- Upgrade Gitaly to 1.13.0. !24429
+- Actually set raise_on_unfiltered_parameters to true. !24443 (Jasper Maes)
+- Refactored NoteableDiscussion by extracting ResolveDiscussionButton. !24505 (Martin Hobert)
+- Extracted JumpToNextDiscussionButton to its own component. !24506 (Martin Hobert)
+- Extracted ReplyPlaceholder to its own component. !24507 (Martin Hobert)
+- Block emojis and symbol characters from users full names. !24523
+- Update GitLab Runner Helm Chart to 0.1.45. !24564
+- Updated docs for fields in pushing mirror from GitLab to GitHub. !24566 (Joseph Yu)
+- Upgrade gitlab-workhorse to 8.1.0. !24571
+- Externalize strings from `/app/views/sent_notifications`. !24576 (George Tsiolis)
+- Adds tracing support for ActiveRecord notifications. !24604
+- Externalize strings from `/app/views/projects/ci`. !24617 (George Tsiolis)
+- Move permission check of manual actions of deployments. !24660
+- Externalize strings from `/app/views/clusters`. !24666 (George Tsiolis)
+- Update UI for admin appearance settings. !24685
+- Externalize strings from `/app/views/projects/pages_domains`. !24723 (George Tsiolis)
+- Externalize strings from `/app/views/projects/milestones`. !24726 (George Tsiolis)
+- Add OpenTracing instrumentation for Action View Render events. !24728
+- Expose version for each application in cluster_status JSON endpoint. !24791
+- Externalize strings from `/app/views/instance_statistics`. !24809 (George Tsiolis)
+- Update cluster application version on updated and installed status. !24810
+- Project list UI improvements. !24855
+- Externalize strings from `/app/views/email_rejection_mailer`. !24869 (George Tsiolis)
+- Update Gitaly to v1.17.0. !24873
+- Update Workhorse to v8.3.0. !24959
+- Upgrade gitaly to 1.18.0. !24981
+- Update Workhorse to v8.3.1.
+- Upgraded Codesandbox smooshpack package.
+- Creates mixin to reduce code duplication between CE and EE in graph component.
+
+
+## 11.7.7 (2019-03-19)
+
+### Security (2 changes)
+
+- Remove project serialization in quick actions response.
+- Fixed ability to see private groups by users not belonging to given group.
+
+
## 11.7.5 (2019-02-06)
### Fixed (8 changes)
@@ -239,6 +793,33 @@ entry.
- Update url placeholder for the sentry configuration page. !24338
+## 11.6.10 (2019-02-28)
+
+### Security (21 changes)
+
+- Stop linking to unrecognized package sources. !55518
+- Check snippet attached file to be moved is within designated directory.
+- Fix potential Addressable::URI::InvalidURIError.
+- Do not display impersonated sessions under active sessions and remove ability to revoke session.
+- Display only information visible to current user on the Milestone page.
+- Show only merge requests visible to user on milestone detail page.
+- Disable issue boards API when issues are disabled.
+- Don't show new issue link after move when a user does not have permissions.
+- Fix git clone revealing private repo's presence.
+- Fix blind SSRF in Prometheus integration by checking URL before querying.
+- Check if desired milestone for an issue is available.
+- Don't allow non-members to see private related MRs.
+- Fix arbitrary file read via diffs during import.
+- Display the correct number of MRs a user has access to.
+- Forbid creating discussions for users with restricted access.
+- Do not disclose milestone titles for unauthorized users.
+- Validate session key when authorizing with GCP to create a cluster.
+- Block local URLs for Kubernetes integration.
+- Limit mermaid rendering to 5K characters.
+- Remove the possibility to share a project with a group that a user is not a member of.
+- Fix leaking private repository information in API.
+
+
## 11.6.8 (2019-01-30)
- No changes.
diff --git a/Dangerfile b/Dangerfile
index 715a2bcbbae..32f4b4d23c3 100644
--- a/Dangerfile
+++ b/Dangerfile
@@ -12,3 +12,4 @@ danger.import_dangerfile(path: 'danger/duplicate_yarn_dependencies')
danger.import_dangerfile(path: 'danger/prettier')
danger.import_dangerfile(path: 'danger/eslint')
danger.import_dangerfile(path: 'danger/roulette')
+danger.import_dangerfile(path: 'danger/single_codebase')
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 39893559155..5e57fb89558 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-1.20.0
+1.29.0
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 917d38ec9f9..d139a75408e 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-8.4.4
+8.7.1
diff --git a/Gemfile b/Gemfile
index 381511be450..222503ee6f5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -18,7 +18,7 @@ gem 'gitlab-default_value_for', '~> 3.1.1', require: 'default_value_for'
gem 'mysql2', '~> 0.4.10', group: :mysql
gem 'pg', '~> 1.1', group: :postgres
-gem 'rugged', '~> 0.27'
+gem 'rugged', '~> 0.28'
gem 'grape-path-helpers', '~> 1.0'
gem 'faraday', '~> 0.12'
@@ -42,11 +42,11 @@ gem 'omniauth-shibboleth', '~> 1.3.0'
gem 'omniauth-twitter', '~> 1.4'
gem 'omniauth_crowd', '~> 2.2.0'
gem 'omniauth-authentiq', '~> 0.3.3'
-gem 'rack-oauth2', '~> 1.2.1'
+gem 'rack-oauth2', '~> 1.9.3'
gem 'jwt', '~> 2.1.0'
# Spam and anti-bot protection
-gem 'recaptcha', '~> 3.0', require: 'recaptcha/rails'
+gem 'recaptcha', '~> 4.11', require: 'recaptcha/rails'
gem 'akismet', '~> 2.0'
# Two-factor authentication
@@ -68,7 +68,7 @@ gem 'gpgme', '~> 2.0.18'
# LDAP Auth
# GitLab fork with several improvements to original library. For full list of changes
# see https://github.com/intridea/omniauth-ldap/compare/master...gitlabhq:master
-gem 'gitlab_omniauth-ldap', '~> 2.0.4', require: 'omniauth-ldap'
+gem 'gitlab_omniauth-ldap', '~> 2.1.1', require: 'omniauth-ldap'
gem 'net-ldap'
# API
@@ -94,13 +94,15 @@ gem 'carrierwave', '~> 1.3'
gem 'mini_magick'
# for backups
-gem 'fog-aws', '~> 2.0.1'
-gem 'fog-core', '~> 1.44'
-gem 'fog-google', '~> 1.7.1'
-gem 'fog-local', '~> 0.3'
-gem 'fog-openstack', '~> 0.1'
+gem 'fog-aws', '~> 3.3'
+# Locked until fog-google resolves https://github.com/fog/fog-google/issues/421.
+# Also see config/initializers/fog_core_patch.rb.
+gem 'fog-core', '= 2.1.0'
+gem 'fog-google', '~> 1.8'
+gem 'fog-local', '~> 0.6'
+gem 'fog-openstack', '~> 1.0'
gem 'fog-rackspace', '~> 0.1.1'
-gem 'fog-aliyun', '~> 0.2.0'
+gem 'fog-aliyun', '~> 0.3'
# for Google storage
gem 'google-api-client', '~> 0.23'
@@ -126,7 +128,7 @@ gem 'asciidoctor', '~> 1.5.8'
gem 'asciidoctor-plantuml', '0.0.8'
gem 'rouge', '~> 3.1'
gem 'truncato', '~> 0.7.11'
-gem 'bootstrap_form', '~> 2.7.0'
+gem 'bootstrap_form', '~> 4.2.0'
gem 'nokogiri', '~> 1.10.1'
gem 'escape_utils', '~> 1.1'
@@ -156,7 +158,7 @@ end
gem 'state_machines-activerecord', '~> 0.5.1'
# Issue tags
-gem 'acts-as-taggable-on', '~> 5.0'
+gem 'acts-as-taggable-on', '~> 6.0'
# Background jobs
gem 'sidekiq', '~> 5.2.1'
@@ -168,7 +170,7 @@ gem 'gitlab-sidekiq-fetcher', '~> 0.4.0', require: 'sidekiq-reliable-fetch'
gem 'fugit', '~> 1.1'
# HTTP requests
-gem 'httparty', '~> 0.13.3'
+gem 'httparty', '~> 0.16.4'
# Colored output to console
gem 'rainbow', '~> 3.0'
@@ -184,7 +186,7 @@ gem 're2', '~> 1.1.1'
# Misc
-gem 'version_sorter', '~> 2.1.0'
+gem 'version_sorter', '~> 2.2.4'
# Export Ruby Regex to Javascript
gem 'js_regex', '~> 3.1'
@@ -263,9 +265,7 @@ gem 'addressable', '~> 2.5.2'
gem 'font-awesome-rails', '~> 4.7'
gem 'gemojione', '~> 3.3'
gem 'gon', '~> 6.2'
-gem 'jquery-atwho-rails', '~> 1.3.2'
gem 'request_store', '~> 1.3'
-gem 'select2-rails', '~> 3.5.9'
gem 'virtus', '~> 1.0.1'
gem 'base32', '~> 0.3.0'
@@ -323,7 +323,7 @@ group :development do
end
group :development, :test do
- gem 'bootsnap', '~> 1.3'
+ gem 'bootsnap', '~> 1.4'
gem 'bullet', '~> 5.5.0', require: !!ENV['ENABLE_BULLET']
gem 'pry-byebug', '~> 3.5.1', platform: :mri
gem 'pry-rails', '~> 0.3.4'
@@ -380,7 +380,7 @@ group :test do
gem 'shoulda-matchers', '~> 3.1.2', require: false
gem 'email_spec', '~> 2.2.0'
gem 'json-schema', '~> 2.8.0'
- gem 'webmock', '~> 2.3.2'
+ gem 'webmock', '~> 3.5.1'
gem 'rails-controller-testing'
gem 'sham_rack', '~> 1.3.6'
gem 'concurrent-ruby', '~> 1.1'
@@ -419,7 +419,8 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 1.10.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 1.13.0', require: 'gitaly'
+
gem 'grpc', '~> 1.15.0'
gem 'google-protobuf', '~> 3.6'
diff --git a/Gemfile.lock b/Gemfile.lock
index 7caeba15809..4c88afc7e24 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -43,8 +43,8 @@ GEM
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
- acts-as-taggable-on (5.0.0)
- activerecord (>= 4.2.8)
+ acts-as-taggable-on (6.0.0)
+ activerecord (~> 5.0)
adamantium (0.2.0)
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
@@ -65,7 +65,7 @@ GEM
atomic (1.1.99)
attr_encrypted (3.1.0)
encryptor (~> 3.0.0)
- attr_required (1.0.0)
+ attr_required (1.0.1)
awesome_print (1.8.0)
axiom-types (0.1.1)
descendants_tracker (~> 0.0.4)
@@ -85,9 +85,11 @@ GEM
binding_ninja (0.2.2)
binding_of_caller (0.8.0)
debug_inspector (>= 0.0.1)
- bootsnap (1.3.2)
+ bootsnap (1.4.1)
msgpack (~> 1.0)
- bootstrap_form (2.7.0)
+ bootstrap_form (4.2.0)
+ actionpack (>= 5.0)
+ activemodel (>= 5.0)
brakeman (4.2.1)
browser (2.5.3)
builder (3.2.3)
@@ -218,32 +220,33 @@ GEM
flowdock (0.7.1)
httparty (~> 0.7)
multi_json
- fog-aliyun (0.2.0)
- fog-core (~> 1.27)
- fog-json (~> 1.0)
+ fog-aliyun (0.3.3)
+ fog-core
+ fog-json
ipaddress (~> 0.8)
xml-simple (~> 1.1)
- fog-aws (2.0.1)
- fog-core (~> 1.38)
- fog-json (~> 1.0)
+ fog-aws (3.3.0)
+ fog-core (~> 2.1)
+ fog-json (~> 1.1)
fog-xml (~> 0.1)
ipaddress (~> 0.8)
- fog-core (1.45.0)
+ fog-core (2.1.0)
builder
excon (~> 0.58)
formatador (~> 0.2)
- fog-google (1.7.1)
- fog-core
- fog-json
- fog-xml
+ mime-types
+ fog-google (1.8.2)
+ fog-core (<= 2.1.0)
+ fog-json (~> 1.2)
+ fog-xml (~> 0.1.0)
google-api-client (~> 0.23.0)
- fog-json (1.0.2)
- fog-core (~> 1.0)
+ fog-json (1.2.0)
+ fog-core
multi_json (~> 1.10)
- fog-local (0.3.1)
- fog-core (~> 1.27)
- fog-openstack (0.1.21)
- fog-core (>= 1.40)
+ fog-local (0.6.0)
+ fog-core (>= 1.27, < 3.0)
+ fog-openstack (1.0.8)
+ fog-core (~> 2.1)
fog-json (>= 1.0)
ipaddress (>= 0.8)
fog-rackspace (0.1.1)
@@ -278,7 +281,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
- gitaly-proto (1.10.0)
+ gitaly-proto (1.13.0)
grpc (~> 1.0)
github-markup (1.7.0)
gitlab-default_value_for (3.1.1)
@@ -290,7 +293,7 @@ GEM
rubocop (~> 0.54.0)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
- gitlab_omniauth-ldap (2.0.4)
+ gitlab_omniauth-ldap (2.1.1)
net-ldap (~> 0.16)
omniauth (~> 1.3)
pyu-ruby-sasl (>= 0.0.3.3, < 0.1)
@@ -309,7 +312,7 @@ GEM
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
google-protobuf (3.6.1)
- googleapis-common-protos-types (1.0.2)
+ googleapis-common-protos-types (1.0.3)
google-protobuf (~> 3.0)
googleauth (0.6.6)
faraday (~> 0.12)
@@ -357,7 +360,7 @@ GEM
thor
tilt
hangouts-chat (0.0.5)
- hashdiff (0.3.4)
+ hashdiff (0.3.8)
hashie (3.5.7)
hashie-forbidden_attributes (0.1.1)
hashie (>= 3.0)
@@ -378,8 +381,8 @@ GEM
domain_name (~> 0.5)
http-form_data (2.1.1)
http_parser.rb (0.6.0)
- httparty (0.13.7)
- json (~> 1.8)
+ httparty (0.16.4)
+ mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.2.0)
@@ -397,7 +400,6 @@ GEM
activesupport
multipart-post
oauth (~> 0.5, >= 0.5.0)
- jquery-atwho-rails (1.3.2)
js_regex (3.1.1)
character_set (~> 1.1)
regexp_parser (~> 1.1)
@@ -583,7 +585,7 @@ GEM
atomic (>= 1.0.0)
peek
redis
- pg (1.1.3)
+ pg (1.1.4)
po_to_json (1.0.1)
json (>= 1.6.0)
powerpack (0.1.1)
@@ -621,12 +623,12 @@ GEM
rack-attack (4.4.1)
rack
rack-cors (1.0.2)
- rack-oauth2 (1.2.3)
- activesupport (>= 2.3)
- attr_required (>= 0.0.5)
- httpclient (>= 2.4)
- multi_json (>= 1.3.6)
- rack (>= 1.1)
+ rack-oauth2 (1.9.3)
+ activesupport
+ attr_required
+ httpclient
+ json-jwt (>= 1.9.0)
+ rack
rack-protection (2.0.5)
rack
rack-proxy (0.6.0)
@@ -679,7 +681,7 @@ GEM
optimist (>= 3.0.0)
rdoc (6.0.4)
re2 (1.1.1)
- recaptcha (3.0.0)
+ recaptcha (4.13.1)
json
recursive-open-struct (1.1.0)
redis (3.3.5)
@@ -784,7 +786,7 @@ GEM
rubyntlm (0.6.2)
rubypants (0.2.0)
rubyzip (1.2.2)
- rugged (0.27.5)
+ rugged (0.28.1)
safe_yaml (1.0.4)
sanitize (4.6.6)
crass (~> 1.0.2)
@@ -810,12 +812,10 @@ GEM
seed-fu (2.3.7)
activerecord (>= 3.1)
activesupport (>= 3.1)
- select2-rails (3.5.9.3)
- thor (~> 0.14)
selenium-webdriver (3.12.0)
childprocess (~> 0.5)
rubyzip (~> 1.2)
- sentry-raven (2.7.4)
+ sentry-raven (2.9.0)
faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9)
sexp_processor (4.11.0)
@@ -916,7 +916,7 @@ GEM
validates_hostname (1.0.6)
activerecord (>= 3.0)
activesupport (>= 3.0)
- version_sorter (2.1.0)
+ version_sorter (2.2.4)
virtus (1.0.5)
axiom-types (~> 0.1)
coercible (~> 1.0)
@@ -925,7 +925,7 @@ GEM
vmstat (2.3.0)
warden (1.2.7)
rack (>= 1.0)
- webmock (2.3.2)
+ webmock (3.5.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
@@ -950,7 +950,7 @@ DEPENDENCIES
RedCloth (~> 4.3.2)
ace-rails-ap (~> 4.1.0)
activerecord_sane_schema_dumper (= 1.0)
- acts-as-taggable-on (~> 5.0)
+ acts-as-taggable-on (~> 6.0)
addressable (~> 2.5.2)
akismet (~> 2.0)
asana (~> 0.8.1)
@@ -965,8 +965,8 @@ DEPENDENCIES
benchmark-ips (~> 2.3.0)
better_errors (~> 2.5.0)
binding_of_caller (~> 0.8.0)
- bootsnap (~> 1.3)
- bootstrap_form (~> 2.7.0)
+ bootsnap (~> 1.4)
+ bootstrap_form (~> 4.2.0)
brakeman (~> 4.2)
browser (~> 2.5)
bullet (~> 5.5.0)
@@ -1002,12 +1002,12 @@ DEPENDENCIES
flipper-active_record (~> 0.13.0)
flipper-active_support_cache_store (~> 0.13.0)
flowdock (~> 0.7)
- fog-aliyun (~> 0.2.0)
- fog-aws (~> 2.0.1)
- fog-core (~> 1.44)
- fog-google (~> 1.7.1)
- fog-local (~> 0.3)
- fog-openstack (~> 0.1)
+ fog-aliyun (~> 0.3)
+ fog-aws (~> 3.3)
+ fog-core (= 2.1.0)
+ fog-google (~> 1.8)
+ fog-local (~> 0.6)
+ fog-openstack (~> 1.0)
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7)
foreman (~> 0.84.0)
@@ -1017,13 +1017,13 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
- gitaly-proto (~> 1.10.0)
+ gitaly-proto (~> 1.13.0)
github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1)
gitlab-markup (~> 1.6.5)
gitlab-sidekiq-fetcher (~> 0.4.0)
gitlab-styles (~> 2.4)
- gitlab_omniauth-ldap (~> 2.0.4)
+ gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2)
google-api-client (~> 0.23)
google-protobuf (~> 3.6)
@@ -1042,12 +1042,11 @@ DEPENDENCIES
health_check (~> 2.6.0)
html-pipeline (~> 2.8)
html2text
- httparty (~> 0.13.3)
+ httparty (~> 0.16.4)
icalendar
influxdb (~> 0.2)
jaeger-client (~> 0.10.0)
jira-ruby (~> 1.4)
- jquery-atwho-rails (~> 1.3.2)
js_regex (~> 3.1)
json-schema (~> 2.8.0)
jwt (~> 2.1.0)
@@ -1104,7 +1103,7 @@ DEPENDENCIES
rack (= 2.0.6)
rack-attack (~> 4.4.1)
rack-cors (~> 1.0.0)
- rack-oauth2 (~> 1.2.1)
+ rack-oauth2 (~> 1.9.3)
rack-proxy (~> 0.6.0)
rails (= 5.0.7.1)
rails-controller-testing
@@ -1116,7 +1115,7 @@ DEPENDENCIES
rbtrace (~> 0.4)
rdoc (~> 6.0)
re2 (~> 1.1.1)
- recaptcha (~> 3.0)
+ recaptcha (~> 4.11)
redis (~> 3.2)
redis-namespace (~> 1.6.0)
redis-rails (~> 5.0.2)
@@ -1137,13 +1136,12 @@ DEPENDENCIES
ruby-progressbar
ruby_parser (~> 3.8)
rubyzip (~> 1.2.2)
- rugged (~> 0.27)
+ rugged (~> 0.28)
sanitize (~> 4.6)
sass (~> 3.5)
sass-rails (~> 5.0.6)
scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7)
- select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.12)
sentry-raven (~> 2.7)
settingslogic (~> 2.0.9)
@@ -1172,10 +1170,10 @@ DEPENDENCIES
unicorn (~> 5.4.1)
unicorn-worker-killer (~> 0.4.4)
validates_hostname (~> 1.0.6)
- version_sorter (~> 2.1.0)
+ version_sorter (~> 2.2.4)
virtus (~> 1.0.1)
vmstat (~> 2.3.0)
- webmock (~> 2.3.2)
+ webmock (~> 3.5.1)
webpack-rails (~> 0.9.10)
wikicloth (= 0.8.1)
diff --git a/PROCESS.md b/PROCESS.md
index 7c3c826f921..1f99cebe081 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -168,8 +168,12 @@ on behalf of the community member.
Every new feature or change should be shipped with its corresponding documentation
in accordance with the
-[documentation process](https://docs.gitlab.com/ee/development/documentation/workflow.html)
-and [structure](https://docs.gitlab.com/ee/development/documentation/structure.html).
+[documentation process](https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html)
+and [structure](https://docs.gitlab.com/ee/development/documentation/structure.html) guides.
+Note that a technical writer will review all changes to documentation. This can occur
+in the same MR as the feature code, but [if there is not sufficient time or need,
+it can be planned via a follow-up issue for doc review](https://docs.gitlab.com/ee/development/documentation/feature-change-workflow.html#1-product-managers-role),
+and another MR, if needed. Regardless, complete docs must be merged with code by the freeze.
#### What happens if these deadlines are missed?
@@ -198,8 +202,6 @@ and to prevent any last minute surprises.
Merge requests should still be complete, following the [definition of done][done].
-#### Feature merge requests
-
If a merge request is not ready, but the developers and Product Manager
responsible for the feature think it is essential that it is in the release,
they can [ask for an exception](#asking-for-an-exception) in advance. This is
@@ -214,34 +216,17 @@ information, see
[Automatic CE->EE merge][automatic_ce_ee_merge] and
[Guidelines for implementing Enterprise Edition features][ee_features].
-#### Documentation merge requests
-
-Documentation is part of the product and must be shipped with the feature.
-
-The single exception for the feature freeze is documentation, and it can
-be left to be **merged up to the 14th** if:
-
-* There is a follow-up issue to add documentation.
-* It is assigned to the developer writing documentation for this feature, and they
- are aware of it.
-* It is in the correct milestone, with the labels ~Documentation, ~Deliverable,
-~missed-deliverable, and "pick into X.Y" applied.
-* It must be reviewed and approved by a technical writer.
-
-For more information read the process for
-[documentation shipped late](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late).
-
### After the 7th
Once the stable branch is frozen, the only MRs that can be cherry-picked into
the stable branch are:
* Fixes for [regressions](#regressions) where the affected version `xx.x` in `regression:xx.x` is the current release. See [Managing bugs](#managing-bugs) section.
-* Fixes for security issues
-* Fixes or improvements to automated QA scenarios
-* [Documentation updates](https://docs.gitlab.com/ee/development/documentation/workflow.html#documentation-shipped-late) for changes in the same release
-* New or updated translations (as long as they do not touch application code)
-* Changes that are behind a feature flag and have the ~"feature flag" label
+* Fixes for security issues.
+* Fixes or improvements to automated QA scenarios.
+* [Documentation improvements](https://docs.gitlab.com/ee/development/documentation/workflow.html) for feature changes made in the same release, though initial docs for these features should have already been merged by the freeze, as required.
+* New or updated translations (as long as they do not touch application code).
+* Changes that are behind a feature flag and have the ~"feature flag" label.
During the feature freeze all merge requests that are meant to go into the
upcoming release should have the correct milestone assigned _and_ the
diff --git a/VERSION b/VERSION
index 106400c03a2..1a954017bf3 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-11.8.0-pre
+11.10.0-pre
diff --git a/app/assets/javascripts/api.js b/app/assets/javascripts/api.js
index 85eb08cc97d..8754c253881 100644
--- a/app/assets/javascripts/api.js
+++ b/app/assets/javascripts/api.js
@@ -12,6 +12,7 @@ const Api = {
projectsPath: '/api/:version/projects.json',
projectPath: '/api/:version/projects/:id',
projectLabelsPath: '/:namespace_path/:project_path/labels',
+ projectMergeRequestsPath: '/api/:version/projects/:id/merge_requests',
projectMergeRequestPath: '/api/:version/projects/:id/merge_requests/:mrid',
projectMergeRequestChangesPath: '/api/:version/projects/:id/merge_requests/:mrid/changes',
projectMergeRequestVersionsPath: '/api/:version/projects/:id/merge_requests/:mrid/versions',
@@ -111,6 +112,22 @@ const Api = {
return axios.get(url);
},
+ /**
+ * Get all Merge Requests for a project, eventually filtering based on
+ * supplied parameters
+ * @param projectPath
+ * @param params
+ * @returns {Promise}
+ */
+ projectMergeRequests(projectPath, params = {}) {
+ const url = Api.buildUrl(Api.projectMergeRequestsPath).replace(
+ ':id',
+ encodeURIComponent(projectPath),
+ );
+
+ return axios.get(url, { params });
+ },
+
// Return Merge Request for project
projectMergeRequest(projectPath, mergeRequestId, params = {}) {
const url = Api.buildUrl(Api.projectMergeRequestPath)
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 73ce3e760ab..3826ecd1ac1 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -8,6 +8,7 @@ import { updateTooltipTitle } from './lib/utils/common_utils';
import { isInVueNoteablePage } from './lib/utils/dom_utils';
import flash from './flash';
import axios from './lib/utils/axios_utils';
+import bp from './breakpoints';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
@@ -264,7 +265,10 @@ export class AwardsHandler {
const css = {
top: `${$addBtn.offset().top + $addBtn.outerHeight()}px`,
};
- if (position === 'right') {
+ // for xs screen we position the element on center
+ if (bp.getBreakpointSize() === 'xs') {
+ css.left = '5%';
+ } else if (position === 'right') {
css.left = `${$addBtn.offset().left - $menu.outerWidth() + 20}px`;
$menu.addClass('is-aligned-right');
} else {
@@ -615,10 +619,18 @@ export class AwardsHandler {
let awardsHandlerPromise = null;
export default function loadAwardsHandler(reload = false) {
if (!awardsHandlerPromise || reload) {
- awardsHandlerPromise = import(/* webpackChunkName: 'emoji' */ './emoji').then(Emoji => {
- const awardsHandler = new AwardsHandler(Emoji);
- awardsHandler.bindEvents();
- return awardsHandler;
+ awardsHandlerPromise = new Promise((resolve, reject) => {
+ import(/* webpackChunkName: 'emoji' */ './emoji')
+ .then(Emoji => {
+ Emoji.initEmojiMap()
+ .then(() => {
+ const awardsHandler = new AwardsHandler(Emoji);
+ awardsHandler.bindEvents();
+ resolve(awardsHandler);
+ })
+ .catch(() => reject);
+ })
+ .catch(() => reject);
});
}
return awardsHandlerPromise;
diff --git a/app/assets/javascripts/badges/components/badge_form.vue b/app/assets/javascripts/badges/components/badge_form.vue
index 85a15b38de1..df74eb2c2f7 100644
--- a/app/assets/javascripts/badges/components/badge_form.vue
+++ b/app/assets/javascripts/badges/components/badge_form.vue
@@ -90,7 +90,7 @@ export default {
},
badgeImageUrlExample() {
const exampleUrl =
- 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/badge.svg';
+ 'https://example.gitlab.com/%{project_path}/badges/%{default_branch}/pipeline.svg';
return sprintf(s__('Badges|e.g. %{exampleUrl}'), {
exampleUrl,
});
diff --git a/app/assets/javascripts/behaviors/gl_emoji.js b/app/assets/javascripts/behaviors/gl_emoji.js
index d1d75658181..9034563d9b3 100644
--- a/app/assets/javascripts/behaviors/gl_emoji.js
+++ b/app/assets/javascripts/behaviors/gl_emoji.js
@@ -1,47 +1,74 @@
import 'document-register-element';
import isEmojiUnicodeSupported from '../emoji/support';
+import { initEmojiMap, getEmojiInfo, emojiFallbackImageSrc, emojiImageTag } from '../emoji';
class GlEmoji extends HTMLElement {
constructor() {
super();
- const emojiUnicode = this.textContent.trim();
- const { name, unicodeVersion, fallbackSrc, fallbackSpriteClass } = this.dataset;
-
- const isEmojiUnicode =
- this.childNodes &&
- Array.prototype.every.call(this.childNodes, childNode => childNode.nodeType === 3);
- const hasImageFallback = fallbackSrc && fallbackSrc.length > 0;
- const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0;
-
- if (emojiUnicode && isEmojiUnicode && !isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)) {
- // CSS sprite fallback takes precedence over image fallback
- if (hasCssSpriteFalback) {
- if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) {
- const emojiSpriteLinkTag = document.createElement('link');
- emojiSpriteLinkTag.setAttribute('rel', 'stylesheet');
- emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path);
- document.head.appendChild(emojiSpriteLinkTag);
- gon.emoji_sprites_css_added = true;
- }
- // IE 11 doesn't like adding multiple at once :(
- this.classList.add('emoji-icon');
- this.classList.add(fallbackSpriteClass);
- } else {
- import(/* webpackChunkName: 'emoji' */ '../emoji')
- .then(({ emojiImageTag, emojiFallbackImageSrc }) => {
- if (hasImageFallback) {
- this.innerHTML = emojiImageTag(name, fallbackSrc);
+ let emojiUnicode = this.textContent.trim();
+ const { fallbackSpriteClass, fallbackSrc, forceFallback } = this.dataset;
+ let { name, unicodeVersion } = this.dataset;
+
+ initEmojiMap()
+ .then(() => {
+ if (!unicodeVersion) {
+ const emojiInfo = getEmojiInfo(name);
+
+ if (emojiInfo) {
+ if (name !== emojiInfo.name) {
+ ({ name } = emojiInfo);
+ this.dataset.name = emojiInfo.name;
+ }
+ unicodeVersion = emojiInfo.u;
+ this.dataset.uni = unicodeVersion;
+
+ if (forceFallback === 'true' && !fallbackSpriteClass) {
+ this.innerHTML = emojiImageTag(name, emojiFallbackImageSrc(name));
} else {
- const src = emojiFallbackImageSrc(name);
- this.innerHTML = emojiImageTag(name, src);
+ emojiUnicode = emojiInfo.e;
+ this.innerHTML = emojiInfo.e;
}
- })
- .catch(() => {
- // do nothing
- });
- }
- }
+
+ this.title = emojiInfo.d;
+ }
+ }
+
+ const isEmojiUnicode =
+ this.childNodes &&
+ Array.prototype.every.call(this.childNodes, childNode => childNode.nodeType === 3);
+ const hasImageFallback = fallbackSrc && fallbackSrc.length > 0;
+ const hasCssSpriteFalback = fallbackSpriteClass && fallbackSpriteClass.length > 0;
+
+ if (
+ emojiUnicode &&
+ isEmojiUnicode &&
+ !isEmojiUnicodeSupported(emojiUnicode, unicodeVersion)
+ ) {
+ // CSS sprite fallback takes precedence over image fallback
+ if (hasCssSpriteFalback) {
+ if (!gon.emoji_sprites_css_added && gon.emoji_sprites_css_path) {
+ const emojiSpriteLinkTag = document.createElement('link');
+ emojiSpriteLinkTag.setAttribute('rel', 'stylesheet');
+ emojiSpriteLinkTag.setAttribute('href', gon.emoji_sprites_css_path);
+ document.head.appendChild(emojiSpriteLinkTag);
+ gon.emoji_sprites_css_added = true;
+ }
+ // IE 11 doesn't like adding multiple at once :(
+ this.classList.add('emoji-icon');
+ this.classList.add(fallbackSpriteClass);
+ } else if (hasImageFallback) {
+ this.innerHTML = emojiImageTag(name, fallbackSrc);
+ } else {
+ const src = emojiFallbackImageSrc(name);
+ this.innerHTML = emojiImageTag(name, src);
+ }
+ }
+ })
+ .catch(error => {
+ // Only reject is already handled in initEmojiMap
+ throw error;
+ });
}
}
diff --git a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
index 9482a9f166d..318b7f77c7b 100644
--- a/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/copy_as_gfm.js
@@ -10,10 +10,10 @@ export class CopyAsGFM {
const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
if (isIOS) return;
- $(document).on('copy', '.md, .wiki', e => {
+ $(document).on('copy', '.md', e => {
CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection);
});
- $(document).on('copy', 'pre.code.highlight, .diff-content .line_content', e => {
+ $(document).on('copy', 'pre.code.highlight, table.code td.line_content', e => {
CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection);
});
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
@@ -99,7 +99,7 @@ export class CopyAsGFM {
}
static transformGFMSelection(documentFragment) {
- const gfmElements = documentFragment.querySelectorAll('.md, .wiki');
+ const gfmElements = documentFragment.querySelectorAll('.md');
switch (gfmElements.length) {
case 0: {
return documentFragment;
@@ -173,7 +173,9 @@ export class CopyAsGFM {
wrapEl.appendChild(node.cloneNode(true));
const doc = DOMParser.fromSchema(schema.default).parse(wrapEl);
- const res = markdownSerializer.default.serialize(doc);
+ const res = markdownSerializer.default.serialize(doc, {
+ tightLists: true,
+ });
return res;
})
.catch(() => {});
diff --git a/app/assets/javascripts/behaviors/markdown/render_gfm.js b/app/assets/javascripts/behaviors/markdown/render_gfm.js
index fc9286d15e6..bfb073fdcdc 100644
--- a/app/assets/javascripts/behaviors/markdown/render_gfm.js
+++ b/app/assets/javascripts/behaviors/markdown/render_gfm.js
@@ -4,6 +4,7 @@ import renderMath from './render_math';
import renderMermaid from './render_mermaid';
import highlightCurrentUser from './highlight_current_user';
import initUserPopovers from '../../user_popovers';
+import initMRPopovers from '../../mr_popover';
// Render GitLab flavoured Markdown
//
@@ -15,6 +16,7 @@ $.fn.renderGFM = function renderGFM() {
renderMermaid(this.find('.js-render-mermaid'));
highlightCurrentUser(this.find('.gfm-project_member').get());
initUserPopovers(this.find('.gfm-project_member').get());
+ initMRPopovers(this.find('.gfm-merge_request').get());
return this;
};
diff --git a/app/assets/javascripts/behaviors/markdown/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
index 35380ca49fb..798114b4b0b 100644
--- a/app/assets/javascripts/behaviors/markdown/render_mermaid.js
+++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js
@@ -1,4 +1,5 @@
import flash from '~/flash';
+import { sprintf, __ } from '../../locale';
// Renders diagrams and flowcharts from text using Mermaid in any element with the
// `js-render-mermaid` class.
@@ -14,6 +15,9 @@ import flash from '~/flash';
// </pre>
//
+// This is an arbitary number; Can be iterated upon when suitable.
+const MAX_CHAR_LIMIT = 5000;
+
export default function renderMermaid($els) {
if (!$els.length) return;
@@ -34,6 +38,21 @@ export default function renderMermaid($els) {
$els.each((i, el) => {
const source = el.textContent;
+ /**
+ * Restrict the rendering to a certain amount of character to
+ * prevent mermaidjs from hanging up the entire thread and
+ * causing a DoS.
+ */
+ if (source && source.length > MAX_CHAR_LIMIT) {
+ el.textContent = sprintf(
+ __(
+ 'Cannot render the image. Maximum character count (%{charLimit}) has been exceeded.',
+ ),
+ { charLimit: MAX_CHAR_LIMIT },
+ );
+ return;
+ }
+
// Remove any extra spans added by the backend syntax highlighting.
Object.assign(el, { textContent: source });
diff --git a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
index 680f2031409..670f66b005e 100644
--- a/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
+++ b/app/assets/javascripts/behaviors/shortcuts/shortcuts_issuable.js
@@ -37,7 +37,7 @@ export default class ShortcutsIssuable extends Shortcuts {
}
// Sanity check: Make sure the selected text comes from a discussion : it can either contain a message...
- let foundMessage = !!documentFragment.querySelector('.md, .wiki');
+ let foundMessage = !!documentFragment.querySelector('.md');
// ... Or come from a message
if (!foundMessage) {
@@ -46,7 +46,7 @@ export default class ShortcutsIssuable extends Shortcuts {
let node = e;
do {
// Text nodes don't define the `matches` method
- if (node.matches && node.matches('.md, .wiki')) {
+ if (node.matches && node.matches('.md')) {
foundMessage = true;
}
node = node.parentNode;
diff --git a/app/assets/javascripts/blob_edit/blob_bundle.js b/app/assets/javascripts/blob_edit/blob_bundle.js
index 5f64175362d..6aaf5bf7296 100644
--- a/app/assets/javascripts/blob_edit/blob_bundle.js
+++ b/app/assets/javascripts/blob_edit/blob_bundle.js
@@ -13,7 +13,7 @@ export default () => {
if (editBlobForm.length) {
const urlRoot = editBlobForm.data('relativeUrlRoot');
const assetsPath = editBlobForm.data('assetsPrefix');
- const filePath = editBlobForm.data('blobFilename');
+ const filePath = `${editBlobForm.data('blobFilename')}`;
const currentAction = $('.js-file-title').data('currentAction');
const projectId = editBlobForm.data('project-id');
const isMarkdown = editBlobForm.data('is-markdown');
diff --git a/app/assets/javascripts/boards/components/issue_due_date.vue b/app/assets/javascripts/boards/components/issue_due_date.vue
index 9c4c6632976..9bc66978198 100644
--- a/app/assets/javascripts/boards/components/issue_due_date.vue
+++ b/app/assets/javascripts/boards/components/issue_due_date.vue
@@ -53,7 +53,7 @@ export default {
} else if (timeDifference === -1) {
return __('Yesterday');
} else if (timeDifference > 0 && timeDifference < 7) {
- return dateFormat(issueDueDate, 'dddd', true);
+ return dateFormat(issueDueDate, 'dddd');
}
return standardDateFormat;
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index f88e9b55988..c4c5fedc615 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -58,6 +58,7 @@ export default () => {
state: boardsStore.state,
loading: true,
boardsEndpoint: $boardApp.dataset.boardsEndpoint,
+ recentBoardsEndpoint: $boardApp.dataset.recentBoardsEndpoint,
listsEndpoint: $boardApp.dataset.listsEndpoint,
boardId: $boardApp.dataset.boardId,
disabled: parseBoolean($boardApp.dataset.disabled),
@@ -75,6 +76,7 @@ export default () => {
created() {
gl.boardService = new BoardService({
boardsEndpoint: this.boardsEndpoint,
+ recentBoardsEndpoint: this.recentBoardsEndpoint,
listsEndpoint: this.listsEndpoint,
bulkUpdatePath: this.bulkUpdatePath,
boardId: this.boardId,
diff --git a/app/assets/javascripts/boards/services/board_service.js b/app/assets/javascripts/boards/services/board_service.js
index 3de6eb056c2..7d463f17ab1 100644
--- a/app/assets/javascripts/boards/services/board_service.js
+++ b/app/assets/javascripts/boards/services/board_service.js
@@ -2,12 +2,13 @@ import axios from '../../lib/utils/axios_utils';
import { mergeUrlParams } from '../../lib/utils/url_utility';
export default class BoardService {
- constructor({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId }) {
+ constructor({ boardsEndpoint, listsEndpoint, bulkUpdatePath, boardId, recentBoardsEndpoint }) {
this.boardsEndpoint = boardsEndpoint;
this.boardId = boardId;
this.listsEndpoint = listsEndpoint;
this.listsEndpointGenerate = `${listsEndpoint}/generate.json`;
this.bulkUpdatePath = bulkUpdatePath;
+ this.recentBoardsEndpoint = `${recentBoardsEndpoint}.json`;
}
generateBoardsPath(id) {
diff --git a/app/assets/javascripts/clusters/clusters_bundle.js b/app/assets/javascripts/clusters/clusters_bundle.js
index 6ebd1ad109e..c95d7608e37 100644
--- a/app/assets/javascripts/clusters/clusters_bundle.js
+++ b/app/assets/javascripts/clusters/clusters_bundle.js
@@ -12,6 +12,8 @@ import {
REQUEST_FAILURE,
UPGRADE_REQUESTED,
UPGRADE_REQUEST_FAILURE,
+ INGRESS,
+ INGRESS_DOMAIN_SUFFIX,
} from './constants';
import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store';
@@ -36,6 +38,7 @@ export default class Clusters {
installRunnerPath,
installJupyterPath,
installKnativePath,
+ updateKnativePath,
installPrometheusPath,
managePrometheusPath,
hasRbac,
@@ -62,6 +65,7 @@ export default class Clusters {
installPrometheusEndpoint: installPrometheusPath,
installJupyterEndpoint: installJupyterPath,
installKnativeEndpoint: installKnativePath,
+ updateKnativeEndpoint: updateKnativePath,
});
this.installApplication = this.installApplication.bind(this);
@@ -74,6 +78,10 @@ export default class Clusters {
this.successApplicationContainer = document.querySelector('.js-cluster-application-notice');
this.showTokenButton = document.querySelector('.js-show-cluster-token');
this.tokenField = document.querySelector('.js-cluster-token');
+ this.ingressDomainHelpText = document.querySelector('.js-ingress-domain-help-text');
+ this.ingressDomainSnippet = this.ingressDomainHelpText.querySelector(
+ '.js-ingress-domain-snippet',
+ );
Clusters.initDismissableCallout();
initSettingsPanels();
@@ -119,8 +127,7 @@ export default class Clusters {
static initDismissableCallout() {
const callout = document.querySelector('.js-cluster-security-warning');
-
- if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
+ PersistentUserCallout.factory(callout);
}
addListeners() {
@@ -129,6 +136,8 @@ export default class Clusters {
eventHub.$on('upgradeApplication', data => this.upgradeApplication(data));
eventHub.$on('upgradeFailed', appId => this.upgradeFailed(appId));
eventHub.$on('dismissUpgradeSuccess', appId => this.dismissUpgradeSuccess(appId));
+ eventHub.$on('saveKnativeDomain', data => this.saveKnativeDomain(data));
+ eventHub.$on('setKnativeHostname', data => this.setKnativeHostname(data));
}
removeListeners() {
@@ -137,6 +146,8 @@ export default class Clusters {
eventHub.$off('upgradeApplication', this.upgradeApplication);
eventHub.$off('upgradeFailed', this.upgradeFailed);
eventHub.$off('dismissUpgradeSuccess', this.dismissUpgradeSuccess);
+ eventHub.$off('saveKnativeDomain');
+ eventHub.$off('setKnativeHostname');
}
initPolling() {
@@ -177,6 +188,10 @@ export default class Clusters {
this.checkForNewInstalls(prevApplicationMap, this.store.state.applications);
this.updateContainer(prevStatus, this.store.state.status, this.store.state.statusReason);
+ this.toggleIngressDomainHelpText(
+ prevApplicationMap[INGRESS],
+ this.store.state.applications[INGRESS],
+ );
}
showToken() {
@@ -272,6 +287,28 @@ export default class Clusters {
this.store.updateAppProperty(appId, 'requestStatus', null);
}
+ toggleIngressDomainHelpText(ingressPreviousState, ingressNewState) {
+ const helpTextHidden = ingressNewState.status !== APPLICATION_STATUS.INSTALLED;
+ const domainSnippetText = `${ingressNewState.externalIp}${INGRESS_DOMAIN_SUFFIX}`;
+
+ if (ingressPreviousState.status !== ingressNewState.status) {
+ this.ingressDomainHelpText.classList.toggle('hide', helpTextHidden);
+ this.ingressDomainSnippet.textContent = domainSnippetText;
+ }
+ }
+
+ saveKnativeDomain(data) {
+ const appId = data.id;
+ this.store.updateAppProperty(appId, 'status', APPLICATION_STATUS.UPDATING);
+ this.service.updateApplication(appId, data.params);
+ }
+
+ setKnativeHostname(data) {
+ const appId = data.id;
+ this.store.updateAppProperty(appId, 'isEditingHostName', true);
+ this.store.updateAppProperty(appId, 'hostname', data.hostname);
+ }
+
destroy() {
this.destroyed = true;
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index 5952e93b9a7..19e5ac1567d 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -191,14 +191,7 @@ export default {
return this.status === APPLICATION_STATUS.UPDATE_ERRORED;
},
upgradeFailureDescription() {
- return sprintf(
- s__(
- 'ClusterIntegration|Something went wrong when upgrading %{title}. Please check the logs and try again.',
- ),
- {
- title: this.title,
- },
- );
+ return s__('ClusterIntegration|Update failed. Please check the logs and try again.');
},
upgradeSuccessDescription() {
return sprintf(s__('ClusterIntegration|%{title} upgraded successfully.'), {
@@ -210,9 +203,9 @@ export default {
if (this.upgradeAvailable && !this.upgradeFailed && !this.isUpgrading) {
label = s__('ClusterIntegration|Upgrade');
} else if (this.isUpgrading) {
- label = s__('ClusterIntegration|Upgrading');
+ label = s__('ClusterIntegration|Updating');
} else if (this.upgradeFailed) {
- label = s__('ClusterIntegration|Retry upgrade');
+ label = s__('ClusterIntegration|Retry update');
}
return label;
@@ -224,6 +217,14 @@ export default {
(this.upgradeRequested && !this.upgradeSuccessful)
);
},
+ shouldShowUpgradeDetails() {
+ // This method only returns true when;
+ // Upgrade was successful OR Upgrade failed
+ // AND new upgrade is unavailable AND version information is present.
+ return (
+ (this.upgradeSuccessful || this.upgradeFailed) && !this.upgradeAvailable && this.version
+ );
+ },
},
watch: {
status() {
@@ -303,7 +304,7 @@ export default {
</div>
<div
- v-if="(upgradeSuccessful || upgradeFailed) && !upgradeAvailable"
+ v-if="shouldShowUpgradeDetails"
class="form-text text-muted label p-0 js-cluster-application-upgrade-details"
>
{{ versionLabel }}
diff --git a/app/assets/javascripts/clusters/components/applications.vue b/app/assets/javascripts/clusters/components/applications.vue
index 0cf187d4189..d54f9ce552c 100644
--- a/app/assets/javascripts/clusters/components/applications.vue
+++ b/app/assets/javascripts/clusters/components/applications.vue
@@ -1,6 +1,7 @@
<script>
import _ from 'underscore';
import helmInstallIllustration from '@gitlab/svgs/dist/illustrations/kubernetes-installation.svg';
+import { GlLoadingIcon } from '@gitlab/ui';
import elasticsearchLogo from 'images/cluster_app_logos/elasticsearch.png';
import gitlabLogo from 'images/cluster_app_logos/gitlab.png';
import helmLogo from 'images/cluster_app_logos/helm.png';
@@ -15,11 +16,15 @@ import { s__, sprintf } from '../../locale';
import applicationRow from './application_row.vue';
import clipboardButton from '../../vue_shared/components/clipboard_button.vue';
import { CLUSTER_TYPE, APPLICATION_STATUS, INGRESS } from '../constants';
+import LoadingButton from '~/vue_shared/components/loading_button.vue';
+import eventHub from '~/clusters/event_hub';
export default {
components: {
applicationRow,
clipboardButton,
+ LoadingButton,
+ GlLoadingIcon,
},
props: {
type: {
@@ -86,53 +91,26 @@ export default {
ingressInstalled() {
return this.applications.ingress.status === APPLICATION_STATUS.INSTALLED;
},
- ingressExternalIp() {
- return this.applications.ingress.externalIp;
+ ingressExternalEndpoint() {
+ return this.applications.ingress.externalIp || this.applications.ingress.externalHostname;
},
certManagerInstalled() {
return this.applications.cert_manager.status === APPLICATION_STATUS.INSTALLED;
},
ingressDescription() {
- const extraCostParagraph = sprintf(
- _.escape(
- s__(
- `ClusterIntegration|%{boldNotice} This will add some extra resources
- like a load balancer, which may incur additional costs depending on
- the hosting provider your Kubernetes cluster is installed on. If you are using
- Google Kubernetes Engine, you can %{pricingLink}.`,
- ),
- ),
- {
- boldNotice: `<strong>${_.escape(s__('ClusterIntegration|Note:'))}</strong>`,
- pricingLink: `<a href="https://cloud.google.com/compute/pricing#lb" target="_blank" rel="noopener noreferrer">
- ${_.escape(s__('ClusterIntegration|check the pricing here'))}</a>`,
- },
- false,
- );
-
- const externalIpParagraph = sprintf(
+ return sprintf(
_.escape(
s__(
- `ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS
- at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}`,
+ `ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}.`,
),
),
{
- ingressHelpLink: `<a href="${this.ingressHelpPath}">
- ${_.escape(s__('ClusterIntegration|More information'))}
- </a>`,
+ pricingLink: `<strong><a href="https://cloud.google.com/compute/pricing#lb"
+ target="_blank" rel="noopener noreferrer">
+ ${_.escape(s__('ClusterIntegration|pricing'))}</a></strong>`,
},
false,
);
-
- return `
- <p>
- ${extraCostParagraph}
- </p>
- <p class="settings-message append-bottom-0">
- ${externalIpParagraph}
- </p>
- `;
},
certManagerDescription() {
return sprintf(
@@ -173,16 +151,70 @@ export default {
jupyterHostname() {
return this.applications.jupyter.hostname;
},
+ knative() {
+ return this.applications.knative;
+ },
knativeInstalled() {
- return this.applications.knative.status === APPLICATION_STATUS.INSTALLED;
+ return (
+ this.knative.status === APPLICATION_STATUS.INSTALLED ||
+ this.knativeUpgrading ||
+ this.knativeUpgradeFailed ||
+ this.knative.status === APPLICATION_STATUS.UPDATED
+ );
+ },
+ knativeUpgrading() {
+ return (
+ this.knative.status === APPLICATION_STATUS.UPDATING ||
+ this.knative.status === APPLICATION_STATUS.SCHEDULED
+ );
},
- knativeExternalIp() {
- return this.applications.knative.externalIp;
+ knativeUpgradeFailed() {
+ return this.knative.status === APPLICATION_STATUS.UPDATE_ERRORED;
+ },
+ knativeExternalEndpoint() {
+ return this.knative.externalIp || this.knative.externalHostname;
+ },
+ knativeDescription() {
+ return sprintf(
+ _.escape(
+ s__(
+ `ClusterIntegration|Installing Knative may incur additional costs. Learn more about %{pricingLink}.`,
+ ),
+ ),
+ {
+ pricingLink: `<strong><a href="https://cloud.google.com/compute/pricing#lb"
+ target="_blank" rel="noopener noreferrer">
+ ${_.escape(s__('ClusterIntegration|pricing'))}</a></strong>`,
+ },
+ false,
+ );
+ },
+ canUpdateKnativeEndpoint() {
+ return this.knativeExternalEndpoint && !this.knativeUpgradeFailed && !this.knativeUpgrading;
+ },
+ knativeHostname: {
+ get() {
+ return this.knative.hostname;
+ },
+ set(hostname) {
+ eventHub.$emit('setKnativeHostname', {
+ id: 'knative',
+ hostname,
+ });
+ },
},
},
created() {
this.helmInstallIllustration = helmInstallIllustration;
},
+ methods: {
+ saveKnativeDomain() {
+ eventHub.$emit('saveKnativeDomain', {
+ id: 'knative',
+ params: { hostname: this.knative.hostname },
+ });
+ },
+ },
};
</script>
@@ -247,31 +279,36 @@ export default {
<template v-if="ingressInstalled">
<div class="form-group">
- <label for="ingress-ip-address">
- {{ s__('ClusterIntegration|Ingress IP Address') }}
+ <label for="ingress-endpoint">
+ {{ s__('ClusterIntegration|Ingress Endpoint') }}
</label>
- <div v-if="ingressExternalIp" class="input-group">
+ <div v-if="ingressExternalEndpoint" class="input-group">
<input
- id="ingress-ip-address"
- :value="ingressExternalIp"
+ id="ingress-endpoint"
+ :value="ingressExternalEndpoint"
type="text"
- class="form-control js-ip-address"
+ class="form-control js-endpoint"
readonly
/>
<span class="input-group-append">
<clipboard-button
- :text="ingressExternalIp"
- :title="s__('ClusterIntegration|Copy Ingress IP Address to clipboard')"
+ :text="ingressExternalEndpoint"
+ :title="s__('ClusterIntegration|Copy Ingress Endpoint to clipboard')"
class="input-group-text js-clipboard-btn"
/>
</span>
</div>
- <input v-else type="text" class="form-control js-ip-address" readonly value="?" />
+ <div v-else class="input-group">
+ <input type="text" class="form-control js-endpoint" readonly />
+ <gl-loading-icon
+ class="position-absolute align-self-center ml-2 js-ingress-ip-loading-icon"
+ />
+ </div>
<p class="form-text text-muted">
{{
s__(`ClusterIntegration|Point a wildcard DNS to this
- generated IP address in order to access
- your application after it has been deployed.`)
+ generated endpoint in order to access
+ your application after it has been deployed.`)
}}
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
{{ __('More information') }}
@@ -279,19 +316,21 @@ export default {
</p>
</div>
- <p v-if="!ingressExternalIp" class="settings-message js-no-ip-message">
+ <p v-if="!ingressExternalEndpoint" class="settings-message js-no-endpoint-message">
{{
- s__(`ClusterIntegration|The IP address is in
- the process of being assigned. Please check your Kubernetes
- cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
+ s__(`ClusterIntegration|The endpoint is in
+ the process of being assigned. Please check your Kubernetes
+ cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
}}
- <a :href="ingressHelpPath" target="_blank" rel="noopener noreferrer">
+ <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
{{ __('More information') }}
</a>
</p>
</template>
- <div v-html="ingressDescription"></div>
+ <template v-if="!ingressInstalled">
+ <div class="bs-callout bs-callout-info" v-html="ingressDescription"></div>
+ </template>
</div>
</application-row>
<application-row
@@ -354,7 +393,6 @@ export default {
<div slot="description" v-html="prometheusDescription"></div>
</application-row>
<application-row
- v-if="isProjectCluster"
id="runner"
:logo-url="gitlabLogo"
:title="applications.runner.title"
@@ -370,9 +408,9 @@ export default {
>
<div slot="description">
{{
- s__(`ClusterIntegration|GitLab Runner connects to this
- project's repository and executes CI/CD jobs,
- pushing results back and deploying,
+ s__(`ClusterIntegration|GitLab Runner connects to the
+ repository and executes CI/CD jobs,
+ pushing results back and deploying
applications to production.`)
}}
</div>
@@ -401,7 +439,7 @@ export default {
}}
</p>
- <template v-if="ingressExternalIp">
+ <template v-if="ingressExternalEndpoint">
<div class="form-group">
<label for="jupyter-hostname">
{{ s__('ClusterIntegration|Jupyter Hostname') }}
@@ -426,7 +464,7 @@ export default {
<p v-if="ingressInstalled" class="form-text text-muted">
{{
s__(`ClusterIntegration|Replace this with your own hostname if you want.
- If you do so, point hostname to Ingress IP Address from above.`)
+ If you do so, point hostname to Ingress IP Address from above.`)
}}
<a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
{{ __('More information') }}
@@ -451,10 +489,10 @@ export default {
>
<div slot="description">
<span v-if="!rbac">
- <p v-if="!rbac" class="bs-callout bs-callout-info append-bottom-0">
+ <p v-if="!rbac" class="rbac-notice bs-callout bs-callout-info append-bottom-0">
{{
s__(`ClusterIntegration|You must have an RBAC-enabled cluster
- to install Knative.`)
+ to install Knative.`)
}}
<a :href="helpPath" target="_blank" rel="noopener noreferrer">
{{ __('More information') }}
@@ -471,76 +509,87 @@ export default {
}}
</p>
- <template v-if="knativeInstalled">
- <div class="form-group">
- <label for="knative-domainname">
- {{ s__('ClusterIntegration|Knative Domain Name:') }}
- </label>
- <input
- id="knative-domainname"
- v-model="applications.knative.hostname"
- type="text"
- class="form-control js-domainname"
- readonly
- />
- </div>
- </template>
- <template v-else-if="helmInstalled && rbac">
- <div class="form-group">
- <label for="knative-domainname">
- {{ s__('ClusterIntegration|Knative Domain Name:') }}
- </label>
- <input
- id="knative-domainname"
- v-model="applications.knative.hostname"
- type="text"
- class="form-control js-domainname"
- />
- </div>
- </template>
- <template v-if="knativeInstalled">
- <div class="form-group">
- <label for="knative-ip-address">
- {{ s__('ClusterIntegration|Knative IP Address:') }}
- </label>
- <div v-if="knativeExternalIp" class="input-group">
+ <div class="row">
+ <template v-if="knativeInstalled || (helmInstalled && rbac)">
+ <div
+ :class="{ 'col-md-6': knativeInstalled, 'col-12': helmInstalled && rbac }"
+ class="form-group col-sm-12 mb-0"
+ >
+ <label for="knative-domainname">
+ <strong>
+ {{ s__('ClusterIntegration|Knative Domain Name:') }}
+ </strong>
+ </label>
<input
- id="knative-ip-address"
- :value="knativeExternalIp"
+ id="knative-domainname"
+ v-model="knativeHostname"
type="text"
- class="form-control js-ip-address"
- readonly
+ class="form-control js-knative-domainname"
/>
- <span class="input-group-append">
- <clipboard-button
- :text="knativeExternalIp"
- :title="s__('ClusterIntegration|Copy Knative IP Address to clipboard')"
- class="input-group-text js-clipboard-btn"
+ </div>
+ </template>
+ <template v-if="knativeInstalled">
+ <div class="form-group col-sm-12 col-md-6 pl-md-0 mb-0 mt-3 mt-md-0">
+ <label for="knative-endpoint">
+ <strong>
+ {{ s__('ClusterIntegration|Knative Endpoint:') }}
+ </strong>
+ </label>
+ <div v-if="knativeExternalEndpoint" class="input-group">
+ <input
+ id="knative-endpoint"
+ :value="knativeExternalEndpoint"
+ type="text"
+ class="form-control js-knative-endpoint"
+ readonly
/>
- </span>
+ <span class="input-group-append">
+ <clipboard-button
+ :text="knativeExternalEndpoint"
+ :title="s__('ClusterIntegration|Copy Knative Endpoint to clipboard')"
+ class="input-group-text js-knative-endpoint-clipboard-btn"
+ />
+ </span>
+ </div>
+ <div v-else class="input-group">
+ <input type="text" class="form-control js-endpoint" readonly />
+ <gl-loading-icon
+ class="position-absolute align-self-center ml-2 js-knative-ip-loading-icon"
+ />
+ </div>
</div>
- <input v-else type="text" class="form-control js-ip-address" readonly value="?" />
- </div>
- <p v-if="!knativeExternalIp" class="settings-message js-no-ip-message">
- {{
- s__(`ClusterIntegration|The IP address is in
- the process of being assigned. Please check your Kubernetes
- cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
- }}
- </p>
+ <p class="form-text text-muted col-12">
+ {{
+ s__(
+ `ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint.`,
+ )
+ }}
+ <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
+ {{ __('More information') }}
+ </a>
+ </p>
- <p>
- {{
- s__(`ClusterIntegration|Point a wildcard DNS to this
- generated IP address in order to access
- your application after it has been deployed.`)
- }}
- <a :href="ingressDnsHelpPath" target="_blank" rel="noopener noreferrer">
- {{ __('More information') }}
- </a>
- </p>
- </template>
+ <p
+ v-if="!knativeExternalEndpoint"
+ class="settings-message js-no-knative-endpoint-message mt-2 mr-3 mb-0 ml-3"
+ >
+ {{
+ s__(`ClusterIntegration|The endpoint is in
+ the process of being assigned. Please check your Kubernetes
+ cluster or Quotas on Google Kubernetes Engine if it takes a long time.`)
+ }}
+ </p>
+
+ <button
+ v-if="canUpdateKnativeEndpoint"
+ class="btn btn-success js-knative-save-domain-button mt-3 ml-3"
+ @click="saveKnativeDomain"
+ >
+ {{ s__('ClusterIntegration|Save changes') }}
+ </button>
+ </template>
+ </div>
</div>
</application-row>
</div>
diff --git a/app/assets/javascripts/clusters/constants.js b/app/assets/javascripts/clusters/constants.js
index 39022879d91..67f481f2afb 100644
--- a/app/assets/javascripts/clusters/constants.js
+++ b/app/assets/javascripts/clusters/constants.js
@@ -28,3 +28,4 @@ export const JUPYTER = 'jupyter';
export const KNATIVE = 'knative';
export const RUNNER = 'runner';
export const CERT_MANAGER = 'cert_manager';
+export const INGRESS_DOMAIN_SUFFIX = '.nip.io';
diff --git a/app/assets/javascripts/clusters/services/clusters_service.js b/app/assets/javascripts/clusters/services/clusters_service.js
index 89dda4b7902..dea33ac44c5 100644
--- a/app/assets/javascripts/clusters/services/clusters_service.js
+++ b/app/assets/javascripts/clusters/services/clusters_service.js
@@ -12,6 +12,9 @@ export default class ClusterService {
jupyter: this.options.installJupyterEndpoint,
knative: this.options.installKnativeEndpoint,
};
+ this.appUpdateEndpointMap = {
+ knative: this.options.updateKnativeEndpoint,
+ };
}
fetchData() {
@@ -22,6 +25,10 @@ export default class ClusterService {
return axios.post(this.appInstallEndpointMap[appId], params);
}
+ updateApplication(appId, params) {
+ return axios.patch(this.appUpdateEndpointMap[appId], params);
+ }
+
static updateCluster(endpoint, data) {
return axios.put(endpoint, data);
}
diff --git a/app/assets/javascripts/clusters/stores/clusters_store.js b/app/assets/javascripts/clusters/stores/clusters_store.js
index d309678be27..92993337f02 100644
--- a/app/assets/javascripts/clusters/stores/clusters_store.js
+++ b/app/assets/javascripts/clusters/stores/clusters_store.js
@@ -25,6 +25,7 @@ export default class ClusterStore {
requestStatus: null,
requestReason: null,
externalIp: null,
+ externalHostname: null,
},
cert_manager: {
title: s__('ClusterIntegration|Cert-Manager'),
@@ -66,7 +67,9 @@ export default class ClusterStore {
requestStatus: null,
requestReason: null,
hostname: null,
+ isEditingHostName: false,
externalIp: null,
+ externalHostname: null,
},
},
};
@@ -119,6 +122,7 @@ export default class ClusterStore {
if (appId === INGRESS) {
this.state.applications.ingress.externalIp = serverAppEntry.external_ip;
+ this.state.applications.ingress.externalHostname = serverAppEntry.external_hostname;
} else if (appId === CERT_MANAGER) {
this.state.applications.cert_manager.email =
this.state.applications.cert_manager.email || serverAppEntry.email;
@@ -129,10 +133,14 @@ export default class ClusterStore {
? `jupyter.${this.state.applications.ingress.externalIp}.nip.io`
: '');
} else if (appId === KNATIVE) {
- this.state.applications.knative.hostname =
- serverAppEntry.hostname || this.state.applications.knative.hostname;
+ if (!this.state.applications.knative.isEditingHostName) {
+ this.state.applications.knative.hostname =
+ serverAppEntry.hostname || this.state.applications.knative.hostname;
+ }
this.state.applications.knative.externalIp =
serverAppEntry.external_ip || this.state.applications.knative.externalIp;
+ this.state.applications.knative.externalHostname =
+ serverAppEntry.external_hostname || this.state.applications.knative.externalHostname;
} else if (appId === RUNNER) {
this.state.applications.runner.version = version;
this.state.applications.runner.upgradeAvailable = upgradeAvailable;
diff --git a/app/assets/javascripts/commons/jquery.js b/app/assets/javascripts/commons/jquery.js
index 009153d0703..2f268419bff 100644
--- a/app/assets/javascripts/commons/jquery.js
+++ b/app/assets/javascripts/commons/jquery.js
@@ -3,7 +3,7 @@ import 'jquery';
// common jQuery plugins
import 'jquery-ujs';
import 'vendor/jquery.endless-scroll';
-import 'vendor/jquery.caret';
-import 'vendor/jquery.atwho';
+import 'jquery.caret'; // must be imported before at.js
+import 'at.js';
import 'vendor/jquery.scrollTo';
import 'jquery.waitforimages';
diff --git a/app/assets/javascripts/commons/polyfills.js b/app/assets/javascripts/commons/polyfills.js
index bffc025ced3..a0ca44caa07 100644
--- a/app/assets/javascripts/commons/polyfills.js
+++ b/app/assets/javascripts/commons/polyfills.js
@@ -7,6 +7,7 @@ import 'core-js/fn/array/includes';
import 'core-js/fn/object/assign';
import 'core-js/fn/object/values';
import 'core-js/fn/promise';
+import 'core-js/fn/promise/finally';
import 'core-js/fn/string/code-point-at';
import 'core-js/fn/string/from-code-point';
import 'core-js/fn/string/includes';
diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js
index 50efecb3475..67fcdd082a2 100644
--- a/app/assets/javascripts/contextual_sidebar.js
+++ b/app/assets/javascripts/contextual_sidebar.js
@@ -4,6 +4,10 @@ import _ from 'underscore';
import bp from './breakpoints';
import { parseBoolean } from '~/lib/utils/common_utils';
+// NOTE: at 1200px nav sidebar should not overlap the content
+// https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24555#note_134136110
+const NAV_SIDEBAR_BREAKPOINT = 1200;
+
export default class ContextualSidebar {
constructor() {
this.initDomElements();
@@ -26,44 +30,54 @@ export default class ContextualSidebar {
bindEvents() {
if (!this.$sidebar.length) return;
- document.addEventListener('click', e => {
- if (
- !e.target.closest('.nav-sidebar') &&
- (bp.getBreakpointSize() === 'sm' || bp.getBreakpointSize() === 'md')
- ) {
- this.toggleCollapsedSidebar(true, true);
- }
- });
this.$openSidebar.on('click', () => this.toggleSidebarNav(true));
this.$closeSidebar.on('click', () => this.toggleSidebarNav(false));
this.$overlay.on('click', () => this.toggleSidebarNav(false));
this.$sidebarToggle.on('click', () => {
- const value = !this.$sidebar.hasClass('sidebar-collapsed-desktop');
- this.toggleCollapsedSidebar(value, true);
+ if (!ContextualSidebar.isDesktopBreakpoint()) {
+ this.toggleSidebarNav(!this.$sidebar.hasClass('sidebar-expanded-mobile'));
+ } else {
+ const value = !this.$sidebar.hasClass('sidebar-collapsed-desktop');
+ this.toggleCollapsedSidebar(value, true);
+ }
});
$(window).on('resize', () => _.debounce(this.render(), 100));
}
+ // TODO: use the breakpoints from breakpoints.js once they have been updated for bootstrap 4
+ // See documentation: https://design.gitlab.com/regions/navigation#contextual-navigation
+ static isDesktopBreakpoint = () => bp.windowWidth() >= NAV_SIDEBAR_BREAKPOINT;
static setCollapsedCookie(value) {
- if (bp.getBreakpointSize() !== 'lg') {
+ if (!ContextualSidebar.isDesktopBreakpoint()) {
return;
}
Cookies.set('sidebar_collapsed', value, { expires: 365 * 10 });
}
toggleSidebarNav(show) {
- this.$sidebar.toggleClass('sidebar-expanded-mobile', show);
- this.$overlay.toggleClass('mobile-nav-open', show);
+ const breakpoint = bp.getBreakpointSize();
+ const dbp = ContextualSidebar.isDesktopBreakpoint();
+
+ this.$sidebar.toggleClass('sidebar-expanded-mobile', !dbp ? show : false);
+ this.$overlay.toggleClass(
+ 'mobile-nav-open',
+ breakpoint === 'xs' || breakpoint === 'sm' ? show : false,
+ );
this.$sidebar.removeClass('sidebar-collapsed-desktop');
}
toggleCollapsedSidebar(collapsed, saveCookie) {
const breakpoint = bp.getBreakpointSize();
+ const dbp = ContextualSidebar.isDesktopBreakpoint();
if (this.$sidebar.length) {
this.$sidebar.toggleClass('sidebar-collapsed-desktop', collapsed);
- this.$page.toggleClass('page-with-icon-sidebar', breakpoint === 'sm' ? true : collapsed);
+ this.$sidebar.toggleClass('sidebar-expanded-mobile', !dbp ? !collapsed : false);
+ this.$page.toggleClass(
+ 'page-with-icon-sidebar',
+ breakpoint === 'xs' || breakpoint === 'sm' ? true : collapsed,
+ );
}
if (saveCookie) {
@@ -84,13 +98,11 @@ export default class ContextualSidebar {
render() {
if (!this.$sidebar.length) return;
- const breakpoint = bp.getBreakpointSize();
-
- if (breakpoint === 'sm' || breakpoint === 'md') {
- this.toggleCollapsedSidebar(true, false);
- } else if (breakpoint === 'lg') {
+ if (!ContextualSidebar.isDesktopBreakpoint()) {
+ this.toggleSidebarNav(false);
+ } else {
const collapse = parseBoolean(Cookies.get('sidebar_collapsed'));
- this.toggleCollapsedSidebar(collapse, false);
+ this.toggleCollapsedSidebar(collapse, true);
}
}
}
diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue
index 8f47931d14a..e8f8c09152a 100644
--- a/app/assets/javascripts/diffs/components/app.vue
+++ b/app/assets/javascripts/diffs/components/app.vue
@@ -5,6 +5,7 @@ import { __ } from '~/locale';
import createFlash from '~/flash';
import { GlLoadingIcon } from '@gitlab/ui';
import PanelResizer from '~/vue_shared/components/panel_resizer.vue';
+import Mousetrap from 'mousetrap';
import eventHub from '../../notes/event_hub';
import CompareVersions from './compare_versions.vue';
import DiffFile from './diff_file.vue';
@@ -18,6 +19,7 @@ import {
MIN_TREE_WIDTH,
MAX_TREE_WIDTH,
TREE_HIDE_STATS_WIDTH,
+ MR_TREE_SHOW_KEY,
} from '../constants';
export default {
@@ -87,7 +89,7 @@ export default {
emailPatchPath: state => state.diffs.emailPatchPath,
}),
...mapState('diffs', ['showTreeList', 'isLoading', 'startVersion']),
- ...mapGetters('diffs', ['isParallelView']),
+ ...mapGetters('diffs', ['isParallelView', 'currentDiffIndex']),
...mapGetters(['isNotesFetched', 'getNoteableData']),
targetBranch() {
return {
@@ -149,6 +151,7 @@ export default {
},
beforeDestroy() {
eventHub.$off('fetchDiffData', this.fetchData);
+ this.removeEventListeners();
},
methods: {
...mapActions(['startTaskList']),
@@ -159,10 +162,14 @@ export default {
'assignDiscussionsToDiff',
'setHighlightedRow',
'cacheTreeListWidth',
+ 'scrollToFile',
+ 'toggleShowTreeList',
]),
fetchData() {
this.fetchDiffFiles()
.then(() => {
+ this.hideTreeListIfJustOneFile();
+
requestIdleCallback(
() => {
this.setDiscussions();
@@ -197,7 +204,42 @@ export default {
this.$nextTick(() => {
window.mrTabs.resetViewContainer();
window.mrTabs.expandViewContainer(this.showTreeList);
+ this.setEventListeners();
});
+ } else {
+ this.removeEventListeners();
+ }
+ },
+ setEventListeners() {
+ Mousetrap.bind(['[', 'k', ']', 'j'], (e, combo) => {
+ switch (combo) {
+ case '[':
+ case 'k':
+ this.jumpToFile(-1);
+ break;
+ case ']':
+ case 'j':
+ this.jumpToFile(+1);
+ break;
+ default:
+ break;
+ }
+ });
+ },
+ removeEventListeners() {
+ Mousetrap.unbind(['[', 'k', ']', 'j']);
+ },
+ jumpToFile(step) {
+ const targetIndex = this.currentDiffIndex + step;
+ if (targetIndex >= 0 && targetIndex < this.diffFiles.length) {
+ this.scrollToFile(this.diffFiles[targetIndex].file_path);
+ }
+ },
+ hideTreeListIfJustOneFile() {
+ const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY);
+
+ if ((storedTreeShow === null && this.diffFiles.length <= 1) || storedTreeShow === 'false') {
+ this.toggleShowTreeList(false);
}
},
},
diff --git a/app/assets/javascripts/diffs/components/compare_versions.vue b/app/assets/javascripts/diffs/components/compare_versions.vue
index 0bf2dde8b96..fe49dfff10b 100644
--- a/app/assets/javascripts/diffs/components/compare_versions.vue
+++ b/app/assets/javascripts/diffs/components/compare_versions.vue
@@ -125,9 +125,9 @@ export default {
>
{{ __('Show latest version') }}
</gl-button>
- <a v-show="hasCollapsedFile" class="btn btn-default append-right-8" @click="expandAllFiles">
+ <gl-button v-show="hasCollapsedFile" class="append-right-8" @click="expandAllFiles">
{{ __('Expand all') }}
- </a>
+ </gl-button>
<settings-dropdown />
</div>
</div>
diff --git a/app/assets/javascripts/diffs/components/diff_file.vue b/app/assets/javascripts/diffs/components/diff_file.vue
index 1141a197c6a..0e779e1be9a 100644
--- a/app/assets/javascripts/diffs/components/diff_file.vue
+++ b/app/assets/javascripts/diffs/components/diff_file.vue
@@ -73,13 +73,23 @@ export default {
if (!newVal && oldVal && !this.hasDiffLines) {
this.handleLoadCollapsedDiff();
}
+
+ this.setFileCollapsed({ filePath: this.file.file_path, collapsed: newVal });
+ },
+ 'file.viewer.collapsed': function setIsCollapsed(newVal) {
+ this.isCollapsed = newVal;
},
},
created() {
eventHub.$on(`loadCollapsedDiff/${this.file.file_hash}`, this.handleLoadCollapsedDiff);
},
methods: {
- ...mapActions('diffs', ['loadCollapsedDiff', 'assignDiscussionsToDiff', 'setRenderIt']),
+ ...mapActions('diffs', [
+ 'loadCollapsedDiff',
+ 'assignDiscussionsToDiff',
+ 'setRenderIt',
+ 'setFileCollapsed',
+ ]),
handleToggle() {
if (!this.hasDiffLines) {
this.handleLoadCollapsedDiff();
diff --git a/app/assets/javascripts/diffs/components/diff_file_header.vue b/app/assets/javascripts/diffs/components/diff_file_header.vue
index 2b801898345..d41d1464166 100644
--- a/app/assets/javascripts/diffs/components/diff_file_header.vue
+++ b/app/assets/javascripts/diffs/components/diff_file_header.vue
@@ -5,7 +5,7 @@ import { polyfillSticky } from '~/lib/utils/sticky';
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 { GlTooltipDirective } from '@gitlab/ui';
+import { GlButton, GlTooltipDirective, GlTooltip, GlLoadingIcon } from '@gitlab/ui';
import { truncateSha } from '~/lib/utils/text_utility';
import { __, s__, sprintf } from '~/locale';
import { diffViewerModes } from '~/ide/constants';
@@ -14,6 +14,9 @@ import DiffStats from './diff_stats.vue';
export default {
components: {
+ GlTooltip,
+ GlLoadingIcon,
+ GlButton,
ClipboardButton,
EditButton,
Icon,
@@ -130,7 +133,7 @@ export default {
polyfillSticky(this.$refs.header);
},
methods: {
- ...mapActions('diffs', ['toggleFileDiscussions']),
+ ...mapActions('diffs', ['toggleFileDiscussions', 'toggleFullDiff']),
handleToggleFile(e, checkTarget) {
if (
!checkTarget ||
@@ -236,12 +239,34 @@ export default {
<a
v-if="diffFile.replaced_view_path"
:href="diffFile.replaced_view_path"
- class="btn view-file js-view-file"
+ class="btn view-file js-view-replaced-file"
v-html="viewReplacedFileButtonText"
>
</a>
- <a :href="diffFile.view_path" class="btn view-file js-view-file" v-html="viewFileButtonText">
- </a>
+ <gl-tooltip :target="() => $refs.viewButton" placement="bottom">
+ <span v-html="viewFileButtonText"></span>
+ </gl-tooltip>
+ <gl-button
+ ref="viewButton"
+ :href="diffFile.view_path"
+ target="blank"
+ class="view-file js-view-file-button"
+ >
+ <icon name="external-link" />
+ </gl-button>
+ <gl-button
+ v-if="!diffFile.is_fully_expanded"
+ class="expand-file js-expand-file"
+ @click="toggleFullDiff(diffFile.file_path)"
+ >
+ <template v-if="diffFile.isShowingFullFile">
+ {{ s__('MRDiff|Show changes only') }}
+ </template>
+ <template v-else>
+ {{ s__('MRDiff|Show full file') }}
+ </template>
+ <gl-loading-icon v-if="diffFile.isLoadingFullFile" inline />
+ </gl-button>
<a
v-if="diffFile.external_url"
@@ -250,7 +275,7 @@ export default {
:title="`View on ${diffFile.formatted_external_url}`"
target="_blank"
rel="noopener noreferrer"
- class="btn btn-file-option"
+ class="btn btn-file-option js-external-url"
>
<icon name="external-link" />
</a>
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 18edbe286ba..bb66ab36283 100644
--- a/app/assets/javascripts/diffs/components/diff_line_note_form.vue
+++ b/app/assets/javascripts/diffs/components/diff_line_note_form.vue
@@ -1,6 +1,7 @@
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import { s__ } from '~/locale';
+import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form';
import noteForm from '../../notes/components/note_form.vue';
import autosave from '../../notes/mixins/autosave';
import { DIFF_NOTE_TYPE } from '../constants';
@@ -9,7 +10,7 @@ export default {
components: {
noteForm,
},
- mixins: [autosave],
+ mixins: [autosave, diffLineNoteFormMixin],
props: {
diffFileHash: {
type: String,
@@ -103,6 +104,7 @@ export default {
:help-page-path="helpPagePath"
save-button-title="Comment"
class="diff-comment-form"
+ @handleFormUpdateAddToReview="addToReview"
@cancelForm="handleCancelCommentForm"
@handleFormUpdate="handleSaveNote"
/>
diff --git a/app/assets/javascripts/diffs/components/edit_button.vue b/app/assets/javascripts/diffs/components/edit_button.vue
index 5d38d545ce8..f0cc5de4b33 100644
--- a/app/assets/javascripts/diffs/components/edit_button.vue
+++ b/app/assets/javascripts/diffs/components/edit_button.vue
@@ -1,5 +1,15 @@
<script>
+import { GlTooltipDirective, GlButton } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+
export default {
+ components: {
+ GlButton,
+ Icon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
props: {
editPath: {
type: String,
@@ -17,12 +27,7 @@ export default {
},
methods: {
handleEditClick(evt) {
- if (!this.canCurrentUserFork || this.canModifyBlob) {
- // if we can Edit, do default Edit button behavior
- return;
- }
-
- if (this.canCurrentUserFork) {
+ if (this.canCurrentUserFork && !this.canModifyBlob) {
evt.preventDefault();
this.$emit('showForkMessage');
}
@@ -32,5 +37,13 @@ export default {
</script>
<template>
- <a :href="editPath" class="btn btn-default js-edit-blob" @click="handleEditClick"> Edit </a>
+ <gl-button
+ v-gl-tooltip.bottom
+ :href="editPath"
+ :title="__('Edit file')"
+ class="js-edit-blob"
+ @click.native="handleEditClick"
+ >
+ <icon name="pencil" />
+ </gl-button>
</template>
diff --git a/app/assets/javascripts/diffs/components/inline_diff_view.vue b/app/assets/javascripts/diffs/components/inline_diff_view.vue
index e781397214d..8c76a555b62 100644
--- a/app/assets/javascripts/diffs/components/inline_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/inline_diff_view.vue
@@ -1,5 +1,6 @@
<script>
import { mapGetters } from 'vuex';
+import draftCommentsMixin from 'ee_else_ce/diffs/mixins/draft_comments';
import inlineDiffTableRow from './inline_diff_table_row.vue';
import inlineDiffCommentRow from './inline_diff_comment_row.vue';
@@ -7,7 +8,10 @@ export default {
components: {
inlineDiffCommentRow,
inlineDiffTableRow,
+ InlineDraftCommentRow: () =>
+ import('ee_component/batch_comments/components/inline_draft_comment_row.vue'),
},
+ mixins: [draftCommentsMixin],
props: {
diffFile: {
type: Object,
@@ -54,6 +58,11 @@ export default {
:line="line"
:help-page-path="helpPagePath"
/>
+ <inline-draft-comment-row
+ v-if="shouldRenderDraftRow(diffFile.file_hash, line)"
+ :key="`draft_${index}`"
+ :draft="draftForLine(diffFile.file_hash, line)"
+ />
</template>
</tbody>
</table>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
index caf0df8a4e3..c60246bf8ef 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_table_row.vue
@@ -140,7 +140,7 @@ export default {
:id="line.left.line_code"
:class="parallelViewLeftLineType"
class="line_content parallel left-side"
- @mousedown.native="handleParallelLineMouseDown"
+ @mousedown="handleParallelLineMouseDown"
v-html="line.left.rich_text"
></td>
</template>
@@ -171,7 +171,7 @@ export default {
},
]"
class="line_content parallel right-side"
- @mousedown.native="handleParallelLineMouseDown"
+ @mousedown="handleParallelLineMouseDown"
v-html="line.right.rich_text"
></td>
</template>
diff --git a/app/assets/javascripts/diffs/components/parallel_diff_view.vue b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
index 1bf693380db..41a80d99850 100644
--- a/app/assets/javascripts/diffs/components/parallel_diff_view.vue
+++ b/app/assets/javascripts/diffs/components/parallel_diff_view.vue
@@ -1,5 +1,6 @@
<script>
import { mapGetters } from 'vuex';
+import draftCommentsMixin from 'ee_else_ce/diffs/mixins/draft_comments';
import parallelDiffTableRow from './parallel_diff_table_row.vue';
import parallelDiffCommentRow from './parallel_diff_comment_row.vue';
@@ -7,7 +8,10 @@ export default {
components: {
parallelDiffTableRow,
parallelDiffCommentRow,
+ ParallelDraftCommentRow: () =>
+ import('ee_component/batch_comments/components/parallel_draft_comment_row.vue'),
},
+ mixins: [draftCommentsMixin],
props: {
diffFile: {
type: Object,
@@ -34,30 +38,34 @@ export default {
</script>
<template>
- <div
+ <table
:class="$options.userColorScheme"
:data-commit-id="commitId"
class="code diff-wrap-lines js-syntax-highlight text-file"
>
- <table>
- <tbody>
- <template v-for="(line, index) in diffLines">
- <parallel-diff-table-row
- :key="line.line_code"
- :file-hash="diffFile.file_hash"
- :context-lines-path="diffFile.context_lines_path"
- :line="line"
- :is-bottom="index + 1 === diffLinesLength"
- />
- <parallel-diff-comment-row
- :key="`dcr-${line.line_code || index}`"
- :line="line"
- :diff-file-hash="diffFile.file_hash"
- :line-index="index"
- :help-page-path="helpPagePath"
- />
- </template>
- </tbody>
- </table>
- </div>
+ <tbody>
+ <template v-for="(line, index) in diffLines">
+ <parallel-diff-table-row
+ :key="line.line_code"
+ :file-hash="diffFile.file_hash"
+ :context-lines-path="diffFile.context_lines_path"
+ :line="line"
+ :is-bottom="index + 1 === diffLinesLength"
+ />
+ <parallel-diff-comment-row
+ :key="`dcr-${line.line_code || index}`"
+ :line="line"
+ :diff-file-hash="diffFile.file_hash"
+ :line-index="index"
+ :help-page-path="helpPagePath"
+ />
+ <parallel-draft-comment-row
+ v-if="shouldRenderParallelDraftRow(diffFile.file_hash, line)"
+ :key="`drafts-${index}`"
+ :line="line"
+ :diff-file-content-sha="diffFile.file_hash"
+ />
+ </template>
+ </tbody>
+ </table>
</template>
diff --git a/app/assets/javascripts/diffs/components/tree_list.vue b/app/assets/javascripts/diffs/components/tree_list.vue
index 8fc3af15bea..384f33e0983 100644
--- a/app/assets/javascripts/diffs/components/tree_list.vue
+++ b/app/assets/javascripts/diffs/components/tree_list.vue
@@ -30,8 +30,9 @@ export default {
filteredTreeList() {
const search = this.search.toLowerCase().trim();
- if (search === '' || this.$options.fuzzyFileFinderEnabled)
+ if (search === '') {
return this.renderTreeList ? this.tree : this.allBlobs;
+ }
return this.allBlobs.reduce((acc, folder) => {
const tree = folder.tree.filter(f => f.path.toLowerCase().indexOf(search) >= 0);
@@ -51,13 +52,11 @@ export default {
},
},
methods: {
- ...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile', 'toggleFileFinder']),
+ ...mapActions('diffs', ['toggleTreeOpen', 'scrollToFile']),
clearSearch() {
this.search = '';
},
},
- shortcutKeyCharacter: `${/Mac/i.test(navigator.userAgent) ? '&#8984;' : 'Ctrl'}+P`,
- diffTreeFiltering: gon.features && gon.features.diffTreeFiltering,
};
</script>
@@ -66,36 +65,21 @@ export default {
<div class="append-bottom-8 position-relative tree-list-search d-flex">
<div class="flex-fill d-flex">
<icon name="search" class="position-absolute tree-list-icon" />
- <template v-if="$options.diffTreeFiltering">
- <input
- v-model="search"
- :placeholder="s__('MergeRequest|Filter files')"
- type="search"
- class="form-control"
- />
- <button
- v-show="search"
- :aria-label="__('Clear search')"
- type="button"
- class="position-absolute bg-transparent tree-list-icon tree-list-clear-icon border-0 p-0"
- @click="clearSearch"
- >
- <icon name="close" />
- </button>
- </template>
- <template v-else>
- <button
- type="button"
- class="form-control text-left text-secondary"
- @click="toggleFileFinder(true)"
- >
- {{ s__('MergeRequest|Search files') }}
- </button>
- <span
- class="position-absolute text-secondary diff-tree-search-shortcut"
- v-html="$options.shortcutKeyCharacter"
- ></span>
- </template>
+ <input
+ v-model="search"
+ :placeholder="s__('MergeRequest|Filter files')"
+ type="search"
+ class="form-control"
+ />
+ <button
+ v-show="search"
+ :aria-label="__('Clear search')"
+ type="button"
+ class="position-absolute bg-transparent tree-list-icon tree-list-clear-icon border-0 p-0"
+ @click="clearSearch"
+ >
+ <icon name="close" />
+ </button>
</div>
</div>
<div :class="{ 'pt-0 tree-list-blobs': !renderTreeList }" class="tree-list-scroll">
diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js
index 7002655ea49..6f380fe6ece 100644
--- a/app/assets/javascripts/diffs/constants.js
+++ b/app/assets/javascripts/diffs/constants.js
@@ -42,3 +42,8 @@ export const INITIAL_TREE_WIDTH = 320;
export const MIN_TREE_WIDTH = 240;
export const MAX_TREE_WIDTH = 400;
export const TREE_HIDE_STATS_WIDTH = 260;
+
+export const OLD_LINE_KEY = 'old_line';
+export const NEW_LINE_KEY = 'new_line';
+export const TYPE_KEY = 'type';
+export const LEFT_LINE_KEY = 'left';
diff --git a/app/assets/javascripts/diffs/mixins/draft_comments.js b/app/assets/javascripts/diffs/mixins/draft_comments.js
new file mode 100644
index 00000000000..cfa722b27f1
--- /dev/null
+++ b/app/assets/javascripts/diffs/mixins/draft_comments.js
@@ -0,0 +1,7 @@
+export default {
+ computed: {
+ shouldRenderDraftRow: () => () => false,
+ shouldRenderParallelDraftRow: () => () => false,
+ draftForLine: () => () => ({}),
+ },
+};
diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js
index 82ff2e3be76..b58ae0d248c 100644
--- a/app/assets/javascripts/diffs/store/actions.js
+++ b/app/assets/javascripts/diffs/store/actions.js
@@ -52,7 +52,9 @@ export const fetchDiffFiles = ({ state, commit }) => {
};
export const setHighlightedRow = ({ commit }, lineCode) => {
+ const fileHash = lineCode.split('_')[0];
commit(types.SET_HIGHLIGHTED_ROW, lineCode);
+ commit(types.UPDATE_CURRENT_DIFF_FILE_ID, fileHash);
};
// This is adding line discussions to the actual lines in the diff tree
@@ -262,13 +264,14 @@ export const scrollToFile = ({ state, commit }, path) => {
document.location.hash = fileHash;
commit(types.UPDATE_CURRENT_DIFF_FILE_ID, fileHash);
-
- setTimeout(() => commit(types.UPDATE_CURRENT_DIFF_FILE_ID, ''), 1000);
};
-export const toggleShowTreeList = ({ commit, state }) => {
+export const toggleShowTreeList = ({ commit, state }, saving = true) => {
commit(types.TOGGLE_SHOW_TREE_LIST);
- localStorage.setItem(MR_TREE_SHOW_KEY, state.showTreeList);
+
+ if (saving) {
+ localStorage.setItem(MR_TREE_SHOW_KEY, state.showTreeList);
+ }
};
export const openDiffFileCommentForm = ({ commit, getters }, formData) => {
@@ -309,5 +312,43 @@ export const cacheTreeListWidth = (_, size) => {
localStorage.setItem(TREE_LIST_WIDTH_STORAGE_KEY, size);
};
+export const requestFullDiff = ({ commit }, filePath) => commit(types.REQUEST_FULL_DIFF, filePath);
+export const receiveFullDiffSucess = ({ commit }, { filePath, data }) =>
+ commit(types.RECEIVE_FULL_DIFF_SUCCESS, { filePath, data });
+export const receiveFullDiffError = ({ commit }, filePath) => {
+ commit(types.RECEIVE_FULL_DIFF_ERROR, filePath);
+ createFlash(s__('MergeRequest|Error loading full diff. Please try again.'));
+};
+
+export const fetchFullDiff = ({ dispatch }, file) =>
+ axios
+ .get(file.context_lines_path, {
+ params: {
+ full: true,
+ from_merge_request: true,
+ },
+ })
+ .then(({ data }) => dispatch('receiveFullDiffSucess', { filePath: file.file_path, data }))
+ .then(() => scrollToElement(`#${file.file_hash}`))
+ .catch(() => dispatch('receiveFullDiffError', file.file_path));
+
+export const toggleFullDiff = ({ dispatch, getters, state }, filePath) => {
+ const file = state.diffFiles.find(f => f.file_path === filePath);
+
+ dispatch('requestFullDiff', filePath);
+
+ if (file.isShowingFullFile) {
+ dispatch('loadCollapsedDiff', file)
+ .then(() => dispatch('assignDiscussionsToDiff', getters.getDiffFileDiscussions(file)))
+ .then(() => scrollToElement(`#${file.file_hash}`))
+ .catch(() => dispatch('receiveFullDiffError', filePath));
+ } else {
+ dispatch('fetchFullDiff', file);
+ }
+};
+
+export const setFileCollapsed = ({ commit }, { filePath, collapsed }) =>
+ commit(types.SET_FILE_COLLAPSED, { filePath, collapsed });
+
// 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 4e7e5306995..bc27e263bff 100644
--- a/app/assets/javascripts/diffs/store/getters.js
+++ b/app/assets/javascripts/diffs/store/getters.js
@@ -100,5 +100,12 @@ export const diffFilesLength = state => state.diffFiles.length;
export const getCommentFormForDiffFile = state => fileHash =>
state.commentForms.find(form => form.fileHash === fileHash);
+/**
+ * Returns index of a currently selected diff in diffFiles
+ * @returns {number}
+ */
+export const currentDiffIndex = state =>
+ Math.max(0, state.diffFiles.findIndex(diff => diff.file_hash === state.currentDiffFileId));
+
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
diff --git a/app/assets/javascripts/diffs/store/modules/diff_state.js b/app/assets/javascripts/diffs/store/modules/diff_state.js
index 47f78a5db54..cf4dd93dbfb 100644
--- a/app/assets/javascripts/diffs/store/modules/diff_state.js
+++ b/app/assets/javascripts/diffs/store/modules/diff_state.js
@@ -1,13 +1,10 @@
import Cookies from 'js-cookie';
import { getParameterValues } from '~/lib/utils/url_utility';
-import bp from '~/breakpoints';
-import { parseBoolean } from '~/lib/utils/common_utils';
-import { INLINE_DIFF_VIEW_TYPE, DIFF_VIEW_COOKIE_NAME, MR_TREE_SHOW_KEY } from '../../constants';
+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;
-const storedTreeShow = localStorage.getItem(MR_TREE_SHOW_KEY);
export default () => ({
isLoading: true,
@@ -23,8 +20,7 @@ export default () => ({
diffViewType: viewTypeFromQueryString || viewTypeFromCookie || defaultViewType,
tree: [],
treeEntries: {},
- showTreeList:
- storedTreeShow === null ? bp.getBreakpointSize() !== 'xs' : parseBoolean(storedTreeShow),
+ showTreeList: true,
currentDiffFileId: '',
projectPath: '',
commentForms: [],
diff --git a/app/assets/javascripts/diffs/store/mutation_types.js b/app/assets/javascripts/diffs/store/mutation_types.js
index 71ad108ce88..adf56eba3f8 100644
--- a/app/assets/javascripts/diffs/store/mutation_types.js
+++ b/app/assets/javascripts/diffs/store/mutation_types.js
@@ -23,3 +23,8 @@ export const SET_TREE_DATA = 'SET_TREE_DATA';
export const SET_RENDER_TREE_LIST = 'SET_RENDER_TREE_LIST';
export const SET_SHOW_WHITESPACE = 'SET_SHOW_WHITESPACE';
export const TOGGLE_FILE_FINDER_VISIBLE = 'TOGGLE_FILE_FINDER_VISIBLE';
+
+export const REQUEST_FULL_DIFF = 'REQUEST_FULL_DIFF';
+export const RECEIVE_FULL_DIFF_SUCCESS = 'RECEIVE_FULL_DIFF_SUCCESS';
+export const RECEIVE_FULL_DIFF_ERROR = 'RECEIVE_FULL_DIFF_ERROR';
+export const SET_FILE_COLLAPSED = 'SET_FILE_COLLAPSED';
diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js
index 5a27388863c..856f4eaf00a 100644
--- a/app/assets/javascripts/diffs/store/mutations.js
+++ b/app/assets/javascripts/diffs/store/mutations.js
@@ -6,8 +6,10 @@ import {
addContextLines,
prepareDiffData,
isDiscussionApplicableToLine,
+ convertExpandLines,
} from './utils';
import * as types from './mutation_types';
+import { OLD_LINE_KEY, NEW_LINE_KEY, TYPE_KEY, LEFT_LINE_KEY } from '../constants';
export default {
[types.SET_BASE_CONFIG](state, options) {
@@ -102,7 +104,10 @@ export default {
[types.EXPAND_ALL_FILES](state) {
state.diffFiles = state.diffFiles.map(file => ({
...file,
- collapsed: false,
+ viewer: {
+ ...file.viewer,
+ collapsed: false,
+ },
}));
},
@@ -248,4 +253,61 @@ export default {
[types.TOGGLE_FILE_FINDER_VISIBLE](state, visible) {
state.fileFinderVisible = visible;
},
+ [types.REQUEST_FULL_DIFF](state, filePath) {
+ const file = findDiffFile(state.diffFiles, filePath, 'file_path');
+
+ file.isLoadingFullFile = true;
+ },
+ [types.RECEIVE_FULL_DIFF_ERROR](state, filePath) {
+ const file = findDiffFile(state.diffFiles, filePath, 'file_path');
+
+ file.isLoadingFullFile = false;
+ },
+ [types.RECEIVE_FULL_DIFF_SUCCESS](state, { filePath, data }) {
+ const file = findDiffFile(state.diffFiles, filePath, 'file_path');
+
+ file.isShowingFullFile = true;
+ file.isLoadingFullFile = false;
+
+ file.highlighted_diff_lines = convertExpandLines({
+ diffLines: file.highlighted_diff_lines,
+ typeKey: [TYPE_KEY],
+ oldLineKey: [OLD_LINE_KEY],
+ newLineKey: [NEW_LINE_KEY],
+ data,
+ mapLine: ({ line, oldLine, newLine }) => ({
+ ...line,
+ old_line: oldLine,
+ new_line: newLine,
+ line_code: `${file.file_hash}_${oldLine}_${newLine}`,
+ }),
+ });
+
+ file.parallel_diff_lines = convertExpandLines({
+ diffLines: file.parallel_diff_lines,
+ typeKey: [LEFT_LINE_KEY, TYPE_KEY],
+ oldLineKey: [LEFT_LINE_KEY, OLD_LINE_KEY],
+ newLineKey: [LEFT_LINE_KEY, NEW_LINE_KEY],
+ data,
+ mapLine: ({ line, oldLine, newLine }) => ({
+ left: {
+ ...line,
+ old_line: oldLine,
+ line_code: `${file.file_hash}_${oldLine}_${newLine}`,
+ },
+ right: {
+ ...line,
+ new_line: newLine,
+ line_code: `${file.file_hash}_${newLine}_${oldLine}`,
+ },
+ }),
+ });
+ },
+ [types.SET_FILE_COLLAPSED](state, { filePath, collapsed }) {
+ const file = state.diffFiles.find(f => f.file_path === filePath);
+
+ if (file && file.viewer) {
+ file.viewer.collapsed = collapsed;
+ }
+ },
};
diff --git a/app/assets/javascripts/diffs/store/utils.js b/app/assets/javascripts/diffs/store/utils.js
index 247d1e65fea..27a79369a24 100644
--- a/app/assets/javascripts/diffs/store/utils.js
+++ b/app/assets/javascripts/diffs/store/utils.js
@@ -15,8 +15,8 @@ import {
TREE_TYPE,
} from '../constants';
-export function findDiffFile(files, hash) {
- return files.filter(file => file.file_hash === hash)[0];
+export function findDiffFile(files, match, matchKey = 'file_hash') {
+ return files.find(file => file[matchKey] === match);
}
export const getReversePosition = linePosition => {
@@ -250,6 +250,8 @@ export function prepareDiffData(diffData) {
renderIt: showingLines < LINES_TO_BE_RENDERED_DIRECTLY,
collapsed:
file.viewer.name === diffViewerModes.text && showingLines > MAX_LINES_TO_BE_RENDERED,
+ isShowingFullFile: false,
+ isLoadingFullFile: false,
discussions: [],
});
}
@@ -411,3 +413,37 @@ export const getDiffMode = diffFile => {
diffModes.replaced
);
};
+
+export const convertExpandLines = ({
+ diffLines,
+ data,
+ typeKey,
+ oldLineKey,
+ newLineKey,
+ mapLine,
+}) => {
+ const dataLength = data.length;
+
+ return diffLines.reduce((acc, line, i) => {
+ if (_.property(typeKey)(line) === 'match') {
+ const beforeLine = diffLines[i - 1];
+ const afterLine = diffLines[i + 1];
+ const beforeLineIndex = _.property(newLineKey)(beforeLine) || 0;
+ const afterLineIndex = _.property(newLineKey)(afterLine) - 1 || dataLength;
+
+ acc.push(
+ ...data.slice(beforeLineIndex, afterLineIndex).map((l, index) => ({
+ ...mapLine({
+ line: { ...l, hasForm: false, discussions: [] },
+ oldLine: (_.property(oldLineKey)(beforeLine) || 0) + index + 1,
+ newLine: (_.property(newLineKey)(beforeLine) || 0) + index + 1,
+ }),
+ })),
+ );
+ } else {
+ acc.push(line);
+ }
+
+ return acc;
+ }, []);
+};
diff --git a/app/assets/javascripts/emoji/index.js b/app/assets/javascripts/emoji/index.js
index cd8dff40b88..b9b3b344524 100644
--- a/app/assets/javascripts/emoji/index.js
+++ b/app/assets/javascripts/emoji/index.js
@@ -1,13 +1,63 @@
import _ from 'underscore';
-import emojiMap from 'emojis/digests.json';
+import createFlash from '~/flash';
+import { s__ } from '~/locale';
import emojiAliases from 'emojis/aliases.json';
+import axios from '../lib/utils/axios_utils';
+import csrf from '../lib/utils/csrf';
-export const validEmojiNames = [...Object.keys(emojiMap), ...Object.keys(emojiAliases)];
+import AccessorUtilities from '../lib/utils/accessor';
+
+let emojiMap = null;
+let validEmojiNames = null;
+
+export const EMOJI_VERSION = '1';
+const EMOJI_VERSION_LOCALSTORAGE = `EMOJIS_${EMOJI_VERSION}`;
+
+const isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe();
+
+export function initEmojiMap() {
+ return new Promise((resolve, reject) => {
+ if (emojiMap) {
+ resolve(emojiMap);
+ } else if (isLocalStorageAvailable && window.localStorage.getItem(EMOJI_VERSION_LOCALSTORAGE)) {
+ emojiMap = JSON.parse(window.localStorage.getItem(EMOJI_VERSION_LOCALSTORAGE));
+ validEmojiNames = [...Object.keys(emojiMap), ...Object.keys(emojiAliases)];
+ resolve(emojiMap);
+ } else {
+ // We load the JSON from server
+ const axiosInstance = axios.create();
+
+ // If the static JSON file is on a CDN we don't want to send the default CSRF token
+ if (gon.asset_host) {
+ delete axiosInstance.defaults.headers.common[csrf.headerKey];
+ }
+
+ axiosInstance
+ .get(`${gon.relative_url_root || ''}/-/emojis/${EMOJI_VERSION}/emojis.json`)
+ .then(({ data }) => {
+ emojiMap = data;
+ validEmojiNames = [...Object.keys(emojiMap), ...Object.keys(emojiAliases)];
+ resolve(emojiMap);
+ if (isLocalStorageAvailable) {
+ window.localStorage.setItem(EMOJI_VERSION_LOCALSTORAGE, JSON.stringify(emojiMap));
+ }
+ })
+ .catch(err => {
+ createFlash(s__('Emojis|Something went wrong while loading emojis.'));
+ reject(err);
+ });
+ }
+ });
+}
export function normalizeEmojiName(name) {
return Object.prototype.hasOwnProperty.call(emojiAliases, name) ? emojiAliases[name] : name;
}
+export function getValidEmojiNames() {
+ return validEmojiNames;
+}
+
export function isEmojiNameValid(name) {
return validEmojiNames.indexOf(name) >= 0;
}
@@ -36,8 +86,8 @@ export function getEmojiCategoryMap() {
};
Object.keys(emojiMap).forEach(name => {
const emoji = emojiMap[name];
- if (emojiCategoryMap[emoji.category]) {
- emojiCategoryMap[emoji.category].push(name);
+ if (emojiCategoryMap[emoji.c]) {
+ emojiCategoryMap[emoji.c].push(name);
}
});
}
@@ -58,8 +108,9 @@ export function getEmojiInfo(query) {
}
export function emojiFallbackImageSrc(inputName) {
- const { name, digest } = getEmojiInfo(inputName);
- return `${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/emoji/${name}-${digest}.png`;
+ const { name } = getEmojiInfo(inputName);
+ return `${gon.asset_host || ''}${gon.relative_url_root ||
+ ''}/-/emojis/${EMOJI_VERSION}/${name}.png`;
}
export function emojiImageTag(name, src) {
@@ -68,9 +119,8 @@ export function emojiImageTag(name, src) {
export function glEmojiTag(inputName, options) {
const opts = { sprite: false, forceFallback: false, ...options };
- const { name, ...emojiInfo } = getEmojiInfo(inputName);
+ const name = normalizeEmojiName(inputName);
- const fallbackImageSrc = emojiFallbackImageSrc(name);
const fallbackSpriteClass = `emoji-${name}`;
const classList = [];
@@ -79,24 +129,19 @@ export function glEmojiTag(inputName, options) {
classList.push(fallbackSpriteClass);
}
const classAttribute = classList.length > 0 ? `class="${classList.join(' ')}"` : '';
+
const fallbackSpriteAttribute = opts.sprite
? `data-fallback-sprite-class="${fallbackSpriteClass}"`
: '';
- let contents = emojiInfo.moji;
- if (opts.forceFallback && !opts.sprite) {
- contents = emojiImageTag(name, fallbackImageSrc);
- }
+ const forceFallbackAttribute = opts.forceFallback ? 'data-force-fallback="true"' : '';
return `
<gl-emoji
${classAttribute}
data-name="${name}"
- data-fallback-src="${fallbackImageSrc}"
${fallbackSpriteAttribute}
- data-unicode-version="${emojiInfo.unicodeVersion}"
- title="${emojiInfo.description}"
+ ${forceFallbackAttribute}
>
- ${contents}
</gl-emoji>
`;
}
diff --git a/app/assets/javascripts/emoji/support/index.js b/app/assets/javascripts/emoji/support/index.js
index 1f7852dd487..ed9bfb8bc78 100644
--- a/app/assets/javascripts/emoji/support/index.js
+++ b/app/assets/javascripts/emoji/support/index.js
@@ -5,6 +5,9 @@ import getUnicodeSupportMap from './unicode_support_map';
let browserUnicodeSupportMap;
export default function isEmojiUnicodeSupportedByBrowser(emojiUnicode, unicodeVersion) {
+ // Our Spec browser would fail producing emoji maps
+ if (/\bHeadlessChrome\//.test(navigator.userAgent)) return true;
+
browserUnicodeSupportMap = browserUnicodeSupportMap || getUnicodeSupportMap();
return isEmojiUnicodeSupported(browserUnicodeSupportMap, emojiUnicode, unicodeVersion);
}
diff --git a/app/assets/javascripts/environments/components/confirm_rollback_modal.vue b/app/assets/javascripts/environments/components/confirm_rollback_modal.vue
new file mode 100644
index 00000000000..a8ee3f4ac10
--- /dev/null
+++ b/app/assets/javascripts/environments/components/confirm_rollback_modal.vue
@@ -0,0 +1,108 @@
+<script>
+/**
+ * Render modal to confirm rollback/redeploy.
+ */
+
+import _ from 'underscore';
+import { GlModal } from '@gitlab/ui';
+import { s__, sprintf } from '~/locale';
+
+import eventHub from '../event_hub';
+
+export default {
+ name: 'ConfirmRollbackModal',
+
+ components: {
+ GlModal,
+ },
+
+ props: {
+ environment: {
+ type: Object,
+ required: true,
+ },
+ },
+
+ computed: {
+ modalTitle() {
+ const title = this.environment.isLastDeployment
+ ? s__('Environments|Re-deploy environment %{name}?')
+ : s__('Environments|Rollback environment %{name}?');
+
+ return sprintf(title, {
+ name: _.escape(this.environment.name),
+ });
+ },
+
+ commitShortSha() {
+ const { last_deployment } = this.environment;
+ return this.commitData(last_deployment, 'short_id');
+ },
+
+ commitUrl() {
+ const { last_deployment } = this.environment;
+ return this.commitData(last_deployment, 'commit_path');
+ },
+
+ commitTitle() {
+ const { last_deployment } = this.environment;
+ return this.commitData(last_deployment, 'title');
+ },
+
+ modalText() {
+ const linkStart = `<a class="commit-sha" href="${_.escape(this.commitUrl)}">`;
+ const commitId = _.escape(this.commitShortSha);
+ const linkEnd = '</a>';
+ const name = _.escape(this.name);
+ const body = this.environment.isLastDeployment
+ ? s__(
+ 'Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?',
+ )
+ : s__(
+ 'Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?',
+ );
+ return sprintf(
+ body,
+ {
+ commitId,
+ linkStart,
+ linkEnd,
+ name,
+ },
+ false,
+ );
+ },
+
+ modalActionText() {
+ return this.environment.isLastDeployment
+ ? s__('Environments|Re-deploy')
+ : s__('Environments|Rollback');
+ },
+ },
+
+ methods: {
+ onOk() {
+ eventHub.$emit('rollbackEnvironment', this.environment);
+ },
+
+ commitData(lastDeployment, key) {
+ if (lastDeployment && lastDeployment.commit) {
+ return lastDeployment.commit[key];
+ }
+
+ return '';
+ },
+ },
+};
+</script>
+<template>
+ <gl-modal
+ :title="modalTitle"
+ modal-id="confirm-rollback-modal"
+ :ok-title="modalActionText"
+ ok-variant="danger"
+ @ok="onOk"
+ >
+ <p v-html="modalText"></p>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/environments/components/container.vue b/app/assets/javascripts/environments/components/container.vue
index 6ece8b92a30..be80661223c 100644
--- a/app/assets/javascripts/environments/components/container.vue
+++ b/app/assets/javascripts/environments/components/container.vue
@@ -1,14 +1,16 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
-import tablePagination from '../../vue_shared/components/table_pagination.vue';
-import environmentTable from '../components/environments_table.vue';
+import TablePagination from '~/vue_shared/components/table_pagination.vue';
+import containerMixin from 'ee_else_ce/environments/mixins/container_mixin';
+import EnvironmentTable from '../components/environments_table.vue';
export default {
components: {
- environmentTable,
- tablePagination,
+ EnvironmentTable,
+ TablePagination,
GlLoadingIcon,
},
+ mixins: [containerMixin],
props: {
isLoading: {
type: Boolean,
@@ -47,7 +49,15 @@ export default {
<slot name="emptyState"></slot>
<div v-if="!isLoading && environments.length > 0" class="table-holder">
- <environment-table :environments="environments" :can-read-environment="canReadEnvironment" />
+ <environment-table
+ :environments="environments"
+ :can-read-environment="canReadEnvironment"
+ :canary-deployment-feature-id="canaryDeploymentFeatureId"
+ :show-canary-deployment-callout="showCanaryDeploymentCallout"
+ :user-callouts-path="userCalloutsPath"
+ :lock-promotion-svg-path="lockPromotionSvgPath"
+ :help-canary-deployments-path="helpCanaryDeploymentsPath"
+ />
<table-pagination
v-if="pagination && pagination.totalPages > 1"
diff --git a/app/assets/javascripts/environments/components/environment_item.vue b/app/assets/javascripts/environments/components/environment_item.vue
index 503c1b38f71..a092bdfbc6c 100644
--- a/app/assets/javascripts/environments/components/environment_item.vue
+++ b/app/assets/javascripts/environments/components/environment_item.vue
@@ -3,8 +3,8 @@ import Timeago from 'timeago.js';
import _ from 'underscore';
import { GlTooltipDirective } from '@gitlab/ui';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
-import { humanize } from '~/lib/utils/text_utility';
import Icon from '~/vue_shared/components/icon.vue';
+import environmentItemMixin from 'ee_else_ce/environments/mixins/environment_item_mixin';
import ActionsComponent from './environment_actions.vue';
import ExternalUrlComponent from './environment_external_url.vue';
import StopComponent from './environment_stop.vue';
@@ -35,10 +35,10 @@ export default {
TerminalButtonComponent,
MonitoringButtonComponent,
},
-
directives: {
GlTooltip: GlTooltipDirective,
},
+ mixins: [environmentItemMixin],
props: {
model: {
@@ -156,7 +156,7 @@ export default {
const combinedActions = (manualActions || []).concat(scheduledActions || []);
return combinedActions.map(action => ({
...action,
- name: humanize(action.name),
+ name: action.name,
}));
},
@@ -468,9 +468,18 @@ export default {
<div v-if="!model.isFolder" class="table-mobile-header" role="rowheader">
{{ s__('Environments|Environment') }}
</div>
+
+ <span v-if="shouldRenderDeployBoard" class="deploy-board-icon" @click="toggleDeployBoard">
+ <icon :name="deployIconName" />
+ </span>
+
<span v-if="!model.isFolder" class="environment-name table-mobile-content">
<a class="qa-environment-link" :href="environmentPath"> {{ model.name }} </a>
+ <span v-if="isProtected" class="badge badge-success">
+ {{ s__('Environments|protected') }}
+ </span>
</span>
+
<span v-else class="folder-name" role="button" @click="onClickFolder">
<icon :name="folderIconName" class="folder-icon" />
@@ -556,6 +565,7 @@ export default {
<rollback-component
v-if="canRetry"
+ :environment="model"
:is-last-deployment="isLastDeployment"
:retry-url="retryUrl"
/>
diff --git a/app/assets/javascripts/environments/components/environment_rollback.vue b/app/assets/javascripts/environments/components/environment_rollback.vue
index 50c86af057c..266cdc42518 100644
--- a/app/assets/javascripts/environments/components/environment_rollback.vue
+++ b/app/assets/javascripts/environments/components/environment_rollback.vue
@@ -5,29 +5,38 @@
*
* Makes a post request when the button is clicked.
*/
+import { GlTooltipDirective, GlLoadingIcon, GlModalDirective, GlButton } from '@gitlab/ui';
import { s__ } from '~/locale';
-import { GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
+import ConfirmRollbackModal from './confirm_rollback_modal.vue';
import eventHub from '../event_hub';
export default {
components: {
Icon,
GlLoadingIcon,
+ GlButton,
+ ConfirmRollbackModal,
},
directives: {
GlTooltip: GlTooltipDirective,
+ GlModal: GlModalDirective,
},
props: {
- retryUrl: {
- type: String,
- default: '',
- },
-
isLastDeployment: {
type: Boolean,
default: true,
},
+
+ environment: {
+ type: Object,
+ required: true,
+ },
+
+ retryUrl: {
+ type: String,
+ required: true,
+ },
},
data() {
return {
@@ -45,23 +54,31 @@ export default {
methods: {
onClick() {
- this.isLoading = true;
-
- eventHub.$emit('postAction', { endpoint: this.retryUrl });
+ eventHub.$emit('requestRollbackEnvironment', {
+ ...this.environment,
+ retryUrl: this.retryUrl,
+ isLastDeployment: this.isLastDeployment,
+ });
+ eventHub.$on('rollbackEnvironment', environment => {
+ if (environment.id === this.environment.id) {
+ this.isLoading = true;
+ }
+ });
},
},
};
</script>
<template>
- <button
+ <gl-button
v-gl-tooltip
+ v-gl-modal.confirm-rollback-modal
+ variant="secondary"
:disabled="isLoading"
:title="title"
- type="button"
- class="btn d-none d-sm-none d-md-block"
+ class="d-none d-md-block"
@click="onClick"
>
<icon v-if="isLastDeployment" name="repeat" /> <icon v-else name="redo" />
<gl-loading-icon v-if="isLoading" />
- </button>
+ </gl-button>
</template>
diff --git a/app/assets/javascripts/environments/components/environments_app.vue b/app/assets/javascripts/environments/components/environments_app.vue
index aa2417d3194..ec78240217b 100644
--- a/app/assets/javascripts/environments/components/environments_app.vue
+++ b/app/assets/javascripts/environments/components/environments_app.vue
@@ -1,4 +1,5 @@
<script>
+import envrionmentsAppMixin from 'ee_else_ce/environments/mixins/environments_app_mixin';
import Flash from '../../flash';
import { s__ } from '../../locale';
import emptyState from './empty_state.vue';
@@ -6,14 +7,16 @@ 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';
+import ConfirmRollbackModal from './confirm_rollback_modal.vue';
export default {
components: {
emptyState,
StopEnvironmentModal,
+ ConfirmRollbackModal,
},
- mixins: [CIPaginationMixin, environmentsMixin],
+ mixins: [CIPaginationMixin, environmentsMixin, envrionmentsAppMixin],
props: {
endpoint: {
@@ -87,14 +90,15 @@ export default {
<template>
<div :class="cssContainerClass">
<stop-environment-modal :environment="environmentInStopModal" />
+ <confirm-rollback-modal :environment="environmentInRollbackModal" />
<div class="top-area">
<tabs :tabs="tabs" scope="environments" @onChangeTab="onChangeTab" />
<div v-if="canCreateEnvironment && !isLoading" class="nav-controls">
- <a :href="newEnvironmentPath" class="btn btn-success">{{
- s__('Environments|New environment')
- }}</a>
+ <a :href="newEnvironmentPath" class="btn btn-success">
+ {{ s__('Environments|New environment') }}
+ </a>
</div>
</div>
@@ -103,6 +107,11 @@ export default {
:environments="state.environments"
:pagination="state.paginationInformation"
:can-read-environment="canReadEnvironment"
+ :canary-deployment-feature-id="canaryDeploymentFeatureId"
+ :show-canary-deployment-callout="showCanaryDeploymentCallout"
+ :user-callouts-path="userCalloutsPath"
+ :lock-promotion-svg-path="lockPromotionSvgPath"
+ :help-canary-deployments-path="helpCanaryDeploymentsPath"
@onChangePage="onChangePage"
>
<empty-state
diff --git a/app/assets/javascripts/environments/components/environments_table.vue b/app/assets/javascripts/environments/components/environments_table.vue
index e2c304de00a..55613d815ce 100644
--- a/app/assets/javascripts/environments/components/environments_table.vue
+++ b/app/assets/javascripts/environments/components/environments_table.vue
@@ -3,27 +3,40 @@
* Render environments table.
*/
import { GlLoadingIcon } from '@gitlab/ui';
-import environmentItem from './environment_item.vue';
+import _ from 'underscore';
+import environmentTableMixin from 'ee_else_ce/environments/mixins/environments_table_mixin';
+import EnvironmentItem from './environment_item.vue';
export default {
components: {
- environmentItem,
+ EnvironmentItem,
GlLoadingIcon,
+ DeployBoard: () => import('ee_component/environments/components/deploy_board_component.vue'),
+ CanaryDeploymentCallout: () =>
+ import('ee_component/environments/components/canary_deployment_callout.vue'),
},
-
+ mixins: [environmentTableMixin],
props: {
environments: {
type: Array,
required: true,
default: () => [],
},
-
canReadEnvironment: {
type: Boolean,
required: false,
default: false,
},
},
+ computed: {
+ sortedEnvironments() {
+ return this.sortEnvironments(this.environments).map(env =>
+ this.shouldRenderFolderContent(env)
+ ? { ...env, children: this.sortEnvironments(env.children) }
+ : env,
+ );
+ },
+ },
methods: {
folderUrl(model) {
return `${window.location.pathname}/folders/${model.folderName}`;
@@ -31,6 +44,30 @@ export default {
shouldRenderFolderContent(env) {
return env.isFolder && env.isOpen && env.children && env.children.length > 0;
},
+ sortEnvironments(environments) {
+ /*
+ * The sorting algorithm should sort in the following priorities:
+ *
+ * 1. folders first,
+ * 2. last updated descending,
+ * 3. by name ascending,
+ *
+ * the sorting algorithm must:
+ *
+ * 1. Sort by name ascending,
+ * 2. Reverse (sort by name descending),
+ * 3. Sort by last deployment ascending,
+ * 4. Reverse (last deployment descending, name ascending),
+ * 5. Put folders first.
+ */
+ return _.chain(environments)
+ .sortBy(env => (env.isFolder ? env.folderName : env.name))
+ .reverse()
+ .sortBy(env => (env.last_deployment ? env.last_deployment.created_at : '0000'))
+ .reverse()
+ .sortBy(env => (env.isFolder ? -1 : 1))
+ .value();
+ },
},
};
</script>
@@ -53,7 +90,7 @@ export default {
{{ s__('Environments|Updated') }}
</div>
</div>
- <template v-for="(model, i) in environments" :model="model">
+ <template v-for="(model, i) in sortedEnvironments" :model="model">
<div
is="environment-item"
:key="`environment-item-${i}`"
@@ -61,6 +98,21 @@ export default {
:can-read-environment="canReadEnvironment"
/>
+ <div
+ v-if="shouldRenderDeployBoard(model)"
+ :key="`deploy-board-row-${i}`"
+ class="js-deploy-board-row"
+ >
+ <div class="deploy-board-container">
+ <deploy-board
+ :deploy-board-data="model.deployBoardData"
+ :is-loading="model.isLoadingDeployBoard"
+ :is-empty="model.isEmptyDeployBoard"
+ :logs-path="model.logs_path"
+ />
+ </div>
+ </div>
+
<template v-if="shouldRenderFolderContent(model)">
<div v-if="model.isLoadingFolderContent" :key="`loading-item-${i}`">
<gl-loading-icon :size="2" class="prepend-top-16" />
@@ -77,13 +129,24 @@ export default {
<div :key="`sub-div-${i}`">
<div class="text-center prepend-top-10">
- <a :href="folderUrl(model)" class="btn btn-default">{{
- s__('Environments|Show all')
- }}</a>
+ <a :href="folderUrl(model)" class="btn btn-default">
+ {{ s__('Environments|Show all') }}
+ </a>
</div>
</div>
</template>
</template>
+
+ <template v-if="shouldShowCanaryCallout(model)">
+ <canary-deployment-callout
+ :key="`canary-promo-${i}`"
+ :canary-deployment-feature-id="canaryDeploymentFeatureId"
+ :user-callouts-path="userCalloutsPath"
+ :lock-promotion-svg-path="lockPromotionSvgPath"
+ :help-canary-deployments-path="helpCanaryDeploymentsPath"
+ :data-js-canary-promo-key="i"
+ />
+ </template>
</template>
</div>
</template>
diff --git a/app/assets/javascripts/environments/folder/environments_folder_bundle.js b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
index 56e7f69cad6..c1bfe8d05fe 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_bundle.js
+++ b/app/assets/javascripts/environments/folder/environments_folder_bundle.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import canaryCalloutMixin from 'ee_else_ce/environments/mixins/canary_callout_mixin';
import environmentsFolderApp from './environments_folder_view.vue';
import { parseBoolean } from '../../lib/utils/common_utils';
import Translate from '../../vue_shared/translate';
@@ -11,6 +12,7 @@ export default () =>
components: {
environmentsFolderApp,
},
+ mixins: [canaryCalloutMixin],
data() {
const environmentsData = document.querySelector(this.$options.el).dataset;
@@ -28,6 +30,7 @@ export default () =>
folderName: this.folderName,
cssContainerClass: this.cssContainerClass,
canReadEnvironment: this.canReadEnvironment,
+ ...this.canaryCalloutProps,
},
});
},
diff --git a/app/assets/javascripts/environments/folder/environments_folder_view.vue b/app/assets/javascripts/environments/folder/environments_folder_view.vue
index 80f0e00400b..6fd0561f682 100644
--- a/app/assets/javascripts/environments/folder/environments_folder_view.vue
+++ b/app/assets/javascripts/environments/folder/environments_folder_view.vue
@@ -1,4 +1,5 @@
<script>
+import folderMixin from 'ee_else_ce/environments/mixins/environments_folder_view_mixin';
import environmentsMixin from '../mixins/environments_mixin';
import CIPaginationMixin from '../../vue_shared/mixins/ci_pagination_api_mixin';
import StopEnvironmentModal from '../components/stop_environment_modal.vue';
@@ -8,7 +9,7 @@ export default {
StopEnvironmentModal,
},
- mixins: [environmentsMixin, CIPaginationMixin],
+ mixins: [environmentsMixin, CIPaginationMixin, folderMixin],
props: {
endpoint: {
@@ -41,7 +42,8 @@ export default {
<div v-if="!isLoading" class="top-area">
<h4 class="js-folder-name environments-folder-name">
- {{ s__('Environments|Environments') }} / <b>{{ folderName }}</b>
+ {{ s__('Environments|Environments') }} /
+ <b>{{ folderName }}</b>
</h4>
<tabs :tabs="tabs" scope="environments" @onChangeTab="onChangeTab" />
@@ -52,6 +54,11 @@ export default {
:environments="state.environments"
:pagination="state.paginationInformation"
:can-read-environment="canReadEnvironment"
+ :canary-deployment-feature-id="canaryDeploymentFeatureId"
+ :show-canary-deployment-callout="showCanaryDeploymentCallout"
+ :user-callouts-path="userCalloutsPath"
+ :lock-promotion-svg-path="lockPromotionSvgPath"
+ :help-canary-deployments-path="helpCanaryDeploymentsPath"
@onChangePage="onChangePage"
/>
</div>
diff --git a/app/assets/javascripts/environments/index.js b/app/assets/javascripts/environments/index.js
index 6af66d0f86e..b53d42f202b 100644
--- a/app/assets/javascripts/environments/index.js
+++ b/app/assets/javascripts/environments/index.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import canaryCalloutMixin from 'ee_else_ce/environments/mixins/canary_callout_mixin';
import environmentsComponent from './components/environments_app.vue';
import { parseBoolean } from '../lib/utils/common_utils';
import Translate from '../vue_shared/translate';
@@ -11,6 +12,7 @@ export default () =>
components: {
environmentsComponent,
},
+ mixins: [canaryCalloutMixin],
data() {
const environmentsData = document.querySelector(this.$options.el).dataset;
@@ -32,6 +34,7 @@ export default () =>
cssContainerClass: this.cssContainerClass,
canCreateEnvironment: this.canCreateEnvironment,
canReadEnvironment: this.canReadEnvironment,
+ ...this.canaryCalloutProps,
},
});
},
diff --git a/app/assets/javascripts/environments/mixins/canary_callout_mixin.js b/app/assets/javascripts/environments/mixins/canary_callout_mixin.js
new file mode 100644
index 00000000000..f6d3d67b777
--- /dev/null
+++ b/app/assets/javascripts/environments/mixins/canary_callout_mixin.js
@@ -0,0 +1,5 @@
+export default {
+ computed: {
+ canaryCalloutProps() {},
+ },
+};
diff --git a/app/assets/javascripts/environments/mixins/container_mixin.js b/app/assets/javascripts/environments/mixins/container_mixin.js
new file mode 100644
index 00000000000..f2907c120f8
--- /dev/null
+++ b/app/assets/javascripts/environments/mixins/container_mixin.js
@@ -0,0 +1,29 @@
+export default {
+ props: {
+ canaryDeploymentFeatureId: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ showCanaryDeploymentCallout: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ userCalloutsPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ lockPromotionSvgPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ helpCanaryDeploymentsPath: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ },
+};
diff --git a/app/assets/javascripts/environments/mixins/environment_item_mixin.js b/app/assets/javascripts/environments/mixins/environment_item_mixin.js
new file mode 100644
index 00000000000..2dfed36ec99
--- /dev/null
+++ b/app/assets/javascripts/environments/mixins/environment_item_mixin.js
@@ -0,0 +1,13 @@
+export default {
+ computed: {
+ deployIconName() {
+ return '';
+ },
+ shouldRenderDeployBoard() {
+ return false;
+ },
+ },
+ methods: {
+ toggleDeployBoard() {},
+ },
+};
diff --git a/app/assets/javascripts/environments/mixins/environments_app_mixin.js b/app/assets/javascripts/environments/mixins/environments_app_mixin.js
new file mode 100644
index 00000000000..fc805b9235a
--- /dev/null
+++ b/app/assets/javascripts/environments/mixins/environments_app_mixin.js
@@ -0,0 +1,32 @@
+export default {
+ props: {
+ canaryDeploymentFeatureId: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ showCanaryDeploymentCallout: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ userCalloutsPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ lockPromotionSvgPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ helpCanaryDeploymentsPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+ metods: {
+ toggleDeployBoard() {},
+ },
+};
diff --git a/app/assets/javascripts/environments/mixins/environments_folder_view_mixin.js b/app/assets/javascripts/environments/mixins/environments_folder_view_mixin.js
new file mode 100644
index 00000000000..e793a7cadf2
--- /dev/null
+++ b/app/assets/javascripts/environments/mixins/environments_folder_view_mixin.js
@@ -0,0 +1,29 @@
+export default {
+ props: {
+ canaryDeploymentFeatureId: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ showCanaryDeploymentCallout: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ userCalloutsPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ lockPromotionSvgPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ helpCanaryDeploymentsPath: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ },
+};
diff --git a/app/assets/javascripts/environments/mixins/environments_mixin.js b/app/assets/javascripts/environments/mixins/environments_mixin.js
index e81a1525df0..a5812b173dc 100644
--- a/app/assets/javascripts/environments/mixins/environments_mixin.js
+++ b/app/assets/javascripts/environments/mixins/environments_mixin.js
@@ -3,13 +3,13 @@
*/
import _ from 'underscore';
import Visibility from 'visibilityjs';
+import EnvironmentsStore from 'ee_else_ce/environments/stores/environments_store';
import Poll from '../../lib/utils/poll';
import { getParameterByName } from '../../lib/utils/common_utils';
import { s__ } from '../../locale';
import Flash from '../../flash';
import eventHub from '../event_hub';
-import EnvironmentsStore from '../stores/environments_store';
import EnvironmentsService from '../services/environments_service';
import tablePagination from '../../vue_shared/components/table_pagination.vue';
import environmentTable from '../components/environments_table.vue';
@@ -36,6 +36,7 @@ export default {
page: getParameterByName('page') || '1',
requestData: {},
environmentInStopModal: {},
+ environmentInRollbackModal: {},
};
},
@@ -43,7 +44,11 @@ export default {
saveData(resp) {
this.isLoading = false;
- if (_.isEqual(resp.config.params, this.requestData)) {
+ // Prevent the absence of the nested flag from causing mismatches
+ const response = this.filterNilValues(resp.config.params);
+ const request = this.filterNilValues(this.requestData);
+
+ if (_.isEqual(response, request)) {
this.store.storeAvailableCount(resp.data.available_count);
this.store.storeStoppedCount(resp.data.stopped_count);
this.store.storeEnvironments(resp.data.environments);
@@ -51,6 +56,10 @@ export default {
}
},
+ filterNilValues(obj) {
+ return _.omit(obj, value => _.isUndefined(value) || _.isNull(value));
+ },
+
/**
* Handles URL and query parameter changes.
* When the user uses the pagination or the tabs,
@@ -64,10 +73,9 @@ export default {
// fetch new data
return this.service
.fetchEnvironments(this.requestData)
- .then(response => this.successCallback(response))
- .then(() => {
- // restart polling
- this.poll.restart({ data: this.requestData });
+ .then(response => {
+ this.successCallback(response);
+ this.poll.enable({ data: this.requestData, response });
})
.catch(() => {
this.errorCallback();
@@ -109,6 +117,10 @@ export default {
this.environmentInStopModal = environment;
},
+ updateRollbackModal(environment) {
+ this.environmentInRollbackModal = environment;
+ },
+
stopEnvironment(environment) {
const endpoint = environment.stop_path;
const errorMessage = s__(
@@ -116,6 +128,16 @@ export default {
);
this.postAction({ endpoint, errorMessage });
},
+
+ rollbackEnvironment(environment) {
+ const { retryUrl, isLastDeployment } = environment;
+ const errorMessage = isLastDeployment
+ ? s__('Environments|An error occurred while re-deploying the environment, please try again')
+ : s__(
+ 'Environments|An error occurred while rolling back the environment, please try again',
+ );
+ this.postAction({ endpoint: retryUrl, errorMessage });
+ },
},
computed: {
@@ -174,11 +196,17 @@ export default {
eventHub.$on('postAction', this.postAction);
eventHub.$on('requestStopEnvironment', this.updateStopModal);
eventHub.$on('stopEnvironment', this.stopEnvironment);
+
+ eventHub.$on('requestRollbackEnvironment', this.updateRollbackModal);
+ eventHub.$on('rollbackEnvironment', this.rollbackEnvironment);
},
beforeDestroy() {
eventHub.$off('postAction', this.postAction);
eventHub.$off('requestStopEnvironment', this.updateStopModal);
eventHub.$off('stopEnvironment', this.stopEnvironment);
+
+ eventHub.$off('requestRollbackEnvironment', this.updateRollbackModal);
+ eventHub.$off('rollbackEnvironment', this.rollbackEnvironment);
},
};
diff --git a/app/assets/javascripts/environments/mixins/environments_table_mixin.js b/app/assets/javascripts/environments/mixins/environments_table_mixin.js
new file mode 100644
index 00000000000..208f1a7373d
--- /dev/null
+++ b/app/assets/javascripts/environments/mixins/environments_table_mixin.js
@@ -0,0 +1,10 @@
+export default {
+ methods: {
+ shouldShowCanaryCallout() {
+ return false;
+ },
+ shouldRenderDeployBoard() {
+ return false;
+ },
+ },
+};
diff --git a/app/assets/javascripts/environments/stores/environments_store.js b/app/assets/javascripts/environments/stores/environments_store.js
index ac9a31c202c..5fb420e9da5 100644
--- a/app/assets/javascripts/environments/stores/environments_store.js
+++ b/app/assets/javascripts/environments/stores/environments_store.js
@@ -1,4 +1,6 @@
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
+import { setDeployBoard } from 'ee_else_ce/environments/stores/helpers';
+
/**
* Environments Store.
*
@@ -31,6 +33,14 @@ export default class EnvironmentsStore {
* If the `size` is bigger than 1, it means it should be rendered as a folder.
* In those cases we add `isFolder` key in order to render it properly.
*
+ * Top level environments - when the size is 1 - with `rollout_status`
+ * can render a deploy board. We add `isDeployBoardVisible` and `deployBoardData`
+ * keys to those environments.
+ * The first key will let's us know if we should or not render the deploy board.
+ * It will be toggled when the user clicks to seee the deploy board.
+ *
+ * The second key will allow us to update the environment with the received deploy board data.
+ *
* @param {Array} environments
* @returns {Array}
*/
@@ -63,6 +73,7 @@ export default class EnvironmentsStore {
filtered = Object.assign(filtered, env);
}
+ filtered = setDeployBoard(oldEnvironmentState, filtered);
return filtered;
});
@@ -71,6 +82,20 @@ export default class EnvironmentsStore {
return filteredEnvironments;
}
+ /**
+ * Stores the pagination information needed to render the pagination for the
+ * table.
+ *
+ * Normalizes the headers to uppercase since they can be provided either
+ * in uppercase or lowercase.
+ *
+ * Parses to an integer the normalized ones needed for the pagination component.
+ *
+ * Stores the normalized and parsed information.
+ *
+ * @param {Object} pagination = {}
+ * @return {Object}
+ */
setPagination(pagination = {}) {
const normalizedHeaders = normalizeHeaders(pagination);
const paginationInformation = parseIntPagination(normalizedHeaders);
diff --git a/app/assets/javascripts/environments/stores/helpers.js b/app/assets/javascripts/environments/stores/helpers.js
new file mode 100644
index 00000000000..8eba6c00601
--- /dev/null
+++ b/app/assets/javascripts/environments/stores/helpers.js
@@ -0,0 +1,8 @@
+/**
+ * Deploy boards are EE only.
+ *
+ * @param {Object} environment
+ * @returns {Object}
+ */
+// eslint-disable-next-line import/prefer-default-export
+export const setDeployBoard = (oldEnvironmentState, environment) => environment;
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
index 6981afe1ead..43ae54133af 100644
--- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
+++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue
@@ -48,7 +48,7 @@ export default {
}
},
methods: {
- ...mapActions(['startPolling']),
+ ...mapActions(['startPolling', 'restartPolling']),
},
};
</script>
@@ -56,19 +56,17 @@ export default {
<template>
<div>
<div v-if="errorTrackingEnabled">
- <div v-if="loading" class="py-3"><gl-loading-icon :size="3" /></div>
+ <div v-if="loading" class="py-3">
+ <gl-loading-icon :size="3" />
+ </div>
<div v-else>
<div class="d-flex justify-content-end">
- <gl-button class="my-3 ml-auto" variant="primary" :href="externalUrl" target="_blank"
- >View in Sentry <icon name="external-link" />
+ <gl-button class="my-3 ml-auto" variant="primary" :href="externalUrl" target="_blank">
+ {{ __('View in Sentry') }}
+ <icon name="external-link" />
</gl-button>
</div>
- <gl-table
- :items="errors"
- :fields="$options.fields"
- :show-empty="true"
- :empty-text="__('No errors to display')"
- >
+ <gl-table :items="errors" :fields="$options.fields" :show-empty="true">
<template slot="HEAD_events" slot-scope="data">
<div class="text-right">{{ data.label }}</div>
</template>
@@ -102,6 +100,14 @@ export default {
<time-ago :time="errors.item.lastSeen" class="text-secondary" />
</div>
</template>
+ <template slot="empty">
+ <div ref="empty">
+ {{ __('No errors to display.') }}
+ <gl-link class="js-try-again" @click="restartPolling">
+ {{ __('Check again') }}
+ </gl-link>
+ </div>
+ </template>
</gl-table>
</div>
</div>
diff --git a/app/assets/javascripts/error_tracking/store/actions.js b/app/assets/javascripts/error_tracking/store/actions.js
index 2e192c958ba..d42e4f145dc 100644
--- a/app/assets/javascripts/error_tracking/store/actions.js
+++ b/app/assets/javascripts/error_tracking/store/actions.js
@@ -2,11 +2,11 @@ import Service from '../services';
import * as types from './mutation_types';
import createFlash from '~/flash';
import Poll from '~/lib/utils/poll';
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
let eTagPoll;
-export function startPolling({ commit }, endpoint) {
+export function startPolling({ commit, dispatch }, endpoint) {
eTagPoll = new Poll({
resource: Service,
method: 'getErrorList',
@@ -18,14 +18,35 @@ export function startPolling({ commit }, endpoint) {
commit(types.SET_ERRORS, data.errors);
commit(types.SET_EXTERNAL_URL, data.external_url);
commit(types.SET_LOADING, false);
+ dispatch('stopPolling');
},
- errorCallback: () => {
+ errorCallback: response => {
+ let errorMessage = '';
+ if (response && response.data && response.data.message) {
+ errorMessage = response.data.message;
+ }
commit(types.SET_LOADING, false);
- createFlash(__('Failed to load errors from Sentry'));
+ createFlash(
+ sprintf(__(`Failed to load errors from Sentry. Error message: %{errorMessage}`), {
+ errorMessage,
+ }),
+ );
},
});
eTagPoll.makeRequest();
}
+export const stopPolling = () => {
+ if (eTagPoll) eTagPoll.stop();
+};
+
+export function restartPolling({ commit }) {
+ commit(types.SET_ERRORS, []);
+ commit(types.SET_EXTERNAL_URL, '');
+ commit(types.SET_LOADING, true);
+
+ if (eTagPoll) eTagPoll.restart();
+}
+
export default () => {};
diff --git a/app/assets/javascripts/error_tracking_settings/components/app.vue b/app/assets/javascripts/error_tracking_settings/components/app.vue
new file mode 100644
index 00000000000..50eb3e63b7c
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/components/app.vue
@@ -0,0 +1,129 @@
+<script>
+import { mapActions, mapGetters, mapState } from 'vuex';
+import { GlButton } from '@gitlab/ui';
+import ProjectDropdown from './project_dropdown.vue';
+import ErrorTrackingForm from './error_tracking_form.vue';
+
+export default {
+ components: { ProjectDropdown, ErrorTrackingForm, GlButton },
+ props: {
+ initialApiHost: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ initialEnabled: {
+ type: String,
+ required: true,
+ },
+ initialProject: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ initialToken: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ listProjectsEndpoint: {
+ type: String,
+ required: true,
+ },
+ operationsSettingsEndpoint: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ ...mapGetters([
+ 'dropdownLabel',
+ 'hasProjects',
+ 'invalidProjectLabel',
+ 'isProjectInvalid',
+ 'projectSelectionLabel',
+ ]),
+ ...mapState([
+ 'apiHost',
+ 'connectError',
+ 'connectSuccessful',
+ 'enabled',
+ 'projects',
+ 'selectedProject',
+ 'settingsLoading',
+ 'token',
+ ]),
+ },
+ created() {
+ this.setInitialState({
+ apiHost: this.initialApiHost,
+ enabled: this.initialEnabled,
+ project: this.initialProject,
+ token: this.initialToken,
+ listProjectsEndpoint: this.listProjectsEndpoint,
+ operationsSettingsEndpoint: this.operationsSettingsEndpoint,
+ });
+ },
+ methods: {
+ ...mapActions([
+ 'fetchProjects',
+ 'setInitialState',
+ 'updateApiHost',
+ 'updateEnabled',
+ 'updateSelectedProject',
+ 'updateSettings',
+ 'updateToken',
+ ]),
+ handleSubmit() {
+ this.updateSettings();
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div class="form-check form-group">
+ <input
+ id="error-tracking-enabled"
+ :checked="enabled"
+ class="form-check-input"
+ type="checkbox"
+ @change="updateEnabled($event.target.checked)"
+ />
+ <label class="form-check-label" for="error-tracking-enabled">{{
+ s__('ErrorTracking|Active')
+ }}</label>
+ </div>
+ <error-tracking-form
+ :api-host="apiHost"
+ :connect-error="connectError"
+ :connect-successful="connectSuccessful"
+ :token="token"
+ @handle-connect="fetchProjects"
+ @update-api-host="updateApiHost"
+ @update-token="updateToken"
+ />
+ <div class="form-group">
+ <project-dropdown
+ :has-projects="hasProjects"
+ :invalid-project-label="invalidProjectLabel"
+ :is-project-invalid="isProjectInvalid"
+ :dropdown-label="dropdownLabel"
+ :project-selection-label="projectSelectionLabel"
+ :projects="projects"
+ :selected-project="selectedProject"
+ :token="token"
+ @select-project="updateSelectedProject"
+ />
+ </div>
+ <gl-button
+ :disabled="settingsLoading"
+ class="js-error-tracking-button"
+ variant="success"
+ @click="handleSubmit"
+ >
+ {{ __('Save changes') }}
+ </gl-button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/error_tracking_settings/components/error_tracking_form.vue b/app/assets/javascripts/error_tracking_settings/components/error_tracking_form.vue
new file mode 100644
index 00000000000..060d8e25227
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/components/error_tracking_form.vue
@@ -0,0 +1,91 @@
+<script>
+import { GlButton, GlFormInput } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+
+export default {
+ components: { GlButton, GlFormInput, Icon },
+ props: {
+ apiHost: {
+ type: String,
+ required: true,
+ },
+ connectError: {
+ type: Boolean,
+ required: true,
+ },
+ connectSuccessful: {
+ type: Boolean,
+ required: true,
+ },
+ token: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ tokenInputState() {
+ return this.connectError ? false : null;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div class="form-group">
+ <label class="label-bold" for="error-tracking-api-host">{{ __('Sentry API URL') }}</label>
+ <div class="row">
+ <div class="col-8 col-md-9 gl-pr-0">
+ <gl-form-input
+ id="error-tracking-api-host"
+ :value="apiHost"
+ placeholder="https://mysentryserver.com"
+ @input="$emit('update-api-host', $event)"
+ />
+ </div>
+ </div>
+ <p class="form-text text-muted">
+ {{ s__('ErrorTracking|Find your hostname in your Sentry account settings page') }}
+ </p>
+ </div>
+ <div class="form-group" :class="{ 'gl-show-field-errors': connectError }">
+ <label class="label-bold" for="error-tracking-token">{{
+ s__('ErrorTracking|Auth Token')
+ }}</label>
+ <div class="row">
+ <div class="col-8 col-md-9 gl-pr-0">
+ <gl-form-input
+ id="error-tracking-token"
+ :value="token"
+ :state="tokenInputState"
+ @input="$emit('update-token', $event)"
+ />
+ </div>
+ <div class="col-4 col-md-3 gl-pl-0">
+ <gl-button
+ class="js-error-tracking-connect prepend-left-5"
+ @click="$emit('handle-connect')"
+ >
+ {{ __('Connect') }}
+ </gl-button>
+ <icon
+ v-show="connectSuccessful"
+ class="js-error-tracking-connect-success prepend-left-5 text-success align-middle"
+ :aria-label="__('Projects Successfully Retrieved')"
+ name="check-circle"
+ />
+ </div>
+ </div>
+ <p v-if="connectError" class="gl-field-error">
+ {{ s__('ErrorTracking|Connection has failed. Re-check Auth Token and try again.') }}
+ </p>
+ <p v-else class="form-text text-muted">
+ {{
+ s__(
+ "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects",
+ )
+ }}
+ </p>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/error_tracking_settings/components/project_dropdown.vue b/app/assets/javascripts/error_tracking_settings/components/project_dropdown.vue
new file mode 100644
index 00000000000..82df02afafd
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/components/project_dropdown.vue
@@ -0,0 +1,82 @@
+<script>
+import { GlDropdown, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+import { getDisplayName } from '../utils';
+
+export default {
+ components: {
+ GlDropdown,
+ GlDropdownHeader,
+ GlDropdownItem,
+ Icon,
+ },
+ props: {
+ dropdownLabel: {
+ type: String,
+ required: true,
+ },
+ hasProjects: {
+ type: Boolean,
+ required: true,
+ },
+ invalidProjectLabel: {
+ type: String,
+ required: true,
+ },
+ isProjectInvalid: {
+ type: Boolean,
+ required: true,
+ },
+ projects: {
+ type: Array,
+ required: true,
+ },
+ selectedProject: {
+ type: Object,
+ required: false,
+ default: null,
+ },
+ projectSelectionLabel: {
+ type: String,
+ required: true,
+ },
+ token: {
+ type: String,
+ required: true,
+ },
+ },
+ methods: {
+ getDisplayName,
+ },
+};
+</script>
+
+<template>
+ <div :class="{ 'gl-show-field-errors': isProjectInvalid }">
+ <label class="label-bold" for="project-dropdown">{{ __('Project') }}</label>
+ <div class="row">
+ <gl-dropdown
+ id="project-dropdown"
+ class="col-8 col-md-9 gl-pr-0"
+ :disabled="!hasProjects"
+ menu-class="w-100 mw-100"
+ toggle-class="dropdown-menu-toggle w-100 gl-field-error-outline"
+ :text="dropdownLabel"
+ >
+ <gl-dropdown-item
+ v-for="project in projects"
+ :key="`${project.organizationSlug}.${project.slug}`"
+ class="w-100"
+ @click="$emit('select-project', project)"
+ >{{ getDisplayName(project) }}</gl-dropdown-item
+ >
+ </gl-dropdown>
+ </div>
+ <p v-if="isProjectInvalid" class="js-project-dropdown-error gl-field-error">
+ {{ invalidProjectLabel }}
+ </p>
+ <p v-else-if="!hasProjects" class="js-project-dropdown-label form-text text-muted">
+ {{ projectSelectionLabel }}
+ </p>
+ </div>
+</template>
diff --git a/app/assets/javascripts/error_tracking_settings/index.js b/app/assets/javascripts/error_tracking_settings/index.js
new file mode 100644
index 00000000000..ce315963723
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/index.js
@@ -0,0 +1,27 @@
+import Vue from 'vue';
+import ErrorTrackingSettings from './components/app.vue';
+import createStore from './store';
+
+export default () => {
+ const formContainerEl = document.querySelector('.js-error-tracking-form');
+ const {
+ dataset: { apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint },
+ } = formContainerEl;
+
+ return new Vue({
+ el: formContainerEl,
+ store: createStore(),
+ render(createElement) {
+ return createElement(ErrorTrackingSettings, {
+ props: {
+ initialApiHost: apiHost,
+ initialEnabled: enabled,
+ initialProject: project,
+ initialToken: token,
+ listProjectsEndpoint,
+ operationsSettingsEndpoint,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/error_tracking_settings/store/actions.js b/app/assets/javascripts/error_tracking_settings/store/actions.js
new file mode 100644
index 00000000000..95105797807
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/store/actions.js
@@ -0,0 +1,91 @@
+import { __ } from '~/locale';
+import axios from '~/lib/utils/axios_utils';
+import { refreshCurrentPage } from '~/lib/utils/url_utility';
+import createFlash from '~/flash';
+import { transformFrontendSettings } from '../utils';
+import * as types from './mutation_types';
+
+export const requestProjects = ({ commit }) => {
+ commit(types.RESET_CONNECT);
+};
+
+export const receiveProjectsSuccess = ({ commit }, projects) => {
+ commit(types.UPDATE_CONNECT_SUCCESS);
+ commit(types.RECEIVE_PROJECTS, projects);
+};
+
+export const receiveProjectsError = ({ commit }) => {
+ commit(types.UPDATE_CONNECT_ERROR);
+ commit(types.CLEAR_PROJECTS);
+};
+
+export const fetchProjects = ({ dispatch, state }) => {
+ dispatch('requestProjects');
+ return axios
+ .post(state.listProjectsEndpoint, {
+ error_tracking_setting: {
+ api_host: state.apiHost,
+ token: state.token,
+ },
+ })
+ .then(({ data: { projects } }) => {
+ dispatch('receiveProjectsSuccess', projects);
+ })
+ .catch(() => {
+ dispatch('receiveProjectsError');
+ });
+};
+
+export const requestSettings = ({ commit }) => {
+ commit(types.UPDATE_SETTINGS_LOADING, true);
+};
+
+export const receiveSettingsError = ({ commit }, { response = {} }) => {
+ const message = response.data && response.data.message ? response.data.message : '';
+
+ createFlash(`${__('There was an error saving your changes.')} ${message}`, 'alert');
+ commit(types.UPDATE_SETTINGS_LOADING, false);
+};
+
+export const updateSettings = ({ dispatch, state }) => {
+ dispatch('requestSettings');
+ return axios
+ .patch(state.operationsSettingsEndpoint, {
+ project: {
+ error_tracking_setting_attributes: {
+ ...transformFrontendSettings(state),
+ },
+ },
+ })
+ .then(() => {
+ refreshCurrentPage();
+ })
+ .catch(err => {
+ dispatch('receiveSettingsError', err);
+ });
+};
+
+export const updateApiHost = ({ commit }, apiHost) => {
+ commit(types.UPDATE_API_HOST, apiHost);
+ commit(types.RESET_CONNECT);
+};
+
+export const updateEnabled = ({ commit }, enabled) => {
+ commit(types.UPDATE_ENABLED, enabled);
+};
+
+export const updateToken = ({ commit }, token) => {
+ commit(types.UPDATE_TOKEN, token);
+ commit(types.RESET_CONNECT);
+};
+
+export const updateSelectedProject = ({ commit }, selectedProject) => {
+ commit(types.UPDATE_SELECTED_PROJECT, selectedProject);
+};
+
+export const setInitialState = ({ commit }, data) => {
+ commit(types.SET_INITIAL_STATE, data);
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/error_tracking_settings/store/getters.js b/app/assets/javascripts/error_tracking_settings/store/getters.js
new file mode 100644
index 00000000000..a008b181907
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/store/getters.js
@@ -0,0 +1,44 @@
+import _ from 'underscore';
+import { __, s__, sprintf } from '~/locale';
+import { getDisplayName } from '../utils';
+
+export const hasProjects = state => !!state.projects && state.projects.length > 0;
+
+export const isProjectInvalid = (state, getters) =>
+ !!state.selectedProject &&
+ getters.hasProjects &&
+ !state.projects.some(project => _.isMatch(state.selectedProject, project));
+
+export const dropdownLabel = (state, getters) => {
+ if (state.selectedProject !== null) {
+ return getDisplayName(state.selectedProject);
+ }
+ if (!getters.hasProjects) {
+ return s__('ErrorTracking|No projects available');
+ }
+ return s__('ErrorTracking|Select project');
+};
+
+export const invalidProjectLabel = state => {
+ if (state.selectedProject) {
+ return sprintf(
+ __('Project "%{name}" is no longer available. Select another project to continue.'),
+ {
+ name: state.selectedProject.name,
+ },
+ );
+ }
+ return '';
+};
+
+export const projectSelectionLabel = state => {
+ if (state.token) {
+ return s__(
+ "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown.",
+ );
+ }
+ return s__('ErrorTracking|To enable project selection, enter a valid Auth Token');
+};
+
+// prevent babel-plugin-rewire from generating an invalid default during karma tests
+export default () => {};
diff --git a/app/assets/javascripts/error_tracking_settings/store/index.js b/app/assets/javascripts/error_tracking_settings/store/index.js
new file mode 100644
index 00000000000..560f265a2ea
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/store/index.js
@@ -0,0 +1,16 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import createState from './state';
+import * as actions from './actions';
+import * as getters from './getters';
+import mutations from './mutations';
+
+Vue.use(Vuex);
+
+export default () =>
+ new Vuex.Store({
+ state: createState(),
+ actions,
+ getters,
+ mutations,
+ });
diff --git a/app/assets/javascripts/error_tracking_settings/store/mutation_types.js b/app/assets/javascripts/error_tracking_settings/store/mutation_types.js
new file mode 100644
index 00000000000..b4f8a237947
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/store/mutation_types.js
@@ -0,0 +1,11 @@
+export const CLEAR_PROJECTS = 'CLEAR_PROJECTS';
+export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
+export const RECEIVE_PROJECTS = 'RECEIVE_PROJECTS';
+export const RESET_CONNECT = 'RESET_CONNECT';
+export const UPDATE_API_HOST = 'UPDATE_API_HOST';
+export const UPDATE_CONNECT_ERROR = 'UPDATE_CONNECT_ERROR';
+export const UPDATE_CONNECT_SUCCESS = 'UPDATE_CONNECT_SUCCESS';
+export const UPDATE_ENABLED = 'UPDATE_ENABLED';
+export const UPDATE_SELECTED_PROJECT = 'UPDATE_SELECTED_PROJECT';
+export const UPDATE_SETTINGS_LOADING = 'UPDATE_SETTINGS_LOADING';
+export const UPDATE_TOKEN = 'UPDATE_TOKEN';
diff --git a/app/assets/javascripts/error_tracking_settings/store/mutations.js b/app/assets/javascripts/error_tracking_settings/store/mutations.js
new file mode 100644
index 00000000000..4089d1ee94e
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/store/mutations.js
@@ -0,0 +1,61 @@
+import _ from 'underscore';
+import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
+import * as types from './mutation_types';
+import { projectKeys } from '../utils';
+
+export default {
+ [types.CLEAR_PROJECTS](state) {
+ state.projects = [];
+ },
+ [types.RECEIVE_PROJECTS](state, projects) {
+ state.projects = projects
+ .map(convertObjectPropsToCamelCase)
+ // The `pick` strips out extra properties returned from Sentry.
+ // Such properties could be problematic later, e.g. when checking whether `projects` contains `selectedProject`
+ .map(project => _.pick(project, projectKeys));
+ },
+ [types.RESET_CONNECT](state) {
+ state.connectSuccessful = false;
+ state.connectError = false;
+ },
+ [types.SET_INITIAL_STATE](
+ state,
+ { apiHost, enabled, project, token, listProjectsEndpoint, operationsSettingsEndpoint },
+ ) {
+ state.enabled = parseBoolean(enabled);
+ state.apiHost = apiHost;
+ state.token = token;
+ state.listProjectsEndpoint = listProjectsEndpoint;
+ state.operationsSettingsEndpoint = operationsSettingsEndpoint;
+
+ if (project) {
+ state.selectedProject = _.pick(
+ convertObjectPropsToCamelCase(JSON.parse(project)),
+ projectKeys,
+ );
+ }
+ },
+ [types.UPDATE_API_HOST](state, apiHost) {
+ state.apiHost = apiHost;
+ },
+ [types.UPDATE_ENABLED](state, enabled) {
+ state.enabled = enabled;
+ },
+ [types.UPDATE_TOKEN](state, token) {
+ state.token = token;
+ },
+ [types.UPDATE_SELECTED_PROJECT](state, selectedProject) {
+ state.selectedProject = selectedProject;
+ },
+ [types.UPDATE_SETTINGS_LOADING](state, settingsLoading) {
+ state.settingsLoading = settingsLoading;
+ },
+ [types.UPDATE_CONNECT_SUCCESS](state) {
+ state.connectSuccessful = true;
+ state.connectError = false;
+ },
+ [types.UPDATE_CONNECT_ERROR](state) {
+ state.connectSuccessful = false;
+ state.connectError = true;
+ },
+};
diff --git a/app/assets/javascripts/error_tracking_settings/store/state.js b/app/assets/javascripts/error_tracking_settings/store/state.js
new file mode 100644
index 00000000000..98219d33f4d
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/store/state.js
@@ -0,0 +1,12 @@
+export default () => ({
+ apiHost: '',
+ enabled: false,
+ token: '',
+ projects: [],
+ selectedProject: null,
+ settingsLoading: false,
+ connectSuccessful: false,
+ connectError: false,
+ listProjectsEndpoint: '',
+ operationsSettingsEndpoint: '',
+});
diff --git a/app/assets/javascripts/error_tracking_settings/utils.js b/app/assets/javascripts/error_tracking_settings/utils.js
new file mode 100644
index 00000000000..6613e04ee0e
--- /dev/null
+++ b/app/assets/javascripts/error_tracking_settings/utils.js
@@ -0,0 +1,18 @@
+export const projectKeys = ['name', 'organizationName', 'organizationSlug', 'slug'];
+
+export const transformFrontendSettings = ({ apiHost, enabled, token, selectedProject }) => {
+ const project = selectedProject
+ ? {
+ slug: selectedProject.slug,
+ name: selectedProject.name,
+ organization_name: selectedProject.organizationName,
+ organization_slug: selectedProject.organizationSlug,
+ }
+ : null;
+
+ return { api_host: apiHost || null, enabled, token: token || null, project };
+};
+
+export const getDisplayName = project => `${project.organizationName} | ${project.name}`;
+
+export default () => {};
diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
new file mode 100644
index 00000000000..0b24d9fc920
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js
@@ -0,0 +1,28 @@
+export default IssuableTokenKeys => {
+ const wipToken = {
+ key: 'wip',
+ type: 'string',
+ param: '',
+ symbol: '',
+ icon: 'admin',
+ tag: 'Yes or No',
+ lowercaseValueOnSubmit: true,
+ uppercaseTokenName: true,
+ capitalizeTokenValue: true,
+ };
+
+ IssuableTokenKeys.tokenKeys.push(wipToken);
+ IssuableTokenKeys.tokenKeysWithAlternative.push(wipToken);
+
+ const targetBranchToken = {
+ key: 'target-branch',
+ type: 'string',
+ param: '',
+ symbol: '',
+ icon: 'arrow-right',
+ tag: 'branch',
+ };
+
+ IssuableTokenKeys.tokenKeys.push(targetBranchToken);
+ IssuableTokenKeys.tokenKeysWithAlternative.push(targetBranchToken);
+};
diff --git a/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js
index 934375023ba..691d165c585 100644
--- a/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/admin_runners_filtered_search_token_keys.js
@@ -17,6 +17,14 @@ const tokenKeys = [
icon: 'cube',
tag: 'type',
},
+ {
+ key: 'tag',
+ type: 'array',
+ param: 'name[]',
+ symbol: '~',
+ icon: 'tag',
+ tag: '~tag',
+ },
];
const AdminRunnersFilteredSearchTokenKeys = new FilteredSearchTokenKeys(tokenKeys);
diff --git a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
new file mode 100644
index 00000000000..be867a3838d
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
@@ -0,0 +1,164 @@
+import DropdownHint from './dropdown_hint';
+import DropdownUser from './dropdown_user';
+import DropdownNonUser from './dropdown_non_user';
+import DropdownEmoji from './dropdown_emoji';
+import NullDropdown from './null_dropdown';
+import DropdownAjaxFilter from './dropdown_ajax_filter';
+import DropdownUtils from './dropdown_utils';
+import { mergeUrlParams } from '../lib/utils/url_utility';
+
+export default class AvailableDropdownMappings {
+ constructor(container, baseEndpoint, groupsOnly, includeAncestorGroups, includeDescendantGroups) {
+ this.container = container;
+ this.baseEndpoint = baseEndpoint;
+ this.groupsOnly = groupsOnly;
+ this.includeAncestorGroups = includeAncestorGroups;
+ this.includeDescendantGroups = includeDescendantGroups;
+ this.filteredSearchInput = this.container.querySelector('.filtered-search');
+ }
+
+ getAllowedMappings(supportedTokens) {
+ return this.buildMappings(supportedTokens, this.getMappings());
+ }
+
+ buildMappings(supportedTokens, availableMappings) {
+ const allowedMappings = {
+ hint: {
+ reference: null,
+ gl: DropdownHint,
+ element: this.container.querySelector('#js-dropdown-hint'),
+ },
+ };
+
+ supportedTokens.forEach(type => {
+ if (availableMappings[type]) {
+ allowedMappings[type] = availableMappings[type];
+ }
+ });
+
+ return allowedMappings;
+ }
+
+ getMappings() {
+ return {
+ author: {
+ reference: null,
+ gl: DropdownUser,
+ element: this.container.querySelector('#js-dropdown-author'),
+ },
+ assignee: {
+ reference: null,
+ gl: DropdownUser,
+ element: this.container.querySelector('#js-dropdown-assignee'),
+ },
+ milestone: {
+ reference: null,
+ gl: DropdownNonUser,
+ extraArguments: {
+ endpoint: this.getMilestoneEndpoint(),
+ symbol: '%',
+ },
+ element: this.container.querySelector('#js-dropdown-milestone'),
+ },
+ label: {
+ reference: null,
+ gl: DropdownNonUser,
+ extraArguments: {
+ endpoint: this.getLabelsEndpoint(),
+ symbol: '~',
+ preprocessing: DropdownUtils.duplicateLabelPreprocessing,
+ },
+ element: this.container.querySelector('#js-dropdown-label'),
+ },
+ 'my-reaction': {
+ reference: null,
+ gl: DropdownEmoji,
+ element: this.container.querySelector('#js-dropdown-my-reaction'),
+ },
+ wip: {
+ reference: null,
+ gl: DropdownNonUser,
+ element: this.container.querySelector('#js-dropdown-wip'),
+ },
+ confidential: {
+ reference: null,
+ gl: DropdownNonUser,
+ element: this.container.querySelector('#js-dropdown-confidential'),
+ },
+ status: {
+ reference: null,
+ gl: NullDropdown,
+ element: this.container.querySelector('#js-dropdown-admin-runner-status'),
+ },
+ type: {
+ reference: null,
+ gl: NullDropdown,
+ element: this.container.querySelector('#js-dropdown-admin-runner-type'),
+ },
+ tag: {
+ reference: null,
+ gl: DropdownAjaxFilter,
+ extraArguments: {
+ endpoint: this.getRunnerTagsEndpoint(),
+ symbol: '~',
+ },
+ element: this.container.querySelector('#js-dropdown-runner-tag'),
+ },
+ 'target-branch': {
+ reference: null,
+ gl: DropdownNonUser,
+ extraArguments: {
+ endpoint: this.getMergeRequestTargetBranchesEndpoint(),
+ symbol: '',
+ },
+ element: this.container.querySelector('#js-dropdown-target-branch'),
+ },
+ };
+ }
+
+ getMilestoneEndpoint() {
+ return `${this.baseEndpoint}/milestones.json`;
+ }
+
+ getLabelsEndpoint() {
+ let endpoint = `${this.baseEndpoint}/labels.json?`;
+
+ if (this.groupsOnly) {
+ endpoint = `${endpoint}only_group_labels=true&`;
+ }
+
+ if (this.includeAncestorGroups) {
+ endpoint = `${endpoint}include_ancestor_groups=true&`;
+ }
+
+ if (this.includeDescendantGroups) {
+ endpoint = `${endpoint}include_descendant_groups=true`;
+ }
+
+ return endpoint;
+ }
+
+ getRunnerTagsEndpoint() {
+ return `${this.baseEndpoint}/admin/runners/tag_list.json`;
+ }
+
+ getMergeRequestTargetBranchesEndpoint() {
+ const endpoint = `${gon.relative_url_root ||
+ ''}/autocomplete/merge_request_target_branches.json`;
+
+ const params = {
+ group_id: this.getGroupId(),
+ project_id: this.getProjectId(),
+ };
+
+ return mergeUrlParams(params, endpoint);
+ }
+
+ getGroupId() {
+ return this.filteredSearchInput.getAttribute('data-group-id') || '';
+ }
+
+ getProjectId() {
+ return this.filteredSearchInput.getAttribute('data-project-id') || '';
+ }
+}
diff --git a/app/assets/javascripts/filtered_search/dropdown_ajax_filter.js b/app/assets/javascripts/filtered_search/dropdown_ajax_filter.js
new file mode 100644
index 00000000000..b27bb63c220
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/dropdown_ajax_filter.js
@@ -0,0 +1,68 @@
+import createFlash from '../flash';
+import AjaxFilter from '../droplab/plugins/ajax_filter';
+import FilteredSearchDropdown from './filtered_search_dropdown';
+import DropdownUtils from './dropdown_utils';
+import FilteredSearchTokenizer from './filtered_search_tokenizer';
+import { __ } from '~/locale';
+
+export default class DropdownAjaxFilter extends FilteredSearchDropdown {
+ constructor(options = {}) {
+ const { tokenKeys, endpoint, symbol } = options;
+
+ super(options);
+
+ this.tokenKeys = tokenKeys;
+ this.endpoint = endpoint;
+ this.symbol = symbol;
+
+ this.config = {
+ AjaxFilter: this.ajaxFilterConfig(),
+ };
+ }
+
+ ajaxFilterConfig() {
+ return {
+ endpoint: `${gon.relative_url_root || ''}${this.endpoint}`,
+ searchKey: 'search',
+ searchValueFunction: this.getSearchInput.bind(this),
+ loadingTemplate: this.loadingTemplate,
+ onError() {
+ createFlash(__('An error occurred fetching the dropdown data.'));
+ },
+ };
+ }
+
+ itemClicked(e) {
+ super.itemClicked(e, selected =>
+ selected.querySelector('.dropdown-light-content').innerText.trim(),
+ );
+ }
+
+ renderContent(forceShowList = false) {
+ this.droplab.changeHookList(this.hookId, this.dropdown, [AjaxFilter], this.config);
+ super.renderContent(forceShowList);
+ }
+
+ getSearchInput() {
+ const query = DropdownUtils.getSearchInput(this.input);
+ const { lastToken } = FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get());
+
+ let value = lastToken || '';
+
+ if (value[0] === this.symbol) {
+ value = value.slice(1);
+ }
+
+ // Removes the first character if it is a quotation so that we can search
+ // with multiple words
+ if (value[0] === '"' || value[0] === "'") {
+ value = value.slice(1);
+ }
+
+ return value;
+ }
+
+ init() {
+ this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init();
+ }
+}
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index d5027590bb7..f1e7be6bde1 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -1,54 +1,34 @@
-import Flash from '../flash';
-import AjaxFilter from '../droplab/plugins/ajax_filter';
-import FilteredSearchDropdown from './filtered_search_dropdown';
import { addClassIfElementExists } from '../lib/utils/dom_utils';
-import DropdownUtils from './dropdown_utils';
-import FilteredSearchTokenizer from './filtered_search_tokenizer';
+import DropdownAjaxFilter from './dropdown_ajax_filter';
-export default class DropdownUser extends FilteredSearchDropdown {
+export default class DropdownUser extends DropdownAjaxFilter {
constructor(options = {}) {
- const { tokenKeys } = options;
- super(options);
- this.config = {
- AjaxFilter: {
- endpoint: `${gon.relative_url_root || ''}/autocomplete/users.json`,
- searchKey: 'search',
- params: {
- active: true,
- group_id: this.getGroupId(),
- project_id: this.getProjectId(),
- current_user: true,
- },
- searchValueFunction: this.getSearchInput.bind(this),
- loadingTemplate: this.loadingTemplate,
- onLoadingFinished: () => {
- this.hideCurrentUser();
- },
- onError() {
- /* eslint-disable no-new */
- new Flash('An error occurred fetching the dropdown data.');
- /* eslint-enable no-new */
- },
+ super({
+ ...options,
+ endpoint: '/autocomplete/users.json',
+ symbol: '@',
+ });
+ }
+
+ ajaxFilterConfig() {
+ return {
+ ...super.ajaxFilterConfig(),
+ params: {
+ active: true,
+ group_id: this.getGroupId(),
+ project_id: this.getProjectId(),
+ current_user: true,
+ },
+ onLoadingFinished: () => {
+ this.hideCurrentUser();
},
};
- this.tokenKeys = tokenKeys;
}
hideCurrentUser() {
addClassIfElementExists(this.dropdown.querySelector('.js-current-user'), 'hidden');
}
- itemClicked(e) {
- super.itemClicked(e, selected =>
- selected.querySelector('.dropdown-light-content').innerText.trim(),
- );
- }
-
- renderContent(forceShowList = false) {
- this.droplab.changeHookList(this.hookId, this.dropdown, [AjaxFilter], this.config);
- super.renderContent(forceShowList);
- }
-
getGroupId() {
return this.input.getAttribute('data-group-id');
}
@@ -56,27 +36,4 @@ export default class DropdownUser extends FilteredSearchDropdown {
getProjectId() {
return this.input.getAttribute('data-project-id');
}
-
- getSearchInput() {
- const query = DropdownUtils.getSearchInput(this.input);
- const { lastToken } = FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get());
-
- let value = lastToken || '';
-
- if (value[0] === '@') {
- value = value.slice(1);
- }
-
- // Removes the first character if it is a quotation so that we can search
- // with multiple words
- if (value[0] === '"' || value[0] === "'") {
- value = value.slice(1);
- }
-
- return value;
- }
-
- init() {
- this.droplab.addHook(this.input, this.dropdown, [AjaxFilter], this.config).init();
- }
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
index 57ec6603d80..cb0a84b490b 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js
@@ -1,13 +1,9 @@
+import AvailableDropdownMappings from 'ee_else_ce/filtered_search/available_dropdown_mappings';
import _ from 'underscore';
import DropLab from '~/droplab/drop_lab';
import FilteredSearchContainer from './container';
import FilteredSearchTokenKeys from './filtered_search_token_keys';
import DropdownUtils from './dropdown_utils';
-import DropdownHint from './dropdown_hint';
-import DropdownEmoji from './dropdown_emoji';
-import DropdownNonUser from './dropdown_non_user';
-import DropdownUser from './dropdown_user';
-import NullDropdown from './null_dropdown';
import FilteredSearchVisualTokens from './filtered_search_visual_tokens';
export default class FilteredSearchDropdownManager {
@@ -49,96 +45,15 @@ export default class FilteredSearchDropdownManager {
setupMapping() {
const supportedTokens = this.filteredSearchTokenKeys.getKeys();
- const allowedMappings = {
- hint: {
- reference: null,
- gl: DropdownHint,
- element: this.container.querySelector('#js-dropdown-hint'),
- },
- };
- const availableMappings = {
- author: {
- reference: null,
- gl: DropdownUser,
- element: this.container.querySelector('#js-dropdown-author'),
- },
- assignee: {
- reference: null,
- gl: DropdownUser,
- element: this.container.querySelector('#js-dropdown-assignee'),
- },
- milestone: {
- reference: null,
- gl: DropdownNonUser,
- extraArguments: {
- endpoint: this.getMilestoneEndpoint(),
- symbol: '%',
- },
- element: this.container.querySelector('#js-dropdown-milestone'),
- },
- label: {
- reference: null,
- gl: DropdownNonUser,
- extraArguments: {
- endpoint: this.getLabelsEndpoint(),
- symbol: '~',
- preprocessing: DropdownUtils.duplicateLabelPreprocessing,
- },
- element: this.container.querySelector('#js-dropdown-label'),
- },
- 'my-reaction': {
- reference: null,
- gl: DropdownEmoji,
- element: this.container.querySelector('#js-dropdown-my-reaction'),
- },
- wip: {
- reference: null,
- gl: DropdownNonUser,
- element: this.container.querySelector('#js-dropdown-wip'),
- },
- status: {
- reference: null,
- gl: NullDropdown,
- element: this.container.querySelector('#js-dropdown-admin-runner-status'),
- },
- type: {
- reference: null,
- gl: NullDropdown,
- element: this.container.querySelector('#js-dropdown-admin-runner-type'),
- },
- };
-
- supportedTokens.forEach(type => {
- if (availableMappings[type]) {
- allowedMappings[type] = availableMappings[type];
- }
- });
-
- this.mapping = allowedMappings;
- }
-
- getMilestoneEndpoint() {
- const endpoint = `${this.baseEndpoint}/milestones.json`;
-
- return endpoint;
- }
-
- getLabelsEndpoint() {
- let endpoint = `${this.baseEndpoint}/labels.json?`;
-
- if (this.groupsOnly) {
- endpoint = `${endpoint}only_group_labels=true&`;
- }
-
- if (this.includeAncestorGroups) {
- endpoint = `${endpoint}include_ancestor_groups=true&`;
- }
-
- if (this.includeDescendantGroups) {
- endpoint = `${endpoint}include_descendant_groups=true`;
- }
+ const availableMappings = new AvailableDropdownMappings(
+ this.container,
+ this.baseEndpoint,
+ this.groupsOnly,
+ this.includeAncestorGroups,
+ this.includeDescendantGroups,
+ );
- return endpoint;
+ this.mapping = availableMappings.getAllowedMappings(supportedTokens);
}
static addWordToInput(tokenName, tokenValue = '', clicked = false, options = {}) {
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 33c82778c79..0c2e87521d9 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -504,14 +504,7 @@ export default class FilteredSearchManager {
const match = this.filteredSearchTokenKeys.searchByKeyParam(keyParam);
if (match) {
- // Use lastIndexOf because the token key is allowed to contain underscore
- // e.g. 'my_reaction' is the token key of 'my_reaction_emoji'
- const lastIndexOf = keyParam.lastIndexOf('_');
- let sanitizedKey = lastIndexOf !== -1 ? keyParam.slice(0, lastIndexOf) : keyParam;
- // Replace underscore with hyphen in the sanitizedkey.
- // e.g. 'my_reaction' => 'my-reaction'
- sanitizedKey = sanitizedKey.replace('_', '-');
- const { symbol } = match;
+ const { key, symbol } = match;
let quotationsToUse = '';
if (sanitizedValue.indexOf(' ') !== -1) {
@@ -520,10 +513,10 @@ export default class FilteredSearchManager {
}
hasFilteredSearch = true;
- const canEdit = this.canEdit && this.canEdit(sanitizedKey, sanitizedValue);
+ const canEdit = this.canEdit && this.canEdit(key, sanitizedValue);
const { uppercaseTokenName, capitalizeTokenValue } = match;
FilteredSearchVisualTokens.addFilterVisualToken(
- sanitizedKey,
+ key,
`${symbol}${quotationsToUse}${sanitizedValue}${quotationsToUse}`,
{
canEdit,
diff --git a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
index b70da240833..11ed85504ec 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_token_keys.js
@@ -72,20 +72,20 @@ export default class FilteredSearchTokenKeys {
);
}
- addExtraTokensForMergeRequests() {
- const wipToken = {
- key: 'wip',
+ addExtraTokensForIssues() {
+ const confidentialToken = {
+ key: 'confidential',
type: 'string',
param: '',
symbol: '',
- icon: 'admin',
+ icon: 'eye-slash',
tag: 'Yes or No',
lowercaseValueOnSubmit: true,
- uppercaseTokenName: true,
+ uppercaseTokenName: false,
capitalizeTokenValue: true,
};
- this.tokenKeys.push(wipToken);
- this.tokenKeysWithAlternative.push(wipToken);
+ this.tokenKeys.push(confidentialToken);
+ this.tokenKeysWithAlternative.push(confidentialToken);
}
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
index 5090b0bdc3c..315cd6f64da 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_visual_tokens.js
@@ -1,10 +1,6 @@
-import _ from 'underscore';
-import AjaxCache from '~/lib/utils/ajax_cache';
+import VisualTokenValue from 'ee_else_ce/filtered_search/visual_token_value';
import { objectToQueryString } from '~/lib/utils/common_utils';
-import Flash from '../flash';
import FilteredSearchContainer from './container';
-import UsersCache from '../lib/utils/users_cache';
-import DropdownUtils from './dropdown_utils';
export default class FilteredSearchVisualTokens {
static getLastVisualTokenBeforeInput() {
@@ -20,21 +16,6 @@ export default class FilteredSearchVisualTokens {
};
}
- /**
- * Returns a computed API endpoint
- * and query string composed of values from endpointQueryParams
- * @param {String} endpoint
- * @param {String} endpointQueryParams
- */
- static getEndpointWithQueryParams(endpoint, endpointQueryParams) {
- if (!endpointQueryParams) {
- return endpoint;
- }
-
- const queryString = objectToQueryString(JSON.parse(endpointQueryParams));
- return `${endpoint}?${queryString}`;
- }
-
static unselectTokens() {
const otherTokens = FilteredSearchContainer.container.querySelectorAll(
'.js-visual-token .selectable.selected',
@@ -76,122 +57,33 @@ export default class FilteredSearchVisualTokens {
`;
}
- static setTokenStyle(tokenContainer, backgroundColor, textColor) {
- const token = tokenContainer;
-
- token.style.backgroundColor = backgroundColor;
- token.style.color = textColor;
-
- if (textColor === '#FFFFFF') {
- const removeToken = token.querySelector('.remove-token');
- removeToken.classList.add('inverted');
- }
-
- return token;
- }
-
- static updateLabelTokenColor(tokenValueContainer, tokenValue) {
- const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search');
- const { baseEndpoint } = filteredSearchInput.dataset;
- const labelsEndpoint = FilteredSearchVisualTokens.getEndpointWithQueryParams(
- `${baseEndpoint}/labels.json`,
- filteredSearchInput.dataset.endpointQueryParams,
- );
-
- return AjaxCache.retrieve(labelsEndpoint)
- .then(labels => {
- const matchingLabel = (labels || []).find(
- label => `~${DropdownUtils.getEscapedText(label.title)}` === tokenValue,
- );
-
- if (!matchingLabel) {
- return;
- }
-
- FilteredSearchVisualTokens.setTokenStyle(
- tokenValueContainer,
- matchingLabel.color,
- matchingLabel.text_color,
- );
- })
- .catch(() => new Flash('An error occurred while fetching label colors.'));
- }
-
- static updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
- const username = tokenValue.replace(/^@/, '');
- return (
- UsersCache.retrieve(username)
- .then(user => {
- if (!user) {
- return;
- }
-
- /* eslint-disable no-param-reassign */
- tokenValueContainer.dataset.originalValue = tokenValue;
- tokenValueElement.innerHTML = `
- <img class="avatar s20" src="${user.avatar_url}" alt="">
- ${_.escape(user.name)}
- `;
- /* eslint-enable no-param-reassign */
- })
- // ignore error and leave username in the search bar
- .catch(() => {})
- );
- }
-
- static updateEmojiTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue) {
- const container = tokenValueContainer;
- const element = tokenValueElement;
-
- return (
- import(/* webpackChunkName: 'emoji' */ '../emoji')
- .then(Emoji => {
- if (!Emoji.isEmojiNameValid(tokenValue)) {
- return;
- }
-
- container.dataset.originalValue = tokenValue;
- element.innerHTML = Emoji.glEmojiTag(tokenValue);
- })
- // ignore error and leave emoji name in the search bar
- .catch(() => {})
- );
- }
-
static renderVisualTokenValue(parentElement, tokenName, tokenValue) {
+ const tokenType = tokenName.toLowerCase();
const tokenValueContainer = parentElement.querySelector('.value-container');
const tokenValueElement = tokenValueContainer.querySelector('.value');
tokenValueElement.innerText = tokenValue;
- if (['none', 'any'].includes(tokenValue.toLowerCase())) {
- return;
- }
-
- const tokenType = tokenName.toLowerCase();
+ const visualTokenValue = new VisualTokenValue(tokenValue, tokenType);
- if (tokenType === 'label') {
- FilteredSearchVisualTokens.updateLabelTokenColor(tokenValueContainer, tokenValue);
- } else if (tokenType === 'author' || tokenType === 'assignee') {
- FilteredSearchVisualTokens.updateUserTokenAppearance(
- tokenValueContainer,
- tokenValueElement,
- tokenValue,
- );
- } else if (tokenType === 'my-reaction') {
- FilteredSearchVisualTokens.updateEmojiTokenAppearance(
- tokenValueContainer,
- tokenValueElement,
- tokenValue,
- );
- }
+ visualTokenValue.render(tokenValueContainer, tokenValueElement);
}
static addVisualTokenElement(name, value, options = {}) {
- const { isSearchTerm = false, canEdit, uppercaseTokenName, capitalizeTokenValue } = options;
+ const {
+ isSearchTerm = false,
+ canEdit,
+ uppercaseTokenName,
+ capitalizeTokenValue,
+ tokenClass = `search-token-${name.toLowerCase()}`,
+ } = options;
const li = document.createElement('li');
li.classList.add('js-visual-token');
li.classList.add(isSearchTerm ? 'filtered-search-term' : 'filtered-search-token');
+ if (!isSearchTerm) {
+ li.classList.add(tokenClass);
+ }
+
if (value) {
li.innerHTML = FilteredSearchVisualTokens.createVisualTokenElementHTML({
canEdit,
@@ -318,6 +210,21 @@ export default class FilteredSearchVisualTokens {
}
}
+ /**
+ * Returns a computed API endpoint
+ * and query string composed of values from endpointQueryParams
+ * @param {String} endpoint
+ * @param {String} endpointQueryParams
+ */
+ static getEndpointWithQueryParams(endpoint, endpointQueryParams) {
+ if (!endpointQueryParams) {
+ return endpoint;
+ }
+
+ const queryString = objectToQueryString(JSON.parse(endpointQueryParams));
+ return `${endpoint}?${queryString}`;
+ }
+
static editToken(token) {
const input = FilteredSearchContainer.container.querySelector('.filtered-search');
diff --git a/app/assets/javascripts/filtered_search/visual_token_value.js b/app/assets/javascripts/filtered_search/visual_token_value.js
new file mode 100644
index 00000000000..24532d88cf3
--- /dev/null
+++ b/app/assets/javascripts/filtered_search/visual_token_value.js
@@ -0,0 +1,125 @@
+import _ from 'underscore';
+import FilteredSearchContainer from '~/filtered_search/container';
+import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
+import AjaxCache from '~/lib/utils/ajax_cache';
+import DropdownUtils from '~/filtered_search/dropdown_utils';
+import Flash from '~/flash';
+import UsersCache from '~/lib/utils/users_cache';
+
+export default class VisualTokenValue {
+ constructor(tokenValue, tokenType) {
+ this.tokenValue = tokenValue;
+ this.tokenType = tokenType;
+ }
+
+ render(tokenValueContainer, tokenValueElement) {
+ const { tokenType, tokenValue } = this;
+
+ if (['none', 'any'].includes(tokenValue.toLowerCase())) {
+ return;
+ }
+
+ if (tokenType === 'label') {
+ this.updateLabelTokenColor(tokenValueContainer);
+ } else if (tokenType === 'author' || tokenType === 'assignee') {
+ this.updateUserTokenAppearance(tokenValueContainer, tokenValueElement);
+ } else if (tokenType === 'my-reaction') {
+ this.updateEmojiTokenAppearance(tokenValueContainer, tokenValueElement);
+ }
+ }
+
+ updateUserTokenAppearance(tokenValueContainer, tokenValueElement) {
+ const { tokenValue } = this;
+ const username = this.tokenValue.replace(/^@/, '');
+
+ return (
+ UsersCache.retrieve(username)
+ .then(user => {
+ if (!user) {
+ return;
+ }
+
+ /* eslint-disable no-param-reassign */
+ tokenValueContainer.dataset.originalValue = tokenValue;
+ tokenValueElement.innerHTML = `
+ <img class="avatar s20" src="${user.avatar_url}" alt="">
+ ${_.escape(user.name)}
+ `;
+ /* eslint-enable no-param-reassign */
+ })
+ // ignore error and leave username in the search bar
+ .catch(() => {})
+ );
+ }
+
+ updateLabelTokenColor(tokenValueContainer) {
+ const { tokenValue } = this;
+ const filteredSearchInput = FilteredSearchContainer.container.querySelector('.filtered-search');
+ const { baseEndpoint } = filteredSearchInput.dataset;
+ const labelsEndpoint = FilteredSearchVisualTokens.getEndpointWithQueryParams(
+ `${baseEndpoint}/labels.json`,
+ filteredSearchInput.dataset.endpointQueryParams,
+ );
+
+ return AjaxCache.retrieve(labelsEndpoint)
+ .then(labels => {
+ const matchingLabel = (labels || []).find(
+ label => `~${DropdownUtils.getEscapedText(label.title)}` === tokenValue,
+ );
+
+ if (!matchingLabel) {
+ return;
+ }
+
+ VisualTokenValue.setTokenStyle(
+ tokenValueContainer,
+ matchingLabel.color,
+ matchingLabel.text_color,
+ );
+ })
+ .catch(() => new Flash('An error occurred while fetching label colors.'));
+ }
+
+ static setTokenStyle(tokenValueContainer, backgroundColor, textColor) {
+ const token = tokenValueContainer;
+
+ token.style.backgroundColor = backgroundColor;
+ token.style.color = textColor;
+
+ if (textColor === '#FFFFFF') {
+ const removeToken = token.querySelector('.remove-token');
+ removeToken.classList.add('inverted');
+ }
+
+ return token;
+ }
+
+ updateEmojiTokenAppearance(tokenValueContainer, tokenValueElement) {
+ const container = tokenValueContainer;
+ const element = tokenValueElement;
+ const value = this.tokenValue;
+
+ return (
+ import(/* webpackChunkName: 'emoji' */ '../emoji')
+ .then(Emoji => {
+ Emoji.initEmojiMap()
+ .then(() => {
+ if (!Emoji.isEmojiNameValid(value)) {
+ return;
+ }
+
+ container.dataset.originalValue = value;
+ element.innerHTML = Emoji.glEmojiTag(value);
+ })
+ // ignore error and leave emoji name in the search bar
+ .catch(err => {
+ throw err;
+ });
+ })
+ // ignore error and leave emoji name in the search bar
+ .catch(importError => {
+ throw importError;
+ })
+ );
+ }
+}
diff --git a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
index 2cbc7c7077b..42d14b65b3a 100644
--- a/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
+++ b/app/assets/javascripts/frequent_items/components/frequent_items_list_item.vue
@@ -82,8 +82,14 @@ export default {
<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" />
+ <img v-if="hasAvatar" :src="avatarUrl" class="avatar rect-avatar s32" />
+ <identicon
+ v-else
+ :entity-id="itemId"
+ :entity-name="itemName"
+ size-class="s32"
+ class="rect-avatar"
+ />
</div>
<div class="frequent-items-item-metadata-container">
<div :title="itemName" class="frequent-items-item-title" v-html="highlightedItemName"></div>
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js
index 570d3b712e0..50ea13edf63 100644
--- a/app/assets/javascripts/gfm_auto_complete.js
+++ b/app/assets/javascripts/gfm_auto_complete.js
@@ -195,11 +195,15 @@ class GfmAutoComplete {
title += ` (${m.count})`;
}
+ const GROUP_TYPE = 'Group';
+
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase();
+
+ const rectAvatarClass = m.type === GROUP_TYPE ? 'rect-avatar' : '';
const imgAvatar = `<img src="${m.avatar_url}" alt="${
m.username
- }" class="avatar avatar-inline center s26"/>`;
- const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
+ }" class="avatar ${rectAvatarClass} avatar-inline center s26"/>`;
+ const txtAvatar = `<div class="avatar ${rectAvatarClass} center avatar-inline s26">${autoCompleteAvatar}</div>`;
return {
username: m.username,
@@ -483,9 +487,15 @@ class GfmAutoComplete {
this.loadData($input, at, this.cachedData[at]);
} else if (GfmAutoComplete.atTypeMap[at] === 'emojis') {
import(/* webpackChunkName: 'emoji' */ './emoji')
- .then(({ validEmojiNames, glEmojiTag }) => {
- this.loadData($input, at, validEmojiNames);
- GfmAutoComplete.glEmojiTag = glEmojiTag;
+ .then(({ initEmojiMap, getValidEmojiNames, glEmojiTag }) => {
+ initEmojiMap()
+ .then(() => {
+ this.loadData($input, at, getValidEmojiNames());
+ GfmAutoComplete.glEmojiTag = glEmojiTag;
+ })
+ .catch(() => {
+ this.isLoadingData[at] = false;
+ });
})
.catch(() => {
this.isLoadingData[at] = false;
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index a8ac2f510a4..27d8669b256 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -656,23 +656,7 @@ GitLabDropdown = (function() {
if (this.options.renderMenu) {
return this.options.renderMenu(html);
} else {
- var ul = document.createElement('ul');
-
- for (var i = 0; i < html.length; i += 1) {
- var el = html[i];
-
- if (el instanceof $) {
- el = el.get(0);
- }
-
- if (typeof el === 'string') {
- ul.innerHTML += el;
- } else {
- ul.appendChild(el);
- }
- }
-
- return ul;
+ return $('<ul>').append(html);
}
};
diff --git a/app/assets/javascripts/groups/components/app.vue b/app/assets/javascripts/groups/components/app.vue
index 29dc2d6a8a3..aa50fd8ff62 100644
--- a/app/assets/javascripts/groups/components/app.vue
+++ b/app/assets/javascripts/groups/components/app.vue
@@ -244,7 +244,7 @@ export default {
<gl-loading-icon
v-if="isLoading"
:label="s__('GroupsTree|Loading groups')"
- :size="2"
+ size="md"
class="loading-animation prepend-top-20"
/>
<groups-component
diff --git a/app/assets/javascripts/groups/components/group_item.vue b/app/assets/javascripts/groups/components/group_item.vue
index 688bd37cc56..d5130cd331d 100644
--- a/app/assets/javascripts/groups/components/group_item.vue
+++ b/app/assets/javascripts/groups/components/group_item.vue
@@ -88,7 +88,7 @@ export default {
</div>
<div
:class="{ 'content-loading': group.isChildrenLoading }"
- class="avatar-container s24 d-none d-sm-flex"
+ class="avatar-container rect-avatar s24 d-none d-sm-flex"
>
<a :href="group.relativePath" class="no-expand">
<img v-if="hasAvatar" :src="group.avatarUrl" class="avatar s24" />
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
index 2b44438f849..9161eb3d9b1 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/radio_group.vue
@@ -38,8 +38,8 @@ export default {
},
},
computed: {
- ...mapState('commit', ['commitAction']),
- ...mapGetters('commit', ['newBranchName']),
+ ...mapState('commit', ['commitAction', 'newBranchName']),
+ ...mapGetters('commit', ['placeholderBranchName']),
tooltipTitle() {
return this.disabled ? this.title : '';
},
@@ -73,7 +73,8 @@ export default {
</label>
<div v-if="commitAction === value && showInput" class="ide-commit-new-branch">
<input
- :placeholder="newBranchName"
+ :placeholder="placeholderBranchName"
+ :value="newBranchName"
type="text"
class="form-control monospace"
@input="updateBranchName($event.target.value)"
diff --git a/app/assets/javascripts/ide/components/new_dropdown/index.vue b/app/assets/javascripts/ide/components/new_dropdown/index.vue
index d7a7b1b4d78..593a9162a06 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/index.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/index.vue
@@ -1,7 +1,6 @@
<script>
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';
@@ -9,7 +8,6 @@ import { modalTypes } from '../../constants';
export default {
components: {
icon,
- newModal,
upload,
ItemButton,
},
diff --git a/app/assets/javascripts/ide/components/new_dropdown/modal.vue b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
index c9c4e9e86f8..412b07553dc 100644
--- a/app/assets/javascripts/ide/components/new_dropdown/modal.vue
+++ b/app/assets/javascripts/ide/components/new_dropdown/modal.vue
@@ -1,6 +1,7 @@
<script>
import $ from 'jquery';
-import { __ } from '~/locale';
+import flash from '~/flash';
+import { __, sprintf, s__ } from '~/locale';
import { mapActions, mapState, mapGetters } from 'vuex';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { modalTypes } from '../../constants';
@@ -15,18 +16,20 @@ export default {
};
},
computed: {
- ...mapState(['entryModal']),
+ ...mapState(['entries', 'entryModal']),
...mapGetters('fileTemplates', ['templateTypes']),
entryName: {
get() {
+ const entryPath = this.entryModal.entry.path;
+
if (this.entryModal.type === modalTypes.rename) {
- return this.name || this.entryModal.entry.name;
+ return this.name || entryPath;
}
- return this.name || (this.entryModal.path !== '' ? `${this.entryModal.path}/` : '');
+ return this.name || (entryPath ? `${entryPath}/` : '');
},
set(val) {
- this.name = val;
+ this.name = val.trim();
},
},
modalTitle() {
@@ -62,10 +65,40 @@ export default {
...mapActions(['createTempEntry', 'renameEntry']),
submitForm() {
if (this.entryModal.type === modalTypes.rename) {
- this.renameEntry({
- path: this.entryModal.entry.path,
- name: this.entryName,
- });
+ if (this.entries[this.entryName] && !this.entries[this.entryName].deleted) {
+ flash(
+ sprintf(s__('The name %{entryName} is already taken in this directory.'), {
+ entryName: this.entryName,
+ }),
+ 'alert',
+ document,
+ null,
+ false,
+ true,
+ );
+ } else {
+ let parentPath = this.entryName.split('/');
+ const entryName = parentPath.pop();
+ parentPath = parentPath.join('/');
+
+ const createPromise =
+ parentPath && !this.entries[parentPath]
+ ? this.createTempEntry({ name: parentPath, type: 'tree' })
+ : Promise.resolve();
+
+ createPromise
+ .then(() =>
+ this.renameEntry({
+ path: this.entryModal.entry.path,
+ name: entryName,
+ entryPath: null,
+ parentPath,
+ }),
+ )
+ .catch(() =>
+ flash(__('Error creating a new path'), 'alert', document, null, false, true),
+ );
+ }
} else {
this.createTempEntry({
name: this.name,
@@ -82,7 +115,14 @@ export default {
$('#ide-new-entry').modal('toggle');
},
focusInput() {
+ const name = this.entries[this.entryName] ? this.entries[this.entryName].name : null;
+ const inputValue = this.$refs.fieldName.value;
+
this.$refs.fieldName.focus();
+
+ if (name) {
+ this.$refs.fieldName.setSelectionRange(inputValue.indexOf(name), inputValue.length);
+ }
},
closedModal() {
this.name = '';
diff --git a/app/assets/javascripts/ide/lib/files.js b/app/assets/javascripts/ide/lib/files.js
new file mode 100644
index 00000000000..5dfba8fe531
--- /dev/null
+++ b/app/assets/javascripts/ide/lib/files.js
@@ -0,0 +1,113 @@
+import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
+import { decorateData, sortTree } from '../stores/utils';
+
+export const splitParent = path => {
+ const idx = path.lastIndexOf('/');
+
+ return {
+ parent: idx >= 0 ? path.substring(0, idx) : null,
+ name: idx >= 0 ? path.substring(idx + 1) : path,
+ };
+};
+
+/**
+ * Create file objects from a list of file paths.
+ */
+export const decorateFiles = ({
+ data,
+ projectId,
+ branchId,
+ tempFile = false,
+ content = '',
+ base64 = false,
+}) => {
+ const treeList = [];
+ const entries = {};
+
+ // These mutable variable references end up being exported and used by `createTempEntry`
+ let file;
+ let parentPath;
+
+ const insertParent = path => {
+ if (!path) {
+ return null;
+ } else if (entries[path]) {
+ return entries[path];
+ }
+
+ const { parent, name } = splitParent(path);
+ const parentFolder = parent && insertParent(parent);
+ parentPath = parentFolder && parentFolder.path;
+
+ const tree = decorateData({
+ projectId,
+ branchId,
+ id: path,
+ name,
+ path,
+ url: `/${projectId}/tree/${branchId}/-/${path}/`,
+ type: 'tree',
+ parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`,
+ tempFile,
+ changed: tempFile,
+ opened: tempFile,
+ parentPath,
+ });
+
+ Object.assign(entries, {
+ [path]: tree,
+ });
+
+ if (parentFolder) {
+ parentFolder.tree.push(tree);
+ } else {
+ treeList.push(tree);
+ }
+
+ return tree;
+ };
+
+ data.forEach(path => {
+ const { parent, name } = splitParent(path);
+
+ const fileFolder = parent && insertParent(parent);
+
+ if (name) {
+ parentPath = fileFolder && fileFolder.path;
+
+ file = decorateData({
+ projectId,
+ branchId,
+ id: path,
+ name,
+ path,
+ url: `/${projectId}/blob/${branchId}/-/${path}`,
+ type: 'blob',
+ parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`,
+ tempFile,
+ changed: tempFile,
+ content,
+ base64,
+ previewMode: viewerInformationForPath(name),
+ parentPath,
+ });
+
+ Object.assign(entries, {
+ [path]: file,
+ });
+
+ if (fileFolder) {
+ fileFolder.tree.push(file);
+ } else {
+ treeList.push(file);
+ }
+ }
+ });
+
+ return {
+ entries,
+ treeList: sortTree(treeList),
+ file,
+ parentPath,
+ };
+};
diff --git a/app/assets/javascripts/ide/services/index.js b/app/assets/javascripts/ide/services/index.js
index 13449592e62..ba33b6826d6 100644
--- a/app/assets/javascripts/ide/services/index.js
+++ b/app/assets/javascripts/ide/services/index.js
@@ -40,6 +40,9 @@ export default {
getProjectData(namespace, project) {
return Api.project(`${namespace}/${project}`);
},
+ getProjectMergeRequests(projectId, params = {}) {
+ return Api.projectMergeRequests(projectId, params);
+ },
getProjectMergeRequestData(projectId, mergeRequestId, params = {}) {
return Api.projectMergeRequest(projectId, mergeRequestId, params);
},
diff --git a/app/assets/javascripts/ide/stores/actions.js b/app/assets/javascripts/ide/stores/actions.js
index e10a132ab4b..7b660bda081 100644
--- a/app/assets/javascripts/ide/stores/actions.js
+++ b/app/assets/javascripts/ide/stores/actions.js
@@ -3,7 +3,7 @@ import Vue from 'vue';
import { visitUrl } from '~/lib/utils/url_utility';
import flash from '~/flash';
import * as types from './mutation_types';
-import FilesDecoratorWorker from './workers/files_decorator_worker';
+import { decorateFiles } from '../lib/files';
import { stageKeys } from '../constants';
export const redirectToUrl = (_, url) => visitUrl(url);
@@ -56,7 +56,6 @@ export const createTempEntry = (
{ name, type, content = '', base64 = false },
) =>
new Promise(resolve => {
- const worker = new FilesDecoratorWorker();
const fullName = name.slice(-1) !== '/' && type === 'tree' ? `${name}/` : name;
if (state.entries[name]) {
@@ -74,31 +73,7 @@ export const createTempEntry = (
return null;
}
- worker.addEventListener('message', ({ data }) => {
- const { file, parentPath } = data;
-
- worker.terminate();
-
- commit(types.CREATE_TMP_ENTRY, {
- data,
- projectId: state.currentProjectId,
- branchId: state.currentBranchId,
- });
-
- if (type === 'blob') {
- commit(types.TOGGLE_FILE_OPEN, file.path);
- commit(types.ADD_FILE_TO_CHANGED, file.path);
- dispatch('setFileActive', file.path);
- }
-
- if (parentPath && !state.entries[parentPath].opened) {
- commit(types.TOGGLE_TREE_OPEN, parentPath);
- }
-
- resolve(file);
- });
-
- worker.postMessage({
+ const data = decorateFiles({
data: [fullName],
projectId: state.currentProjectId,
branchId: state.currentBranchId,
@@ -107,6 +82,25 @@ export const createTempEntry = (
base64,
content,
});
+ const { file, parentPath } = data;
+
+ commit(types.CREATE_TMP_ENTRY, {
+ data,
+ projectId: state.currentProjectId,
+ branchId: state.currentBranchId,
+ });
+
+ if (type === 'blob') {
+ commit(types.TOGGLE_FILE_OPEN, file.path);
+ commit(types.ADD_FILE_TO_CHANGED, file.path);
+ dispatch('setFileActive', file.path);
+ }
+
+ if (parentPath && !state.entries[parentPath].opened) {
+ commit(types.TOGGLE_TREE_OPEN, parentPath);
+ }
+
+ resolve(file);
return null;
});
@@ -215,15 +209,27 @@ export const deleteEntry = ({ commit, dispatch, state }, path) => {
export const resetOpenFiles = ({ commit }) => commit(types.RESET_OPEN_FILES);
-export const renameEntry = ({ dispatch, commit, state }, { path, name, entryPath = null }) => {
+export const renameEntry = (
+ { dispatch, commit, state },
+ { path, name, entryPath = null, parentPath },
+) => {
const entry = state.entries[entryPath || path];
- commit(types.RENAME_ENTRY, { path, name, entryPath });
+ commit(types.RENAME_ENTRY, { path, name, entryPath, parentPath });
if (entry.type === 'tree') {
- state.entries[entryPath || path].tree.forEach(f =>
- dispatch('renameEntry', { path, name, entryPath: f.path }),
- );
+ const slashedParentPath = parentPath ? `${parentPath}/` : '';
+ const targetEntry = entryPath ? entryPath.split('/').pop() : name;
+ const newParentPath = `${slashedParentPath}${targetEntry}`;
+
+ state.entries[entryPath || path].tree.forEach(f => {
+ dispatch('renameEntry', {
+ path,
+ name,
+ entryPath: f.path,
+ parentPath: newParentPath,
+ });
+ });
}
if (!entryPath && !entry.tempFile) {
diff --git a/app/assets/javascripts/ide/stores/actions/merge_request.js b/app/assets/javascripts/ide/stores/actions/merge_request.js
index 18c24369996..362ced248a1 100644
--- a/app/assets/javascripts/ide/stores/actions/merge_request.js
+++ b/app/assets/javascripts/ide/stores/actions/merge_request.js
@@ -4,6 +4,38 @@ import service from '../../services';
import * as types from '../mutation_types';
import { activityBarViews } from '../../constants';
+export const getMergeRequestsForBranch = ({ commit }, { projectId, branchId } = {}) =>
+ service
+ .getProjectMergeRequests(`${projectId}`, {
+ source_branch: branchId,
+ order_by: 'created_at',
+ per_page: 1,
+ })
+ .then(({ data }) => {
+ if (data.length > 0) {
+ const currentMR = data[0];
+
+ commit(types.SET_MERGE_REQUEST, {
+ projectPath: projectId,
+ mergeRequestId: currentMR.iid,
+ mergeRequest: currentMR,
+ });
+
+ commit(types.SET_CURRENT_MERGE_REQUEST, `${currentMR.iid}`);
+ }
+ })
+ .catch(e => {
+ flash(
+ __(`Error fetching merge requests for ${branchId}`),
+ 'alert',
+ document,
+ null,
+ false,
+ true,
+ );
+ throw e;
+ });
+
export const getMergeRequestData = (
{ commit, dispatch, state },
{ projectId, mergeRequestId, targetProjectId = null, force = false } = {},
diff --git a/app/assets/javascripts/ide/stores/actions/project.js b/app/assets/javascripts/ide/stores/actions/project.js
index b65f631c99c..06ed5c0b572 100644
--- a/app/assets/javascripts/ide/stores/actions/project.js
+++ b/app/assets/javascripts/ide/stores/actions/project.js
@@ -136,17 +136,24 @@ export const openBranch = ({ dispatch, state }, { projectId, branchId, basePath
return dispatch('getFiles', {
projectId,
branchId,
- }).then(() => {
- if (basePath) {
- const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
- const treeEntryKey = Object.keys(state.entries).find(
- key => key === path && !state.entries[key].pending,
- );
- const treeEntry = state.entries[treeEntryKey];
+ })
+ .then(() => {
+ if (basePath) {
+ const path = basePath.slice(-1) === '/' ? basePath.slice(0, -1) : basePath;
+ const treeEntryKey = Object.keys(state.entries).find(
+ key => key === path && !state.entries[key].pending,
+ );
+ const treeEntry = state.entries[treeEntryKey];
- if (treeEntry) {
- dispatch('handleTreeEntryAction', treeEntry);
+ if (treeEntry) {
+ dispatch('handleTreeEntryAction', treeEntry);
+ }
}
- }
- });
+ })
+ .then(() => {
+ dispatch('getMergeRequestsForBranch', {
+ projectId,
+ branchId,
+ });
+ });
};
diff --git a/app/assets/javascripts/ide/stores/actions/tree.js b/app/assets/javascripts/ide/stores/actions/tree.js
index de5f6050074..3d83e4a9ba5 100644
--- a/app/assets/javascripts/ide/stores/actions/tree.js
+++ b/app/assets/javascripts/ide/stores/actions/tree.js
@@ -1,7 +1,8 @@
+import _ from 'underscore';
import { __ } from '../../../locale';
import service from '../../services';
import * as types from '../mutation_types';
-import FilesDecoratorWorker from '../workers/files_decorator_worker';
+import { decorateFiles } from '../../lib/files';
export const toggleTreeOpen = ({ commit }, path) => {
commit(types.TOGGLE_TREE_OPEN, path);
@@ -32,6 +33,19 @@ export const handleTreeEntryAction = ({ commit, dispatch }, row) => {
dispatch('showTreeEntry', row.path);
};
+export const setDirectoryData = ({ state, commit }, { projectId, branchId, treeList }) => {
+ const selectedTree = state.trees[`${projectId}/${branchId}`];
+
+ commit(types.SET_DIRECTORY_DATA, {
+ treePath: `${projectId}/${branchId}`,
+ data: treeList,
+ });
+ commit(types.TOGGLE_LOADING, {
+ entry: selectedTree,
+ forceValue: false,
+ });
+};
+
export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } = {}) =>
new Promise((resolve, reject) => {
if (
@@ -45,31 +59,19 @@ export const getFiles = ({ state, commit, dispatch }, { projectId, branchId } =
service
.getFiles(selectedProject.web_url, branchId)
.then(({ data }) => {
- const worker = new FilesDecoratorWorker();
- worker.addEventListener('message', e => {
- const { entries, treeList } = e.data;
- const selectedTree = state.trees[`${projectId}/${branchId}`];
-
- commit(types.SET_ENTRIES, entries);
- commit(types.SET_DIRECTORY_DATA, {
- treePath: `${projectId}/${branchId}`,
- data: treeList,
- });
- commit(types.TOGGLE_LOADING, {
- entry: selectedTree,
- forceValue: false,
- });
-
- worker.terminate();
-
- resolve();
- });
-
- worker.postMessage({
+ const { entries, treeList } = decorateFiles({
data,
projectId,
branchId,
});
+
+ commit(types.SET_ENTRIES, entries);
+
+ // Defer setting the directory data because this triggers some intense rendering.
+ // The entries is all we need to load the file editor.
+ _.defer(() => dispatch('setDirectoryData', { projectId, branchId, treeList }));
+
+ resolve();
})
.catch(e => {
if (e.response.status === 404) {
diff --git a/app/assets/javascripts/ide/stores/modules/commit/getters.js b/app/assets/javascripts/ide/stores/modules/commit/getters.js
index 03777e6c10b..bbe40b2ec2f 100644
--- a/app/assets/javascripts/ide/stores/modules/commit/getters.js
+++ b/app/assets/javascripts/ide/stores/modules/commit/getters.js
@@ -14,7 +14,7 @@ const createTranslatedTextForFiles = (files, text) => {
export const discardDraftButtonDisabled = state =>
state.commitMessage === '' || state.submitCommitLoading;
-export const newBranchName = (state, _, rootState) =>
+export const placeholderBranchName = (state, _, rootState) =>
`${gon.current_username}-${rootState.currentBranchId}-patch-${`${new Date().getTime()}`.substr(
-BRANCH_SUFFIX_COUNT,
)}`;
@@ -25,7 +25,7 @@ export const branchName = (state, getters, rootState) => {
state.commitAction === consts.COMMIT_TO_NEW_BRANCH_MR
) {
if (state.newBranchName === '') {
- return getters.newBranchName;
+ return getters.placeholderBranchName;
}
return state.newBranchName;
diff --git a/app/assets/javascripts/ide/stores/mutations.js b/app/assets/javascripts/ide/stores/mutations.js
index 78cdfda74f0..9b9f4b21f1c 100644
--- a/app/assets/javascripts/ide/stores/mutations.js
+++ b/app/assets/javascripts/ide/stores/mutations.js
@@ -206,19 +206,17 @@ export default {
}
}
},
- [types.RENAME_ENTRY](state, { path, name, entryPath = null }) {
+ [types.RENAME_ENTRY](state, { path, name, entryPath = null, parentPath }) {
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) : '';
+ const slashedParentPath = parentPath ? `${parentPath}/` : '';
+ const newPath = entryPath
+ ? `${slashedParentPath}${oldEntry.name}`
+ : `${slashedParentPath}${name}`;
state.entries[newPath] = {
...oldEntry,
id: newPath,
- key: `${name}-${oldEntry.type}-${oldEntry.id}`,
+ key: `${newPath}-${oldEntry.type}-${oldEntry.id}`,
path: newPath,
name: entryPath ? oldEntry.name : name,
tempFile: true,
@@ -228,6 +226,7 @@ export default {
parentPath,
raw: '',
};
+
oldEntry.moved = true;
oldEntry.movedPath = newPath;
@@ -256,6 +255,7 @@ export default {
Vue.delete(state.entries, oldEntry.path);
}
},
+
...projectMutations,
...mergeRequestMutation,
...fileMutations,
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 0ede76fd1e0..0b2a18e9c8a 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -75,8 +75,7 @@ export const decorateData = entity => {
parentPath = '',
} = entity;
- return {
- ...dataStructure(),
+ return Object.assign(dataStructure(), {
id,
projectId,
branchId,
@@ -97,7 +96,7 @@ export const decorateData = entity => {
file_lock,
html,
parentPath,
- };
+ });
};
export const findEntry = (tree, type, name, prop = 'name') =>
diff --git a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js b/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js
deleted file mode 100644
index fa35c215880..00000000000
--- a/app/assets/javascripts/ide/stores/workers/files_decorator_worker.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
-import { decorateData, sortTree } from '../utils';
-
-// eslint-disable-next-line no-restricted-globals
-self.addEventListener('message', e => {
- const { data, projectId, branchId, tempFile = false, content = '', base64 = false } = e.data;
-
- const treeList = [];
- let file;
- let parentPath;
- const entries = data.reduce((acc, path) => {
- const pathSplit = path.split('/');
- const blobName = pathSplit.pop().trim();
-
- if (pathSplit.length > 0) {
- pathSplit.reduce((pathAcc, folderName) => {
- const parentFolder = acc[pathAcc[pathAcc.length - 1]];
- const folderPath = `${parentFolder ? `${parentFolder.path}/` : ''}${folderName}`;
- const foundEntry = acc[folderPath];
-
- if (!foundEntry) {
- parentPath = parentFolder ? parentFolder.path : null;
-
- const tree = decorateData({
- projectId,
- branchId,
- id: folderPath,
- name: folderName,
- path: folderPath,
- url: `/${projectId}/tree/${branchId}/-/${folderPath}/`,
- type: 'tree',
- parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`,
- tempFile,
- changed: tempFile,
- opened: tempFile,
- parentPath,
- });
-
- Object.assign(acc, {
- [folderPath]: tree,
- });
-
- if (parentFolder) {
- parentFolder.tree.push(tree);
- } else {
- treeList.push(tree);
- }
-
- pathAcc.push(tree.path);
- } else {
- pathAcc.push(foundEntry.path);
- }
-
- return pathAcc;
- }, []);
- }
-
- if (blobName !== '') {
- const fileFolder = acc[pathSplit.join('/')];
- parentPath = fileFolder ? fileFolder.path : null;
-
- file = decorateData({
- projectId,
- branchId,
- id: path,
- name: blobName,
- path,
- url: `/${projectId}/blob/${branchId}/-/${path}`,
- type: 'blob',
- parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`,
- tempFile,
- changed: tempFile,
- content,
- base64,
- previewMode: viewerInformationForPath(blobName),
- parentPath,
- });
-
- Object.assign(acc, {
- [path]: file,
- });
-
- if (fileFolder) {
- fileFolder.tree.push(file);
- } else {
- treeList.push(file);
- }
- }
-
- return acc;
- }, {});
-
- // eslint-disable-next-line no-restricted-globals
- self.postMessage({
- entries,
- treeList: sortTree(treeList),
- file,
- parentPath,
- });
-});
diff --git a/app/assets/javascripts/import_projects/index.js b/app/assets/javascripts/import_projects/index.js
index 5c77484aee1..2d99d716609 100644
--- a/app/assets/javascripts/import_projects/index.js
+++ b/app/assets/javascripts/import_projects/index.js
@@ -3,7 +3,7 @@ import { mapActions } from 'vuex';
import Translate from '../vue_shared/translate';
import ImportProjectsTable from './components/import_projects_table.vue';
import { parseBoolean } from '../lib/utils/common_utils';
-import store from './store';
+import createStore from './store';
Vue.use(Translate);
@@ -20,6 +20,7 @@ export default function mountImportProjectsTable(mountElement) {
ciCdOnly,
} = mountElement.dataset;
+ const store = createStore();
return new Vue({
el: mountElement,
store,
diff --git a/app/assets/javascripts/import_projects/store/index.js b/app/assets/javascripts/import_projects/store/index.js
index 6ac9bfd8189..f666e2ebf33 100644
--- a/app/assets/javascripts/import_projects/store/index.js
+++ b/app/assets/javascripts/import_projects/store/index.js
@@ -7,9 +7,10 @@ import mutations from './mutations';
Vue.use(Vuex);
-export default new Vuex.Store({
- state: state(),
- actions,
- mutations,
- getters,
-});
+export default () =>
+ new Vuex.Store({
+ state: state(),
+ actions,
+ mutations,
+ getters,
+ });
diff --git a/app/assets/javascripts/issuable_suggestions/index.js b/app/assets/javascripts/issuable_suggestions/index.js
index 2c80cf1797a..40916c9d27f 100644
--- a/app/assets/javascripts/issuable_suggestions/index.js
+++ b/app/assets/javascripts/issuable_suggestions/index.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import defaultClient from '~/lib/graphql';
+import createDefaultClient from '~/lib/graphql';
import App from './components/app.vue';
Vue.use(VueApollo);
@@ -10,7 +10,7 @@ export default function() {
const issueTitle = document.getElementById('issue_title');
const { projectPath } = el.dataset;
const apolloProvider = new VueApollo({
- defaultClient,
+ defaultClient: createDefaultClient(),
});
return new Vue({
diff --git a/app/assets/javascripts/issue_show/components/description.vue b/app/assets/javascripts/issue_show/components/description.vue
index 58f14bac8c8..732184dc782 100644
--- a/app/assets/javascripts/issue_show/components/description.vue
+++ b/app/assets/javascripts/issue_show/components/description.vue
@@ -140,7 +140,7 @@ export default {
'issue-realtime-pre-pulse': preAnimation,
'issue-realtime-trigger-pulse': pulseAnimation,
}"
- class="wiki"
+ class="md"
v-html="descriptionHtml"
></div>
<textarea
diff --git a/app/assets/javascripts/jobs/components/commit_block.vue b/app/assets/javascripts/jobs/components/commit_block.vue
index 7076a79dd5d..b651a6e4bfb 100644
--- a/app/assets/javascripts/jobs/components/commit_block.vue
+++ b/app/assets/javascripts/jobs/components/commit_block.vue
@@ -39,7 +39,7 @@ export default {
</gl-link>
<clipboard-button
- :text="commit.short_id"
+ :text="commit.id"
:title="__('Copy commit SHA to clipboard')"
css-class="btn btn-clipboard btn-transparent"
/>
diff --git a/app/assets/javascripts/jobs/components/job_app.vue b/app/assets/javascripts/jobs/components/job_app.vue
index d473d6a482d..dbadd224251 100644
--- a/app/assets/javascripts/jobs/components/job_app.vue
+++ b/app/assets/javascripts/jobs/components/job_app.vue
@@ -34,6 +34,7 @@ export default {
StuckBlock,
Sidebar,
GlLoadingIcon,
+ SharedRunner: () => import('ee_component/jobs/components/shared_runner_limit_block.vue'),
},
mixins: [delayedJobMixin],
props: {
@@ -84,6 +85,7 @@ export default {
'shouldRenderCalloutMessage',
'shouldRenderTriggeredLabel',
'hasEnvironment',
+ 'shouldRenderSharedRunnerLimitWarning',
'hasTrace',
'emptyStateIllustration',
'isScrollingDown',
@@ -221,6 +223,14 @@ export default {
:runners-path="runnerSettingsUrl"
/>
+ <shared-runner
+ v-if="shouldRenderSharedRunnerLimitWarning"
+ class="js-shared-runner-limit"
+ :quota-used="job.runners.quota.used"
+ :quota-limit="job.runners.quota.limit"
+ :runners-path="runnerHelpUrl"
+ />
+
<environments-block
v-if="hasEnvironment"
class="js-job-environment"
diff --git a/app/assets/javascripts/jobs/components/stages_dropdown.vue b/app/assets/javascripts/jobs/components/stages_dropdown.vue
index c5076d65ff9..6e92b599b0a 100644
--- a/app/assets/javascripts/jobs/components/stages_dropdown.vue
+++ b/app/assets/javascripts/jobs/components/stages_dropdown.vue
@@ -1,5 +1,6 @@
<script>
import _ from 'underscore';
+import { GlLink } from '@gitlab/ui';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
@@ -7,6 +8,7 @@ export default {
components: {
CiIcon,
Icon,
+ GlLink,
},
props: {
pipeline: {
@@ -26,6 +28,12 @@ export default {
hasRef() {
return !_.isEmpty(this.pipeline.ref);
},
+ isTriggeredByMergeRequest() {
+ return Boolean(this.pipeline.merge_request);
+ },
+ isMergeRequestPipeline() {
+ return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline);
+ },
},
methods: {
onStageClick(stage) {
@@ -36,16 +44,41 @@ export default {
</script>
<template>
<div class="block-last dropdown">
- <ci-icon :status="pipeline.details.status" class="vertical-align-middle" />
+ <div class="js-pipeline-info">
+ <ci-icon :status="pipeline.details.status" class="vertical-align-middle" />
- <span class="font-weight-bold">{{ __('Pipeline') }}</span>
- <a :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path"
- >#{{ pipeline.id }}</a
- >
- <template v-if="hasRef">
- {{ __('from') }}
- <a :href="pipeline.ref.path" class="link-commit ref-name">{{ pipeline.ref.name }}</a>
- </template>
+ <span class="font-weight-bold">{{ s__('Job|Pipeline') }}</span>
+ <gl-link :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path"
+ >#{{ pipeline.id }}</gl-link
+ >
+ <template v-if="hasRef">
+ {{ s__('Job|for') }}
+
+ <template v-if="isTriggeredByMergeRequest">
+ <gl-link :href="pipeline.merge_request.path" class="link-commit ref-name js-mr-link"
+ >!{{ pipeline.merge_request.iid }}</gl-link
+ >
+ {{ s__('Job|with') }}
+ <gl-link
+ :href="pipeline.merge_request.source_branch_path"
+ class="link-commit ref-name js-source-branch-link"
+ >{{ pipeline.merge_request.source_branch }}</gl-link
+ >
+
+ <template v-if="isMergeRequestPipeline">
+ {{ s__('Job|into') }}
+ <gl-link
+ :href="pipeline.merge_request.target_branch_path"
+ class="link-commit ref-name js-target-branch-link"
+ >{{ pipeline.merge_request.target_branch }}</gl-link
+ >
+ </template>
+ </template>
+ <gl-link v-else :href="pipeline.ref.path" class="link-commit ref-name">{{
+ pipeline.ref.name
+ }}</gl-link>
+ </template>
+ </div>
<button
type="button"
diff --git a/app/assets/javascripts/jobs/store/getters.js b/app/assets/javascripts/jobs/store/getters.js
index 98911717381..73c1cbc3a99 100644
--- a/app/assets/javascripts/jobs/store/getters.js
+++ b/app/assets/javascripts/jobs/store/getters.js
@@ -28,6 +28,17 @@ export const emptyStateIllustration = state =>
export const emptyStateAction = state =>
(state.job && state.job.status && state.job.status.action) || null;
+/**
+ * Shared runners limit is only rendered when
+ * used quota is bigger or equal than the limit
+ *
+ * @returns {Boolean}
+ */
+export const shouldRenderSharedRunnerLimitWarning = state =>
+ !_.isEmpty(state.job.runners) &&
+ !_.isEmpty(state.job.runners.quota) &&
+ state.job.runners.quota.used >= state.job.runners.quota.limit;
+
export const isScrollingDown = state => isScrolledToBottom() && !state.isTraceComplete;
export const hasRunnersForProject = state =>
diff --git a/app/assets/javascripts/labels_select.js b/app/assets/javascripts/labels_select.js
index f7a611fbca0..cca4927c115 100644
--- a/app/assets/javascripts/labels_select.js
+++ b/app/assets/javascripts/labels_select.js
@@ -199,8 +199,8 @@ export default class LabelsSelect {
.catch(() => flash(__('Error fetching labels.')));
},
renderRow: function(label, instance) {
- var $a,
- $li,
+ var linkEl,
+ listItemEl,
color,
colorEl,
indeterminate,
@@ -209,12 +209,11 @@ export default class LabelsSelect {
spacing,
i,
marked,
- dropdownName,
dropdownValue;
- $li = $('<li>');
- $a = $('<a href="#">');
+
selectedClass = [];
removesAll = label.id <= 0 || label.id == null;
+
if ($dropdown.hasClass('js-filter-bulk-update')) {
indeterminate = $dropdown.data('indeterminate') || [];
marked = $dropdown.data('marked') || [];
@@ -233,7 +232,6 @@ export default class LabelsSelect {
}
} else {
if (this.id(label)) {
- dropdownName = $dropdown.data('fieldName');
dropdownValue = this.id(label)
.toString()
.replace(/'/g, "\\'");
@@ -241,7 +239,7 @@ export default class LabelsSelect {
if (
$form.find(
"input[type='hidden'][name='" +
- dropdownName +
+ this.fieldName +
"'][value='" +
dropdownValue +
"']",
@@ -251,24 +249,34 @@ export default class LabelsSelect {
}
}
- if ($dropdown.hasClass('js-multiselect') && removesAll) {
+ if (this.multiSelect && removesAll) {
selectedClass.push('dropdown-clear-active');
}
}
+
if (label.color) {
colorEl =
"<span class='dropdown-label-box' style='background: " + label.color + "'></span>";
} else {
colorEl = '';
}
+
+ linkEl = document.createElement('a');
+ linkEl.href = '#';
+
// We need to identify which items are actually labels
if (label.id) {
selectedClass.push('label-item');
- $a.attr('data-label-id', label.id);
+ linkEl.dataset.labelId = label.id;
}
- $a.addClass(selectedClass.join(' ')).html(`${colorEl} ${_.escape(label.title)}`);
- // Return generated html
- return $li.html($a).prop('outerHTML');
+
+ linkEl.className = selectedClass.join(' ');
+ linkEl.innerHTML = `${colorEl} ${_.escape(label.title)}`;
+
+ listItemEl = document.createElement('li');
+ listItemEl.appendChild(linkEl);
+
+ return listItemEl;
},
search: {
fields: ['title'],
diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js
index 20a0f142d9e..64e4e899f44 100644
--- a/app/assets/javascripts/lib/graphql.js
+++ b/app/assets/javascripts/lib/graphql.js
@@ -1,9 +1,11 @@
import ApolloClient from 'apollo-boost';
import csrf from '~/lib/utils/csrf';
-export default new ApolloClient({
- uri: `${gon.relative_url_root}/api/graphql`,
- headers: {
- [csrf.headerKey]: csrf.token,
- },
-});
+export default (clientState = {}) =>
+ new ApolloClient({
+ uri: `${gon.relative_url_root}/api/graphql`,
+ headers: {
+ [csrf.headerKey]: csrf.token,
+ },
+ clientState,
+ });
diff --git a/app/assets/javascripts/lib/utils/autosave.js b/app/assets/javascripts/lib/utils/autosave.js
new file mode 100644
index 00000000000..023c336db02
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/autosave.js
@@ -0,0 +1,32 @@
+import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
+
+export const clearDraft = autosaveKey => {
+ try {
+ window.localStorage.removeItem(`autosave/${autosaveKey}`);
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.error(e);
+ }
+};
+
+export const getDraft = autosaveKey => {
+ try {
+ return window.localStorage.getItem(`autosave/${autosaveKey}`);
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.error(e);
+ return null;
+ }
+};
+
+export const updateDraft = (autosaveKey, text) => {
+ try {
+ window.localStorage.setItem(`autosave/${autosaveKey}`, text);
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.error(e);
+ }
+};
+
+export const getDiscussionReplyKey = (noteableType, discussionId) =>
+ ['Note', capitalizeFirstCharacter(noteableType), discussionId, 'Reply'].join('/');
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index 29fe460017e..1af6b63efc9 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -456,21 +456,6 @@ export const historyPushState = newUrl => {
export const parseBoolean = value => (value && value.toString()) === 'true';
/**
- * Converts permission provided as strings to booleans.
- *
- * @param {String} string
- * @returns {Boolean}
- */
-export const convertPermissionToBoolean = permission => {
- if (process.env.NODE_ENV !== 'production') {
- // eslint-disable-next-line no-console
- console.warn('convertPermissionToBoolean is deprecated! Please use parseBoolean instead.');
- }
-
- return parseBoolean(permission);
-};
-
-/**
* @callback backOffCallback
* @param {Function} next
* @param {Function} stop
@@ -723,6 +708,14 @@ export const NavigationType = {
TYPE_RESERVED: 255,
};
+/**
+ * Returns the value of `gon.ee`
+ * Used to check if it's the EE codebase or the CE one.
+ *
+ * @returns Boolean
+ */
+export const isEE = () => window.gon && window.gon.ee;
+
window.gl = window.gl || {};
window.gl.utils = {
...(window.gl.utils || {}),
diff --git a/app/assets/javascripts/lib/utils/http_status.js b/app/assets/javascripts/lib/utils/http_status.js
index 14c02218990..37ad1676f7a 100644
--- a/app/assets/javascripts/lib/utils/http_status.js
+++ b/app/assets/javascripts/lib/utils/http_status.js
@@ -16,6 +16,7 @@ const httpStatusCodes = {
IM_USED: 226,
MULTIPLE_CHOICES: 300,
BAD_REQUEST: 400,
+ UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
UNPROCESSABLE_ENTITY: 422,
diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js
index 2ccc51c35f7..19c4de6083d 100644
--- a/app/assets/javascripts/lib/utils/number_utils.js
+++ b/app/assets/javascripts/lib/utils/number_utils.js
@@ -80,3 +80,22 @@ export function numberToHumanSize(size) {
}
return `${bytesToGiB(size).toFixed(2)} GiB`;
}
+
+/**
+ * A simple method that returns the value of a + b
+ * It seems unessesary, but when combined with a reducer it
+ * adds up all the values in an array.
+ *
+ * e.g. `[1, 2, 3, 4, 5].reduce(sum) // => 15`
+ *
+ * @param {Float} a
+ * @param {Float} b
+ * @example
+ * // return 15
+ * [1, 2, 3, 4, 5].reduce(sum);
+ *
+ * // returns 6
+ * Object.values([{a: 1, b: 2, c: 3].reduce(sum);
+ * @returns {Float} The summed value
+ */
+export const sum = (a = 0, b = 0) => a + b;
diff --git a/app/assets/javascripts/lib/utils/poll.js b/app/assets/javascripts/lib/utils/poll.js
index 198711cf427..a900ff34bf5 100644
--- a/app/assets/javascripts/lib/utils/poll.js
+++ b/app/assets/javascripts/lib/utils/poll.js
@@ -63,6 +63,10 @@ export default class Poll {
const headers = normalizeHeaders(response.headers);
const pollInterval = parseInt(headers[this.intervalHeader], 10);
if (pollInterval > 0 && successCodes.indexOf(response.status) !== -1 && this.canPoll) {
+ if (this.timeoutID) {
+ clearTimeout(this.timeoutID);
+ }
+
this.timeoutID = setTimeout(() => {
this.makeRequest();
}, pollInterval);
@@ -101,15 +105,25 @@ export default class Poll {
}
/**
- * Restarts polling after it has been stoped
+ * Enables polling after it has been stopped
*/
- restart(options) {
- // update data
+ enable(options) {
if (options && options.data) {
this.options.data = options.data;
}
this.canPoll = true;
+
+ if (options && options.response) {
+ this.checkConditions(options.response);
+ }
+ }
+
+ /**
+ * Restarts polling after it has been stopped and makes a request
+ */
+ restart(options) {
+ this.enable(options);
this.makeRequest();
}
}
diff --git a/app/assets/javascripts/lib/utils/simple_poll.js b/app/assets/javascripts/lib/utils/simple_poll.js
index 473f179ad86..576a9ec880c 100644
--- a/app/assets/javascripts/lib/utils/simple_poll.js
+++ b/app/assets/javascripts/lib/utils/simple_poll.js
@@ -1,10 +1,10 @@
-export default (fn, interval = 2000, timeout = 60000) => {
+export default (fn, { interval = 2000, timeout = 60000 } = {}) => {
const startTime = Date.now();
return new Promise((resolve, reject) => {
const stop = arg => (arg instanceof Error ? reject(arg) : resolve(arg));
const next = () => {
- if (Date.now() - startTime < timeout) {
+ if (timeout === 0 || Date.now() - startTime < timeout) {
setTimeout(fn.bind(null, next, stop), interval);
} else {
reject(new Error('SIMPLE_POLL_TIMEOUT'));
diff --git a/app/assets/javascripts/lib/utils/webpack.js b/app/assets/javascripts/lib/utils/webpack.js
index 308ad9784e4..a4dad6f1615 100644
--- a/app/assets/javascripts/lib/utils/webpack.js
+++ b/app/assets/javascripts/lib/utils/webpack.js
@@ -6,5 +6,5 @@ export function resetServiceWorkersPublicPath() {
// see: https://webpack.js.org/guides/public-path/
const relativeRootPath = (gon && gon.relative_url_root) || '';
const webpackAssetPath = `${relativeRootPath}/assets/webpack/`;
- __webpack_public_path__ = webpackAssetPath; // eslint-disable-line camelcase
+ window.__webpack_public_path__ = webpackAssetPath; // eslint-disable-line
}
diff --git a/app/assets/javascripts/members.js b/app/assets/javascripts/members.js
index bd263c75a3d..af2697444f2 100644
--- a/app/assets/javascripts/members.js
+++ b/app/assets/javascripts/members.js
@@ -16,25 +16,33 @@ export default class Members {
gl.utils.disableButtonIfEmptyField('#user_ids', 'input[name=commit]', 'change');
}
+ dropdownClicked(options) {
+ this.formSubmit(null, options.$el);
+ }
+
+ // eslint-disable-next-line class-methods-use-this
+ dropdownToggleLabel(selected, $el) {
+ return $el.text();
+ }
+
+ // eslint-disable-next-line class-methods-use-this
+ dropdownIsSelectable(selected, $el) {
+ return !$el.hasClass('is-active');
+ }
+
initGLDropdown() {
$('.js-member-permissions-dropdown').each((i, btn) => {
const $btn = $(btn);
$btn.glDropdown({
selectable: true,
- isSelectable(selected, $el) {
- return !$el.hasClass('is-active');
- },
+ isSelectable: (selected, $el) => this.dropdownIsSelectable(selected, $el),
fieldName: $btn.data('fieldName'),
id(selected, $el) {
return $el.data('id');
},
- toggleLabel(selected, $el) {
- return $el.text();
- },
- clicked: options => {
- this.formSubmit(null, options.$el);
- },
+ toggleLabel: (selected, $el) => this.dropdownToggleLabel(selected, $el, $btn),
+ clicked: options => this.dropdownClicked(options),
});
});
}
@@ -55,6 +63,7 @@ export default class Members {
$toggle.enable();
$dateInput.enable();
}
+
// eslint-disable-next-line class-methods-use-this
getMemberListItems($el) {
const $memberListItem = $el.is('.member') ? $el : $(`#${$el.data('elId')}`);
diff --git a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
index c2de0379d23..3cb406b819d 100644
--- a/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
+++ b/app/assets/javascripts/merge_conflicts/components/parallel_conflict_lines.js
@@ -16,7 +16,7 @@ import utilsMixin from '../mixins/line_conflict_utils';
},
},
template: `
- <table>
+ <table class="diff-wrap-lines code js-syntax-highlight">
<tr class="line_holder parallel" v-for="section in file.parallelLines">
<template v-for="line in section">
<td class="diff-line-num header" :class="lineCssClass(line)" v-if="line.isHeader"></td>
diff --git a/app/assets/javascripts/mirrors/ssh_mirror.js b/app/assets/javascripts/mirrors/ssh_mirror.js
index 5bdf5d6277a..547c078ec55 100644
--- a/app/assets/javascripts/mirrors/ssh_mirror.js
+++ b/app/assets/javascripts/mirrors/ssh_mirror.js
@@ -20,6 +20,7 @@ export default class SSHMirror {
this.$btnDetectHostKeys = this.$form.find('.js-detect-host-keys');
this.$btnSSHHostsShowAdvanced = this.$form.find('.btn-show-advanced');
this.$dropdownAuthType = this.$form.find('.js-mirror-auth-type');
+ this.$hiddenAuthType = this.$form.find('.js-hidden-mirror-auth-type');
this.$wellAuthTypeChanging = this.$form.find('.js-well-changing-auth');
this.$wellPasswordAuth = this.$form.find('.js-well-password-auth');
@@ -167,6 +168,7 @@ export default class SSHMirror {
this.$wellPasswordAuth.collapse('hide');
this.$wellSSHAuth.collapse('hide');
+ this.updateHiddenAuthType(selectedAuthType);
// This request should happen only if selected Auth type was SSH
// and SSH Public key was not present on page load
@@ -234,6 +236,12 @@ export default class SSHMirror {
toggleAuthWell(authType) {
this.$wellPasswordAuth.collapse(authType === AUTH_METHOD.PASSWORD ? 'show' : 'hide');
this.$wellSSHAuth.collapse(authType === AUTH_METHOD.SSH ? 'show' : 'hide');
+ this.updateHiddenAuthType(authType);
+ }
+
+ updateHiddenAuthType(authType) {
+ this.$hiddenAuthType.val(authType);
+ this.$hiddenAuthType.prop('disabled', authType === AUTH_METHOD.SSH);
}
/**
diff --git a/app/assets/javascripts/monitoring/components/charts/area.vue b/app/assets/javascripts/monitoring/components/charts/area.vue
index 14c02db7bcc..41783d311ef 100644
--- a/app/assets/javascripts/monitoring/components/charts/area.vue
+++ b/app/assets/javascripts/monitoring/components/charts/area.vue
@@ -4,6 +4,7 @@ import dateFormat from 'dateformat';
import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import Icon from '~/vue_shared/components/icon.vue';
+import { chartHeight, graphTypes, lineTypes } from '../../constants';
let debouncedResize;
@@ -19,7 +20,6 @@ export default {
required: true,
validator(data) {
return (
- data.queries &&
Array.isArray(data.queries) &&
data.queries.filter(query => {
if (Array.isArray(query.result)) {
@@ -51,21 +51,44 @@ export default {
return {
tooltip: {
title: '',
- content: '',
+ content: [],
isDeployment: false,
sha: '',
},
width: 0,
- height: 0,
- scatterSymbol: undefined,
+ height: chartHeight,
+ svgs: {},
+ primaryColor: null,
};
},
computed: {
chartData() {
- return this.graphData.queries.reduce((accumulator, query) => {
- accumulator[query.unit] = query.result.reduce((acc, res) => acc.concat(res.values), []);
- return accumulator;
- }, {});
+ return this.graphData.queries.map(query => {
+ const { appearance } = query;
+ const lineType =
+ appearance && appearance.line && appearance.line.type
+ ? appearance.line.type
+ : lineTypes.default;
+ const lineColor = lineType === lineTypes.threshold ? this.primaryColor : undefined;
+
+ return {
+ name: this.formatLegendLabel(query),
+ data: this.concatenateResults(query.result),
+ lineStyle: {
+ type: lineType,
+ color: lineColor,
+ },
+ itemStyle: {
+ color: lineColor,
+ },
+ areaStyle: {
+ opacity:
+ appearance && appearance.area && typeof appearance.area.opacity === 'number'
+ ? appearance.area.opacity
+ : undefined,
+ },
+ };
+ });
},
chartOptions() {
return {
@@ -78,28 +101,25 @@ export default {
axisPointer: {
snap: true,
},
- nameTextStyle: {
- padding: [18, 0, 0, 0],
- },
},
yAxis: {
name: this.yAxisLabel,
axisLabel: {
formatter: value => value.toFixed(3),
},
- nameTextStyle: {
- padding: [0, 0, 36, 0],
- },
- },
- legend: {
- formatter: this.xAxisLabel,
},
series: this.scatterSeries,
+ dataZoom: this.dataZoomConfig,
};
},
+ dataZoomConfig() {
+ const handleIcon = this.svgs['scroll-handle'];
+
+ return handleIcon ? { handleIcon } : {};
+ },
earliestDatapoint() {
- return Object.values(this.chartData).reduce((acc, data) => {
- const [[timestamp]] = data.sort(([a], [b]) => {
+ return this.chartData.reduce((acc, series) => {
+ const [[timestamp]] = series.data.sort(([a], [b]) => {
if (a < b) {
return -1;
}
@@ -129,18 +149,17 @@ export default {
},
scatterSeries() {
return {
- type: 'scatter',
+ type: graphTypes.deploymentData,
data: this.recentDeployments.map(deployment => [deployment.createdAt, 0]),
- symbol: this.scatterSymbol,
+ symbol: this.svgs.rocket,
symbolSize: 14,
+ itemStyle: {
+ color: this.primaryColor,
+ },
};
},
- xAxisLabel() {
- return this.graphData.queries.map(query => query.label).join(', ');
- },
yAxisLabel() {
- const [query] = this.graphData.queries;
- return `${this.graphData.y_label} (${query.unit})`;
+ return `${this.graphData.y_label}`;
},
},
watch: {
@@ -152,35 +171,54 @@ export default {
created() {
debouncedResize = debounceByAnimationFrame(this.onResize);
window.addEventListener('resize', debouncedResize);
- this.getScatterSymbol();
+ this.setSvg('rocket');
+ this.setSvg('scroll-handle');
},
methods: {
+ concatenateResults(results) {
+ return results.reduce((acc, result) => acc.concat(result.values), []);
+ },
+ formatLegendLabel(query) {
+ return `${query.label}`;
+ },
formatTooltipText(params) {
- const [seriesData] = params.seriesData;
- this.tooltip.isDeployment = seriesData.componentSubType === 'scatter';
this.tooltip.title = dateFormat(params.value, 'dd mmm yyyy, h:MMTT');
- if (this.tooltip.isDeployment) {
- const [deploy] = this.recentDeployments.filter(
- deployment => deployment.createdAt === seriesData.value[0],
- );
- this.tooltip.sha = deploy.sha.substring(0, 8);
- } else {
- this.tooltip.content = `${this.yAxisLabel} ${seriesData.value[1].toFixed(3)}`;
- }
+ this.tooltip.content = [];
+ params.seriesData.forEach(seriesData => {
+ if (seriesData.componentSubType === graphTypes.deploymentData) {
+ this.tooltip.isDeployment = true;
+ const [deploy] = this.recentDeployments.filter(
+ deployment => deployment.createdAt === seriesData.value[0],
+ );
+ this.tooltip.sha = deploy.sha.substring(0, 8);
+ } else {
+ const { seriesName } = seriesData;
+ // seriesData.value contains the chart's [x, y] value pair
+ // seriesData.value[1] is threfore the chart y value
+ const value = seriesData.value[1].toFixed(3);
+
+ this.tooltip.content.push({
+ name: seriesName,
+ value,
+ });
+ }
+ });
},
- getScatterSymbol() {
- getSvgIconPathContent('rocket')
+ setSvg(name) {
+ getSvgIconPathContent(name)
.then(path => {
if (path) {
- this.scatterSymbol = `path://${path}`;
+ this.$set(this.svgs, name, `path://${path}`);
}
})
.catch(() => {});
},
+ onChartUpdated(chart) {
+ [this.primaryColor] = chart.getOption().color;
+ },
onResize() {
- const { width, height } = this.$refs.areaChart.$el.getBoundingClientRect();
+ const { width } = this.$refs.areaChart.$el.getBoundingClientRect();
this.width = width;
- this.height = height;
},
},
};
@@ -201,6 +239,7 @@ export default {
:thresholds="alertData"
:width="width"
:height="height"
+ @updated="onChartUpdated"
>
<template slot="tooltipTitle">
<div v-if="tooltip.isDeployment">
@@ -214,7 +253,13 @@ export default {
{{ tooltip.sha }}
</div>
<template v-else>
- {{ tooltip.content }}
+ <div
+ v-for="(content, key) in tooltip.content"
+ :key="key"
+ class="d-flex justify-content-between"
+ >
+ {{ content.name }} {{ content.value }}
+ </div>
</template>
</template>
</gl-area-chart>
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 895a57785bc..7883a3f9abc 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -1,4 +1,5 @@
<script>
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { s__ } from '~/locale';
import Icon from '~/vue_shared/components/icon.vue';
import Flash from '../../flash';
@@ -17,6 +18,8 @@ export default {
GraphGroup,
EmptyState,
Icon,
+ GlDropdown,
+ GlDropdownItem,
},
props: {
hasMetrics: {
@@ -157,28 +160,21 @@ export default {
<template>
<div v-if="!showEmptyState" class="prometheus-graphs prepend-top-default">
<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
- v-if="store.environmentsData.length > 0"
- class="dropdown-menu dropdown-menu-selectable dropdown-menu-drop-up"
+ <strong>{{ s__('Metrics|Environment') }}</strong>
+ <gl-dropdown
+ class="prepend-left-10 js-environments-dropdown"
+ toggle-class="dropdown-menu-toggle"
+ :text="currentEnvironmentName"
+ :disabled="store.environmentsData.length === 0"
+ >
+ <gl-dropdown-item
+ v-for="environment in store.environmentsData"
+ :key="environment.id"
+ :active="environment.name === currentEnvironmentName"
+ active-class="is-active"
+ >{{ environment.name }}</gl-dropdown-item
>
- <ul>
- <li v-for="environment in store.environmentsData" :key="environment.id">
- <a
- :href="environment.metrics_path"
- :class="{ 'is-active': environment.name == currentEnvironmentName }"
- class="dropdown-item"
- >{{ environment.name }}</a
- >
- </li>
- </ul>
- </div>
- </div>
+ </gl-dropdown>
</div>
<graph-group
v-for="(groupData, index) in store.groups"
diff --git a/app/assets/javascripts/monitoring/constants.js b/app/assets/javascripts/monitoring/constants.js
new file mode 100644
index 00000000000..869173b6572
--- /dev/null
+++ b/app/assets/javascripts/monitoring/constants.js
@@ -0,0 +1,10 @@
+export const chartHeight = 300;
+
+export const graphTypes = {
+ deploymentData: 'scatter',
+};
+
+export const lineTypes = {
+ default: 'solid',
+ threshold: 'dashed',
+};
diff --git a/app/assets/javascripts/mr_notes/index.js b/app/assets/javascripts/mr_notes/index.js
index 9e99aa4f724..8eccba07c38 100644
--- a/app/assets/javascripts/mr_notes/index.js
+++ b/app/assets/javascripts/mr_notes/index.js
@@ -1,11 +1,9 @@
-import $ from 'jquery';
import Vue from 'vue';
-import { mapActions, mapState, mapGetters } from 'vuex';
+import store from 'ee_else_ce/mr_notes/stores';
+import initNotesApp from './init_notes';
import initDiffsApp from '../diffs';
-import notesApp from '../notes/components/notes_app.vue';
import discussionCounter from '../notes/components/discussion_counter.vue';
import initDiscussionFilters from '../notes/discussion_filters';
-import store from './stores';
import MergeRequest from '../merge_request';
import { resetServiceWorkersPublicPath } from '../lib/utils/webpack';
@@ -18,68 +16,7 @@ export default function initMrNotes() {
action: mrShowNode.dataset.mrAction,
});
- // eslint-disable-next-line no-new
- new Vue({
- el: '#js-vue-mr-discussions',
- name: 'MergeRequestDiscussions',
- components: {
- notesApp,
- },
- store,
- data() {
- const notesDataset = document.getElementById('js-vue-mr-discussions').dataset;
- const noteableData = JSON.parse(notesDataset.noteableData);
- noteableData.noteableType = notesDataset.noteableType;
- noteableData.targetType = notesDataset.targetType;
-
- return {
- noteableData,
- currentUserData: JSON.parse(notesDataset.currentUserData),
- notesData: JSON.parse(notesDataset.notesData),
- helpPagePath: notesDataset.helpPagePath,
- };
- },
- computed: {
- ...mapGetters(['discussionTabCounter']),
- ...mapState({
- activeTab: state => state.page.activeTab,
- }),
- },
- watch: {
- discussionTabCounter() {
- this.updateDiscussionTabCounter();
- },
- },
- created() {
- this.setActiveTab(window.mrTabs.getCurrentAction());
- },
- mounted() {
- this.notesCountBadge = $('.issuable-details').find('.notes-tab .badge');
- $(document).on('visibilitychange', this.updateDiscussionTabCounter);
- window.mrTabs.eventHub.$on('MergeRequestTabChange', this.setActiveTab);
- },
- beforeDestroy() {
- $(document).off('visibilitychange', this.updateDiscussionTabCounter);
- window.mrTabs.eventHub.$off('MergeRequestTabChange', this.setActiveTab);
- },
- methods: {
- ...mapActions(['setActiveTab']),
- updateDiscussionTabCounter() {
- this.notesCountBadge.text(this.discussionTabCounter);
- },
- },
- render(createElement) {
- return createElement('notes-app', {
- props: {
- noteableData: this.noteableData,
- notesData: this.notesData,
- userData: this.currentUserData,
- shouldShow: this.activeTab === 'show',
- helpPagePath: this.helpPagePath,
- },
- });
- },
- });
+ initNotesApp();
// eslint-disable-next-line no-new
new Vue({
diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js
new file mode 100644
index 00000000000..842a209a545
--- /dev/null
+++ b/app/assets/javascripts/mr_notes/init_notes.js
@@ -0,0 +1,70 @@
+import $ from 'jquery';
+import Vue from 'vue';
+import { mapActions, mapState, mapGetters } from 'vuex';
+import store from 'ee_else_ce/mr_notes/stores';
+import notesApp from '../notes/components/notes_app.vue';
+
+export default () => {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: '#js-vue-mr-discussions',
+ name: 'MergeRequestDiscussions',
+ components: {
+ notesApp,
+ },
+ store,
+ data() {
+ const notesDataset = document.getElementById('js-vue-mr-discussions').dataset;
+ const noteableData = JSON.parse(notesDataset.noteableData);
+ noteableData.noteableType = notesDataset.noteableType;
+ noteableData.targetType = notesDataset.targetType;
+
+ return {
+ noteableData,
+ currentUserData: JSON.parse(notesDataset.currentUserData),
+ notesData: JSON.parse(notesDataset.notesData),
+ helpPagePath: notesDataset.helpPagePath,
+ };
+ },
+ computed: {
+ ...mapGetters(['discussionTabCounter']),
+ ...mapState({
+ activeTab: state => state.page.activeTab,
+ }),
+ },
+ watch: {
+ discussionTabCounter() {
+ this.updateDiscussionTabCounter();
+ },
+ },
+ created() {
+ this.setActiveTab(window.mrTabs.getCurrentAction());
+ },
+ mounted() {
+ this.notesCountBadge = $('.issuable-details').find('.notes-tab .badge');
+ $(document).on('visibilitychange', this.updateDiscussionTabCounter);
+ window.mrTabs.eventHub.$on('MergeRequestTabChange', this.setActiveTab);
+ },
+ beforeDestroy() {
+ $(document).off('visibilitychange', this.updateDiscussionTabCounter);
+ window.mrTabs.eventHub.$off('MergeRequestTabChange', this.setActiveTab);
+ },
+ methods: {
+ ...mapActions(['setActiveTab']),
+ updateDiscussionTabCounter() {
+ this.notesCountBadge.text(this.discussionTabCounter);
+ },
+ },
+ render(createElement) {
+ return createElement('notes-app', {
+ props: {
+ noteableData: this.noteableData,
+ notesData: this.notesData,
+ userData: this.currentUserData,
+ shouldShow: this.activeTab === 'show',
+ helpPagePath: this.helpPagePath,
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/mr_popover/components/mr_popover.vue b/app/assets/javascripts/mr_popover/components/mr_popover.vue
new file mode 100644
index 00000000000..8e2d8fa816a
--- /dev/null
+++ b/app/assets/javascripts/mr_popover/components/mr_popover.vue
@@ -0,0 +1,110 @@
+<script>
+import { GlPopover, GlSkeletonLoading } from '@gitlab/ui';
+import Icon from '../../vue_shared/components/icon.vue';
+import CiIcon from '../../vue_shared/components/ci_icon.vue';
+import timeagoMixin from '../../vue_shared/mixins/timeago';
+import query from '../queries/merge_request.graphql';
+import { mrStates, humanMRStates } from '../constants';
+
+export default {
+ name: 'MRPopover',
+ components: {
+ GlPopover,
+ GlSkeletonLoading,
+ Icon,
+ CiIcon,
+ },
+ mixins: [timeagoMixin],
+ props: {
+ target: {
+ type: HTMLAnchorElement,
+ required: true,
+ },
+ projectPath: {
+ type: String,
+ required: true,
+ },
+ mergeRequestIID: {
+ type: String,
+ required: true,
+ },
+ mergeRequestTitle: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ mergeRequest: {},
+ };
+ },
+ computed: {
+ detailedStatus() {
+ return this.mergeRequest.headPipeline && this.mergeRequest.headPipeline.detailedStatus;
+ },
+ formattedTime() {
+ return this.timeFormated(this.mergeRequest.createdAt);
+ },
+ statusBoxClass() {
+ switch (this.mergeRequest.state) {
+ case mrStates.merged:
+ return 'status-box-mr-merged';
+ case mrStates.closed:
+ return 'status-box-closed';
+ default:
+ return 'status-box-open';
+ }
+ },
+ stateHumanName() {
+ switch (this.mergeRequest.state) {
+ case mrStates.merged:
+ return humanMRStates.merged;
+ case mrStates.closed:
+ return humanMRStates.closed;
+ default:
+ return humanMRStates.open;
+ }
+ },
+ showDetails() {
+ return Object.keys(this.mergeRequest).length > 0;
+ },
+ },
+ apollo: {
+ mergeRequest: {
+ query,
+ update: data => data.project.mergeRequest,
+ variables() {
+ const { projectPath, mergeRequestIID } = this;
+
+ return {
+ projectPath,
+ mergeRequestIID,
+ };
+ },
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-popover :target="target" boundary="viewport" placement="top" show>
+ <div class="mr-popover">
+ <div v-if="$apollo.loading">
+ <gl-skeleton-loading :lines="1" class="animation-container-small mt-1" />
+ </div>
+ <div v-else-if="showDetails" class="d-flex align-items-center justify-content-between">
+ <div class="d-inline-flex align-items-center">
+ <div :class="`issuable-status-box status-box ${statusBoxClass}`">
+ {{ stateHumanName }}
+ </div>
+ <span class="text-secondary">Opened <time v-text="formattedTime"></time></span>
+ </div>
+ <ci-icon v-if="detailedStatus" :status="detailedStatus" />
+ </div>
+ <h5 class="my-2">{{ mergeRequestTitle }}</h5>
+ <div class="text-secondary">
+ {{ `${projectPath}!${mergeRequestIID}` }}
+ </div>
+ </div>
+ </gl-popover>
+</template>
diff --git a/app/assets/javascripts/mr_popover/constants.js b/app/assets/javascripts/mr_popover/constants.js
new file mode 100644
index 00000000000..433df844c80
--- /dev/null
+++ b/app/assets/javascripts/mr_popover/constants.js
@@ -0,0 +1,10 @@
+export const mrStates = {
+ merged: 'merged',
+ closed: 'closed',
+};
+
+export const humanMRStates = {
+ merged: 'Merged',
+ closed: 'Closed',
+ open: 'Open',
+};
diff --git a/app/assets/javascripts/mr_popover/index.js b/app/assets/javascripts/mr_popover/index.js
new file mode 100644
index 00000000000..cc686b401d2
--- /dev/null
+++ b/app/assets/javascripts/mr_popover/index.js
@@ -0,0 +1,62 @@
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import MRPopover from './components/mr_popover.vue';
+import createDefaultClient from '~/lib/graphql';
+
+let renderedPopover;
+let renderFn;
+
+const handleUserPopoverMouseOut = ({ target }) => {
+ target.removeEventListener('mouseleave', handleUserPopoverMouseOut);
+
+ if (renderFn) {
+ clearTimeout(renderFn);
+ }
+ if (renderedPopover) {
+ renderedPopover.$destroy();
+ renderedPopover = null;
+ }
+};
+
+/**
+ * Adds a MergeRequestPopover component to the body, hands over as much data as the target element has in data attributes.
+ * loads based on data-project-path and data-iid more data about an MR from the API and sets it on the popover
+ */
+const handleMRPopoverMount = apolloProvider => ({ target }) => {
+ // Add listener to actually remove it again
+ target.addEventListener('mouseleave', handleUserPopoverMouseOut);
+
+ const { projectPath, mrTitle, iid } = target.dataset;
+ const mergeRequest = {};
+
+ renderFn = setTimeout(() => {
+ const MRPopoverComponent = Vue.extend(MRPopover);
+ renderedPopover = new MRPopoverComponent({
+ propsData: {
+ target,
+ projectPath,
+ mergeRequestIID: iid,
+ mergeRequest,
+ mergeRequestTitle: mrTitle,
+ },
+ apolloProvider,
+ });
+
+ renderedPopover.$mount();
+ }, 200); // 200ms delay so not every mouseover triggers Popover + API Call
+};
+
+export default elements => {
+ const mrLinks = elements || [...document.querySelectorAll('.gfm-merge_request')];
+ if (mrLinks.length > 0) {
+ Vue.use(VueApollo);
+
+ const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient(),
+ });
+
+ mrLinks.forEach(el => {
+ el.addEventListener('mouseenter', handleMRPopoverMount(apolloProvider));
+ });
+ }
+};
diff --git a/app/assets/javascripts/mr_popover/queries/merge_request.graphql b/app/assets/javascripts/mr_popover/queries/merge_request.graphql
new file mode 100644
index 00000000000..0bb9bc03bc7
--- /dev/null
+++ b/app/assets/javascripts/mr_popover/queries/merge_request.graphql
@@ -0,0 +1,14 @@
+query mergeRequest($projectPath: ID!, $mergeRequestIID: ID!) {
+ project(fullPath: $projectPath) {
+ mergeRequest(iid: $mergeRequestIID) {
+ createdAt
+ state
+ headPipeline {
+ detailedStatus {
+ icon
+ group
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index c9c01354333..94d2e2b53e9 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -11,8 +11,8 @@ import $ from 'jquery';
import _ from 'underscore';
import Cookies from 'js-cookie';
import Autosize from 'autosize';
-import 'vendor/jquery.caret'; // required by jquery.atwho
-import 'vendor/jquery.atwho';
+import 'jquery.caret'; // required by at.js
+import 'at.js';
import AjaxCache from '~/lib/utils/ajax_cache';
import Vue from 'vue';
import syntaxHighlight from '~/syntax_highlight';
diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue
index d8947e8ca50..ab758a9e922 100644
--- a/app/assets/javascripts/notes/components/diff_with_note.vue
+++ b/app/assets/javascripts/notes/components/diff_with_note.vue
@@ -72,8 +72,8 @@ export default {
:can-current-user-fork="false"
:expanded="!discussion.diff_file.viewer.collapsed"
/>
- <div v-if="isTextFile" :class="$options.userColorSchemeClass" class="diff-content code">
- <table>
+ <div v-if="isTextFile" class="diff-content">
+ <table class="code js-syntax-highlight" :class="$options.userColorSchemeClass">
<template v-if="hasTruncatedDiffLines">
<tr
v-for="line in discussion.truncated_diff_lines"
@@ -81,8 +81,8 @@ export default {
:key="line.line_code"
class="line_holder"
>
- <td class="diff-line-num old_line">{{ line.old_line }}</td>
- <td class="diff-line-num new_line">{{ line.new_line }}</td>
+ <td :class="line.type" class="diff-line-num old_line">{{ line.old_line }}</td>
+ <td :class="line.type" class="diff-line-num new_line">{{ line.new_line }}</td>
<td :class="line.type" class="line_content" v-html="line.rich_text"></td>
</tr>
</template>
diff --git a/app/assets/javascripts/notes/components/discussion_filter.vue b/app/assets/javascripts/notes/components/discussion_filter.vue
index e03d6e9cd02..47951591e82 100644
--- a/app/assets/javascripts/notes/components/discussion_filter.vue
+++ b/app/assets/javascripts/notes/components/discussion_filter.vue
@@ -7,7 +7,9 @@ import {
DISCUSSION_FILTERS_DEFAULT_VALUE,
HISTORY_ONLY_FILTER_VALUE,
DISCUSSION_TAB_LABEL,
+ DISCUSSION_FILTER_TYPES,
} from '../constants';
+import notesEventHub from '../event_hub';
export default {
components: {
@@ -20,7 +22,7 @@ export default {
},
selectedValue: {
type: Number,
- default: null,
+ default: DISCUSSION_FILTERS_DEFAULT_VALUE,
required: false,
},
},
@@ -46,6 +48,7 @@ export default {
this.toggleFilters(currentTab);
}
+ notesEventHub.$on('dropdownSelect', this.selectFilter);
window.addEventListener('hashchange', this.handleLocationHash);
this.handleLocationHash();
},
@@ -53,6 +56,7 @@ export default {
this.toggleCommentsForm();
},
destroyed() {
+ notesEventHub.$off('dropdownSelect', this.selectFilter);
window.removeEventListener('hashchange', this.handleLocationHash);
},
methods: {
@@ -86,12 +90,23 @@ export default {
this.setTargetNoteHash(hash);
}
},
+ filterType(value) {
+ if (value === 0) {
+ return DISCUSSION_FILTER_TYPES.ALL;
+ } else if (value === 1) {
+ return DISCUSSION_FILTER_TYPES.COMMENTS;
+ }
+ return DISCUSSION_FILTER_TYPES.HISTORY;
+ },
},
};
</script>
<template>
- <div v-if="displayFilters" class="discussion-filter-container d-inline-block align-bottom">
+ <div
+ v-if="displayFilters"
+ class="discussion-filter-container js-discussion-filter-container d-inline-block align-bottom"
+ >
<button
id="discussion-filter-dropdown"
ref="dropdownToggle"
@@ -102,12 +117,17 @@ export default {
{{ currentFilter.title }} <icon name="chevron-down" />
</button>
<div
+ ref="dropdownMenu"
class="dropdown-menu dropdown-menu-selectable dropdown-menu-right"
aria-labelledby="discussion-filter-dropdown"
>
<div class="dropdown-content">
<ul>
- <li v-for="filter in filters" :key="filter.value">
+ <li
+ v-for="filter in filters"
+ :key="filter.value"
+ :data-filter-type="filterType(filter.value)"
+ >
<button
:class="{ 'is-active': filter.value === currentValue }"
class="qa-filter-options"
diff --git a/app/assets/javascripts/notes/components/discussion_filter_note.vue b/app/assets/javascripts/notes/components/discussion_filter_note.vue
new file mode 100644
index 00000000000..889731df180
--- /dev/null
+++ b/app/assets/javascripts/notes/components/discussion_filter_note.vue
@@ -0,0 +1,52 @@
+<script>
+import { GlButton } from '@gitlab/ui';
+import Icon from '~/vue_shared/components/icon.vue';
+import { __, sprintf } from '~/locale';
+
+import notesEventHub from '../event_hub';
+
+export default {
+ components: {
+ GlButton,
+ Icon,
+ },
+ computed: {
+ timelineContent() {
+ return sprintf(
+ __(
+ "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options.",
+ ),
+ {
+ startTag: `<b>`,
+ endTag: `</b>`,
+ },
+ false,
+ );
+ },
+ },
+ methods: {
+ selectFilter(value) {
+ notesEventHub.$emit('dropdownSelect', value);
+ },
+ },
+};
+</script>
+
+<template>
+ <li class="timeline-entry note note-wrapper discussion-filter-note js-discussion-filter-note">
+ <div class="timeline-icon d-none d-lg-flex">
+ <icon name="comment" />
+ </div>
+ <div class="timeline-content">
+ <div v-html="timelineContent"></div>
+ <div class="discussion-filter-actions mt-2">
+ <gl-button variant="default" @click="selectFilter(0)">
+ {{ __('Show all activity') }}
+ </gl-button>
+ <gl-button variant="default" @click="selectFilter(1)">
+ {{ __('Show comments only') }}
+ </gl-button>
+ </div>
+ </div>
+ </li>
+</template>
diff --git a/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue b/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue
new file mode 100644
index 00000000000..e413398696a
--- /dev/null
+++ b/app/assets/javascripts/notes/components/discussion_resolve_with_issue_button.vue
@@ -0,0 +1,34 @@
+<script>
+import Icon from '~/vue_shared/components/icon.vue';
+import { GlTooltipDirective, GlButton } from '@gitlab/ui';
+
+export default {
+ name: 'ResolveWithIssueButton',
+ components: {
+ Icon,
+ GlButton,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ url: {
+ type: String,
+ required: true,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="btn-group" role="group">
+ <gl-button
+ v-gl-tooltip
+ :href="url"
+ :title="s__('MergeRequests|Resolve this discussion in a new issue')"
+ class="new-issue-for-discussion discussion-create-issue-btn"
+ >
+ <icon name="issue-new" />
+ </gl-button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/notes/components/note_actions.vue b/app/assets/javascripts/notes/components/note_actions.vue
index de1ea0f58d6..fc73726857d 100644
--- a/app/assets/javascripts/notes/components/note_actions.vue
+++ b/app/assets/javascripts/notes/components/note_actions.vue
@@ -158,7 +158,6 @@ export default {
href="#"
title="Add reaction"
>
- <gl-loading-icon inline />
<icon
css-classes="link-highlight award-control-icon-neutral"
name="emoji_slightly_smiling_face"
diff --git a/app/assets/javascripts/notes/components/note_actions/reply_button.vue b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
index f50cab81efe..be8e42af9ea 100644
--- a/app/assets/javascripts/notes/components/note_actions/reply_button.vue
+++ b/app/assets/javascripts/notes/components/note_actions/reply_button.vue
@@ -18,7 +18,7 @@ export default {
<div class="note-actions-item">
<gl-button
ref="button"
- v-gl-tooltip.bottom
+ v-gl-tooltip
class="note-action-button"
variant="transparent"
:title="__('Reply to comment')"
diff --git a/app/assets/javascripts/notes/components/note_body.vue b/app/assets/javascripts/notes/components/note_body.vue
index fb1d98355b3..ff303d0f55a 100644
--- a/app/assets/javascripts/notes/components/note_body.vue
+++ b/app/assets/javascripts/notes/components/note_body.vue
@@ -95,7 +95,6 @@ export default {
<div ref="note-body" :class="{ 'js-task-list-container': canEdit }" class="note-body">
<suggestions
v-if="hasSuggestion && !isEditing"
- class="note-text md"
:suggestions="note.suggestions"
:note-html="note.note_html"
:line-type="lineType"
diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue
index 92258a25438..57d6b181bd7 100644
--- a/app/assets/javascripts/notes/components/note_form.vue
+++ b/app/assets/javascripts/notes/components/note_form.vue
@@ -7,6 +7,7 @@ import markdownField from '../../vue_shared/components/markdown/field.vue';
import issuableStateMixin from '../mixins/issuable_state';
import resolvable from '../mixins/resolvable';
import { __ } from '~/locale';
+import { getDraft, updateDraft } from '~/lib/utils/autosave';
export default {
name: 'NoteForm',
@@ -65,10 +66,21 @@ export default {
required: false,
default: '',
},
+ autosaveKey: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
+ let updatedNoteBody = this.noteBody;
+
+ if (!updatedNoteBody && this.autosaveKey) {
+ updatedNoteBody = getDraft(this.autosaveKey) || '';
+ }
+
return {
- updatedNoteBody: this.noteBody,
+ updatedNoteBody,
conflictWhileEditing: false,
isSubmitting: false,
isResolving: this.resolveDiscussion,
@@ -175,6 +187,12 @@ export default {
// Sends information about confirm message and if the textarea has changed
this.$emit('cancelForm', shouldConfirm, this.noteBody !== this.updatedNoteBody);
},
+ onInput() {
+ if (this.autosaveKey) {
+ const { autosaveKey, updatedNoteBody: text } = this;
+ updateDraft(autosaveKey, text);
+ }
+ },
},
};
</script>
@@ -218,6 +236,7 @@ export default {
@keydown.ctrl.enter="handleKeySubmit()"
@keydown.up="editMyLastNote()"
@keydown.esc="cancelHandler(true)"
+ @input="onInput"
></textarea>
</markdown-field>
<div class="note-form-actions clearfix">
diff --git a/app/assets/javascripts/notes/components/note_header.vue b/app/assets/javascripts/notes/components/note_header.vue
index 7b39901024d..5c59c0c32dd 100644
--- a/app/assets/javascripts/notes/components/note_header.vue
+++ b/app/assets/javascripts/notes/components/note_header.vue
@@ -69,7 +69,7 @@ export default {
type="button"
@click="handleToggle"
>
- <i :class="toggleChevronClass" class="fa" aria-hidden="true"> </i>
+ <i :class="toggleChevronClass" class="fa" aria-hidden="true"></i>
{{ __('Toggle discussion') }}
</button>
</div>
@@ -81,35 +81,31 @@ export default {
:data-user-id="author.id"
:data-username="author.username"
>
+ <slot name="note-header-info"></slot>
<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>
+ <span class="note-headline-light">@{{ author.username }}</span>
</a>
- <span v-else> {{ __('A deleted user') }} </span>
- <span class="note-headline-light">
- <span class="note-headline-meta">
- <span class="system-note-message"> <slot></slot> </span>
- <template v-if="createdAt">
- <span class="system-note-separator">
- <template v-if="actionText">
- {{ actionText }}
- </template>
- </span>
- <a
- :href="noteTimestampLink"
- class="note-timestamp system-note-separator"
- @click="updateTargetNoteHash"
- >
- <time-ago-tooltip :time="createdAt" tooltip-placement="bottom" />
- </a>
- </template>
- <i
- class="fa fa-spinner fa-spin editing-spinner"
- aria-label="Comment is being updated"
- aria-hidden="true"
+ <span v-else>{{ __('A deleted user') }}</span>
+ <span class="note-headline-light note-headline-meta">
+ <span class="system-note-message"> <slot></slot> </span>
+ <template v-if="createdAt">
+ <span class="system-note-separator">
+ <template v-if="actionText">{{ actionText }}</template>
+ </span>
+ <a
+ :href="noteTimestampLink"
+ class="note-timestamp system-note-separator"
+ @click="updateTargetNoteHash"
>
- </i>
- </span>
+ <time-ago-tooltip :time="createdAt" tooltip-placement="bottom" />
+ </a>
+ </template>
+ <i
+ class="fa fa-spinner fa-spin editing-spinner"
+ aria-label="Comment is being updated"
+ aria-hidden="true"
+ ></i>
</span>
</div>
</template>
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 2d6fd8b116f..a3d664a738f 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -4,8 +4,10 @@ import { mapActions, mapGetters } from 'vuex';
import { GlTooltipDirective } from '@gitlab/ui';
import { truncateSha } from '~/lib/utils/text_utility';
import { s__, __, sprintf } from '~/locale';
+import { clearDraft, getDiscussionReplyKey } from '~/lib/utils/autosave';
import systemNote from '~/vue_shared/components/notes/system_note.vue';
import icon from '~/vue_shared/components/icon.vue';
+import diffLineNoteFormMixin from 'ee_else_ce/notes/mixins/diff_line_note_form';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
import Flash from '../../flash';
import { SYSTEM_NOTE } from '../constants';
@@ -20,11 +22,11 @@ import noteForm from './note_form.vue';
import diffWithNote from './diff_with_note.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
import placeholderSystemNote from '../../vue_shared/components/notes/placeholder_system_note.vue';
-import autosave from '../mixins/autosave';
import noteable from '../mixins/noteable';
import resolvable from '../mixins/resolvable';
import discussionNavigation from '../mixins/discussion_navigation';
import ReplyPlaceholder from './discussion_reply_placeholder.vue';
+import ResolveWithIssueButton from './discussion_resolve_with_issue_button.vue';
import jumpToNextDiscussionButton from './discussion_jump_to_next_button.vue';
import eventHub from '../event_hub';
@@ -44,13 +46,15 @@ export default {
ReplyPlaceholder,
placeholderNote,
placeholderSystemNote,
+ ResolveWithIssueButton,
systemNote,
+ DraftNote: () => import('ee_component/batch_comments/components/draft_note.vue'),
TimelineEntryItem,
},
directives: {
GlTooltip: GlTooltipDirective,
},
- mixins: [autosave, noteable, resolvable, discussionNavigation],
+ mixins: [noteable, resolvable, discussionNavigation, diffLineNoteFormMixin],
props: {
discussion: {
type: Object,
@@ -83,13 +87,10 @@ export default {
},
},
data() {
- const { diff_discussion: isDiffDiscussion, resolved } = this.discussion;
-
return {
isReplying: false,
isResolving: false,
resolveAsThread: true,
- isRepliesCollapsed: Boolean(!isDiffDiscussion && resolved),
};
},
computed: {
@@ -102,7 +103,10 @@ export default {
'showJumpToNextDiscussion',
]),
author() {
- return this.initialDiscussion.author;
+ return this.firstNote.author;
+ },
+ autosaveKey() {
+ return getDiscussionReplyKey(this.firstNote.noteable_type, this.discussion.id);
},
canReply() {
return this.getNoteableData.current_user.can_create_note;
@@ -113,7 +117,7 @@ export default {
hasReplies() {
return this.discussion.notes.length > 1;
},
- initialDiscussion() {
+ firstNote() {
return this.discussion.notes.slice(0, 1)[0];
},
replies() {
@@ -171,11 +175,11 @@ export default {
return '';
},
- shouldShowDiscussions() {
- const { expanded, resolved } = this.discussion;
- const isResolvedNonDiffDiscussion = !this.discussion.diff_discussion && resolved;
-
- return expanded || this.alwaysExpanded || isResolvedNonDiffDiscussion;
+ isExpanded() {
+ return this.discussion.expanded || this.alwaysExpanded;
+ },
+ shouldHideDiscussionBody() {
+ return this.shouldRenderDiffs && !this.isExpanded;
},
actionText() {
const linkStart = `<a href="${_.escape(this.discussion.discussion_path)}">`;
@@ -234,17 +238,8 @@ export default {
url: this.discussion.discussion_path,
};
},
- },
- watch: {
- isReplying() {
- if (this.isReplying) {
- this.$nextTick(() => {
- // Pass an extra key to separate reply and note edit forms
- this.initAutoSave({ ...this.initialDiscussion, ...this.discussion }, ['Reply']);
- });
- } else {
- this.disposeAutoSave();
- }
+ resolveWithIssuePath() {
+ return !this.discussionResolved && this.discussion.resolve_with_issue_path;
},
},
created() {
@@ -284,9 +279,6 @@ export default {
toggleDiscussionHandler() {
this.toggleDiscussion({ discussionId: this.discussion.id });
},
- toggleReplies() {
- this.isRepliesCollapsed = !this.isRepliesCollapsed;
- },
showReplyForm() {
this.isReplying = true;
},
@@ -305,7 +297,7 @@ export default {
}
this.isReplying = false;
- this.resetAutoSave();
+ clearDraft(this.autosaveKey);
},
saveReply(noteText, form, callback) {
const postData = {
@@ -331,7 +323,7 @@ export default {
this.isReplying = false;
this.saveNote(replyData)
.then(() => {
- this.resetAutoSave();
+ clearDraft(this.autosaveKey);
callback();
})
.catch(err => {
@@ -383,8 +375,8 @@ Please check your network connection and try again.`;
<div class="timeline-content">
<note-header
:author="author"
- :created-at="initialDiscussion.created_at"
- :note-id="initialDiscussion.id"
+ :created-at="firstNote.created_at"
+ :note-id="firstNote.id"
:include-toggle="true"
:expanded="discussion.expanded"
@toggleHandler="toggleDiscussionHandler"
@@ -407,7 +399,7 @@ Please check your network connection and try again.`;
/>
</div>
</div>
- <div v-if="shouldShowDiscussions" class="discussion-body">
+ <div v-if="!shouldHideDiscussionBody" class="discussion-body">
<component
:is="wrapperComponent"
v-bind="wrapperComponentProps"
@@ -417,8 +409,8 @@ Please check your network connection and try again.`;
<ul class="notes">
<template v-if="shouldGroupReplies">
<component
- :is="componentName(initialDiscussion)"
- :note="componentData(initialDiscussion)"
+ :is="componentName(firstNote)"
+ :note="componentData(firstNote)"
:line="line"
:commit="commit"
:help-page-path="helpPagePath"
@@ -438,11 +430,11 @@ Please check your network connection and try again.`;
</component>
<toggle-replies-widget
v-if="hasReplies"
- :collapsed="isRepliesCollapsed"
+ :collapsed="!isExpanded"
:replies="replies"
- @toggle="toggleReplies"
+ @toggle="toggleDiscussionHandler"
/>
- <template v-if="!isRepliesCollapsed">
+ <template v-if="isExpanded">
<component
:is="componentName(note)"
v-for="note in replies"
@@ -469,7 +461,7 @@ Please check your network connection and try again.`;
</template>
</ul>
<div
- v-if="!isRepliesCollapsed || !hasReplies"
+ v-if="isExpanded || !hasReplies"
:class="{ 'is-replying': isReplying }"
class="discussion-reply-holder"
>
@@ -487,16 +479,10 @@ Please check your network connection and try again.`;
class="btn-group discussion-actions ml-sm-2"
role="group"
>
- <div v-if="!discussionResolved" class="btn-group" role="group">
- <a
- v-gl-tooltip
- :href="discussion.resolve_with_issue_path"
- :title="s__('MergeRequests|Resolve this discussion in a new issue')"
- class="new-issue-for-discussion btn btn-default discussion-create-issue-btn"
- >
- <icon name="issue-new" />
- </a>
- </div>
+ <resolve-with-issue-button
+ v-if="resolveWithIssuePath"
+ :url="resolveWithIssuePath"
+ />
<jump-to-next-discussion-button
v-if="shouldShowJumpToNextDiscussion"
@onClick="jumpToNextDiscussion"
@@ -511,6 +497,8 @@ Please check your network connection and try again.`;
:is-editing="false"
:line="diffLine"
save-button-title="Comment"
+ :autosave-key="autosaveKey"
+ @handleFormUpdateAddToReview="addReplyToReview"
@handleFormUpdate="saveReply"
@cancelForm="cancelReplyForm"
/>
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 04e74a43acc..5fa0ab3de98 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -219,7 +219,7 @@ export default {
:class="classNameBindings"
:data-award-url="note.toggle_award_path"
:data-note-id="note.id"
- class="note note-wrapper"
+ class="note note-wrapper qa-noteable-note-item"
>
<div v-once class="timeline-icon">
<user-avatar-link
diff --git a/app/assets/javascripts/notes/components/notes_app.vue b/app/assets/javascripts/notes/components/notes_app.vue
index a63571edcea..e2bd59f7631 100644
--- a/app/assets/javascripts/notes/components/notes_app.vue
+++ b/app/assets/javascripts/notes/components/notes_app.vue
@@ -6,6 +6,7 @@ import * as constants from '../constants';
import eventHub from '../event_hub';
import noteableNote from './noteable_note.vue';
import noteableDiscussion from './noteable_discussion.vue';
+import discussionFilterNote from './discussion_filter_note.vue';
import systemNote from '../../vue_shared/components/notes/system_note.vue';
import commentForm from './comment_form.vue';
import placeholderNote from '../../vue_shared/components/notes/placeholder_note.vue';
@@ -24,6 +25,7 @@ export default {
placeholderNote,
placeholderSystemNote,
skeletonLoadingContainer,
+ discussionFilterNote,
},
props: {
noteableData: {
@@ -90,8 +92,15 @@ export default {
this.fetchNotes();
}
},
+ allDiscussions() {
+ if (this.discussonsCount) {
+ this.discussonsCount.textContent = this.allDiscussions.length;
+ }
+ },
},
created() {
+ this.discussonsCount = document.querySelector('.js-discussions-count');
+
this.setNotesData(this.notesData);
this.setNoteableData(this.noteableData);
this.setUserData(this.userData);
@@ -228,6 +237,7 @@ export default {
:help-page-path="helpPagePath"
/>
</template>
+ <discussion-filter-note v-show="commentsDisabled" />
</ul>
<comment-form v-if="!commentsDisabled" :noteable-type="noteableType" />
diff --git a/app/assets/javascripts/notes/constants.js b/app/assets/javascripts/notes/constants.js
index 78d365fe94b..fba3db8542c 100644
--- a/app/assets/javascripts/notes/constants.js
+++ b/app/assets/javascripts/notes/constants.js
@@ -24,3 +24,9 @@ export const NOTEABLE_TYPE_MAPPING = {
MergeRequest: MERGE_REQUEST_NOTEABLE_TYPE,
Epic: EPIC_NOTEABLE_TYPE,
};
+
+export const DISCUSSION_FILTER_TYPES = {
+ ALL: 'all',
+ COMMENTS: 'comments',
+ HISTORY: 'history',
+};
diff --git a/app/assets/javascripts/notes/discussion_filters.js b/app/assets/javascripts/notes/discussion_filters.js
index 5c5f38a3fb0..cdf9a46c5aa 100644
--- a/app/assets/javascripts/notes/discussion_filters.js
+++ b/app/assets/javascripts/notes/discussion_filters.js
@@ -6,12 +6,16 @@ export default store => {
if (discussionFilterEl) {
const { defaultFilter, notesFilters } = discussionFilterEl.dataset;
- const selectedValue = defaultFilter ? parseInt(defaultFilter, 10) : null;
const filterValues = notesFilters ? JSON.parse(notesFilters) : {};
const filters = Object.keys(filterValues).map(entry => ({
title: entry,
value: filterValues[entry],
}));
+ const props = { filters };
+
+ if (defaultFilter) {
+ props.selectedValue = parseInt(defaultFilter, 10);
+ }
return new Vue({
el: discussionFilterEl,
@@ -21,12 +25,7 @@ export default store => {
},
store,
render(createElement) {
- return createElement('discussion-filter', {
- props: {
- filters,
- selectedValue,
- },
- });
+ return createElement('discussion-filter', { props });
},
});
}
diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js
index 4883266dae5..30372103590 100644
--- a/app/assets/javascripts/notes/index.js
+++ b/app/assets/javascripts/notes/index.js
@@ -6,9 +6,8 @@ import createStore from './stores';
document.addEventListener('DOMContentLoaded', () => {
const store = createStore();
- initDiscussionFilters(store);
-
- return new Vue({
+ // eslint-disable-next-line no-new
+ new Vue({
el: '#js-vue-notes',
components: {
notesApp,
@@ -49,4 +48,6 @@ document.addEventListener('DOMContentLoaded', () => {
});
},
});
+
+ initDiscussionFilters(store);
});
diff --git a/app/assets/javascripts/notes/mixins/diff_line_note_form.js b/app/assets/javascripts/notes/mixins/diff_line_note_form.js
new file mode 100644
index 00000000000..188556e8921
--- /dev/null
+++ b/app/assets/javascripts/notes/mixins/diff_line_note_form.js
@@ -0,0 +1,10 @@
+export default {
+ computed: {
+ draftForDiscussion: () => () => ({}),
+ },
+ methods: {
+ showDraft: () => false,
+ addReplyToReview: () => {},
+ addToReview: () => {},
+ },
+};
diff --git a/app/assets/javascripts/notes/mixins/resolvable.js b/app/assets/javascripts/notes/mixins/resolvable.js
index 8edf3d088bb..2329727bca2 100644
--- a/app/assets/javascripts/notes/mixins/resolvable.js
+++ b/app/assets/javascripts/notes/mixins/resolvable.js
@@ -31,6 +31,10 @@ export default {
},
methods: {
resolveHandler(resolvedState = false) {
+ if (this.note && this.note.isDraft) {
+ return this.$emit('toggleResolveStatus');
+ }
+
this.isResolving = true;
const isResolved = this.discussionResolved || resolvedState;
const discussion = this.resolveAsThread;
diff --git a/app/assets/javascripts/notes/stores/getters.js b/app/assets/javascripts/notes/stores/getters.js
index 5026c13dab5..fcc8889b0c7 100644
--- a/app/assets/javascripts/notes/stores/getters.js
+++ b/app/assets/javascripts/notes/stores/getters.js
@@ -191,6 +191,9 @@ export const firstUnresolvedDiscussionId = (state, getters) => diffOrder => {
return getters.unresolvedDiscussionsIdsByDate[0];
};
+export const getDiscussion = state => discussionId =>
+ state.discussions.find(discussion => discussion.id === discussionId);
+
export const commentsDisabled = state => state.commentsDisabled;
// prevent babel-plugin-rewire from generating an invalid default during karma tests
diff --git a/app/assets/javascripts/notes/stores/mutations.js b/app/assets/javascripts/notes/stores/mutations.js
index ae6f8b7790a..fa44ef2d057 100644
--- a/app/assets/javascripts/notes/stores/mutations.js
+++ b/app/assets/javascripts/notes/stores/mutations.js
@@ -193,6 +193,10 @@ export default {
const noteObj = utils.findNoteObjectById(state.discussions, note.discussion_id);
if (noteObj.individual_note) {
+ if (note.type === constants.DISCUSSION_NOTE) {
+ noteObj.individual_note = false;
+ }
+
noteObj.notes.splice(0, 1, note);
} else {
const comment = utils.findNoteObjectById(noteObj.notes, note.id);
diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
index 260484726f3..ff758fcb4fe 100644
--- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js
+++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
@@ -1,10 +1,11 @@
import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => {
- IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests();
+ addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
diff --git a/app/assets/javascripts/pages/groups/clusters/index/index.js b/app/assets/javascripts/pages/groups/clusters/index/index.js
index 21efc4f6d00..30d519d0e37 100644
--- a/app/assets/javascripts/pages/groups/clusters/index/index.js
+++ b/app/assets/javascripts/pages/groups/clusters/index/index.js
@@ -2,6 +2,5 @@ import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () => {
const callout = document.querySelector('.gcp-signup-offer');
-
- if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
+ PersistentUserCallout.factory(callout);
});
diff --git a/app/assets/javascripts/pages/groups/details/index.js b/app/assets/javascripts/pages/groups/details/index.js
new file mode 100644
index 00000000000..3bcaa0f0232
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/details/index.js
@@ -0,0 +1,5 @@
+import initGroupDetails from '../shared/group_details';
+
+document.addEventListener('DOMContentLoaded', () => {
+ initGroupDetails('details');
+});
diff --git a/app/assets/javascripts/pages/groups/group_members/index/index.js b/app/assets/javascripts/pages/groups/group_members/index/index.js
index c22a164cd4e..e4f4c3b574e 100644
--- a/app/assets/javascripts/pages/groups/group_members/index/index.js
+++ b/app/assets/javascripts/pages/groups/group_members/index/index.js
@@ -1,7 +1,7 @@
/* eslint-disable no-new */
import memberExpirationDate from '~/member_expiration_date';
-import Members from '~/members';
+import Members from 'ee_else_ce/members';
import UsersSelect from '~/users_select';
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/groups/index.js b/app/assets/javascripts/pages/groups/index.js
index a63a0dbc6b1..451be6497de 100644
--- a/app/assets/javascripts/pages/groups/index.js
+++ b/app/assets/javascripts/pages/groups/index.js
@@ -3,8 +3,7 @@ import initGkeDropdowns from '~/projects/gke_cluster_dropdowns';
function initGcpSignupCallout() {
const callout = document.querySelector('.gcp-signup-offer');
-
- if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
+ PersistentUserCallout.factory(callout);
}
document.addEventListener('DOMContentLoaded', () => {
diff --git a/app/assets/javascripts/pages/groups/issues/index.js b/app/assets/javascripts/pages/groups/issues/index.js
index 736c6a62610..35d4b034654 100644
--- a/app/assets/javascripts/pages/groups/issues/index.js
+++ b/app/assets/javascripts/pages/groups/issues/index.js
@@ -1,9 +1,11 @@
import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import { FILTERED_SEARCH } from '~/pages/constants';
-import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
+import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
document.addEventListener('DOMContentLoaded', () => {
+ IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
+
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
isGroupDecendent: true,
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js
index 339ce67438a..12a26fd88fa 100644
--- a/app/assets/javascripts/pages/groups/merge_requests/index.js
+++ b/app/assets/javascripts/pages/groups/merge_requests/index.js
@@ -1,10 +1,11 @@
import projectSelect from '~/project_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
+import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
import { FILTERED_SEARCH } from '~/pages/constants';
document.addEventListener('DOMContentLoaded', () => {
- IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests();
+ addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
diff --git a/app/assets/javascripts/pages/groups/shared/group_details.js b/app/assets/javascripts/pages/groups/shared/group_details.js
new file mode 100644
index 00000000000..01ef3f1db2b
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/shared/group_details.js
@@ -0,0 +1,31 @@
+/* eslint-disable no-new */
+
+import { getPagePath } from '~/lib/utils/common_utils';
+import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants';
+import NewGroupChild from '~/groups/new_group_child';
+import notificationsDropdown from '~/notifications_dropdown';
+import NotificationsForm from '~/notifications_form';
+import ProjectsList from '~/projects_list';
+import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
+import GroupTabs from './group_tabs';
+
+export default function initGroupDetails(actionName = 'show') {
+ const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
+ const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED];
+ const paths = window.location.pathname.split('/');
+ const subpath = paths[paths.length - 1];
+ let action = loadableActions.includes(subpath) ? subpath : getPagePath(1);
+ if (actionName && action === actionName) {
+ action = 'show'; // 'show' resets GroupTabs to default action through base class
+ }
+
+ new GroupTabs({ parentEl: '.groups-listing', action });
+ new ShortcutsNavigation();
+ new NotificationsForm();
+ notificationsDropdown();
+ new ProjectsList();
+
+ if (newGroupChildWrapper) {
+ new NewGroupChild(newGroupChildWrapper);
+ }
+}
diff --git a/app/assets/javascripts/pages/groups/show/group_tabs.js b/app/assets/javascripts/pages/groups/shared/group_tabs.js
index c6fe61d2bd9..c6fe61d2bd9 100644
--- a/app/assets/javascripts/pages/groups/show/group_tabs.js
+++ b/app/assets/javascripts/pages/groups/shared/group_tabs.js
diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js
index 3a45fd70d02..af924e74f1f 100644
--- a/app/assets/javascripts/pages/groups/show/index.js
+++ b/app/assets/javascripts/pages/groups/show/index.js
@@ -1,28 +1,5 @@
-/* eslint-disable no-new */
-
-import { getPagePath } from '~/lib/utils/common_utils';
-import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants';
-import NewGroupChild from '~/groups/new_group_child';
-import notificationsDropdown from '~/notifications_dropdown';
-import NotificationsForm from '~/notifications_form';
-import ProjectsList from '~/projects_list';
-import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
-import GroupTabs from './group_tabs';
+import initGroupDetails from '../shared/group_details';
document.addEventListener('DOMContentLoaded', () => {
- const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
- const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED];
- const paths = window.location.pathname.split('/');
- const subpath = paths[paths.length - 1];
- const action = loadableActions.includes(subpath) ? subpath : getPagePath(1);
-
- new GroupTabs({ parentEl: '.groups-listing', action });
- new ShortcutsNavigation();
- new NotificationsForm();
- notificationsDropdown();
- new ProjectsList();
-
- if (newGroupChildWrapper) {
- new NewGroupChild(newGroupChildWrapper);
- }
+ initGroupDetails();
});
diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js
index 0dd0d5336fc..c9d3bbc8c39 100644
--- a/app/assets/javascripts/pages/profiles/show/index.js
+++ b/app/assets/javascripts/pages/profiles/show/index.js
@@ -56,30 +56,34 @@ document.addEventListener('DOMContentLoaded', () => {
import(/* webpackChunkName: 'emoji' */ '~/emoji')
.then(Emoji => {
- const emojiMenu = new EmojiMenu(
- Emoji,
- toggleEmojiMenuButtonSelector,
- 'js-status-emoji-menu',
- selectEmojiCallback,
- );
- emojiMenu.bindEvents();
+ Emoji.initEmojiMap()
+ .then(() => {
+ const emojiMenu = new EmojiMenu(
+ Emoji,
+ toggleEmojiMenuButtonSelector,
+ 'js-status-emoji-menu',
+ selectEmojiCallback,
+ );
+ emojiMenu.bindEvents();
- const defaultEmojiTag = Emoji.glEmojiTag(defaultStatusEmoji);
- statusMessageField.addEventListener('input', () => {
- const hasStatusMessage = statusMessageField.value.trim() !== '';
- const statusEmoji = findStatusEmoji();
- if (hasStatusMessage && statusEmoji) {
- return;
- }
+ const defaultEmojiTag = Emoji.glEmojiTag(defaultStatusEmoji);
+ statusMessageField.addEventListener('input', () => {
+ const hasStatusMessage = statusMessageField.value.trim() !== '';
+ const statusEmoji = findStatusEmoji();
+ if (hasStatusMessage && statusEmoji) {
+ return;
+ }
- if (hasStatusMessage) {
- toggleNoEmojiPlaceholder(false);
- toggleEmojiMenuButton.innerHTML += defaultEmojiTag;
- } else if (statusEmoji.dataset.name === defaultStatusEmoji) {
- toggleNoEmojiPlaceholder(true);
- removeStatusEmoji();
- }
- });
+ if (hasStatusMessage) {
+ toggleNoEmojiPlaceholder(false);
+ toggleEmojiMenuButton.innerHTML += defaultEmojiTag;
+ } else if (statusEmoji.dataset.name === defaultStatusEmoji) {
+ toggleNoEmojiPlaceholder(true);
+ removeStatusEmoji();
+ }
+ });
+ })
+ .catch(() => createFlash('Failed to load emoji list.'));
})
.catch(() => createFlash('Failed to load emoji list.'));
});
diff --git a/app/assets/javascripts/pages/projects/clusters/index/index.js b/app/assets/javascripts/pages/projects/clusters/index/index.js
index 21efc4f6d00..30d519d0e37 100644
--- a/app/assets/javascripts/pages/projects/clusters/index/index.js
+++ b/app/assets/javascripts/pages/projects/clusters/index/index.js
@@ -2,6 +2,5 @@ import PersistentUserCallout from '~/persistent_user_callout';
document.addEventListener('DOMContentLoaded', () => {
const callout = document.querySelector('.gcp-signup-offer');
-
- if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
+ PersistentUserCallout.factory(callout);
});
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
index b0345b4e50d..d4bd02c14e9 100644
--- a/app/assets/javascripts/pages/projects/index.js
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -13,7 +13,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (newClusterViews.indexOf(page) > -1) {
const callout = document.querySelector('.gcp-signup-offer');
- if (callout) new PersistentUserCallout(callout); // eslint-disable-line no-new
+ PersistentUserCallout.factory(callout);
initGkeDropdowns();
}
diff --git a/app/assets/javascripts/pages/projects/issues/edit/index.js b/app/assets/javascripts/pages/projects/issues/edit/index.js
index ffc84dc106b..aecc6484b26 100644
--- a/app/assets/javascripts/pages/projects/issues/edit/index.js
+++ b/app/assets/javascripts/pages/projects/issues/edit/index.js
@@ -1,3 +1,3 @@
-import initForm from '../form';
+import initForm from 'ee_else_ce/pages/projects/issues/form';
document.addEventListener('DOMContentLoaded', initForm);
diff --git a/app/assets/javascripts/pages/projects/issues/form.js b/app/assets/javascripts/pages/projects/issues/form.js
index f99023ad8e7..941c4552579 100644
--- a/app/assets/javascripts/pages/projects/issues/form.js
+++ b/app/assets/javascripts/pages/projects/issues/form.js
@@ -2,7 +2,7 @@
import $ from 'jquery';
import GLForm from '~/gl_form';
-import IssuableForm from '~/issuable_form';
+import IssuableForm from 'ee_else_ce/issuable_form';
import LabelsSelect from '~/labels_select';
import MilestoneSelect from '~/milestone_select';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
diff --git a/app/assets/javascripts/pages/projects/issues/index/index.js b/app/assets/javascripts/pages/projects/issues/index/index.js
index a56c0bb6be8..8bf0c2edc71 100644
--- a/app/assets/javascripts/pages/projects/issues/index/index.js
+++ b/app/assets/javascripts/pages/projects/issues/index/index.js
@@ -4,11 +4,13 @@ import IssuableIndex from '~/issuable_index';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import UsersSelect from '~/users_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
-import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
+import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
document.addEventListener('DOMContentLoaded', () => {
+ IssuableFilteredSearchTokenKeys.addExtraTokensForIssues();
+
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
diff --git a/app/assets/javascripts/pages/projects/issues/new/index.js b/app/assets/javascripts/pages/projects/issues/new/index.js
index ffc84dc106b..aecc6484b26 100644
--- a/app/assets/javascripts/pages/projects/issues/new/index.js
+++ b/app/assets/javascripts/pages/projects/issues/new/index.js
@@ -1,3 +1,3 @@
-import initForm from '../form';
+import initForm from 'ee_else_ce/pages/projects/issues/form';
document.addEventListener('DOMContentLoaded', initForm);
diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
index ec39db12e74..0bcca22e40f 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
@@ -2,12 +2,13 @@ import IssuableIndex from '~/issuable_index';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import UsersSelect from '~/users_select';
import initFilteredSearch from '~/pages/search/init_filtered_search';
+import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests';
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/pages/constants';
import { ISSUABLE_INDEX } from '~/pages/projects/constants';
document.addEventListener('DOMContentLoaded', () => {
- IssuableFilteredSearchTokenKeys.addExtraTokensForMergeRequests();
+ addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys);
initFilteredSearch({
page: FILTERED_SEARCH.MERGE_REQUESTS,
diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
index e3971618da5..8f0dc8554e2 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js
@@ -4,7 +4,7 @@ import $ from 'jquery';
import Diff from '~/diff';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GLForm from '~/gl_form';
-import IssuableForm from '~/issuable_form';
+import IssuableForm from 'ee_else_ce/issuable_form';
import LabelsSelect from '~/labels_select';
import MilestoneSelect from '~/milestone_select';
import IssuableTemplateSelectors from '~/templates/issuable_template_selectors';
diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js
index adbe744290a..f39765818e7 100644
--- a/app/assets/javascripts/pages/projects/project_members/index.js
+++ b/app/assets/javascripts/pages/projects/project_members/index.js
@@ -1,7 +1,7 @@
+import Members from 'ee_else_ce/members';
import memberExpirationDate from '../../../member_expiration_date';
import UsersSelect from '../../../users_select';
import groupsSelect from '../../../groups_select';
-import Members from '../../../members';
document.addEventListener('DOMContentLoaded', () => {
memberExpirationDate('.js-access-expiration-date-groups');
diff --git a/app/assets/javascripts/pages/projects/settings/operations/show/index.js b/app/assets/javascripts/pages/projects/settings/operations/show/index.js
new file mode 100644
index 00000000000..73c745179be
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/settings/operations/show/index.js
@@ -0,0 +1,5 @@
+import mountErrorTrackingForm from '~/error_tracking_settings';
+
+document.addEventListener('DOMContentLoaded', () => {
+ mountErrorTrackingForm();
+});
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index afa099d0e0b..61204c37307 100644
--- a/app/assets/javascripts/pages/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
@@ -10,6 +10,12 @@ import { __ } from '~/locale';
const d3 = { select, scaleLinear, scaleThreshold };
+const firstDayOfWeekChoices = Object.freeze({
+ sunday: 0,
+ monday: 1,
+ saturday: 6,
+});
+
const LOADING_HTML = `
<div class="text-center">
<i class="fa fa-spinner fa-spin user-calendar-activities-loading"></i>
@@ -49,7 +55,7 @@ export default class ActivityCalendar {
timestamps,
calendarActivitiesPath,
utcOffset = 0,
- firstDayOfWeek = 0,
+ firstDayOfWeek = firstDayOfWeekChoices.sunday,
monthsAgo = 12,
) {
this.calendarActivitiesPath = calendarActivitiesPath;
@@ -206,11 +212,16 @@ export default class ActivityCalendar {
},
];
- if (this.firstDayOfWeek === 1) {
+ if (this.firstDayOfWeek === firstDayOfWeekChoices.monday) {
days.push({
text: 'S',
y: 29 + this.dayYPos(7),
});
+ } else if (this.firstDayOfWeek === firstDayOfWeekChoices.saturday) {
+ days.push({
+ text: 'S',
+ y: 29 + this.dayYPos(6),
+ });
}
this.svg
diff --git a/app/assets/javascripts/pages/users/user_tabs.js b/app/assets/javascripts/pages/users/user_tabs.js
index 39cd891c111..7f800d20835 100644
--- a/app/assets/javascripts/pages/users/user_tabs.js
+++ b/app/assets/javascripts/pages/users/user_tabs.js
@@ -91,6 +91,7 @@ export default class UserTabs {
this.actions = Object.keys(this.loaded);
this.bindEvents();
+ // TODO: refactor to make this configurable via constructor params with a default value of 'show'
if (this.action === 'show') {
this.action = this.defaultAction;
}
@@ -221,7 +222,7 @@ export default class UserTabs {
const monthsAgo = UserTabs.getVisibleCalendarPeriod($calendarWrap);
const calendarActivitiesPath = $calendarWrap.data('calendarActivitiesPath');
const utcOffset = $calendarWrap.data('utcOffset');
- const calendarHint = __('Issues, merge requests, pushes and comments.');
+ const calendarHint = __('Issues, merge requests, pushes, and comments.');
$calendarWrap.html(CALENDAR_TEMPLATE);
diff --git a/app/assets/javascripts/persistent_user_callout.js b/app/assets/javascripts/persistent_user_callout.js
index 1e34e74a152..4a08e158f6b 100644
--- a/app/assets/javascripts/persistent_user_callout.js
+++ b/app/assets/javascripts/persistent_user_callout.js
@@ -31,4 +31,12 @@ export default class PersistentUserCallout {
Flash(__('An error occurred while dismissing the alert. Refresh the page and try again.'));
});
}
+
+ static factory(container) {
+ if (!container) {
+ return undefined;
+ }
+
+ return new PersistentUserCallout(container);
+ }
}
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 09a50d25020..348c407f1b5 100644
--- a/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
+++ b/app/assets/javascripts/pipelines/components/graph/stage_column_component.vue
@@ -1,5 +1,6 @@
<script>
import _ from 'underscore';
+import stageColumnMixin from 'ee_else_ce/pipelines/mixins/stage_column_mixin';
import JobItem from './job_item.vue';
import JobGroupDropdown from './job_group_dropdown.vue';
@@ -8,6 +9,7 @@ export default {
JobItem,
JobGroupDropdown,
},
+ mixins: [stageColumnMixin],
props: {
title: {
type: String,
@@ -32,9 +34,6 @@ export default {
groupId(group) {
return `ci-badge-${_.escape(group.name)}`;
},
- buildConnnectorClass(index) {
- return index === 0 && !this.isFirstColumn ? 'left-connector' : '';
- },
pipelineActionRequestComplete() {
this.$emit('refreshPipelineGraph');
},
diff --git a/app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue b/app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue
new file mode 100644
index 00000000000..4cafd147511
--- /dev/null
+++ b/app/assets/javascripts/pipelines/components/pipeline_stop_modal.vue
@@ -0,0 +1,97 @@
+<script>
+import _ from 'underscore';
+import GlModal from '~/vue_shared/components/gl_modal.vue';
+import { GlLink } from '@gitlab/ui';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
+import { s__, sprintf } from '~/locale';
+
+/**
+ * Pipeline Stop Modal.
+ *
+ * Renders the modal used to confirm stopping a pipeline.
+ */
+export default {
+ components: {
+ GlModal,
+ GlLink,
+ ClipboardButton,
+ CiIcon,
+ },
+ props: {
+ pipeline: {
+ type: Object,
+ required: true,
+ deep: true,
+ },
+ },
+ computed: {
+ modalTitle() {
+ return sprintf(
+ s__('Pipeline|Stop pipeline #%{pipelineId}?'),
+ {
+ pipelineId: `${this.pipeline.id}`,
+ },
+ false,
+ );
+ },
+ modalText() {
+ return sprintf(
+ s__(`Pipeline|You’re about to stop pipeline %{pipelineId}.`),
+ {
+ pipelineId: `<strong>#${this.pipeline.id}</strong>`,
+ },
+ false,
+ );
+ },
+ hasRef() {
+ return !_.isEmpty(this.pipeline.ref);
+ },
+ },
+ methods: {
+ emitSubmit(event) {
+ this.$emit('submit', event);
+ },
+ },
+};
+</script>
+<template>
+ <gl-modal
+ id="confirmation-modal"
+ :header-title-text="modalTitle"
+ :footer-primary-button-text="s__('Pipeline|Stop pipeline')"
+ footer-primary-button-variant="danger"
+ @submit="emitSubmit($event)"
+ >
+ <p v-html="modalText"></p>
+
+ <p v-if="pipeline">
+ <ci-icon
+ v-if="pipeline.details"
+ :status="pipeline.details.status"
+ class="vertical-align-middle"
+ />
+
+ <span class="font-weight-bold">{{ __('Pipeline') }}</span>
+
+ <a :href="pipeline.path" class="js-pipeline-path link-commit qa-pipeline-path"
+ >#{{ pipeline.id }}</a
+ >
+ <template v-if="hasRef">
+ {{ __('from') }}
+ <a :href="pipeline.ref.path" class="link-commit ref-name">{{ pipeline.ref.name }}</a>
+ </template>
+ </p>
+
+ <template v-if="pipeline.commit">
+ <p>
+ <span class="font-weight-bold">{{ __('Commit') }}</span>
+
+ <gl-link :href="pipeline.commit.commit_path" class="js-commit-sha commit-sha link-commit">
+ {{ pipeline.commit.short_id }}
+ </gl-link>
+ </p>
+ <p>{{ pipeline.commit.title }}</p>
+ </template>
+ </gl-modal>
+</template>
diff --git a/app/assets/javascripts/pipelines/components/pipeline_url.vue b/app/assets/javascripts/pipelines/components/pipeline_url.vue
index 918622ef8dc..3e7bf20470c 100644
--- a/app/assets/javascripts/pipelines/components/pipeline_url.vue
+++ b/app/assets/javascripts/pipelines/components/pipeline_url.vue
@@ -110,12 +110,12 @@ export default {
{{ __('stuck') }}
</span>
<span
- v-if="pipeline.flags.merge_request"
+ v-if="pipeline.flags.detached_merge_request_pipeline"
v-gl-tooltip
- :title="__('This pipeline is run in a merge request context')"
- class="js-pipeline-url-mergerequest badge badge-info"
+ :title="__('This pipeline is run on the source branch')"
+ class="js-pipeline-url-detached badge badge-info"
>
- {{ __('merge request') }}
+ {{ __('detached') }}
</span>
</div>
</div>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index a250e3236f5..244d332f38f 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -78,11 +78,11 @@ export default {
<gl-button
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
- class="js-pipeline-action-link no-btn btn"
+ class="js-pipeline-action-link no-btn btn d-flex align-items-center justify-content-between flex-wrap"
@click="onClickAction(action)"
>
{{ action.name }}
- <span v-if="action.scheduled_at" class="pull-right">
+ <span v-if="action.scheduled_at">
<icon name="clock" />
<gl-countdown :end-date-string="action.scheduled_at" />
</span>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_table.vue
index 1c60ae6a152..fcd1f119df0 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table.vue
@@ -1,7 +1,6 @@
<script>
-import Modal from '~/vue_shared/components/gl_modal.vue';
-import { s__, sprintf } from '~/locale';
import PipelinesTableRowComponent from './pipelines_table_row.vue';
+import PipelineStopModal from './pipeline_stop_modal.vue';
import eventHub from '../event_hub';
/**
@@ -12,7 +11,7 @@ import eventHub from '../event_hub';
export default {
components: {
PipelinesTableRowComponent,
- Modal,
+ PipelineStopModal,
},
props: {
pipelines: {
@@ -36,30 +35,11 @@ export default {
data() {
return {
pipelineId: 0,
+ pipeline: {},
endpoint: '',
cancelingPipeline: null,
};
},
- computed: {
- modalTitle() {
- return sprintf(
- s__('Pipeline|Stop pipeline #%{pipelineId}?'),
- {
- pipelineId: `${this.pipelineId}`,
- },
- false,
- );
- },
- modalText() {
- return sprintf(
- s__('Pipeline|You’re about to stop pipeline %{pipelineId}.'),
- {
- pipelineId: `<strong>#${this.pipelineId}</strong>`,
- },
- false,
- );
- },
- },
created() {
eventHub.$on('openConfirmationModal', this.setModalData);
},
@@ -68,7 +48,8 @@ export default {
},
methods: {
setModalData(data) {
- this.pipelineId = data.pipelineId;
+ this.pipelineId = data.pipeline.id;
+ this.pipeline = data.pipeline;
this.endpoint = data.endpoint;
},
onSubmit() {
@@ -103,15 +84,6 @@ export default {
:view-type="viewType"
:canceling-pipeline="cancelingPipeline"
/>
-
- <modal
- id="confirmation-modal"
- :header-title-text="modalTitle"
- :footer-primary-button-text="s__('Pipeline|Stop pipeline')"
- footer-primary-button-variant="danger"
- @submit="onSubmit"
- >
- <span v-html="modalText"></span>
- </modal>
+ <pipeline-stop-modal :pipeline="pipeline" @submit="onSubmit" />
</div>
</template>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index da42698c255..1c44427e720 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -243,7 +243,7 @@ export default {
methods: {
handleCancelClick() {
eventHub.$emit('openConfirmationModal', {
- pipelineId: this.pipeline.id,
+ pipeline: this.pipeline,
endpoint: this.pipeline.cancel_path,
});
},
@@ -272,10 +272,11 @@ export default {
:tag="commitTag"
:commit-ref="commitRef"
:commit-url="commitUrl"
+ :merge-request-ref="pipeline.merge_request"
:short-sha="commitShortSha"
:title="commitTitle"
:author="commitAuthor"
- :show-branch="!isChildView"
+ :show-ref-info="!isChildView"
/>
</div>
</div>
diff --git a/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js b/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js
new file mode 100644
index 00000000000..dd79ade5bc9
--- /dev/null
+++ b/app/assets/javascripts/pipelines/mixins/graph_pipeline_bundle_mixin.js
@@ -0,0 +1,16 @@
+import Flash from '~/flash';
+import { __ } from '~/locale';
+
+export default {
+ methods: {
+ clickTriggeredByPipeline() {},
+ clickTriggeredPipeline() {},
+ requestRefreshPipelineGraph() {
+ // When an action is clicked
+ // (wether in the dropdown or in the main nodes, we refresh the big graph)
+ this.mediator
+ .refreshPipeline()
+ .catch(() => Flash(__('An error occurred while making the request.')));
+ },
+ },
+};
diff --git a/app/assets/javascripts/pipelines/mixins/pipelines.js b/app/assets/javascripts/pipelines/mixins/pipelines.js
index 32bfa47e5f2..3cc9d0a3a4e 100644
--- a/app/assets/javascripts/pipelines/mixins/pipelines.js
+++ b/app/assets/javascripts/pipelines/mixins/pipelines.js
@@ -27,11 +27,7 @@ export default {
},
computed: {
shouldRenderPagination() {
- return (
- !this.isLoading &&
- this.state.pipelines.length &&
- this.state.pageInfo.total > this.state.pageInfo.perPage
- );
+ return !this.isLoading;
},
},
beforeMount() {
@@ -94,8 +90,7 @@ export default {
this.isLoading = false;
this.successCallback(response);
- // restart polling
- this.poll.restart({ data: this.requestData });
+ this.poll.enable({ data: this.requestData, response });
})
.catch(() => {
this.isLoading = false;
diff --git a/app/assets/javascripts/pipelines/mixins/stage_column_mixin.js b/app/assets/javascripts/pipelines/mixins/stage_column_mixin.js
new file mode 100644
index 00000000000..64283ed0e58
--- /dev/null
+++ b/app/assets/javascripts/pipelines/mixins/stage_column_mixin.js
@@ -0,0 +1,7 @@
+export default {
+ methods: {
+ buildConnnectorClass(index) {
+ return index === 0 && !this.isFirstColumn ? 'left-connector' : '';
+ },
+ },
+};
diff --git a/app/assets/javascripts/pipelines/pipeline_details_bundle.js b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
index dc9befe6349..6660f8120f8 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_bundle.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_bundle.js
@@ -2,8 +2,9 @@ import Vue from 'vue';
import Flash from '~/flash';
import Translate from '~/vue_shared/translate';
import { __ } from '~/locale';
+import pipelineGraph from 'ee_else_ce/pipelines/components/graph/graph_component.vue';
+import GraphEEMixin from 'ee_else_ce/pipelines/mixins/graph_pipeline_bundle_mixin';
import PipelinesMediator from './pipeline_details_mediator';
-import pipelineGraph from './components/graph/graph_component.vue';
import pipelineHeader from './components/header_component.vue';
import eventHub from './event_hub';
@@ -22,20 +23,12 @@ export default () => {
components: {
pipelineGraph,
},
+ mixins: [GraphEEMixin],
data() {
return {
mediator,
};
},
- methods: {
- requestRefreshPipelineGraph() {
- // When an action is clicked
- // (wether in the dropdown or in the main nodes, we refresh the big graph)
- this.mediator
- .refreshPipeline()
- .catch(() => Flash(__('An error occurred while making the request.')));
- },
- },
render(createElement) {
return createElement('pipeline-graph', {
props: {
@@ -44,6 +37,10 @@ export default () => {
},
on: {
refreshPipelineGraph: this.requestRefreshPipelineGraph,
+ onClickTriggeredBy: (parentPipeline, pipeline) =>
+ this.clickTriggeredByPipeline(parentPipeline, pipeline),
+ onClickTriggered: (parentPipeline, pipeline) =>
+ this.clickTriggeredPipeline(parentPipeline, pipeline),
},
});
},
diff --git a/app/assets/javascripts/pipelines/pipeline_details_mediator.js b/app/assets/javascripts/pipelines/pipeline_details_mediator.js
index bd1e1895660..d67d88c4dba 100644
--- a/app/assets/javascripts/pipelines/pipeline_details_mediator.js
+++ b/app/assets/javascripts/pipelines/pipeline_details_mediator.js
@@ -19,6 +19,7 @@ export default class pipelinesMediator {
this.poll = new Poll({
resource: this.service,
method: 'getPipeline',
+ data: this.store.state.expandedPipelines ? this.getExpandedParameters() : undefined,
successCallback: this.successCallback.bind(this),
errorCallback: this.errorCallback.bind(this),
});
@@ -56,6 +57,19 @@ export default class pipelinesMediator {
.getPipeline()
.then(response => this.successCallback(response))
.catch(() => this.errorCallback())
- .finally(() => this.poll.restart());
+ .finally(() =>
+ this.poll.restart(
+ this.store.state.expandedPipelines ? this.getExpandedParameters() : undefined,
+ ),
+ );
+ }
+
+ /**
+ * Backend expects paramets in the following format: `expanded[]=id&expanded[]=id`
+ */
+ getExpandedParameters() {
+ return {
+ expanded: this.store.state.expandedPipelines,
+ };
}
}
diff --git a/app/assets/javascripts/pipelines/services/pipeline_service.js b/app/assets/javascripts/pipelines/services/pipeline_service.js
index a53a9cc8365..e44eb9cdfd1 100644
--- a/app/assets/javascripts/pipelines/services/pipeline_service.js
+++ b/app/assets/javascripts/pipelines/services/pipeline_service.js
@@ -5,8 +5,8 @@ export default class PipelineService {
this.pipeline = endpoint;
}
- getPipeline() {
- return axios.get(this.pipeline);
+ getPipeline(params) {
+ return axios.get(this.pipeline, { params });
}
// eslint-disable-next-line class-methods-use-this
diff --git a/app/assets/javascripts/pipelines/stores/pipeline_store.js b/app/assets/javascripts/pipelines/stores/pipeline_store.js
index 052e34a8aef..259278b6410 100644
--- a/app/assets/javascripts/pipelines/stores/pipeline_store.js
+++ b/app/assets/javascripts/pipelines/stores/pipeline_store.js
@@ -1,7 +1,6 @@
export default class PipelineStore {
constructor() {
this.state = {};
-
this.state.pipeline = {};
}
diff --git a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
index 5f8a4946f4a..fd5d5f86401 100644
--- a/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
+++ b/app/assets/javascripts/projects/gke_cluster_dropdowns/components/gke_zone_dropdown.vue
@@ -34,7 +34,7 @@ export default {
},
errorMessage() {
return sprintf(
- s__('ClusterIntegration|An error occured while trying to fetch project zones: %{error}'),
+ s__('ClusterIntegration|An error occurred while trying to fetch project zones: %{error}'),
{ error: this.gapiError },
);
},
diff --git a/app/assets/javascripts/projects/project_new.js b/app/assets/javascripts/projects/project_new.js
index d65e73a3f9c..784eec1ea55 100644
--- a/app/assets/javascripts/projects/project_new.js
+++ b/app/assets/javascripts/projects/project_new.js
@@ -125,6 +125,22 @@ const bindEvents = () => {
text: 'Spring',
icon: '.template-option .icon-spring',
},
+ iosswift: {
+ text: 'iOS (Swift)',
+ icon: '.template-option svg.icon-gitlab',
+ },
+ dotnetcore: {
+ text: '.NET Core',
+ icon: '.template-option .icon-dotnet',
+ },
+ android: {
+ text: 'Android',
+ icon: '.template-option svg.icon-android',
+ },
+ gomicro: {
+ text: 'Go Micro',
+ icon: '.template-option .icon-gomicro',
+ },
hugo: {
text: 'Pages/Hugo',
icon: '.template-option .icon-hugo',
@@ -145,6 +161,26 @@ const bindEvents = () => {
text: 'Pages/Hexo',
icon: '.template-option .icon-hexo',
},
+ nfhugo: {
+ text: 'Netlify/Hugo',
+ icon: '.template-option .icon-netlify',
+ },
+ nfjekyll: {
+ text: 'Netlify/Jekyll',
+ icon: '.template-option .icon-netlify',
+ },
+ nfplainhtml: {
+ text: 'Netlify/Plain HTML',
+ icon: '.template-option .icon-netlify',
+ },
+ nfgitbook: {
+ text: 'Netlify/GitBook',
+ icon: '.template-option .icon-netlify',
+ },
+ nfhexo: {
+ text: 'Netlify/Hexo',
+ icon: '.template-option .icon-netlify',
+ },
};
const selectedTemplate = templates[value];
diff --git a/app/assets/javascripts/releases/store/actions.js b/app/assets/javascripts/releases/store/actions.js
index baa2251403e..e0a922d5ef6 100644
--- a/app/assets/javascripts/releases/store/actions.js
+++ b/app/assets/javascripts/releases/store/actions.js
@@ -11,7 +11,7 @@ export const requestReleases = ({ commit }) => commit(types.REQUEST_RELEASES);
/**
* Fetches the main endpoint.
* Will dispatch requestNamespace action before starting the request.
- * Will dispatch receiveNamespaceSuccess if the request is successfull
+ * Will dispatch receiveNamespaceSuccess if the request is successful
* Will dispatch receiveNamesapceError if the request returns an error
*
* @param {String} projectId
@@ -30,7 +30,7 @@ export const receiveReleasesSuccess = ({ commit }, data) =>
export const receiveReleasesError = ({ commit }) => {
commit(types.RECEIVE_RELEASES_ERROR);
- createFlash(__('An error occured while fetching the releases. Please try again.'));
+ createFlash(__('An error occurred while fetching the releases. Please try again.'));
};
// prevent babel-plugin-rewire from generating an invalid default during karma tests
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
index 7f86741ed29..e9ed05e30cd 100644
--- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -66,19 +66,23 @@ export default {
import(/* webpackChunkName: 'emoji' */ '~/emoji')
.then(Emoji => {
- if (this.emoji) {
- this.emojiTag = Emoji.glEmojiTag(this.emoji);
- }
- this.noEmoji = this.emoji === '';
- this.defaultEmojiTag = Emoji.glEmojiTag('speech_balloon');
+ Emoji.initEmojiMap()
+ .then(() => {
+ if (this.emoji) {
+ this.emojiTag = Emoji.glEmojiTag(this.emoji);
+ }
+ this.noEmoji = this.emoji === '';
+ this.defaultEmojiTag = Emoji.glEmojiTag('speech_balloon');
- this.emojiMenu = new EmojiMenuInModal(
- Emoji,
- toggleEmojiMenuButtonSelector,
- emojiMenuClass,
- this.setEmoji,
- this.$refs.userStatusForm,
- );
+ this.emojiMenu = new EmojiMenuInModal(
+ Emoji,
+ toggleEmojiMenuButtonSelector,
+ emojiMenuClass,
+ this.setEmoji,
+ this.$refs.userStatusForm,
+ );
+ })
+ .catch(() => createFlash(__('Failed to load emoji list.')));
})
.catch(() => createFlash(__('Failed to load emoji list.')));
},
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 4017630d6ef..8c71615dff2 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -93,23 +93,22 @@ function UsersSelect(currentUser, els, options = {}) {
}
// Save current selected user to the DOM
- const input = document.createElement('input');
- input.type = 'hidden';
- input.name = $dropdown.data('fieldName');
-
- const currentUserInfo = $dropdown.data('currentUserInfo');
-
- if (currentUserInfo) {
- input.value = currentUserInfo.id;
- input.dataset.meta = _.escape(currentUserInfo.name);
- } else if (_this.currentUser) {
- input.value = _this.currentUser.id;
- }
+ const currentUserInfo = $dropdown.data('currentUserInfo') || {};
+ const currentUser = _this.currentUser || {};
+ const fieldName = $dropdown.data('fieldName');
+ const userName = currentUserInfo.name;
+ const userId = currentUserInfo.id || currentUser.id;
+
+ const inputHtmlString = _.template(`
+ <input type="hidden" name="<%- fieldName %>"
+ data-meta="<%- userName %>"
+ value="<%- userId %>" />
+ `)({ fieldName, userName, userId });
if ($selectbox) {
- $dropdown.parent().before(input);
+ $dropdown.parent().before(inputHtmlString);
} else {
- $dropdown.after(input);
+ $dropdown.after(inputHtmlString);
}
};
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 2f2a37347af..da0a9483f8e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/deployment.vue
@@ -54,6 +54,12 @@ export default {
deployTimeago() {
return this.timeFormated(this.deployment.deployed_at);
},
+ deploymentExternalUrl() {
+ if (this.deployment.changes && this.deployment.changes.length === 1) {
+ return this.deployment.changes[0].external_url;
+ }
+ return this.deployment.external_url;
+ },
hasExternalUrls() {
return !!(this.deployment.external_url && this.deployment.external_url_formatted);
},
@@ -78,7 +84,7 @@ export default {
: '';
},
shouldRenderDropdown() {
- return this.deployment.changes && this.deployment.changes.length > 0;
+ return this.deployment.changes && this.deployment.changes.length > 1;
},
showMemoryUsage() {
return this.hasMetrics && this.showMetrics;
@@ -154,12 +160,12 @@ export default {
v-if="shouldRenderDropdown"
class="js-mr-wigdet-deployment-dropdown inline"
:items="deployment.changes"
- :main-action-link="deployment.external_url"
+ :main-action-link="deploymentExternalUrl"
filter-key="path"
>
<template slot="mainAction" slot-scope="slotProps">
<review-app-link
- :link="deployment.external_url"
+ :link="deploymentExternalUrl"
:css-class="`deploy-link js-deploy-url inline ${slotProps.className}`"
/>
</template>
@@ -183,7 +189,7 @@ export default {
</filtered-search-dropdown>
<review-app-link
v-else
- :link="deployment.external_url"
+ :link="deploymentExternalUrl"
css-class="js-deploy-url js-deploy-url-feature-flag deploy-link btn btn-default btn-sm inlin"
/>
</template>
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 3b9fc2661ef..50ab7ead582 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
@@ -135,7 +135,7 @@ export default {
<span class="dropdown">
<button
type="button"
- class="btn dropdown-toggle"
+ class="btn dropdown-toggle qa-dropdown-toggle"
data-toggle="dropdown"
aria-label="Download as"
aria-haspopup="true"
@@ -145,12 +145,20 @@ export default {
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li>
- <a :href="mr.emailPatchesPath" class="js-download-email-patches" download>
+ <a
+ :href="mr.emailPatchesPath"
+ class="js-download-email-patches qa-download-email-patches"
+ download
+ >
{{ s__('mrWidget|Email patches') }}
</a>
</li>
<li>
- <a :href="mr.plainDiffPath" class="js-download-plain-diff" download>
+ <a
+ :href="mr.plainDiffPath"
+ class="js-download-plain-diff qa-download-plain-diff"
+ download
+ >
{{ s__('mrWidget|Plain diff') }}
</a>
</li>
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 f11cf21b0ca..f5a1ff2f6fd 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
@@ -1,10 +1,12 @@
<script>
/* eslint-disable vue/require-default-prop */
+import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import { sprintf, __ } from '~/locale';
import PipelineStage from '~/pipelines/components/stage.vue';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
import Icon from '~/vue_shared/components/icon.vue';
import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate.vue';
+import mrWidgetPipelineMixin from 'ee_else_ce/vue_merge_request_widget/mixins/mr_widget_pipeline';
export default {
name: 'MRWidgetPipeline',
@@ -13,7 +15,14 @@ export default {
CiIcon,
Icon,
TooltipOnTruncate,
+ GlLink,
+ LinkedPipelinesMiniList: () =>
+ import('ee_component/vue_shared/components/linked_pipelines_mini_list.vue'),
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ mixins: [mrWidgetPipelineMixin],
props: {
pipeline: {
type: Object,
@@ -74,16 +83,21 @@ export default {
false,
);
},
+ isTriggeredByMergeRequest() {
+ return Boolean(this.pipeline.merge_request);
+ },
+ isMergeRequestPipeline() {
+ return Boolean(this.pipeline.flags && this.pipeline.flags.merge_request_pipeline);
+ },
},
};
</script>
<template>
- <div v-if="hasPipeline || hasCIError" class="ci-widget media">
+ <div v-if="hasPipeline || hasCIError" class="ci-widget media js-ci-widget">
<template v-if="hasCIError">
<div
- class="add-border ci-status-icon ci-status-icon-failed ci-error
- js-ci-error append-right-default"
+ 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>
@@ -96,24 +110,58 @@ export default {
<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="font-weight-bold js-pipeline-info-container">
+ {{ s__('Pipeline|Pipeline') }}
+ <gl-link :href="pipeline.path" class="pipeline-id font-weight-normal pipeline-number"
+ >#{{ pipeline.id }}</gl-link
>
-
{{ pipeline.details.status.label }}
-
<template v-if="hasCommitInfo">
- for
- <a
+ {{ s__('Pipeline|for') }}
+ <gl-link
:href="pipeline.commit.commit_path"
class="commit-sha js-commit-link font-weight-normal"
+ >{{ pipeline.commit.short_id }}</gl-link
>
- {{ pipeline.commit.short_id }}</a
- >
- on
+ {{ s__('Pipeline|on') }}
+ <template v-if="isTriggeredByMergeRequest">
+ <gl-link
+ v-gl-tooltip
+ :href="pipeline.merge_request.path"
+ :title="pipeline.merge_request.title"
+ class="font-weight-normal"
+ >!{{ pipeline.merge_request.iid }}</gl-link
+ >
+ {{ s__('Pipeline|with') }}
+ <tooltip-on-truncate
+ :title="pipeline.merge_request.source_branch"
+ truncate-target="child"
+ class="label-branch label-truncate"
+ >
+ <gl-link
+ :href="pipeline.merge_request.source_branch_path"
+ class="font-weight-normal"
+ >{{ pipeline.merge_request.source_branch }}</gl-link
+ >
+ </tooltip-on-truncate>
+
+ <template v-if="isMergeRequestPipeline">
+ {{ s__('Pipeline|into') }}
+ <tooltip-on-truncate
+ :title="pipeline.merge_request.target_branch"
+ truncate-target="child"
+ class="label-branch label-truncate"
+ >
+ <gl-link
+ :href="pipeline.merge_request.target_branch_path"
+ class="font-weight-normal"
+ >{{ pipeline.merge_request.target_branch }}</gl-link
+ >
+ </tooltip-on-truncate>
+ </template>
+ </template>
<tooltip-on-truncate
+ v-else
:title="sourceBranch"
truncate-target="child"
class="label-branch label-truncate"
@@ -121,20 +169,29 @@ export default {
/>
</template>
</div>
- <div v-if="pipeline.coverage" class="coverage">Coverage {{ pipeline.coverage }}%</div>
+ <div v-if="pipeline.coverage" class="coverage">
+ {{ s__('Pipeline|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 class="stage-cell">
+ <linked-pipelines-mini-list v-if="triggeredBy.length" :triggered-by="triggeredBy" />
+ <template v-if="hasStages">
+ <div
+ v-for="(stage, i) in pipeline.details.stages"
+ :key="i"
+ :class="{
+ 'has-downstream': hasDownstream(i),
+ }"
+ class="stage-container dropdown js-mini-pipeline-graph mr-widget-pipeline-stages"
+ >
+ <pipeline-stage :stage="stage" />
+ </div>
+ </template>
</span>
+ <linked-pipelines-mini-list v-if="triggered.length" :triggered="triggered" />
</span>
</div>
</div>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue
index b3c1c0e329d..b6722de5277 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/commit_message_dropdown.vue
@@ -20,7 +20,6 @@ export default {
<div>
<gl-dropdown
right
- no-caret
text="Use an existing commit message"
variant="link"
class="mr-commit-dropdown"
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/commits_header.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/commits_header.vue
index 33963d5e1e6..0312b147b62 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/commits_header.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/commits_header.vue
@@ -14,6 +14,10 @@ export default {
type: Boolean,
required: true,
},
+ isFastForwardEnabled: {
+ type: Boolean,
+ required: true,
+ },
commitsCount: {
type: Number,
required: false,
@@ -37,16 +41,22 @@ export default {
return n__(__('%d commit'), __('%d commits'), this.isSquashEnabled ? 1 : this.commitsCount);
},
modifyLinkMessage() {
- return this.isSquashEnabled ? __('Modify commit messages') : __('Modify merge commit');
+ if (this.isFastForwardEnabled) return __('Modify commit message');
+ else if (this.isSquashEnabled) return __('Modify commit messages');
+ return __('Modify merge commit');
},
ariaLabel() {
return this.expanded ? __('Collapse') : __('Expand');
},
message() {
+ const message = this.isFastForwardEnabled
+ ? s__('mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}.')
+ : s__(
+ 'mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}.',
+ );
+
return sprintf(
- s__(
- 'mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}.',
- ),
+ message,
{
commitCount: `<strong class="commits-count-message">${this.commitsCountMessage}</strong>`,
mergeCommitCount: `<strong>${s__('mrWidgetCommitsAdded|1 merge commit')}</strong>`,
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
index 2a4dff71d9b..11bc8c73ee9 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_failed_to_merge.vue
@@ -80,7 +80,7 @@ export default {
<status-icon :show-disabled-button="true" status="warning" />
<div class="media-body space-children">
<span class="bold">
- <span v-if="mr.mergeError" class="has-error-message"> {{ mergeError }}. </span>
+ <span v-if="mr.mergeError" class="has-error-message"> {{ mergeError }} </span>
<span v-else> {{ s__('mrWidget|Merge failed.') }} </span>
<span :class="{ 'has-custom-error': mr.mergeError }"> {{ timerText }} </span>
</span>
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 ce4207864ea..bb76eb1030d 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
@@ -113,6 +113,12 @@ export default {
shouldShowMergeControls() {
return this.mr.isMergeAllowed || this.shouldShowMergeWhenPipelineSucceedsText;
},
+ shouldShowSquashEdit() {
+ return this.squashBeforeMerge && this.shouldShowSquashBeforeMerge;
+ },
+ shouldShowMergeEdit() {
+ return !this.mr.ffOnlyEnabled;
+ },
},
methods: {
updateMergeCommitMessage(includeDescription) {
@@ -159,9 +165,12 @@ export default {
});
},
initiateMergePolling() {
- simplePoll((continuePolling, stopPolling) => {
- this.handleMergePolling(continuePolling, stopPolling);
- });
+ simplePoll(
+ (continuePolling, stopPolling) => {
+ this.handleMergePolling(continuePolling, stopPolling);
+ },
+ { timeout: 0 },
+ );
},
handleMergePolling(continuePolling, stopPolling) {
this.service
@@ -192,6 +201,7 @@ export default {
})
.catch(() => {
new Flash(__('Something went wrong while merging this merge request. Please try again.')); // eslint-disable-line
+ stopPolling();
});
},
initiateRemoveSourceBranchPolling() {
@@ -321,43 +331,44 @@ export default {
<div v-if="mr.ffOnlyEnabled" class="mr-fast-forward-message">
{{ __('Fast-forward merge without a merge commit') }}
</div>
- <template v-else>
- <commits-header
- :is-squash-enabled="squashBeforeMerge"
- :commits-count="mr.commitsCount"
- :target-branch="mr.targetBranch"
- >
- <ul class="border-top content-list commits-list flex-list">
- <commit-edit
- v-if="squashBeforeMerge"
+ <commits-header
+ v-if="shouldShowSquashEdit || shouldShowMergeEdit"
+ :is-squash-enabled="squashBeforeMerge"
+ :commits-count="mr.commitsCount"
+ :target-branch="mr.targetBranch"
+ :is-fast-forward-enabled="mr.ffOnlyEnabled"
+ >
+ <ul class="border-top content-list commits-list flex-list">
+ <commit-edit
+ v-if="shouldShowSquashEdit"
+ v-model="squashCommitMessage"
+ :label="__('Squash commit message')"
+ input-id="squash-message-edit"
+ squash
+ >
+ <commit-message-dropdown
+ slot="header"
v-model="squashCommitMessage"
- :label="__('Squash commit message')"
- input-id="squash-message-edit"
- squash
- >
- <commit-message-dropdown
- slot="header"
- v-model="squashCommitMessage"
- :commits="mr.commits"
+ :commits="mr.commits"
+ />
+ </commit-edit>
+ <commit-edit
+ v-if="shouldShowMergeEdit"
+ v-model="commitMessage"
+ :label="__('Merge commit message')"
+ input-id="merge-message-edit"
+ >
+ <label slot="checkbox">
+ <input
+ id="include-description"
+ type="checkbox"
+ @change="updateMergeCommitMessage($event.target.checked)"
/>
- </commit-edit>
- <commit-edit
- v-model="commitMessage"
- :label="__('Merge commit message')"
- input-id="merge-message-edit"
- >
- <label slot="checkbox">
- <input
- id="include-description"
- type="checkbox"
- @change="updateMergeCommitMessage($event.target.checked)"
- />
- {{ __('Include merge request description') }}
- </label>
- </commit-edit>
- </ul>
- </commits-header>
- </template>
+ {{ __('Include merge request description') }}
+ </label>
+ </commit-edit>
+ </ul>
+ </commits-header>
</template>
</div>
</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/mixins/mr_widget_pipeline.js b/app/assets/javascripts/vue_merge_request_widget/mixins/mr_widget_pipeline.js
new file mode 100644
index 00000000000..96e8bb45e34
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/mixins/mr_widget_pipeline.js
@@ -0,0 +1,15 @@
+export default {
+ computed: {
+ triggered() {
+ return [];
+ },
+ triggeredBy() {
+ return [];
+ },
+ },
+ methods: {
+ hasDownstream() {
+ return false;
+ },
+ },
+};
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 abbbe19c5ef..57c4dfbe3b7 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
@@ -315,7 +315,7 @@ export default {
:endpoint="mr.testResultsPath"
/>
- <div class="mr-widget-section p-0">
+ <div class="mr-widget-section">
<component :is="componentName" :mr="mr" :service="service" />
<section v-if="shouldRenderCollaborationStatus" class="mr-info-list mr-links">
diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue
index 2f498c4fa2a..25f80219993 100644
--- a/app/assets/javascripts/vue_shared/components/ci_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue
@@ -21,6 +21,8 @@ import Icon from '../../vue_shared/components/icon.vue';
* - Jobs table
* - Jobs show view header
* - Jobs show view sidebar
+ * - Linked pipelines
+ * - Extended MR Popover
*/
const validSizes = [8, 12, 16, 18, 24, 32, 48, 72];
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
index ee685a4b8cd..3f282138bdf 100644
--- a/app/assets/javascripts/vue_shared/components/commit.vue
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -1,5 +1,6 @@
<script>
-import { GlTooltipDirective } from '@gitlab/ui';
+import _ from 'underscore';
+import { GlTooltipDirective, GlLink } from '@gitlab/ui';
import UserAvatarLink from './user_avatar/user_avatar_link.vue';
import Icon from '../../vue_shared/components/icon.vue';
@@ -10,6 +11,7 @@ export default {
components: {
UserAvatarLink,
Icon,
+ GlLink,
},
props: {
/**
@@ -33,6 +35,27 @@ export default {
required: false,
default: () => ({}),
},
+
+ /**
+ * If provided, is used the render the MR IID and link
+ * in place of the branch name. Must contains the
+ * following properties:
+ * - iid (number)
+ * - path (non-empty string)
+ *
+ * May optionally contain the following properties:
+ * - title (string): used in a tooltip if provided
+ *
+ * Any additional properties are ignored.
+ */
+ mergeRequestRef: {
+ type: Object,
+ required: false,
+ default: undefined,
+ validator: ref =>
+ _.isUndefined(ref) || (_.isFinite(ref.iid) && _.isString(ref.path) && !_.isEmpty(ref.path)),
+ },
+
/**
* Used to link to the commit sha.
*/
@@ -70,7 +93,11 @@ export default {
required: false,
default: () => ({}),
},
- showBranch: {
+
+ /**
+ * Indicates whether or not to show the branch/MR ref info
+ */
+ showRefInfo: {
type: Boolean,
required: false,
default: true,
@@ -78,14 +105,12 @@ export default {
},
computed: {
/**
- * Used to verify if all the properties needed to render the commit
- * ref section were provided.
- *
- * @returns {Boolean}
+ * Determines if we shoud render the ref info section based
*/
- hasCommitRef() {
- return this.commitRef && this.commitRef.name && this.commitRef.ref_url;
+ shouldShowRefInfo() {
+ return this.showRefInfo && (this.commitRef || this.mergeRequestRef);
},
+
/**
* Used to verify if all the properties needed to render the commit
* author section were provided.
@@ -109,18 +134,35 @@ export default {
</script>
<template>
<div class="branch-commit">
- <template v-if="hasCommitRef && showBranch">
+ <template v-if="shouldShowRefInfo">
<div class="icon-container">
- <i v-if="tag" class="fa fa-tag" aria-hidden="true"> </i> <icon v-if="!tag" name="fork" />
+ <icon v-if="tag" name="tag" />
+ <icon v-else-if="mergeRequestRef" name="git-merge" />
+ <icon v-else name="branch" />
</div>
- <a v-gl-tooltip :href="commitRef.ref_url" :title="commitRef.name" class="ref-name">
+ <gl-link
+ v-if="mergeRequestRef"
+ v-gl-tooltip
+ :href="mergeRequestRef.path"
+ :title="mergeRequestRef.title"
+ class="ref-name"
+ >
+ {{ mergeRequestRef.iid }}
+ </gl-link>
+ <gl-link
+ v-else
+ v-gl-tooltip
+ :href="commitRef.ref_url"
+ :title="commitRef.name"
+ class="ref-name"
+ >
{{ commitRef.name }}
- </a>
+ </gl-link>
</template>
<icon name="commit" class="commit-icon js-commit-icon" />
- <a :href="commitUrl" class="commit-sha"> {{ shortSha }} </a>
+ <gl-link :href="commitUrl" class="commit-sha"> {{ shortSha }} </gl-link>
<div class="commit-title flex-truncate-parent">
<span v-if="title" class="flex-truncate-child">
@@ -132,7 +174,7 @@ export default {
:tooltip-text="author.username"
class="avatar-image-container"
/>
- <a :href="commitUrl" class="commit-row-message"> {{ title }} </a>
+ <gl-link :href="commitUrl" class="commit-row-message"> {{ title }} </gl-link>
</span>
<span v-else> Can't find HEAD commit for this branch </span>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index c9915f7d685..5fdc915fffb 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -78,8 +78,8 @@ export default {
</script>
<template>
- <div ref="markdown-preview" class="md md-previewer">
+ <div ref="markdown-preview" class="md-previewer">
<gl-skeleton-loading v-if="isLoading" />
- <div v-else v-html="previewContent"></div>
+ <div v-else class="md" v-html="previewContent"></div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/empty_component.js b/app/assets/javascripts/vue_shared/components/empty_component.js
new file mode 100644
index 00000000000..e4402020096
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/empty_component.js
@@ -0,0 +1,3 @@
+export default {
+ render: () => null,
+};
diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
new file mode 100644
index 00000000000..27cfa8abb24
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
@@ -0,0 +1,116 @@
+<script>
+import { GlTooltipDirective } from '@gitlab/ui';
+import { __, sprintf } from '~/locale';
+import IssueMilestone from '~/vue_shared/components/issue/issue_milestone.vue';
+import IssueAssignees from '~/vue_shared/components/issue/issue_assignees.vue';
+import relatedIssuableMixin from '~/vue_shared/mixins/related_issuable_mixin';
+
+export default {
+ name: 'IssueItem',
+ components: {
+ IssueMilestone,
+ IssueAssignees,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ mixins: [relatedIssuableMixin],
+ props: {
+ canReorder: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ computed: {
+ stateTitle() {
+ return sprintf(
+ '<span class="bold">%{state}</span> %{timeInWords}<br/><span class="text-tertiary">%{timestamp}</span>',
+ {
+ state: this.isOpen ? __('Opened') : __('Closed'),
+ timeInWords: this.isOpen ? this.createdAtInWords : this.closedAtInWords,
+ timestamp: this.isOpen ? this.createdAtTimestamp : this.closedAtTimestamp,
+ },
+ );
+ },
+ },
+};
+</script>
+
+<template>
+ <div
+ :class="{
+ 'issuable-info-container': !canReorder,
+ 'card-body': canReorder,
+ }"
+ class="item-body"
+ >
+ <div class="item-contents">
+ <div class="item-title d-flex align-items-center">
+ <icon
+ v-if="hasState"
+ v-tooltip
+ :css-classes="iconClass"
+ :name="iconName"
+ :size="16"
+ :title="stateTitle"
+ :aria-label="state"
+ data-html="true"
+ />
+ <icon
+ v-if="confidential"
+ v-gl-tooltip
+ name="eye-slash"
+ :size="16"
+ :title="__('Confidential')"
+ class="confidential-icon append-right-4"
+ :aria-label="__('Confidential')"
+ />
+ <a :href="computedPath" class="sortable-link">{{ title }}</a>
+ </div>
+ <div class="item-meta">
+ <div class="d-flex align-items-center item-path-id">
+ <icon
+ v-if="hasState"
+ v-tooltip
+ :css-classes="iconClass"
+ :name="iconName"
+ :size="16"
+ :title="stateTitle"
+ :aria-label="state"
+ data-html="true"
+ />
+ <span v-tooltip :title="itemPath" class="path-id-text">{{ itemPath }}</span>
+ {{ pathIdSeparator }}{{ itemId }}
+ </div>
+ <div class="item-meta-child d-flex align-items-center">
+ <issue-milestone
+ v-if="hasMilestone"
+ :milestone="milestone"
+ class="d-flex align-items-center item-milestone"
+ />
+ <slot name="dueDate"></slot>
+ <slot name="weight"></slot>
+ </div>
+ <issue-assignees
+ v-if="assignees.length"
+ :assignees="assignees"
+ class="item-assignees d-inline-flex"
+ />
+ </div>
+ </div>
+ <button
+ v-if="canRemove"
+ ref="removeButton"
+ v-tooltip
+ :disabled="removeDisabled"
+ type="button"
+ class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button"
+ title="Remove"
+ aria-label="Remove"
+ @click="onRemoveRequest"
+ >
+ <icon :size="16" class="btn-item-remove-icon" name="close" />
+ </button>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/loading_button.vue b/app/assets/javascripts/vue_shared/components/loading_button.vue
index 7a53d053eec..216f6c62e69 100644
--- a/app/assets/javascripts/vue_shared/components/loading_button.vue
+++ b/app/assets/javascripts/vue_shared/components/loading_button.vue
@@ -53,7 +53,7 @@ export default {
<template>
<button :class="containerClass" :disabled="loading || disabled" type="button" @click="onClick">
- <transition name="fade">
+ <transition name="fade-in">
<gl-loading-icon
v-if="loading"
:inline="true"
@@ -63,7 +63,7 @@ export default {
class="js-loading-button-icon"
/>
</transition>
- <transition name="fade">
+ <transition name="fade-in">
<slot>
<span v-if="label" class="js-loading-button-label"> {{ label }} </span>
</slot>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 3f607aa2a0a..a4b3131c8e4 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -215,7 +215,7 @@ export default {
<div
v-show="previewMarkdown"
ref="markdown-preview"
- class="md-preview js-vue-md-preview md md-preview-holder"
+ class="js-vue-md-preview md-preview-holder"
>
<suggestions
v-if="hasSuggestion"
@@ -233,7 +233,7 @@ export default {
<div
v-show="previewMarkdown"
ref="markdown-preview"
- class="md-preview js-vue-md-preview md md-preview-holder"
+ class="js-vue-md-preview md md-preview-holder"
v-html="markdownPreview"
></div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index dbfa32cd0ce..cc6ecdb0395 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -79,7 +79,7 @@ export default {
<ul class="nav-links clearfix">
<li :class="{ active: !previewMarkdown }" class="md-header-tab">
<button class="js-write-link" tabindex="-1" type="button" @click="writeMarkdownTab($event)">
- Write
+ {{ __('Write') }}
</button>
</li>
<li :class="{ active: previewMarkdown }" class="md-header-tab">
@@ -89,36 +89,41 @@ export default {
type="button"
@click="previewMarkdownTab($event)"
>
- Preview
+ {{ __('Preview') }}
</button>
</li>
<li :class="{ active: !previewMarkdown }" class="md-header-toolbar">
- <toolbar-button tag="**" button-title="Add bold text" icon="bold" />
- <toolbar-button tag="*" button-title="Add italic text" icon="italic" />
- <toolbar-button :prepend="true" tag="> " button-title="Insert a quote" icon="quote" />
- <toolbar-button tag="`" tag-block="```" button-title="Insert code" icon="code" />
+ <toolbar-button tag="**" :button-title="__('Add bold text')" icon="bold" />
+ <toolbar-button tag="*" :button-title="__('Add italic text')" icon="italic" />
+ <toolbar-button
+ :prepend="true"
+ tag="> "
+ :button-title="__('Insert a quote')"
+ icon="quote"
+ />
+ <toolbar-button tag="`" tag-block="```" :button-title="__('Insert code')" icon="code" />
<toolbar-button
tag="[{text}](url)"
tag-select="url"
- button-title="Add a link"
+ :button-title="__('Add a link')"
icon="link"
/>
<toolbar-button
:prepend="true"
tag="* "
- button-title="Add a bullet list"
+ :button-title="__('Add a bullet list')"
icon="list-bulleted"
/>
<toolbar-button
:prepend="true"
tag="1. "
- button-title="Add a numbered list"
+ :button-title="__('Add a numbered list')"
icon="list-numbered"
/>
<toolbar-button
:prepend="true"
tag="* [ ] "
- button-title="Add a task list"
+ :button-title="__('Add a task list')"
icon="task-done"
/>
<toolbar-button
@@ -139,11 +144,11 @@ export default {
/>
<button
v-gl-tooltip
- aria-label="Go full screen"
+ :aria-label="__('Go full screen')"
class="toolbar-btn toolbar-fullscreen-btn js-zen-enter"
data-container="body"
tabindex="-1"
- title="Go full screen"
+ :title="__('Go full screen')"
type="button"
>
<icon name="screen-full" />
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
index dcda701f049..177d78cb904 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
@@ -130,6 +130,6 @@ export default {
<template>
<div>
<div class="flash-container js-suggestions-flash"></div>
- <div v-show="isRendered" ref="container" v-html="noteHtml"></div>
+ <div v-show="isRendered" ref="container" class="md" v-html="noteHtml"></div>
</div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
index 8d3a3009c55..a50f49c1279 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
@@ -57,7 +57,7 @@ export default {
</div>
</div>
<div class="note-body">
- <div class="note-text">
+ <div class="note-text md">
<p>{{ note.body }}</p>
</div>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
index b0af8399955..acc179b3834 100644
--- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
@@ -93,7 +93,7 @@ export default {
'system-note-commit-list': hasMoreCommits,
'hide-shade': expanded,
}"
- class="note-text"
+ class="note-text md"
v-html="note.note_html"
></div>
<div v-if="hasMoreCommits" class="flex-list">
diff --git a/app/assets/javascripts/vue_shared/components/project_avatar/default.vue b/app/assets/javascripts/vue_shared/components/project_avatar/default.vue
index b399c232937..881b5059d2a 100644
--- a/app/assets/javascripts/vue_shared/components/project_avatar/default.vue
+++ b/app/assets/javascripts/vue_shared/components/project_avatar/default.vue
@@ -26,7 +26,7 @@ export default {
</script>
<template>
- <span :class="sizeClass" class="avatar-container project-avatar">
+ <span :class="sizeClass" class="avatar-container rect-avatar project-avatar">
<project-avatar-image
v-if="project.avatar_url"
:link-href="project.path"
@@ -34,6 +34,12 @@ export default {
:img-alt="project.name"
:img-size="size"
/>
- <identicon v-else :entity-id="project.id" :entity-name="project.name" :size-class="sizeClass" />
+ <identicon
+ v-else
+ :entity-id="project.id"
+ :entity-name="project.name"
+ :size-class="sizeClass"
+ class="rect-avatar"
+ />
</span>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/select2_select.vue b/app/assets/javascripts/vue_shared/components/select2_select.vue
index 19c5da0461a..3074ea859cc 100644
--- a/app/assets/javascripts/vue_shared/components/select2_select.vue
+++ b/app/assets/javascripts/vue_shared/components/select2_select.vue
@@ -1,5 +1,6 @@
<script>
import $ from 'jquery';
+import 'select2/select2';
export default {
name: 'Select2Select',
diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue
index 2a34b4630f2..8e0b08032f7 100644
--- a/app/assets/javascripts/vue_shared/components/table_pagination.vue
+++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue
@@ -54,15 +54,14 @@ export default {
return this.pageInfo.nextPage;
},
getItems() {
- const total = this.pageInfo.totalPages;
- const { page } = this.pageInfo;
+ const { totalPages, nextPage, previousPage, page } = this.pageInfo;
const items = [];
if (page > 1) {
items.push({ title: FIRST, first: true });
}
- if (page > 1) {
+ if (previousPage) {
items.push({ title: PREV, prev: true });
} else {
items.push({ title: PREV, disabled: true, prev: true });
@@ -70,32 +69,34 @@ export default {
if (page > UI_LIMIT) items.push({ title: SPREAD, separator: true });
- const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
- const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, total);
+ if (totalPages) {
+ const start = Math.max(page - PAGINATION_UI_BUTTON_LIMIT, 1);
+ const end = Math.min(page + PAGINATION_UI_BUTTON_LIMIT, totalPages);
- for (let i = start; i <= end; i += 1) {
- const isActive = i === page;
- items.push({ title: i, active: isActive, page: true });
- }
+ for (let i = start; i <= end; i += 1) {
+ const isActive = i === page;
+ items.push({ title: i, active: isActive, page: true });
+ }
- if (total - page > PAGINATION_UI_BUTTON_LIMIT) {
- items.push({ title: SPREAD, separator: true, page: true });
+ if (totalPages - page > PAGINATION_UI_BUTTON_LIMIT) {
+ items.push({ title: SPREAD, separator: true, page: true });
+ }
}
- if (page === total) {
- items.push({ title: NEXT, disabled: true, next: true });
- } else if (total - page >= 1) {
+ if (nextPage) {
items.push({ title: NEXT, next: true });
+ } else {
+ items.push({ title: NEXT, disabled: true, next: true });
}
- if (total - page >= 1) {
+ if (totalPages && totalPages - page >= 1) {
items.push({ title: LAST, last: true });
}
return items;
},
showPagination() {
- return this.pageInfo.totalPages > 1;
+ return this.pageInfo.nextPage || this.pageInfo.previousPage;
},
},
methods: {
@@ -149,9 +150,9 @@ export default {
}"
class="page-item"
>
- <a class="page-link" @click.prevent="changePage(item.title, item.disabled)">
+ <button type="button" class="page-link" @click="changePage(item.title, item.disabled)">
{{ item.title }}
- </a>
+ </button>
</li>
</ul>
</div>
diff --git a/app/assets/javascripts/vue_shared/mixins/is_ee.js b/app/assets/javascripts/vue_shared/mixins/is_ee.js
new file mode 100644
index 00000000000..8e00d93ef18
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/mixins/is_ee.js
@@ -0,0 +1,10 @@
+import Vue from 'vue';
+import { isEE } from '~/lib/utils/common_utils';
+
+Vue.mixin({
+ computed: {
+ isEE() {
+ return isEE();
+ },
+ },
+});
diff --git a/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js b/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js
new file mode 100644
index 00000000000..455ae832234
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/mixins/related_issuable_mixin.js
@@ -0,0 +1,155 @@
+import _ from 'underscore';
+import { formatDate } from '~/lib/utils/datetime_utility';
+import tooltip from '~/vue_shared/directives/tooltip';
+import icon from '~/vue_shared/components/icon.vue';
+import timeagoMixin from '~/vue_shared/mixins/timeago';
+
+const mixins = {
+ data() {
+ return {
+ removeDisabled: false,
+ };
+ },
+ props: {
+ idKey: {
+ type: Number,
+ required: true,
+ },
+ displayReference: {
+ type: String,
+ required: true,
+ },
+ pathIdSeparator: {
+ type: String,
+ required: true,
+ },
+ eventNamespace: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ confidential: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ title: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ path: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ state: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ createdAt: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ closedAt: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ milestone: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
+ dueDate: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ assignees: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ weight: {
+ type: Number,
+ required: false,
+ default: 0,
+ },
+ canRemove: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ components: {
+ icon,
+ },
+ directives: {
+ tooltip,
+ },
+ mixins: [timeagoMixin],
+ computed: {
+ hasState() {
+ return this.state && this.state.length > 0;
+ },
+ isOpen() {
+ return this.state === 'opened';
+ },
+ isClosed() {
+ return this.state === 'closed';
+ },
+ hasTitle() {
+ return this.title.length > 0;
+ },
+ hasMilestone() {
+ return !_.isEmpty(this.milestone);
+ },
+ iconName() {
+ return this.isOpen ? 'issue-open-m' : 'issue-close';
+ },
+ iconClass() {
+ return this.isOpen ? 'issue-token-state-icon-open' : 'issue-token-state-icon-closed';
+ },
+ computedLinkElementType() {
+ return this.path.length > 0 ? 'a' : 'span';
+ },
+ computedPath() {
+ return this.path.length ? this.path : null;
+ },
+ itemPath() {
+ return this.displayReference.split(this.pathIdSeparator)[0];
+ },
+ itemId() {
+ return this.displayReference.split(this.pathIdSeparator).pop();
+ },
+ createdAtInWords() {
+ return this.createdAt ? this.timeFormated(this.createdAt) : '';
+ },
+ createdAtTimestamp() {
+ return this.createdAt ? formatDate(new Date(this.createdAt)) : '';
+ },
+ closedAtInWords() {
+ return this.closedAt ? this.timeFormated(this.closedAt) : '';
+ },
+ closedAtTimestamp() {
+ return this.closedAt ? formatDate(new Date(this.closedAt)) : '';
+ },
+ },
+ methods: {
+ onRemoveRequest() {
+ let namespacePrefix = '';
+ if (this.eventNamespace && this.eventNamespace.length > 0) {
+ namespacePrefix = `${this.eventNamespace}`;
+ }
+
+ this.$emit(`${namespacePrefix}RemoveRequest`, this.idKey);
+
+ this.removeDisabled = true;
+ },
+ },
+};
+
+export default mixins;
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index bdf20866197..86189143525 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -2,8 +2,6 @@
* This is a manifest file that'll automatically include all the stylesheets available in this directory
* and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
* the top of the compiled file, but it's generally better to create a new file per style scope.
- *= require jquery.atwho
- *= require select2
*= require_self
*= require cropper.css
*/
@@ -16,8 +14,10 @@
* directory.
*/
+@import "../../../node_modules/at.js/dist/css/jquery.atwho";
@import "../../../node_modules/pikaday/scss/pikaday";
@import "../../../node_modules/dropzone/dist/basic";
+@import "../../../node_modules/select2/select2";
/*
* GitLab UI framework
@@ -40,16 +40,6 @@
@import "components/**/*";
/*
- * Code highlight
- */
-@import "highlight/dark";
-@import "highlight/monokai";
-@import "highlight/solarized_dark";
-@import "highlight/solarized_light";
-@import "highlight/white";
-@import "highlight/none";
-
-/*
* Styles for JS behaviors.
*/
@import "behaviors";
diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss
index c8357f7751c..93377b8dd91 100644
--- a/app/assets/stylesheets/bootstrap_migration.scss
+++ b/app/assets/stylesheets/bootstrap_migration.scss
@@ -343,16 +343,6 @@ input[type=color].form-control {
}
}
-// 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;
-}
-
/*
Bootstrap 4.1.2 introduced a new default vertical alignment which breaks our icons,
so we need to reset the vertical alignment to the default value. See:
diff --git a/app/assets/stylesheets/components/dashboard_skeleton.scss b/app/assets/stylesheets/components/dashboard_skeleton.scss
new file mode 100644
index 00000000000..42ede599bc6
--- /dev/null
+++ b/app/assets/stylesheets/components/dashboard_skeleton.scss
@@ -0,0 +1,80 @@
+.dashboard-cards {
+ margin-right: -$gl-padding-8;
+ margin-left: -$gl-padding-8;
+}
+
+.dashboard-card {
+ &-header {
+ &-warning {
+ background-color: $orange-100;
+ }
+
+ &-failed {
+ background-color: $red-100;
+ }
+ }
+
+ &-body {
+ height: 120px;
+
+ &-warning {
+ background-color: $orange-50;
+ }
+
+ &-failed {
+ background-color: $red-50;
+ }
+ }
+
+ &-time-ago {
+ &-icon {
+ color: $gray-500;
+ }
+ }
+
+ &-footer {
+ border-radius: $gl-padding;
+ height: $gl-padding-32;
+
+ &-failed {
+ background-color: $red-100;
+ }
+
+ &-arrow {
+ color: $gray-300;
+ }
+
+ &-downstream {
+ margin-right: -$gl-padding-8;
+ }
+
+ &-extra {
+ background-color: $gray-400;
+ font-size: 10px;
+ line-height: $gl-line-height;
+ width: $gl-padding;
+ }
+ }
+
+ &-skeleton-info {
+ border-radius: $gl-padding;
+ height: $gl-padding;
+ overflow: hidden;
+
+ &::after {
+ content: ' ';
+ display: block;
+ animation: blockTextShine 1s linear infinite forwards;
+ background-repeat: no-repeat;
+ background-size: cover;
+ background-image: linear-gradient(to right,
+ $gray-100 0%,
+ $gray-50 20%,
+ $gray-100 40%,
+ $gray-100 100%);
+ border-radius: $gl-padding;
+ height: $gl-padding;
+ margin-top: -$gl-padding-8;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/components/popover.scss b/app/assets/stylesheets/components/popover.scss
index 2f4d30fe923..7d46b262a69 100644
--- a/app/assets/stylesheets/components/popover.scss
+++ b/app/assets/stylesheets/components/popover.scss
@@ -7,3 +7,10 @@
line-height: $gl-line-height;
}
}
+
+.mr-popover {
+ .text-secondary {
+ font-size: 12px;
+ line-height: 1.33;
+ }
+}
diff --git a/app/assets/stylesheets/components/related_items_list.scss b/app/assets/stylesheets/components/related_items_list.scss
index 048a5c0300c..edf7b26ebaa 100644
--- a/app/assets/stylesheets/components/related_items_list.scss
+++ b/app/assets/stylesheets/components/related_items_list.scss
@@ -195,8 +195,7 @@ $item-weight-max-width: 48px;
}
.mr-status-wrapper,
-.mr-ci-status
- {
+.mr-ci-status {
line-height: 0;
}
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 62d471bc30c..ab9047c54e4 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -60,8 +60,11 @@
@import 'framework/memory_graph';
@import 'framework/responsive_tables';
@import 'framework/stacked_progress_bar';
+@import 'framework/sortable';
@import 'framework/ci_variable_list';
@import 'framework/feature_highlight';
@import 'framework/terms';
@import 'framework/read_more';
@import 'framework/flex_grid';
+@import 'framework/system_messages';
+@import "framework/spinner";
diff --git a/app/assets/stylesheets/framework/animations.scss b/app/assets/stylesheets/framework/animations.scss
index 70d50c74ca9..257d788873c 100644
--- a/app/assets/stylesheets/framework/animations.scss
+++ b/app/assets/stylesheets/framework/animations.scss
@@ -27,7 +27,7 @@
&.flipOutY,
&.bounceIn,
&.bounceOut {
- @include webkit-prefix(animation-duration, .75s);
+ @include webkit-prefix(animation-duration, 0.75s);
}
&.short {
@@ -73,22 +73,10 @@
@mixin disable-all-animation {
/*CSS transitions*/
- -o-transition-property: none !important;
- -moz-transition-property: none !important;
- -ms-transition-property: none !important;
- -webkit-transition-property: none !important;
transition-property: none !important;
/*CSS transforms*/
- -o-transform: none !important;
- -moz-transform: none !important;
- -ms-transform: none !important;
- -webkit-transform: none !important;
transform: none !important;
/*CSS animations*/
- -webkit-animation: none !important;
- -moz-animation: none !important;
- -o-animation: none !important;
- -ms-animation: none !important;
animation: none !important;
}
@@ -202,7 +190,7 @@ a {
}
}
- [class^="skeleton-line-"] {
+ [class^='skeleton-line-'] {
position: relative;
background-color: $gray-100;
height: 10px;
@@ -218,13 +206,11 @@ a {
animation: blockTextShine 1s linear infinite forwards;
background-repeat: no-repeat;
background-size: cover;
- background-image: linear-gradient(
- to right,
- $gray-100 0%,
- $gray-50 20%,
- $gray-100 40%,
- $gray-100 100%
- );
+ background-image: linear-gradient(to right,
+ $gray-100 0%,
+ $gray-50 20%,
+ $gray-100 40%,
+ $gray-100 100%);
height: 10px;
}
}
diff --git a/app/assets/stylesheets/framework/asciidoctor.scss b/app/assets/stylesheets/framework/asciidoctor.scss
index 62493c32833..1586265d40e 100644
--- a/app/assets/stylesheets/framework/asciidoctor.scss
+++ b/app/assets/stylesheets/framework/asciidoctor.scss
@@ -1,7 +1,7 @@
.admonitionblock td.icon {
width: 1%;
- [class^="fa icon-"] {
+ [class^='fa icon-'] {
@extend .fa-2x;
}
diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
index e132aa4c216..37a729c7a63 100644
--- a/app/assets/stylesheets/framework/avatar.scss
+++ b/app/assets/stylesheets/framework/avatar.scss
@@ -45,7 +45,6 @@
display: inline-block;
margin-left: 2px;
flex-shrink: 0;
- -webkit-flex-shrink: 0;
&.s16 { margin-right: 4px; }
&.s24 { margin-right: 4px; }
@@ -62,6 +61,10 @@
border: 0;
}
+ &.avatar-placeholder {
+ border: 0;
+ }
+
&:not([href]):hover {
border-color: darken($gray-normal, 10%);
}
@@ -74,20 +77,48 @@
background-color: $gray-darker;
// 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; }
- &.s64 { font-size: 32px; line-height: 64px; }
- &.s70 { font-size: 34px; line-height: 70px; }
- &.s90 { font-size: 36px; line-height: 88px; }
- &.s100 { font-size: 36px; line-height: 98px; }
- &.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; }
+ &.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; }
+
+ &.s64 { font-size: 32px;
+ line-height: 64px; }
+
+ &.s70 { font-size: 34px;
+ line-height: 70px; }
+
+ &.s90 { font-size: 36px;
+ line-height: 88px; }
+
+ &.s100 { font-size: 36px;
+ line-height: 98px; }
+
+ &.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; }
@@ -120,8 +151,35 @@
align-self: center;
}
- &.s40 { min-width: 40px; min-height: 40px; }
- &.s64 { min-width: 64px; min-height: 64px; }
+ &.s40 { min-width: 40px;
+ min-height: 40px; }
+
+ &.s64 { min-width: 64px;
+ min-height: 64px; }
+}
+
+.rect-avatar {
+ border-radius: $border-radius-small;
+ &.s16 { border-radius: $border-radius-small; }
+ &.s18 { border-radius: $border-radius-small; }
+ &.s19 { border-radius: $border-radius-small; }
+ &.s20 { border-radius: $border-radius-small; }
+ &.s24 { border-radius: $border-radius-default; }
+ &.s26 { border-radius: $border-radius-default; }
+ &.s32 { border-radius: $border-radius-default; }
+ &.s36 { border-radius: $border-radius-default; }
+ &.s40 { border-radius: $border-radius-default; }
+ &.s46 { border-radius: $border-radius-default; }
+ &.s48 { border-radius: $border-radius-large; }
+ &.s60 { border-radius: $border-radius-large; }
+ &.s64 { border-radius: $border-radius-large; }
+ &.s70 { border-radius: $border-radius-large; }
+ &.s90 { border-radius: $border-radius-large; }
+ &.s96 { border-radius: $border-radius-large; }
+ &.s100 { border-radius: $border-radius-large; }
+ &.s110 { border-radius: $border-radius-large; }
+ &.s140 { border-radius: $border-radius-large; }
+ &.s160 { border-radius: $border-radius-large; }
}
.avatar-counter {
diff --git a/app/assets/stylesheets/framework/awards.scss b/app/assets/stylesheets/framework/awards.scss
index 5cfd5bbd4f5..648e1944388 100644
--- a/app/assets/stylesheets/framework/awards.scss
+++ b/app/assets/stylesheets/framework/awards.scss
@@ -23,9 +23,9 @@
box-shadow: 0 6px 12px $award-emoji-menu-shadow;
pointer-events: none;
opacity: 0;
- transform: scale(.2);
+ transform: scale(0.2);
transform-origin: 0 -45px;
- transition: .3s cubic-bezier(.67, .06, .19, 1.44);
+ transition: 0.3s cubic-bezier(0.67, 0.06, 0.19, 1.44);
transition-property: transform, opacity;
&.is-rendered {
@@ -62,7 +62,7 @@
}
.emoji-search {
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC");
+ background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABwAAAAcCAYAAAByDd+UAAAFu0lEQVRIia1WTahkVxH+quqce7vf6zdvJpHoIlkYJ2SiJiIokmQjgoGgIAaEIYuYXWICgojiwkmC4taFwhjcyIDusogEIwwiSSCKPwsdwzAg0SjJ9Izzk5n3+nXfe8+pqizOvd395scfsJqi6dPnnDr11Vc/NJ1OwUTosqJLCmYCHCAC2mSHs+ojZv6AO46Y+20AhIneJsafhPhXVZSXDk7qi+aOLhtQNuBmQtcarAKjTXpn2+l3u2yPunvZSABRucjcAV/eMZuM48/Go/g1d19kc4wq+e8MZjWkbI/P5t2P3RFFbv7SQdyBlBUx8N8OTuqjMcof+N94yMPrY2DMm/ytnb32J0QrY+6AqsHM4Q64O9SKDmerKDD3Oy/tNL9vk342CC8RuU6n0ymCMHb22scu7zQngtASOjUHE1BX4UUAv4b7Ow6qiXCXuz/UdvogAAweDY943/b4cAz0ZlYHXeMsnT07RVb7wMUr8ykI4H5HVkMd5Rcb4/jNURVOL5qErAaAUUdCCIJ5kx5q2nw8m39ImEAAsjpE6PStB0YfMcd1wqqG3Xn7A3PfZyyKnNjaqD4fmE/fCNKshirIyY1xvI+Av6g5QIAIIWX7cJPssboSiBBEeKmsZne0Sb8kzAUWNYyq8NvbDo0fZ6beqxuLmqOOMr/lwOh+YXpXtbjERGja9JyZ9+HxpXKb9Gj5oywRESbj+Cj1ENG1QViTGBl1FbC1We1tbVRfHWIoQkhqH9xbpE92XUbb6VJZ1R4crjRz1JWcDMJvLdoMcyAEhjuwHo8Bfndg3mbszhOY+adVlMtD3po51OwzIQiEaams7oeJhxRw1FFOVpFRRUYIhMBAFRnjOsC8IFHHUA4TQQhgAqpAiIFfGbxkIqj54ayGbL7UoOqHCniAEKHLNr26l+D9wQJzeUwMAnfHvEnLECzZRwRV++d60ptjW9VLZeolEJG6GwCCE0CFVNB+Ay0NEqoQYG4YYFu7B8IEVRt3uRzy/osIoLV9QZimWXGHUMFdmI6M64DUF2Je88R9VZqCSP+QlcF5k+4tCzSsXaqjINuK6UyE0+s/mk6/qFq8oAIL9pqMLhkGsNrOyoOIlszust3aJv0U9+kFdwjTGwWl1YdF+KWlQSZ0Se/psj8yGVdg5tJyfH96EBWmLtoEMwMzMFt031NzGWLLzKhC+KV7H5ZeeaMOPxemma2x68puc0LN3+/u6LJiePS6MKHvn4wu6cPzJj0hsioeMfDrEvjv5r6W9gBvjKJujuKzQ0URIZj75NylvT+mbHfXQa4rwAMaVRTMm/SFyzvNy0yF6+4AM+1ubcSnqkAIUjQKl1RKSbE5jt+vovx1MBqF0WW7/d1Z80ab9BtmuJ3Xk5cJKds9TZt/uLPXvtiTrQ+dIwqfAejUvM1os6FNikXKUHfQ+ekUsXT5u85enJ0CaBSkkGEo1syUQ+DfMdE/4GA1uzupf9zdbzhOmLsF4efHVXjaHHAzmDtGdQRd/Nc5wAEJjNki3XfhyvwVNz80xANrht3LsENY9cBBdN1L9GUyyvFRFZ42t75sBvCQRykbRlU4tT2pPxoCvzx09d4GmPs200M6wKdWSDGK8mppYSWdhAlt0qeaLv+IadXU9/Evq4FAZ8ej+LmtcTxaRX4NWI0Uag5Vg1p5MYg8BnlhXIdPHDow+vTWZvVMVttXDLqkTzZdPj6Qii6cP1cSvIdl3iQkNYyi9HH0I22y+93tY3DcQkTZgQtM+POoCr8x97eylkmtrgKuztrvXJ21x/aNKuqIkZ/fntRfCdcTfhUTAIhRzoDojJD0aSNLLwMzmpT7+JaLtyf1MwDo6qz9djFaUq3t9MlFmy/c1OCSceY9fMsVaL9mvH9ocXdkdWxv1scAePG0THAhMOaLdOw/Gvxfxb1w4eCapyIENUcV5M3/u8FitAxZ25P6GAHT3UX39Srw+QOb1ZffA98Dl2Wy1BYkAAAAAElFTkSuQmCC');
background-repeat: no-repeat;
background-position: right 5px center;
background-size: 16px;
@@ -90,7 +90,7 @@
background: none;
border: 0;
border-radius: $border-radius-base;
- transition: transform .15s cubic-bezier(.3, 0, .2, 2);
+ transition: transform 0.15s cubic-bezier(0.3, 0, 0.2, 2);
&:hover {
background-color: transparent;
diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss
index 91dbb2a6365..cbd390e7145 100644
--- a/app/assets/stylesheets/framework/blank.scss
+++ b/app/assets/stylesheets/framework/blank.scss
@@ -69,6 +69,7 @@
@include media-breakpoint-up(sm) {
display: flex;
+ height: 100%;
align-items: center;
padding: 50px 30px;
}
@@ -99,3 +100,30 @@
}
}
}
+
+@include media-breakpoint-up(lg) {
+ .column-large {
+ flex: 2;
+ }
+
+ .column-small {
+ flex: 1;
+ margin-bottom: 15px;
+
+ .blank-state {
+ max-width: 400px;
+ flex-wrap: wrap;
+ margin-left: 15px;
+ }
+
+ .blank-state-icon {
+ margin-bottom: 30px;
+ }
+ }
+}
+
+@include media-breakpoint-down(xs) {
+ .blank-state-icon svg {
+ width: 315px;
+ }
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 43b7c26b272..e6c55252b24 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -86,11 +86,8 @@
}
.block-controls {
- display: -webkit-flex;
display: flex;
- -webkit-justify-content: flex-end;
justify-content: flex-end;
- -webkit-flex: 1;
flex: 1;
.control {
@@ -153,7 +150,7 @@
display: inline-block;
margin-left: 5px;
font-size: 18px;
- color: color("gray");
+ color: color('gray');
}
p {
@@ -228,7 +225,6 @@
}
.group-info {
-
h1 {
display: inline;
font-weight: $gl-font-weight-normal;
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index cb2c8879c5f..695ce014659 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -395,8 +395,6 @@
cursor: default;
&:active {
- -moz-box-shadow: inset 0 0 0 $white-light;
- -webkit-box-shadow: inset 0 0 0 $white-light;
box-shadow: inset 0 0 0 $white-light;
}
}
@@ -445,7 +443,8 @@
border-color: transparent;
}
- &.btn-secondary-hover-link {
+ &.btn-secondary-hover-link,
+ &.btn-default-hover-link {
color: $gl-text-color-secondary;
&:hover,
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index c1f2f5f8c6a..8fc08422d76 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -52,7 +52,16 @@
word-break: break-all;
}
-.hint { font-style: italic; color: $gl-gray-400; }
+.text-underline,
+.text-underline:hover {
+ text-decoration: underline;
+}
+
+.hint {
+ font-style: italic;
+ color: $gl-gray-400;
+}
+
.light { color: $gl-text-color; }
.slead {
@@ -111,7 +120,7 @@ hr {
text-overflow: ellipsis;
white-space: nowrap;
- > div,
+ > div:not(.block),
.str-truncated {
display: inline;
}
@@ -153,13 +162,14 @@ p.time {
text-shadow: none;
}
-.thin_area {
+.thin-area {
height: 150px;
}
// Fix issue with notes & lists creating a bunch of bottom borders.
li.note {
img { max-width: 100%; }
+
.note-title {
li {
border-bottom: 0 !important;
@@ -330,7 +340,7 @@ img.emoji {
.disabled-content {
pointer-events: none;
- opacity: .5;
+ opacity: 0.5;
}
.break-word {
@@ -366,18 +376,23 @@ img.emoji {
.prepend-top-default { margin-top: $gl-padding !important; }
.prepend-top-16 { margin-top: 16px; }
.prepend-top-20 { margin-top: 20px; }
+.prepend-top-32 { margin-top: 32px; }
.prepend-left-4 { margin-left: 4px; }
.prepend-left-5 { margin-left: 5px; }
.prepend-left-8 { margin-left: 8px; }
.prepend-left-10 { margin-left: 10px; }
+.prepend-left-15 { margin-left: 15px; }
.prepend-left-default { margin-left: $gl-padding; }
.prepend-left-20 { margin-left: 20px; }
+.prepend-left-32 { margin-left: 32px; }
.append-right-4 { margin-right: 4px; }
.append-right-5 { margin-right: 5px; }
.append-right-8 { margin-right: 8px; }
.append-right-10 { margin-right: 10px; }
+.append-right-15 { margin-right: 15px; }
.append-right-default { margin-right: $gl-padding; }
.append-right-20 { margin-right: 20px; }
+.prepend-right-32 { margin-right: 32px; }
.append-bottom-0 { margin-bottom: 0; }
.append-bottom-4 { margin-bottom: $gl-padding-4; }
.append-bottom-5 { margin-bottom: 5px; }
@@ -386,15 +401,20 @@ img.emoji {
.append-bottom-15 { margin-bottom: 15px; }
.append-bottom-20 { margin-bottom: 20px; }
.append-bottom-default { margin-bottom: $gl-padding; }
+.prepend-bottom-32 { margin-bottom: 32px; }
.inline { display: inline-block; }
.center { text-align: center; }
+.block { display: block; }
+.flex { display: flex; }
.vertical-align-middle { vertical-align: middle; }
.vertical-align-sub { vertical-align: sub; }
.flex-align-self-center { align-self: center; }
.flex-grow { flex-grow: 1; }
.flex-no-shrink { flex-shrink: 0; }
.ws-initial { white-space: initial; }
+.ws-normal { white-space: normal; }
.overflow-auto { overflow: auto; }
+
.d-flex-center {
display: flex;
align-items: center;
@@ -458,3 +478,7 @@ img.emoji {
background-color: $gray-600;
}
}
+
+.cursor-pointer {
+ cursor: pointer;
+}
diff --git a/app/assets/stylesheets/framework/contextual_sidebar.scss b/app/assets/stylesheets/framework/contextual_sidebar.scss
index 8b6a7017c47..3238b01c6c0 100644
--- a/app/assets/stylesheets/framework/contextual_sidebar.scss
+++ b/app/assets/stylesheets/framework/contextual_sidebar.scss
@@ -5,7 +5,7 @@
padding-left: $contextual-sidebar-collapsed-width;
}
- @include media-breakpoint-up(lg) {
+ @include media-breakpoint-up(xl) {
padding-left: $contextual-sidebar-width;
}
@@ -15,7 +15,7 @@
}
.page-with-icon-sidebar {
- @include media-breakpoint-up(sm) {
+ @include media-breakpoint-up(md) {
padding-left: $contextual-sidebar-collapsed-width;
}
}
@@ -71,6 +71,44 @@
}
}
+@mixin collapse-contextual-sidebar-content {
+ .context-header {
+ height: 60px;
+ width: $contextual-sidebar-collapsed-width;
+
+ a {
+ padding: 10px 4px;
+ }
+ }
+
+ .sidebar-top-level-items > li {
+ .sidebar-sub-level-items {
+ &:not(.flyout-list) {
+ display: none;
+ }
+ }
+ }
+
+ .nav-icon-container {
+ margin-right: 0;
+ }
+
+ .toggle-sidebar-button {
+ padding: 16px;
+ width: $contextual-sidebar-collapsed-width - 1px;
+
+ .collapse-text,
+ .icon-angle-double-left {
+ display: none;
+ }
+
+ .icon-angle-double-right {
+ display: block;
+ margin: 0;
+ }
+ }
+}
+
.nav-sidebar {
transition: width $sidebar-transition-duration, left $sidebar-transition-duration;
position: fixed;
@@ -89,7 +127,7 @@
}
}
- &.sidebar-collapsed-desktop {
+ @mixin collapse-contextual-sidebar {
width: $contextual-sidebar-collapsed-width;
.nav-sidebar-inner-scroll {
@@ -115,6 +153,10 @@
}
}
+ &.sidebar-collapsed-desktop {
+ @include collapse-contextual-sidebar;
+ }
+
&.sidebar-expanded-mobile {
left: 0;
}
@@ -150,7 +192,7 @@
}
}
- @include media-breakpoint-down(xs) {
+ @include media-breakpoint-down(sm) {
left: (-$contextual-sidebar-width);
}
@@ -167,16 +209,19 @@
height: 16px;
width: 16px;
}
+
+ @media (min-width: map-get($grid-breakpoints, md)) and (max-width: map-get($grid-breakpoints, xl) - 1px) {
+ &:not(.sidebar-expanded-mobile) {
+ @include collapse-contextual-sidebar;
+ @include collapse-contextual-sidebar-content;
+ }
+ }
}
.nav-sidebar-inner-scroll {
height: 100%;
width: 100%;
overflow: auto;
-
- @include media-breakpoint-up(sm) {
- overflow: hidden;
- }
}
.with-performance-bar .nav-sidebar {
@@ -346,53 +391,13 @@
}
}
-.toggle-sidebar-button {
- @include media-breakpoint-down(xs) {
- display: none;
- }
-}
-
.collapse-text {
white-space: nowrap;
overflow: hidden;
}
.sidebar-collapsed-desktop {
- .context-header {
- height: 60px;
- width: $contextual-sidebar-collapsed-width;
-
- a {
- padding: 10px 4px;
- }
- }
-
- .sidebar-top-level-items > li {
- .sidebar-sub-level-items {
- &:not(.flyout-list) {
- display: none;
- }
- }
- }
-
- .nav-icon-container {
- margin-right: 0;
- }
-
- .toggle-sidebar-button {
- padding: 16px;
- width: $contextual-sidebar-collapsed-width - 1px;
-
- .collapse-text,
- .icon-angle-double-left {
- display: none;
- }
-
- .icon-angle-double-right {
- display: block;
- margin: 0;
- }
- }
+ @include collapse-contextual-sidebar-content;
}
.fly-out-top-item {
@@ -428,16 +433,14 @@
color: $gl-text-color-secondary;
}
- @include media-breakpoint-down(xs) {
+ @include media-breakpoint-down(sm) {
display: flex;
align-items: center;
i {
font-size: 18px;
}
- }
- @include media-breakpoint-down(xs) {
+ .breadcrumbs-links {
padding-left: $gl-padding;
border-left: 1px solid $gl-text-color-quaternary;
@@ -445,21 +448,25 @@
}
}
-@include media-breakpoint-down(xs) {
+@include media-breakpoint-down(sm) {
.close-nav-button {
display: flex;
}
-}
-.mobile-overlay {
- display: none;
+ .toggle-sidebar-button {
+ display: none;
+ }
- &.mobile-nav-open {
- display: block;
- position: fixed;
- background-color: $black-transparent;
- height: 100%;
- width: 100%;
- z-index: 300;
+ .mobile-overlay {
+ display: none;
+
+ &.mobile-nav-open {
+ display: block;
+ position: fixed;
+ background-color: $black-transparent;
+ height: 100%;
+ width: 100%;
+ z-index: 300;
+ }
}
}
diff --git a/app/assets/stylesheets/framework/emojis.scss b/app/assets/stylesheets/framework/emojis.scss
index be85e03430e..13c5541da92 100644
--- a/app/assets/stylesheets/framework/emojis.scss
+++ b/app/assets/stylesheets/framework/emojis.scss
@@ -2,7 +2,7 @@ gl-emoji {
font-style: normal;
display: inline-flex;
vertical-align: middle;
- font-family: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
+ font-family: 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
font-size: 1.4em;
line-height: 1em;
}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 6108eaa1ad0..8d38310e8e6 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -25,10 +25,6 @@
}
}
- table {
- @extend .table;
- }
-
.file-title {
position: relative;
background-color: $gray-light;
@@ -123,7 +119,7 @@
}
}
- &.wiki {
+ &.md {
padding: $gl-padding;
@include media-breakpoint-up(md) {
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index f48b3ddc912..5bcfd5d1322 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -50,19 +50,15 @@
}
.filtered-search-wrapper {
- display: -webkit-flex;
display: flex;
@include media-breakpoint-down(xs) {
- -webkit-flex-direction: column;
flex-direction: column;
}
.tokens-container {
- display: -webkit-flex;
display: flex;
flex: 1;
- -webkit-flex: 1;
padding-left: 12px;
position: relative;
margin-bottom: 0;
@@ -82,21 +78,18 @@
.input-token:only-child,
.input-token:last-child {
flex: 1;
- -webkit-flex: 1;
max-width: inherit;
}
}
.filtered-search-token,
.filtered-search-term {
- display: -webkit-flex;
display: flex;
flex-shrink: 0;
margin-top: 4px;
margin-bottom: 4px;
.selectable {
- display: -webkit-flex;
display: flex;
}
@@ -115,6 +108,8 @@
}
.value-container {
+ display: flex;
+ align-items: center;
background-color: $white-normal;
color: $filter-value-text-color;
border-radius: 0 2px 2px 0;
@@ -128,7 +123,7 @@
.remove-token {
display: inline-block;
- padding-left: 4px;
+ padding-left: 8px;
padding-right: 0;
.fa-close {
@@ -176,7 +171,6 @@
}
.scroll-container {
- display: -webkit-flex;
display: flex;
overflow-x: auto;
white-space: nowrap;
@@ -186,7 +180,6 @@
.filtered-search-box {
position: relative;
flex: 1;
- display: -webkit-flex;
display: flex;
width: 100%;
min-width: 0;
@@ -194,7 +187,6 @@
background-color: $white-light;
@include media-breakpoint-down(xs) {
- -webkit-flex: 1 1 auto;
flex: 1 1 auto;
margin-bottom: 10px;
}
@@ -349,7 +341,6 @@
}
.filter-dropdown-container {
- display: -webkit-flex;
display: flex;
.dropdown-toggle {
@@ -423,3 +414,10 @@
padding: 8px 16px;
text-align: center;
}
+
+.search-token-target-branch {
+ .value {
+ font-family: $monospace-font;
+ font-size: 13px;
+ }
+}
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index cbf9ee24ec5..3b1d1d67509 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -156,8 +156,6 @@ label {
.select-control {
padding-left: 10px;
padding-right: 10px;
- -webkit-appearance: none;
- -moz-appearance: none;
appearance: none;
&::-ms-expand {
@@ -178,7 +176,8 @@ label {
font-weight: $gl-font-weight-normal;
}
-.form-control::-webkit-input-placeholder {
+
+.form-control::placeholder {
color: $gl-text-color-tertiary;
}
diff --git a/app/assets/stylesheets/framework/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
index 50d4298d418..6943bfbc3d0 100644
--- a/app/assets/stylesheets/framework/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -32,7 +32,7 @@
height: $chip-size;
background: $white-light;
background-image: linear-gradient(135deg, $gray-dark 25%, transparent 0%, transparent 75%, $gray-dark 0%),
- linear-gradient(135deg, $gray-dark 25%, transparent 0%, transparent 75%, $gray-dark 0%);
+ linear-gradient(135deg, $gray-dark 25%, transparent 0%, transparent 75%, $gray-dark 0%);
background-size: $bg-size $bg-size;
background-position: 0 0, $bg-pos $bg-pos;
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 23dcc1817b1..1e025b3a67d 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -39,7 +39,6 @@
.header-content {
width: 100%;
- display: -webkit-flex;
display: flex;
justify-content: space-between;
position: relative;
@@ -47,11 +46,8 @@
padding-left: 0;
.title-container {
- display: -webkit-flex;
display: flex;
- -webkit-align-items: stretch;
align-items: stretch;
- -webkit-flex: 1 1 auto;
flex: 1 1 auto;
padding-top: 0;
overflow: visible;
@@ -60,7 +56,6 @@
.title {
padding-right: 0;
color: currentColor;
- display: -webkit-flex;
display: flex;
position: relative;
margin: 0;
@@ -85,7 +80,6 @@
}
a {
- display: -webkit-flex;
display: flex;
align-items: center;
padding: 2px 8px;
@@ -173,7 +167,6 @@
.navbar-nav {
@include media-breakpoint-down(xs) {
- display: -webkit-flex;
display: flex;
padding-right: 10px;
flex-direction: row;
@@ -258,7 +251,6 @@
> li {
> a,
> button {
- display: -webkit-flex;
display: flex;
align-items: center;
justify-content: center;
@@ -294,7 +286,6 @@
}
.navbar-sub-nav {
- display: -webkit-flex;
display: flex;
margin: 0 0 0 6px;
@@ -313,7 +304,9 @@
}
}
-.caret-down {
+.caret-down,
+.btn .caret-down {
+ top: 0;
height: 11px;
width: 11px;
margin-left: 4px;
@@ -326,14 +319,12 @@
}
.breadcrumbs {
- display: -webkit-flex;
display: flex;
min-height: $breadcrumb-min-height;
color: $gl-text-color;
}
.breadcrumbs-container {
- display: -webkit-flex;
display: flex;
width: 100%;
position: relative;
@@ -344,7 +335,6 @@
}
.breadcrumbs-links {
- -webkit-flex: 1;
flex: 1;
min-width: 0;
align-self: center;
@@ -379,7 +369,6 @@
}
.breadcrumbs-list {
- display: -webkit-flex;
display: flex;
margin-bottom: 0;
line-height: 16px;
@@ -430,7 +419,6 @@
}
.breadcrumbs-extra {
- display: -webkit-flex;
display: flex;
flex: 0 0 auto;
margin-left: auto;
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index 49b9b7014ae..3ab61cc5c47 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -31,6 +31,7 @@
}
}
+.ci-status-icon-preparing,
.ci-status-icon-running {
svg {
fill: $blue-400;
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index d9d4a210f5f..298610a0631 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -15,7 +15,7 @@
word-wrap: break-word;
&::after {
- content: " ";
+ content: ' ';
display: table;
clear: both;
}
@@ -156,6 +156,12 @@ ul.content-list {
margin-top: 3px;
margin-bottom: 4px;
+ &.btn-ldap-override {
+ @include media-breakpoint-up(sm) {
+ margin-bottom: 0;
+ }
+ }
+
&.has-tooltip,
&:last-child {
margin-right: 0;
@@ -167,7 +173,7 @@ ul.content-list {
}
.no-comments {
- opacity: .5;
+ opacity: 0.5;
}
}
@@ -196,8 +202,6 @@ ul.content-list {
// Content list using flexbox
.flex-list {
.flex-row {
- display: -webkit-flex;
- display: -ms-flexbox;
display: flex;
align-items: center;
white-space: nowrap;
diff --git a/app/assets/stylesheets/framework/logo.scss b/app/assets/stylesheets/framework/logo.scss
index 429cfbe7235..c5feefb8c54 100644
--- a/app/assets/stylesheets/framework/logo.scss
+++ b/app/assets/stylesheets/framework/logo.scss
@@ -9,7 +9,6 @@
}
.tanuki-logo {
-
.tanuki-left-ear,
.tanuki-right-ear,
.tanuki-nose {
@@ -34,7 +33,9 @@
.tanuki-left-cheek {
@include include-keyframes(animate-tanuki-left-cheek) {
- 0%, 10%, 100% {
+ 0%,
+ 10%,
+ 100% {
fill: lighten($tanuki-yellow, 25%);
}
@@ -46,11 +47,13 @@
.tanuki-left-eye {
@include include-keyframes(animate-tanuki-left-eye) {
- 10%, 80% {
+ 10%,
+ 80% {
fill: $tanuki-orange;
}
- 20%, 90% {
+ 20%,
+ 90% {
fill: lighten($tanuki-orange, 25%);
}
}
@@ -58,11 +61,13 @@
.tanuki-left-ear {
@include include-keyframes(animate-tanuki-left-ear) {
- 10%, 80% {
+ 10%,
+ 80% {
fill: $tanuki-red;
}
- 20%, 90% {
+ 20%,
+ 90% {
fill: lighten($tanuki-red, 25%);
}
}
@@ -70,11 +75,13 @@
.tanuki-nose {
@include include-keyframes(animate-tanuki-nose) {
- 20%, 70% {
+ 20%,
+ 70% {
fill: $tanuki-red;
}
- 30%, 80% {
+ 30%,
+ 80% {
fill: lighten($tanuki-red, 25%);
}
}
@@ -82,11 +89,13 @@
.tanuki-right-eye {
@include include-keyframes(animate-tanuki-right-eye) {
- 30%, 60% {
+ 30%,
+ 60% {
fill: $tanuki-orange;
}
- 40%, 70% {
+ 40%,
+ 70% {
fill: lighten($tanuki-orange, 25%);
}
}
@@ -94,11 +103,13 @@
.tanuki-right-ear {
@include include-keyframes(animate-tanuki-right-ear) {
- 30%, 60% {
+ 30%,
+ 60% {
fill: $tanuki-red;
}
- 40%, 70% {
+ 40%,
+ 70% {
fill: lighten($tanuki-red, 25%);
}
}
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 961de8402ef..b2cc3e2428a 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -61,6 +61,10 @@
padding-top: 0;
line-height: 19px;
+ &.btn.btn-sm {
+ padding: 2px 5px;
+ }
+
&:focus {
margin-top: -10px;
padding-top: 10px;
@@ -131,38 +135,6 @@
width: 100%;
}
-.md:not(.use-csslab) {
- &.md-preview-holder {
- // Reset ul style types since we're nested inside a ul already
- @include bulleted-list;
- }
-
- // On diffs code should wrap nicely and not overflow
- code {
- white-space: pre-wrap;
- word-break: keep-all;
- }
-
- hr {
- // Darken 'whitesmoke' a bit to make it more visible in note bodies
- border-color: darken($gray-normal, 8%);
- margin: 10px 0;
- }
-
- // Border around images in issue and MR comments.
- img:not(.emoji) {
- border: 1px solid $white-normal;
- padding: 5px;
- margin: 5px 0;
- // Ensure that image does not exceed viewport
- max-height: calc(100vh - 100px);
- }
-
- table:not(.js-syntax-highlight) {
- @include markdown-table;
- }
-}
-
.toolbar-btn {
float: left;
padding: 0 7px;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index b9d0c0d4d96..18eb10c1f23 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -22,32 +22,6 @@
}
/*
- * Mixin for markdown tables
- */
-@mixin markdown-table {
- width: auto;
- display: inline-block;
- overflow-x: auto;
- border: 0;
- border-color: $gl-gray-100;
-
- @supports (width: fit-content) {
- display: block;
- width: fit-content;
- }
-
- tr {
- th {
- border-bottom: solid 2px $gl-gray-100;
- }
-
- td {
- border-color: $gl-gray-100;
- }
- }
-}
-
-/*
* Base mixin for lists in GitLab
*/
@mixin basic-list {
@@ -99,25 +73,6 @@
}
}
-@mixin bulleted-list {
- > ul {
- list-style-type: disc;
-
- ul {
- list-style-type: circle;
-
- ul {
- list-style-type: square;
- }
- }
- }
-}
-
-@mixin dark-diff-match-line {
- color: $dark-diff-match-bg;
- background: $dark-diff-match-color;
-}
-
@mixin webkit-prefix($property, $value) {
#{'-webkit-' + $property}: $value;
#{$property}: $value;
@@ -125,16 +80,13 @@
/* http://phrappe.com/css/conditional-css-for-webkit-based-browsers/ */
@mixin on-webkit-only {
+ /* stylelint-disable-next-line media-feature-name-no-vendor-prefix */
@media screen and (-webkit-min-device-pixel-ratio: 0) {
@content;
}
}
@mixin keyframes($animation-name) {
- @-webkit-keyframes #{$animation-name} {
- @content;
- }
-
@keyframes #{$animation-name} {
@content;
}
@@ -174,12 +126,10 @@
width: 43px;
height: 30px;
transition-duration: 0.3s;
- -webkit-transform: translateZ(0);
- background: linear-gradient(
- to $gradient-direction,
- $gradient-color 45%,
- rgba($gradient-color, 0.4)
- );
+ transform: translateZ(0);
+ background: linear-gradient(to $gradient-direction,
+ $gradient-color 45%,
+ rgba($gradient-color, 0.4));
&.scrolling {
visibility: visible;
@@ -274,8 +224,8 @@
background: $gray-light;
border: 1px solid $border-color;
color: $gl-text-color;
- position: sticky;
position: -webkit-sticky;
+ position: sticky;
top: $header-height;
padding: $grid-size;
diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss
index 3703b7568c8..53222a2bd4d 100644
--- a/app/assets/stylesheets/framework/modal.scss
+++ b/app/assets/stylesheets/framework/modal.scss
@@ -52,14 +52,16 @@
display: flex;
flex-direction: row;
- .btn + .btn {
+ .btn + .btn:not(.dropdown-toggle-split),
+ .btn + .btn-group {
margin-left: $grid-size;
}
@include media-breakpoint-down(xs) {
flex-direction: column;
- .btn + .btn {
+ .btn + .btn:not(.dropdown-toggle-split),
+ .btn + .btn-group {
margin-left: 0;
margin-top: $grid-size;
}
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index 3a117106cff..cd3d6f8297e 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -7,7 +7,6 @@
margin-bottom: $gl-vert-padding;
}
-
.card-header {
padding: $gl-vert-padding $gl-padding;
line-height: 36px;
diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
index 19640ab5986..31297b9d20c 100644
--- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss
+++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss
@@ -181,6 +181,33 @@
margin: 0;
width: 100%;
}
+
+ &.inline {
+ display: flex;
+ flex-flow: row wrap;
+ justify-content: space-between;
+
+ > .btn,
+ > .btn-container,
+ > .dropdown,
+ > input,
+ > form {
+ flex: 1 1 auto;
+ margin: 0 0 10px;
+ margin-left: $gl-padding-top;
+ width: auto;
+
+ &:first-child {
+ margin-left: 0;
+ float: none;
+ }
+ }
+
+ .btn-full {
+ flex: 1 1 100%;
+ margin-left: 0;
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index bcd601e198a..81ccea1e01f 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -32,7 +32,7 @@
}
&::after {
- content: "\f078";
+ content: '\f078';
position: absolute;
z-index: 1;
text-align: center;
@@ -264,6 +264,16 @@
}
}
+.project-result {
+ .project-name {
+ font-weight: $gl-font-weight-bold;
+ }
+
+ .project-path {
+ color: $gl-gray-400;
+ }
+}
+
.user-result {
min-height: 24px;
display: flex;
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index c4dbcf2ddc9..43d0e51e4c9 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -157,3 +157,55 @@
.sidebar-collapsed-icon .sidebar-collapsed-value {
font-size: 12px;
}
+
+.ancestor-tree {
+ .vertical-timeline {
+ position: relative;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+
+ &::before {
+ content: '';
+ border-left: 1px solid $gray-500;
+ position: absolute;
+ top: $gl-padding;
+ bottom: $gl-padding;
+ left: map-get($spacers, 2) - 1px;
+ }
+
+ &-row {
+ margin-top: map-get($spacers, 3);
+
+ &:nth-child(1) {
+ margin-top: 0;
+ }
+ }
+
+ &-icon {
+ /**
+ * 2px extra is to give a little more height than needed
+ * to hide timeline line before/after the element starts/ends
+ */
+ height: map-get($spacers, 4) + 2px;
+ z-index: 1;
+ position: relative;
+ top: -3px;
+ padding: $gl-padding-4 0;
+ background-color: $gray-light;
+
+ &.opened {
+ color: $green-500;
+ }
+
+ &.closed {
+ color: $blue-500;
+ }
+ }
+
+ &-content {
+ line-height: initial;
+ margin-left: $gl-padding-8;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/sortable.scss b/app/assets/stylesheets/framework/sortable.scss
new file mode 100644
index 00000000000..8c070200135
--- /dev/null
+++ b/app/assets/stylesheets/framework/sortable.scss
@@ -0,0 +1,92 @@
+.sortable-container {
+ background-color: $gray-light;
+
+ .flex-list {
+ padding: 5px;
+ margin-bottom: 0;
+ }
+}
+
+.sortable-row {
+ .flex-row {
+ display: flex;
+
+ &.issuable-info-container {
+ padding-right: 0;
+ }
+ }
+
+ .sortable-link {
+ color: $black;
+ }
+}
+
+.gl-sortable {
+ .header {
+ user-select: none;
+
+ &:hover {
+ cursor: pointer;
+ background-color: $gray-100;
+ }
+
+ &:focus {
+ outline: 1px solid $blue-300;
+ }
+ }
+}
+
+.related-issues-list-item {
+ .card-body,
+ .issuable-info-container {
+ padding: $gl-padding-4 $gl-padding-4 $gl-padding-4 $gl-padding;
+
+ .block-truncated {
+ padding: $gl-padding-8 0;
+ line-height: $gl-btn-line-height;
+ }
+
+ @include media-breakpoint-down(md) {
+ padding-left: $gl-padding;
+
+ .block-truncated {
+ flex-direction: column-reverse;
+ padding: $gl-padding-4 0;
+
+ .text-secondary {
+ margin-top: $gl-padding-4;
+ }
+
+ .issue-token-title-text {
+ display: block;
+ }
+ }
+
+ .issue-item-remove-button {
+ align-self: baseline;
+ }
+ }
+
+ @include media-breakpoint-only(md) {
+ .block-truncated .issue-token-title-text {
+ white-space: nowrap;
+ }
+
+ .issue-item-remove-button {
+ align-self: center;
+ }
+ }
+
+ @include media-breakpoint-down(sm) {
+ padding-left: $gl-padding-8;
+
+ .block-truncated .issue-token-title-text {
+ white-space: normal;
+ }
+ }
+ }
+
+ &.is-dragging {
+ padding: 0;
+ }
+}
diff --git a/app/assets/stylesheets/framework/spinner.scss b/app/assets/stylesheets/framework/spinner.scss
new file mode 100644
index 00000000000..91fe75075dc
--- /dev/null
+++ b/app/assets/stylesheets/framework/spinner.scss
@@ -0,0 +1,51 @@
+@mixin spinner-color($color) {
+ border-color: rgba($color, 0.25);
+ border-top-color: $color;
+}
+
+@mixin spinner-size($size, $border-width) {
+ width: $size;
+ height: $size;
+ border-width: $border-width;
+ @include webkit-prefix(transform-origin, 50% 50% calc((#{$size} / 2) + #{$border-width}));
+}
+
+@keyframes spinner-rotate {
+ 0% {
+ transform: rotate(0);
+ }
+
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+.spinner {
+ border-radius: 50%;
+ position: relative;
+ margin: 0 auto;
+ animation-name: spinner-rotate;
+ animation-duration: 0.6s;
+ animation-timing-function: linear;
+ animation-iteration-count: infinite;
+ border-style: solid;
+ display: inline-flex;
+ @include spinner-size(16px, 2px);
+ @include spinner-color($orange-600);
+
+ &.spinner-md {
+ @include spinner-size(32px, 3px);
+ }
+
+ &.spinner-lg {
+ @include spinner-size(64px, 4px);
+ }
+
+ &.spinner-dark {
+ @include spinner-color($gray-700);
+ }
+
+ &.spinner-light {
+ @include spinner-color($white);
+ }
+}
diff --git a/app/assets/stylesheets/framework/system_messages.scss b/app/assets/stylesheets/framework/system_messages.scss
new file mode 100644
index 00000000000..e5edddec71e
--- /dev/null
+++ b/app/assets/stylesheets/framework/system_messages.scss
@@ -0,0 +1,110 @@
+.header-message,
+.footer-message {
+ padding: 0 15px;
+ border: 1px solid transparent;
+ border-radius: 0;
+ position: fixed;
+ left: 0;
+ width: 100%;
+ text-align: center;
+ margin: 0;
+ z-index: 1000;
+
+ p {
+ @include str-truncated(100%);
+ margin-top: -1px;
+ margin-bottom: 0;
+ }
+}
+
+.header-message {
+ top: 0;
+ height: $system-header-height;
+ line-height: $system-header-height;
+}
+
+.footer-message {
+ bottom: 0;
+ height: $system-footer-height;
+ line-height: $system-footer-height;
+}
+
+.with-performance-bar {
+ .header-message {
+ top: $performance-bar-height;
+ }
+}
+
+// System Header
+.with-system-header {
+ // main navigation
+ // login page
+ .navbar-gitlab,
+ .fixed-top {
+ top: $system-header-height;
+ }
+
+ // left sidebar eg: project page
+ // right sidebar eg: MR page
+ .nav-sidebar,
+ .right-sidebar {
+ top: $system-header-height + $header-height;
+ }
+
+ .content-wrapper {
+ margin-top: $system-header-height + $header-height;
+ }
+
+ // Performance Bar
+ // System Header
+ &.with-performance-bar {
+ // main navigation
+ header.navbar-gitlab {
+ top: $performance-bar-height + $system-header-height;
+ }
+
+ .layout-page {
+ margin-top: $header-height + $performance-bar-height + $system-header-height;
+ }
+
+ // left sidebar eg: project page
+ // right sidebar eg: MR page
+ .nav-sidebar,
+ .right-sidebar {
+ top: $header-height + $performance-bar-height + $system-header-height;
+ }
+ }
+}
+
+// System Footer
+.with-system-footer {
+ // left sidebar eg: project page
+ // right sidebar eg: mr page
+ .nav-sidebar,
+ .right-sidebar,
+ // navless pages' footer eg: login page
+ // navless pages' footer border eg: login page
+ &.devise-layout-html body .footer-container,
+ &.devise-layout-html body hr.footer-fixed {
+ bottom: $system-footer-height;
+ }
+}
+
+.fullscreen-layout {
+ .header-message,
+ .footer-message {
+ position: static;
+ top: auto;
+ bottom: auto;
+ }
+
+ .content-wrapper {
+ .with-system-header & {
+ margin-top: 0;
+ }
+
+ .with-system-footer & {
+ margin-top: 0;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 295a5b5ee7a..ba406bac50b 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -161,4 +161,3 @@ table {
border-top: 0;
}
}
-
diff --git a/app/assets/stylesheets/framework/terms.scss b/app/assets/stylesheets/framework/terms.scss
index 3f4be8829d7..b07d6023127 100644
--- a/app/assets/stylesheets/framework/terms.scss
+++ b/app/assets/stylesheets/framework/terms.scss
@@ -13,7 +13,6 @@
.card {
.card-header {
- display: -webkit-flex;
display: flex;
align-items: center;
justify-content: space-between;
diff --git a/app/assets/stylesheets/framework/toggle.scss b/app/assets/stylesheets/framework/toggle.scss
index 8258da07e4d..5f8ac3b7e37 100644
--- a/app/assets/stylesheets/framework/toggle.scss
+++ b/app/assets/stylesheets/framework/toggle.scss
@@ -34,7 +34,7 @@
background: $gl-gray-400;
border-radius: 12px;
padding: 3px;
- transition: all .4s ease;
+ transition: all 0.4s ease;
&::selection,
&::before::selection,
@@ -52,7 +52,7 @@
left: 0;
border-radius: 9px;
background: $feature-toggle-color;
- transition: all .2s ease;
+ transition: all 0.2s ease;
&,
.toggle-icon-svg {
@@ -135,12 +135,18 @@
}
@keyframes animate-enabled {
- 0%, 35% { opacity: 0; }
+ 0%,
+
+ 35% { opacity: 0; }
+
100% { opacity: 1; }
}
@keyframes animate-disabled {
- 0%, 35% { opacity: 0; }
+ 0%,
+
+ 35% { opacity: 0; }
+
100% { opacity: 1; }
}
}
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index bf85acdc0d6..244b414d334 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -1,13 +1,44 @@
-@mixin md-typography {
+/**
+ * Apply Markdown typography
+ *
+ */
+.md:not(.use-csslab) {
color: $gl-text-color;
word-wrap: break-word;
- [dir="auto"] {
+ [dir='auto'] {
text-align: initial;
}
+ *:first-child {
+ margin-top: 0;
+ }
+
+ > :last-child {
+ margin-bottom: 0;
+ }
+
+ p {
+ color: $gl-text-color;
+ margin: 0 0 16px;
+
+ > code {
+ font-weight: inherit;
+ }
+
+ a:not(.no-attachment-icon) img {
+ // Remove bottom padding because
+ // <p> already has $gl-padding bottom
+ margin-bottom: 0;
+ }
+ }
+
a {
color: $blue-600;
+
+ > code {
+ color: $blue-600;
+ }
}
img:not(.emoji) {
@@ -28,18 +59,12 @@
max-width: 100%;
}
- p a:not(.no-attachment-icon) img {
- // Remove bottom padding because
- // <p> already has $gl-padding bottom
- margin-bottom: 0;
- }
-
- *:first-child {
- margin-top: 0;
- }
-
- > :last-child {
- margin-bottom: 0;
+ &:not(.md-file) img:not(.emoji) {
+ border: 1px solid $white-normal;
+ padding: 5px;
+ margin: 5px 0;
+ // Ensure that image does not exceed viewport
+ max-height: calc(100vh - 100px);
}
// Single code lines should wrap
@@ -47,6 +72,7 @@
font-family: $monospace-font;
white-space: pre-wrap;
word-wrap: normal;
+ word-break: keep-all;
}
kbd {
@@ -131,20 +157,34 @@
}
}
- p {
- color: $gl-text-color;
- margin: 0 0 16px;
+ hr {
+ // Darken 'whitesmoke' a bit to make it more visible in note bodies
+ border-color: darken($gray-normal, 8%);
+ margin: 10px 0;
}
- table:not(.js-syntax-highlight) {
+ table:not(.code) {
@extend .table;
@extend .table-bordered;
margin: 16px 0;
color: $gl-text-color;
border: 0;
+ width: auto;
+ display: block;
+ overflow-x: auto;
+
+ tbody {
+ background-color: $white-light;
+ }
+
+ tr {
+ th {
+ border-bottom: solid 2px $gl-gray-200;
+ }
- th {
- background: $label-gray-bg;
+ td {
+ border-color: $gl-gray-200;
+ }
}
}
@@ -173,14 +213,6 @@
}
}
- p > code {
- font-weight: inherit;
- }
-
- a > code {
- color: $blue-600;
- }
-
dd {
margin-left: $gl-padding;
}
@@ -196,6 +228,18 @@
margin: 3px 28px 3px 0 !important;
}
+ > ul {
+ list-style-type: disc;
+
+ ul {
+ list-style-type: circle;
+
+ ul {
+ list-style-type: square;
+ }
+ }
+ }
+
li {
line-height: 1.6em;
margin-left: 25px;
@@ -224,8 +268,8 @@
}
}
- a[href*="/uploads/"],
- a[href*="storage.googleapis.com/google-code-attachments/"] {
+ a[href*='/uploads/'],
+ a[href*='storage.googleapis.com/google-code-attachments/'] {
&::before {
margin-right: 4px;
@@ -233,17 +277,17 @@
font-size: inherit;
text-rendering: auto;
-webkit-font-smoothing: antialiased;
- content: "\f0c6";
+ content: '\f0c6';
}
&:hover::before {
text-decoration: none;
}
- }
- a.no-attachment-icon {
- &::before {
- display: none;
+ &.no-attachment-icon {
+ &::before {
+ display: none;
+ }
}
}
@@ -362,18 +406,6 @@ code {
}
/**
- * Apply Markdown typography
- *
- */
-.wiki:not(.use-csslab) {
- @include md-typography;
-}
-
-.md:not(.use-csslab) {
- @include md-typography;
-}
-
-/**
* Textareas intended for GFM
*
*/
@@ -413,6 +445,7 @@ h4 {
/**
* form text input i.e. search bar, comments, forms, etc.
*/
+/* stylelint-disable selector-no-vendor-prefix */
input,
textarea {
&::-webkit-input-placeholder {
@@ -437,5 +470,6 @@ textarea {
color: $gl-text-color-tertiary;
}
}
+/* stylelint-enable */
.lh-100 { line-height: 1; }
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index dc1a73ed923..efebbd124d0 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -23,6 +23,7 @@ $darken-border-dashed-factor: 25%;
$white-light: #fff;
$white-normal: #f0f0f0;
$white-dark: #eaeaea;
+$white-transparent: rgba(255, 255, 255, 0.8);
$gray-lightest: #fdfdfd;
$gray-light: #fafafa;
@@ -41,13 +42,13 @@ $t-gray-a-04: rgba($black, 0.04);
$t-gray-a-06: rgba($black, 0.06);
$t-gray-a-08: rgba($black, 0.08);
-$gl-gray-100: #dddddd;
-$gl-gray-200: #cccccc;
-$gl-gray-350: #aaaaaa;
-$gl-gray-400: #999999;
-$gl-gray-500: #777777;
-$gl-gray-600: #666666;
-$gl-gray-700: #555555;
+$gl-gray-100: #ddd;
+$gl-gray-200: #ccc;
+$gl-gray-350: #aaa;
+$gl-gray-400: #999;
+$gl-gray-500: #777;
+$gl-gray-600: #666;
+$gl-gray-700: #555;
$green-50: #f1fdf6;
$green-100: #dcf5e7;
@@ -100,7 +101,7 @@ $red-950: #4b140b;
$gray-50: #fafafa;
$gray-100: #f2f2f2;
$gray-200: #dfdfdf;
-$gray-300: #cccccc;
+$gray-300: #ccc;
$gray-400: #bababa;
$gray-500: #a7a7a7;
$gray-600: #919191;
@@ -265,6 +266,7 @@ $container-text-max-width: 540px;
$gl-avatar-size: 40px;
$border-radius-default: 4px;
$border-radius-small: 2px;
+$border-radius-large: 8px;
$default-icon-size: 18px;
$layout-link-gray: #7e7c7c;
$btn-side-margin: 10px;
@@ -276,6 +278,8 @@ $general-hover-transition-duration: 100ms;
$general-hover-transition-curve: linear;
$highlight-changes-color: rgb(235, 255, 232);
$performance-bar-height: 35px;
+$system-header-height: 16px;
+$system-footer-height: $system-header-height;
$flash-height: 52px;
$context-header-height: 60px;
$breadcrumb-min-height: 48px;
@@ -285,6 +289,10 @@ $gl-line-height: 16px;
$gl-line-height-24: 24px;
$gl-line-height-14: 14px;
+$system-header-height: 35px;
+$issue-box-upcoming-bg: #8f8f8f;
+$pages-group-name-color: #4c4e54;
+
/*
* Common component specific colors
*/
@@ -320,8 +328,8 @@ $line-select-yellow: #fcf8e7;
$line-select-yellow-dark: #f0e2bd;
$dark-diff-match-bg: rgba(255, 255, 255, 0.3);
$dark-diff-match-color: rgba(255, 255, 255, 0.1);
-$diff-image-info-color: gray;
-$diff-view-modes-color: gray;
+$diff-image-info-color: #808080;
+$diff-view-modes-color: #808080;
$diff-view-modes-border: #c1c1c1;
$diff-jagged-border-gradient-color: darken($white-normal, 8%);
@@ -340,6 +348,7 @@ $regular-font: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen-San
$dropdown-width: 300px;
$dropdown-min-height: 40px;
$dropdown-max-height: 312px;
+$dropdown-max-height-lg: 445px;
$dropdown-vertical-offset: 4px;
$dropdown-empty-row-bg: rgba(#000, 0.04);
$dropdown-shadow-color: rgba(#000, 0.1);
@@ -406,7 +415,7 @@ $award-emoji-menu-shadow: rgba(0, 0, 0, 0.175);
$award-emoji-positive-add-bg: #fed159;
$award-emoji-positive-add-lines: #bb9c13;
$award-emoji-width: 376px;
-$award-emoji-width-xs: 300px;
+$award-emoji-width-xs: 90%;
/*
* Search Box
@@ -622,6 +631,18 @@ Animation Functions
$dropdown-animation-timing: cubic-bezier(0.23, 1, 0.32, 1);
/*
+GitLab Plans
+*/
+$gl-gold-plan: #d4af37;
+$gl-silver-plan: #91a1ab;
+$gl-bronze-plan: #cd7f32;
+
+/*
+Cross-project Pipelines
+ */
+$linked-project-column-margin: 60px;
+
+/*
Performance Bar
*/
$perf-bar-production: #222;
@@ -645,6 +666,17 @@ $image-comment-cursor-left-offset: 12;
$image-comment-cursor-top-offset: 12;
/*
+Add GitLab Slack Application
+*/
+$add-to-slack-popup-max-width: 400px;
+$add-to-slack-gif-max-width: 850px;
+$add-to-slack-well-max-width: 750px;
+$add-to-slack-logo-size: 100px;
+$double-headed-arrow-width: 100px;
+$double-headed-arrow-height: 25px;
+$right-arrow-size: 16px;
+
+/*
Popup
*/
$popup-triangle-size: 15px;
diff --git a/app/assets/stylesheets/framework/variables_overrides.scss b/app/assets/stylesheets/framework/variables_overrides.scss
index 1dfe2a69a2f..efcc437bd7f 100644
--- a/app/assets/stylesheets/framework/variables_overrides.scss
+++ b/app/assets/stylesheets/framework/variables_overrides.scss
@@ -37,9 +37,14 @@ $h6-font-size: 14px;
$spacer: $grid-size;
$spacers: (
0: 0,
- 1: ($spacer * .5),
+ 1: ($spacer * 0.5),
2: ($spacer),
3: ($spacer * 2),
4: ($spacer * 3),
- 5: ($spacer * 4)
+ 5: ($spacer * 4),
+ 6: ($spacer * 5),
+ 7: ($spacer * 6),
+ 8: ($spacer * 7),
+ 9: ($spacer * 8)
);
+$pagination-color: $gl-text-color;
diff --git a/app/assets/stylesheets/framework/vue_transitions.scss b/app/assets/stylesheets/framework/vue_transitions.scss
index e07a177e153..e3bdc0b0199 100644
--- a/app/assets/stylesheets/framework/vue_transitions.scss
+++ b/app/assets/stylesheets/framework/vue_transitions.scss
@@ -1,9 +1,13 @@
.fade-enter-active,
-.fade-leave-active {
+.fade-leave-active,
+.fade-in-enter-active,
+.fade-out-leave-active {
transition: opacity $sidebar-transition-duration $general-hover-transition-curve;
}
.fade-enter,
+.fade-in-enter,
+.fade-out-leave-to,
.fade-leave-to {
opacity: 0;
}
diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss
index 161943766d4..434cbd6d21c 100644
--- a/app/assets/stylesheets/framework/wells.scss
+++ b/app/assets/stylesheets/framework/wells.scss
@@ -12,6 +12,10 @@
border-bottom: 1px solid $well-inner-border;
}
+ &.borderless {
+ border-bottom: 0;
+ }
+
&.branch-info {
.commit-sha,
.commit-info {
diff --git a/app/assets/stylesheets/highlight/common.scss b/app/assets/stylesheets/highlight/common.scss
new file mode 100644
index 00000000000..ac3214a07d9
--- /dev/null
+++ b/app/assets/stylesheets/highlight/common.scss
@@ -0,0 +1,18 @@
+@import '../framework/variables';
+
+@mixin diff-background($background, $idiff, $border) {
+ background: $background;
+
+ &.line_content span.idiff {
+ background: $idiff;
+ }
+
+ &.diff-line-num {
+ border-color: $border;
+ }
+}
+
+@mixin dark-diff-match-line {
+ color: $dark-diff-match-bg;
+ background: $dark-diff-match-color;
+}
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
deleted file mode 100644
index ca9a2a673f5..00000000000
--- a/app/assets/stylesheets/highlight/dark.scss
+++ /dev/null
@@ -1,259 +0,0 @@
-/* https://github.com/MozMorris/tomorrow-pygments */
-
-/*
-* Dark syntax colors
-*/
-$dark-new-bg: rgba(51, 255, 51, 0.1);
-$dark-new-idiff: rgba(51, 255, 51, 0.2);
-$dark-old-bg: rgba(255, 51, 51, 0.2);
-$dark-old-idiff: rgba(255, 51, 51, 0.25);
-$dark-border: #808080;
-$dark-code-border: #666;
-$dark-main-bg: #1d1f21;
-$dark-main-color: #1d1f21;
-$dark-line-color: #c5c8c6;
-$dark-line-num-color: rgba(255, 255, 255, 0.3);
-$dark-line-num-color-new: #627165;
-$dark-line-num-color-old: #806565;
-$dark-diff-not-empty-bg: #557;
-$dark-highlight-bg: #ffe792;
-$dark-highlight-color: $black;
-$dark-pre-hll-bg: #373b41;
-$dark-hll-bg: #373b41;
-$dark-over-bg: #9f9ab5;
-$dark-expanded-bg: #3e3e3e;
-$dark-c: #969896;
-$dark-err: #c66;
-$dark-k: #b294bb;
-$dark-l: #de935f;
-$dark-n: #c5c8c6;
-$dark-o: #8abeb7;
-$dark-p: #c5c8c6;
-$dark-cm: #969896;
-$dark-cp: #969896;
-$dark-c1: #969896;
-$dark-cs: #969896;
-$dark-gd: #c66;
-$dark-gh: #c5c8c6;
-$dark-gi: #b5bd68;
-$dark-gp: #969896;
-$dark-gu: #8abeb7;
-$dark-kc: #b294bb;
-$dark-kd: #b294bb;
-$dark-kn: #8abeb7;
-$dark-kp: #b294bb;
-$dark-kr: #b294bb;
-$dark-kt: #f0c674;
-$dark-ld: #b5bd68;
-$dark-m: #de935f;
-$dark-s: #b5bd68;
-$dark-na: #81a2be;
-$dark-nb: #c5c8c6;
-$dark-nc: #f0c674;
-$dark-no: #c66;
-$dark-nd: #8abeb7;
-$dark-ni: #c5c8c6;
-$dark-ne: #c66;
-$dark-nf: #81a2be;
-$dark-nl: #c5c8c6;
-$dark-nn: #f0c674;
-$dark-nx: #81a2be;
-$dark-py: #c5c8c6;
-$dark-nt: #8abeb7;
-$dark-nv: #c66;
-$dark-ow: #8abeb7;
-$dark-w: #c5c8c6;
-$dark-mf: #de935f;
-$dark-mh: #de935f;
-$dark-mi: #de935f;
-$dark-mo: #de935f;
-$dark-sb: #b5bd68;
-$dark-sc: #c5c8c6;
-$dark-sd: #969896;
-$dark-s2: #b5bd68;
-$dark-se: #de935f;
-$dark-sh: #b5bd68;
-$dark-si: #de935f;
-$dark-sx: #b5bd68;
-$dark-sr: #b5bd68;
-$dark-s1: #b5bd68;
-$dark-ss: #b5bd68;
-$dark-bp: #c5c8c6;
-$dark-vc: #c66;
-$dark-vg: #c66;
-$dark-vi: #c66;
-$dark-il: #de935f;
-
-.code.dark {
- // Line numbers
- .line-numbers,
- .diff-line-num {
- background-color: $dark-main-bg;
- }
-
- .diff-line-num,
- .diff-line-num a {
- color: $dark-line-num-color;
- }
-
- // Code itself
- pre.code,
- .diff-line-num {
- border-color: $dark-code-border;
- }
-
- &,
- pre.code,
- .line_holder .line_content {
- background-color: $dark-main-bg;
- color: $dark-line-color;
- }
-
- // Diff line
- .line_holder {
- &.match .line_content,
- &.old-nonewline .line_content,
- &.new-nonewline .line_content {
- @include dark-diff-match-line;
- }
-
- td.diff-line-num.hll:not(.empty-cell),
- td.line_content.hll:not(.empty-cell) {
- background-color: $dark-diff-not-empty-bg;
- border-color: darken($dark-diff-not-empty-bg, 15%);
- }
-
- .diff-line-num.new,
- .line_content.new {
- @include diff-background($dark-new-bg, $dark-new-idiff, $dark-border);
-
- &::before,
- a {
- color: $dark-line-num-color-new;
- }
- }
-
- .diff-line-num.old,
- .line_content.old {
- @include diff-background($dark-old-bg, $dark-old-idiff, $dark-border);
-
- &::before,
- a {
- color: $dark-line-num-color-old;
- }
- }
-
- .diff-line-num {
- &.is-over,
- &.hll:not(.empty-cell).is-over {
- background-color: $dark-over-bg;
- border-color: darken($dark-over-bg, 5%);
-
- a {
- color: darken($dark-over-bg, 15%);
- }
- }
- }
-
- .line_content.match {
- @include dark-diff-match-line;
- }
-
- &:not(.diff-expanded) + .diff-expanded,
- &.diff-expanded + .line_holder:not(.diff-expanded) {
- > .diff-line-num,
- > .line_content {
- border-top: 1px solid $black;
- }
- }
-
- &.diff-expanded {
- > .diff-line-num,
- > .line_content {
- background: $dark-expanded-bg;
- border-color: $dark-expanded-bg;
- }
- }
- }
-
- // highlight line via anchor
- pre .hll {
- background-color: $dark-pre-hll-bg !important;
- }
-
- // Search result highlight
- span.highlight_word {
- background-color: $dark-highlight-bg !important;
- color: $dark-highlight-color !important;
- }
-
- // Links to URLs, emails, or dependencies
- .line a {
- color: $dark-na;
- }
-
- .hll { background-color: $dark-hll-bg; }
- .c { color: $dark-c; } /* Comment */
- .err { color: $dark-err; } /* Error */
- .k { color: $dark-k; } /* Keyword */
- .l { color: $dark-l; } /* Literal */
- .n { color: $dark-n; } /* Name */
- .o { color: $dark-o; } /* Operator */
- .p { color: $dark-p; } /* Punctuation */
- .cm { color: $dark-cm; } /* Comment.Multiline */
- .cp { color: $dark-cp; } /* Comment.Preproc */
- .c1 { color: $dark-c1; } /* Comment.Single */
- .cs { color: $dark-cs; } /* Comment.Special */
- .gd { color: $dark-gd; } /* Generic.Deleted */
- .ge { font-style: italic; } /* Generic.Emph */
- .gh { color: $dark-gh; font-weight: $gl-font-weight-bold; } /* Generic.Heading */
- .gi { color: $dark-gi; } /* Generic.Inserted */
- .gp { color: $dark-gp; font-weight: $gl-font-weight-bold; } /* Generic.Prompt */
- .gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */
- .gu { color: $dark-gu; font-weight: $gl-font-weight-bold; } /* Generic.Subheading */
- .kc { color: $dark-kc; } /* Keyword.Constant */
- .kd { color: $dark-kd; } /* Keyword.Declaration */
- .kn { color: $dark-kn; } /* Keyword.Namespace */
- .kp { color: $dark-kp; } /* Keyword.Pseudo */
- .kr { color: $dark-kr; } /* Keyword.Reserved */
- .kt { color: $dark-kt; } /* Keyword.Type */
- .ld { color: $dark-ld; } /* Literal.Date */
- .m { color: $dark-m; } /* Literal.Number */
- .s { color: $dark-s; } /* Literal.String */
- .na { color: $dark-na; } /* Name.Attribute */
- .nb { color: $dark-nb; } /* Name.Builtin */
- .nc { color: $dark-nc; } /* Name.Class */
- .no { color: $dark-no; } /* Name.Constant */
- .nd { color: $dark-nd; } /* Name.Decorator */
- .ni { color: $dark-ni; } /* Name.Entity */
- .ne { color: $dark-ne; } /* Name.Exception */
- .nf { color: $dark-nf; } /* Name.Function */
- .nl { color: $dark-nl; } /* Name.Label */
- .nn { color: $dark-nn; } /* Name.Namespace */
- .nx { color: $dark-nx; } /* Name.Other */
- .py { color: $dark-py; } /* Name.Property */
- .nt { color: $dark-nt; } /* Name.Tag */
- .nv { color: $dark-nv; } /* Name.Variable */
- .ow { color: $dark-ow; } /* Operator.Word */
- .w { color: $dark-w; } /* Text.Whitespace */
- .mf { color: $dark-mf; } /* Literal.Number.Float */
- .mh { color: $dark-mh; } /* Literal.Number.Hex */
- .mi { color: $dark-mi; } /* Literal.Number.Integer */
- .mo { color: $dark-mo; } /* Literal.Number.Oct */
- .sb { color: $dark-sb; } /* Literal.String.Backtick */
- .sc { color: $dark-sc; } /* Literal.String.Char */
- .sd { color: $dark-sd; } /* Literal.String.Doc */
- .s2 { color: $dark-s2; } /* Literal.String.Double */
- .se { color: $dark-se; } /* Literal.String.Escape */
- .sh { color: $dark-sh; } /* Literal.String.Heredoc */
- .si { color: $dark-si; } /* Literal.String.Interpol */
- .sx { color: $dark-sx; } /* Literal.String.Other */
- .sr { color: $dark-sr; } /* Literal.String.Regex */
- .s1 { color: $dark-s1; } /* Literal.String.Single */
- .ss { color: $dark-ss; } /* Literal.String.Symbol */
- .bp { color: $dark-bp; } /* Name.Builtin.Pseudo */
- .vc { color: $dark-vc; } /* Name.Variable.Class */
- .vg { color: $dark-vg; } /* Name.Variable.Global */
- .vi { color: $dark-vi; } /* Name.Variable.Instance */
- .il { color: $dark-il; } /* Literal.Number.Integer.Long */
-}
diff --git a/app/assets/stylesheets/highlight/embedded.scss b/app/assets/stylesheets/highlight/embedded.scss
index 44c8a1d39ec..74364ee4ddb 100644
--- a/app/assets/stylesheets/highlight/embedded.scss
+++ b/app/assets/stylesheets/highlight/embedded.scss
@@ -1,3 +1,3 @@
.code {
- @import "white_base";
+ @import 'white_base';
}
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
deleted file mode 100644
index bc3761d1e47..00000000000
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ /dev/null
@@ -1,257 +0,0 @@
-/* https://github.com/richleland/pygments-css/blob/master/monokai.css */
-
-/*
-* Monokai Colors
-*/
-$monokai-bg: #272822;
-$monokai-border: #555;
-$monokai-text-color: #f8f8f2;
-$monokai-line-num-color: rgba(255, 255, 255, 0.3);
-$monokai-line-num-color-new: #707565;
-$monokai-line-num-color-old: #7e736f;
-$monokai-line-empty-bg: #49483e;
-$monokai-line-empty-border: darken($monokai-line-empty-bg, 15%);
-$monokai-diff-border: #808080;
-$monokai-highlight-bg: #ffe792;
-$monokai-over-bg: #9f9ab5;
-$monokai-expanded-bg: #3e3e3e;
-
-$monokai-new-bg: rgba(166, 226, 46, 0.1);
-$monokai-new-idiff: rgba(166, 226, 46, 0.15);
-
-$monokai-old-bg: rgba(254, 147, 140, 0.15);
-$monokai-old-idiff: rgba(254, 147, 140, 0.2);
-
-$monokai-hll: #49483e;
-$monokai-c: #75715e;
-$monokai-err-color: #960050;
-$monokai-err-bg: #1e0010;
-$monokai-k: #66d9ef;
-$monokai-l: #ae81ff;
-$monokai-n: #f8f8f2;
-$monokai-o: #f92672;
-$monokai-p: #f8f8f2;
-$monokai-cm: #75715e;
-$monokai-cp: #75715e;
-$monokai-c1: #75715e;
-$monokai-cs: #75715e;
-$monokai-kc: #66d9ef;
-$monokai-kd: #66d9ef;
-$monokai-kn: #f92672;
-$monokai-kp: #66d9ef;
-$monokai-kr: #66d9ef;
-$monokai-kt: #66d9ef;
-$monokai-ld: #e6db74;
-$monokai-m: #ae81ff;
-$monokai-s: #e6db74;
-$monokai-na: #a6e22e;
-$monokai-nb: #f8f8f2;
-$monokai-nc: #a6e22e;
-$monokai-no: #66d9ef;
-$monokai-nd: #a6e22e;
-$monokai-ni: #f8f8f2;
-$monokai-ne: #a6e22e;
-$monokai-nf: #a6e22e;
-$monokai-nl: #f8f8f2;
-$monokai-nn: #f8f8f2;
-$monokai-nx: #a6e22e;
-$monokai-py: #f8f8f2;
-$monokai-nt: #f92672;
-$monokai-nv: #f8f8f2;
-$monokai-ow: #f92672;
-$monokai-w: #f8f8f2;
-$monokai-mf: #ae81ff;
-$monokai-mh: #ae81ff;
-$monokai-mi: #ae81ff;
-$monokai-mo: #ae81ff;
-$monokai-sb: #e6db74;
-$monokai-sc: #e6db74;
-$monokai-sd: #e6db74;
-$monokai-s2: #e6db74;
-$monokai-se: #ae81ff;
-$monokai-sh: #e6db74;
-$monokai-si: #e6db74;
-$monokai-sx: #e6db74;
-$monokai-sr: #e6db74;
-$monokai-s1: #e6db74;
-$monokai-ss: #e6db74;
-$monokai-bp: #f8f8f2;
-$monokai-vc: #f8f8f2;
-$monokai-vg: #f8f8f2;
-$monokai-vi: #f8f8f2;
-$monokai-il: #ae81ff;
-$monokai-gu: #75715e;
-$monokai-gd: #f92672;
-$monokai-gi: #a6e22e;
-
-.code.monokai {
- // Line numbers
- .line-numbers,
- .diff-line-num {
- background-color: $monokai-bg;
- }
-
- .diff-line-num,
- .diff-line-num a {
- color: $monokai-line-num-color;
- }
-
- // Code itself
- pre.code,
- .diff-line-num {
- border-color: $monokai-border;
- }
-
- &,
- pre.code,
- .line_holder .line_content {
- background-color: $monokai-bg;
- color: $monokai-text-color;
- }
-
- // Diff line
- .line_holder {
- &.match .line_content,
- &.old-nonewline .line_content,
- &.new-nonewline .line_content {
- @include dark-diff-match-line;
- }
-
- td.diff-line-num.hll:not(.empty-cell),
- td.line_content.hll:not(.empty-cell) {
- background-color: $monokai-line-empty-bg;
- border-color: $monokai-line-empty-border;
- }
-
- .diff-line-num.new,
- .line_content.new {
- @include diff-background($monokai-new-bg, $monokai-new-idiff, $monokai-diff-border);
-
- &::before,
- a {
- color: $monokai-line-num-color-new;
- }
- }
-
- .diff-line-num.old,
- .line_content.old {
- @include diff-background($monokai-old-bg, $monokai-old-idiff, $monokai-diff-border);
-
- &::before,
- a {
- color: $monokai-line-num-color-old;
- }
- }
-
- .diff-line-num {
- &.is-over,
- &.hll:not(.empty-cell).is-over {
- background-color: $monokai-over-bg;
- border-color: darken($monokai-over-bg, 5%);
-
- a {
- color: darken($monokai-over-bg, 15%);
- }
- }
- }
-
- .line_content.match {
- @include dark-diff-match-line;
- }
-
- &:not(.diff-expanded) + .diff-expanded,
- &.diff-expanded + .line_holder:not(.diff-expanded) {
- > .diff-line-num,
- > .line_content {
- border-top: 1px solid $black;
- }
- }
-
- &.diff-expanded {
- > .diff-line-num,
- > .line_content {
- background: $monokai-expanded-bg;
- border-color: $monokai-expanded-bg;
- }
- }
- }
-
- // highlight line via anchor
- pre .hll {
- background-color: $monokai-hll !important;
- }
-
- // Search result highlight
- span.highlight_word {
- background-color: $monokai-highlight-bg !important;
- color: $black !important;
- }
-
- // Links to URLs, emails, or dependencies
- .line a {
- color: $monokai-k;
- }
-
- .hll { background-color: $monokai-hll; }
- .c { color: $monokai-c; } /* Comment */
- .err { color: $monokai-err-color; background-color: $monokai-err-bg; } /* Error */
- .k { color: $monokai-k; } /* Keyword */
- .l { color: $monokai-l; } /* Literal */
- .n { color: $monokai-n; } /* Name */
- .o { color: $monokai-o; } /* Operator */
- .p { color: $monokai-p; } /* Punctuation */
- .cm { color: $monokai-cm; } /* Comment.Multiline */
- .cp { color: $monokai-cp; } /* Comment.Preproc */
- .c1 { color: $monokai-c1; } /* Comment.Single */
- .cs { color: $monokai-cs; } /* Comment.Special */
- .ge { font-style: italic; } /* Generic.Emph */
- .gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */
- .kc { color: $monokai-kc; } /* Keyword.Constant */
- .kd { color: $monokai-kd; } /* Keyword.Declaration */
- .kn { color: $monokai-kn; } /* Keyword.Namespace */
- .kp { color: $monokai-kp; } /* Keyword.Pseudo */
- .kr { color: $monokai-kr; } /* Keyword.Reserved */
- .kt { color: $monokai-kt; } /* Keyword.Type */
- .ld { color: $monokai-ld; } /* Literal.Date */
- .m { color: $monokai-m; } /* Literal.Number */
- .s { color: $monokai-s; } /* Literal.String */
- .na { color: $monokai-na; } /* Name.Attribute */
- .nb { color: $monokai-nb; } /* Name.Builtin */
- .nc { color: $monokai-nc; } /* Name.Class */
- .no { color: $monokai-no; } /* Name.Constant */
- .nd { color: $monokai-nd; } /* Name.Decorator */
- .ni { color: $monokai-ni; } /* Name.Entity */
- .ne { color: $monokai-ne; } /* Name.Exception */
- .nf { color: $monokai-nf; } /* Name.Function */
- .nl { color: $monokai-nl; } /* Name.Label */
- .nn { color: $monokai-nn; } /* Name.Namespace */
- .nx { color: $monokai-nx; } /* Name.Other */
- .py { color: $monokai-py; } /* Name.Property */
- .nt { color: $monokai-nt; } /* Name.Tag */
- .nv { color: $monokai-nv; } /* Name.Variable */
- .ow { color: $monokai-ow; } /* Operator.Word */
- .w { color: $monokai-w; } /* Text.Whitespace */
- .mf { color: $monokai-mf; } /* Literal.Number.Float */
- .mh { color: $monokai-mh; } /* Literal.Number.Hex */
- .mi { color: $monokai-mi; } /* Literal.Number.Integer */
- .mo { color: $monokai-mo; } /* Literal.Number.Oct */
- .sb { color: $monokai-sb; } /* Literal.String.Backtick */
- .sc { color: $monokai-sc; } /* Literal.String.Char */
- .sd { color: $monokai-sd; } /* Literal.String.Doc */
- .s2 { color: $monokai-s2; } /* Literal.String.Double */
- .se { color: $monokai-se; } /* Literal.String.Escape */
- .sh { color: $monokai-sh; } /* Literal.String.Heredoc */
- .si { color: $monokai-si; } /* Literal.String.Interpol */
- .sx { color: $monokai-sx; } /* Literal.String.Other */
- .sr { color: $monokai-sr; } /* Literal.String.Regex */
- .s1 { color: $monokai-s1; } /* Literal.String.Single */
- .ss { color: $monokai-ss; } /* Literal.String.Symbol */
- .bp { color: $monokai-bp; } /* Name.Builtin.Pseudo */
- .vc { color: $monokai-vc; } /* Name.Variable.Class */
- .vg { color: $monokai-vg; } /* Name.Variable.Global */
- .vi { color: $monokai-vi; } /* Name.Variable.Instance */
- .il { color: $monokai-il; } /* Literal.Number.Integer.Long */
- .gu { color: $monokai-gu; } /* Generic.Subheading & Diff Unified/Comment? */
- .gd { color: $monokai-gd; } /* Generic.Deleted & Diff Deleted */
- .gi { color: $monokai-gi; } /* Generic.Inserted & Diff Inserted */
-}
diff --git a/app/assets/stylesheets/highlight/none.scss b/app/assets/stylesheets/highlight/none.scss
deleted file mode 100644
index 4bedb6a8e5b..00000000000
--- a/app/assets/stylesheets/highlight/none.scss
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
-* None Syntax Colors
-*/
-
-
-
-@mixin match-line {
- color: $black-transparent;
- background-color: $white-normal;
-}
-
-.code.none {
- // Line numbers
- .line-numbers,
- .diff-line-num {
- background-color: $gray-light;
- }
-
- .diff-line-num,
- .diff-line-num a {
- color: $black-transparent;
- }
-
- // Code itself
- pre.code,
- .diff-line-num {
- border-color: $white-normal;
- }
-
- &,
- pre.code,
- .line_holder .line_content {
- background-color: $white-light;
- color: $gl-text-color;
- }
-
-// Diff line
-
- $none-over-bg: #ded7fc;
- $none-expanded-border: #e0e0e0;
- $none-expanded-bg: #e0e0e0;
-
- .line_holder {
-
- &.match .line_content,
- .new-nonewline.line_content,
- .old-nonewline.line_content {
- @include match-line;
- }
-
- .diff-line-num {
- &.old {
- a {
- color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%);
- }
- }
-
- &.new {
- a {
- color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%);
- }
- }
-
- &.is-over,
- &.hll:not(.empty-cell).is-over {
- background-color: $none-over-bg;
- border-color: darken($none-over-bg, 5%);
-
- a {
- color: darken($none-over-bg, 15%);
- }
- }
-
- &.hll:not(.empty-cell) {
- background-color: $white-light;
- border-color: $white-normal;
- }
- }
-
- &:not(.diff-expanded) + .diff-expanded,
- &.diff-expanded + .line_holder:not(.diff-expanded) {
- > .diff-line-num,
- > .line_content {
- border-top: 1px solid $none-expanded-border;
- }
- }
-
- &.diff-expanded {
- > .diff-line-num,
- > .line_content {
- background: $none-expanded-bg;
- border-color: $none-expanded-bg;
- }
- }
-
- .line_content {
- &.old {
- background-color: $white-normal;
-
- &::before {
- color: $gl-text-color;
- }
-
- span.idiff {
- background-color: $white-normal;
- text-decoration: underline;
- }
- }
-
- &.new {
- background-color: $white-normal;
-
- &::before {
- color: $gl-text-color;
- }
-
- span.idiff {
- background-color: $white-normal;
- text-decoration: underline;
- }
- }
-
- &.match {
- @include match-line;
- }
-
- &.hll:not(.empty-cell) {
- background-color: $white-normal;
- }
- }
- }
-
- // highlight line via anchor
- pre .hll {
- background-color: $white-normal;
- }
-
- // Search result highlight
- span.highlight_word {
- background-color: $white-normal;
- }
-
- // Links to URLs, emails, or dependencies
- .line a {
- color: $gl-text-color;
- text-decoration: underline;
- }
-
- .hll { background-color: $white-light; }
-
- .gd {
- color: $gl-text-color;
- background-color: $white-light;
-
- .x {
- color: $gl-text-color;
- background-color: $white-light;
- }
- }
-
- .gi {
- color: $gl-text-color;
- background-color: $white-light;
-
- .x {
- color: $gl-text-color;
- background-color: $white-light;
- }
- }
-
- .c { color: $gl-text-color; } /* Comment */
- .err { color: $gl-text-color; } /* Error */
- .g { color: $gl-text-color; } /* Generic */
- .k { color: $gl-text-color; } /* Keyword */
- .l { color: $gl-text-color; } /* Literal */
- .n { color: $gl-text-color; } /* Name */
- .o { color: $gl-text-color; } /* Operator */
- .x { color: $gl-text-color; } /* Other */
- .p { color: $gl-text-color; } /* Punctuation */
- .cm { color: $gl-text-color; } /* Comment.Multiline */
- .cp { color: $gl-text-color; } /* Comment.Preproc */
- .c1 { color: $gl-text-color; } /* Comment.Single */
- .cs { color: $gl-text-color; } /* Comment.Special */
- .ge { color: $gl-text-color; } /* Generic.Emph */
- .gr { color: $gl-text-color; } /* Generic.Error */
- .gh { color: $gl-text-color; } /* Generic.Heading */
- .go { color: $gl-text-color; } /* Generic.Output */
- .gp { color: $gl-text-color; } /* Generic.Prompt */
- .gs { color: $gl-text-color; } /* Generic.Strong */
- .gu { color: $gl-text-color; } /* Generic.Subheading */
- .gt { color: $gl-text-color; } /* Generic.Traceback */
- .kc { color: $gl-text-color; } /* Keyword.Constant */
- .kd { color: $gl-text-color; } /* Keyword.Declaration */
- .kn { color: $gl-text-color; } /* Keyword.Namespace */
- .kp { color: $gl-text-color; } /* Keyword.Pseudo */
- .kr { color: $gl-text-color; } /* Keyword.Reserved */
- .kt { color: $gl-text-color; } /* Keyword.Type */
- .ld { color: $gl-text-color; } /* Literal.Date */
- .m { color: $gl-text-color; } /* Literal.Number */
- .s { color: $gl-text-color; } /* Literal.String */
- .na { color: $gl-text-color; } /* Name.Attribute */
- .nb { color: $gl-text-color; } /* Name.Builtin */
- .nc { color: $gl-text-color; } /* Name.Class */
- .no { color: $gl-text-color; } /* Name.Constant */
- .nd { color: $gl-text-color; } /* Name.Decorator */
- .ni { color: $gl-text-color; } /* Name.Entity */
- .ne { color: $gl-text-color; } /* Name.Exception */
- .nf { color: $gl-text-color; } /* Name.Function */
- .nl { color: $gl-text-color; } /* Name.Label */
- .nn { color: $gl-text-color; } /* Name.Namespace */
- .nx { color: $gl-text-color; } /* Name.Other */
- .py { color: $gl-text-color; } /* Name.Property */
- .nt { color: $gl-text-color; } /* Name.Tag */
- .nv { color: $gl-text-color; } /* Name.Variable */
- .ow { color: $gl-text-color; } /* Operator.Word */
- .w { color: $gl-text-color; } /* Text.Whitespace */
- .mf { color: $gl-text-color; } /* Literal.Number.Float */
- .mh { color: $gl-text-color; } /* Literal.Number.Hex */
- .mi { color: $gl-text-color; } /* Literal.Number.Integer */
- .mo { color: $gl-text-color; } /* Literal.Number.Oct */
- .sb { color: $gl-text-color; } /* Literal.String.Backtick */
- .sc { color: $gl-text-color; } /* Literal.String.Char */
- .sd { color: $gl-text-color; } /* Literal.String.Doc */
- .s2 { color: $gl-text-color; } /* Literal.String.Double */
- .se { color: $gl-text-color; } /* Literal.String.Escape */
- .sh { color: $gl-text-color; } /* Literal.String.Heredoc */
- .si { color: $gl-text-color; } /* Literal.String.Interpol */
- .sx { color: $gl-text-color; } /* Literal.String.Other */
- .sr { color: $gl-text-color; } /* Literal.String.Regex */
- .s1 { color: $gl-text-color; } /* Literal.String.Single */
- .ss { color: $gl-text-color; } /* Literal.String.Symbol */
- .bp { color: $gl-text-color; } /* Name.Builtin.Pseudo */
- .vc { color: $gl-text-color; } /* Name.Variable.Class */
- .vg { color: $gl-text-color; } /* Name.Variable.Global */
- .vi { color: $gl-text-color; } /* Name.Variable.Instance */
- .il { color: $gl-text-color; } /* Literal.Number.Integer.Long */
-
-}
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
deleted file mode 100644
index de7b9424340..00000000000
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ /dev/null
@@ -1,284 +0,0 @@
-/* https://gist.github.com/qguv/7936275 */
-
-/*
-* Solarized dark colors
-*/
-$solarized-dark-new-bg: rgba(133, 153, 0, 0.15);
-$solarized-dark-new-idiff: rgba(133, 153, 0, 0.25);
-$solarized-dark-old-bg: rgba(220, 50, 47, 0.3);
-$solarized-dark-old-idiff: rgba(220, 50, 47, 0.25);
-$solarized-dark-border: #113b46;
-$solarized-dark-pre-bg: #002b36;
-$solarized-dark-pre-color: #93a1a1;
-$solarized-dark-pre-border: #113b46;
-$solarized-dark-line-bg: #002b36;
-$solarized-dark-line-color: rgba(255, 255, 255, 0.3);
-$solarized-dark-line-color-new: #5a766c;
-$solarized-dark-line-color-old: #7a6c71;
-$solarized-dark-highlight: #094554;
-$solarized-dark-hll-bg: #174652;
-$solarized-dark-over-bg: #9f9ab5;
-$solarized-dark-expanded-bg: #010d10;
-$solarized-dark-c: #586e75;
-$solarized-dark-err: #93a1a1;
-$solarized-dark-g: #93a1a1;
-$solarized-dark-k: #859900;
-$solarized-dark-l: #93a1a1;
-$solarized-dark-n: #93a1a1;
-$solarized-dark-o: #859900;
-$solarized-dark-x: #cb4b16;
-$solarized-dark-p: #93a1a1;
-$solarized-dark-cm: #586e75;
-$solarized-dark-cp: #859900;
-$solarized-dark-c1: #586e75;
-$solarized-dark-cs: #859900;
-$solarized-dark-gd: #2aa198;
-$solarized-dark-ge: #93a1a1;
-$solarized-dark-gr: #dc322f;
-$solarized-dark-gh: #cb4b16;
-$solarized-dark-gi: #859900;
-$solarized-dark-go: #93a1a1;
-$solarized-dark-gp: #93a1a1;
-$solarized-dark-gs: #93a1a1;
-$solarized-dark-gu: #cb4b16;
-$solarized-dark-gt: #93a1a1;
-$solarized-dark-kc: #cb4b16;
-$solarized-dark-kd: #268bd2;
-$solarized-dark-kn: #859900;
-$solarized-dark-kp: #859900;
-$solarized-dark-kr: #268bd2;
-$solarized-dark-kt: #dc322f;
-$solarized-dark-ld: #93a1a1;
-$solarized-dark-m: #2aa198;
-$solarized-dark-s: #2aa198;
-$solarized-dark-na: #93a1a1;
-$solarized-dark-nb: #b58900;
-$solarized-dark-nc: #268bd2;
-$solarized-dark-no: #cb4b16;
-$solarized-dark-nd: #268bd2;
-$solarized-dark-ni: #cb4b16;
-$solarized-dark-ne: #cb4b16;
-$solarized-dark-nf: #268bd2;
-$solarized-dark-nl: #93a1a1;
-$solarized-dark-nn: #93a1a1;
-$solarized-dark-nx: #93a1a1;
-$solarized-dark-py: #93a1a1;
-$solarized-dark-nt: #268bd2;
-$solarized-dark-nv: #268bd2;
-$solarized-dark-ow: #859900;
-$solarized-dark-w: #93a1a1;
-$solarized-dark-mf: #2aa198;
-$solarized-dark-mh: #2aa198;
-$solarized-dark-mi: #2aa198;
-$solarized-dark-mo: #2aa198;
-$solarized-dark-sb: #586e75;
-$solarized-dark-sc: #2aa198;
-$solarized-dark-sd: #93a1a1;
-$solarized-dark-s2: #2aa198;
-$solarized-dark-se: #cb4b16;
-$solarized-dark-sh: #93a1a1;
-$solarized-dark-si: #2aa198;
-$solarized-dark-sx: #2aa198;
-$solarized-dark-sr: #dc322f;
-$solarized-dark-s1: #2aa198;
-$solarized-dark-ss: #2aa198;
-$solarized-dark-bp: #268bd2;
-$solarized-dark-vc: #268bd2;
-$solarized-dark-vg: #268bd2;
-$solarized-dark-vi: #268bd2;
-$solarized-dark-il: #2aa198;
-
-.code.solarized-dark {
- // Line numbers
- .line-numbers,
- .diff-line-num {
- background-color: $solarized-dark-line-bg;
- }
-
- .diff-line-num,
- .diff-line-num a {
- color: $solarized-dark-line-color;
- }
-
- // Code itself
- pre.code,
- .diff-line-num {
- border-color: $solarized-dark-pre-border;
- }
-
- &,
- pre.code,
- .line_holder .line_content {
- background-color: $solarized-dark-pre-bg;
- color: $solarized-dark-pre-color;
- }
-
- // Diff line
- .line_holder {
- &.match .line_content,
- &.old-nonewline .line_content,
- &.new-nonewline .line_content {
- @include dark-diff-match-line;
- }
-
- td.diff-line-num.hll:not(.empty-cell),
- td.line_content.hll:not(.empty-cell) {
- background-color: $solarized-dark-hll-bg;
- border-color: darken($solarized-dark-hll-bg, 15%);
- }
-
- .diff-line-num.new,
- .line_content.new {
- @include diff-background($solarized-dark-new-bg, $solarized-dark-new-idiff, $solarized-dark-border);
-
- &::before,
- a {
- color: $solarized-dark-line-color-new;
- }
- }
-
- .diff-line-num.old,
- .line_content.old {
- @include diff-background($solarized-dark-old-bg, $solarized-dark-old-idiff, $solarized-dark-border);
-
- &::before,
- a {
- color: $solarized-dark-line-color-old;
- }
- }
-
- .diff-line-num {
- &.is-over,
- &.hll:not(.empty-cell).is-over {
- background-color: $solarized-dark-over-bg;
- border-color: darken($solarized-dark-over-bg, 5%);
-
- a {
- color: darken($solarized-dark-over-bg, 15%);
- }
- }
- }
-
- .line_content.match {
- @include dark-diff-match-line;
- }
-
- &:not(.diff-expanded) + .diff-expanded,
- &.diff-expanded + .line_holder:not(.diff-expanded) {
- > .diff-line-num,
- > .line_content {
- border-top: 1px solid $black;
- }
- }
-
- &.diff-expanded {
- > .diff-line-num,
- > .line_content {
- background: $solarized-dark-expanded-bg;
- border-color: $solarized-dark-expanded-bg;
- }
- }
- }
-
- // highlight line via anchor
- pre .hll {
- background-color: $solarized-dark-hll-bg !important;
- }
-
- // Search result highlight
- span.highlight_word {
- background-color: $solarized-dark-highlight !important;
- }
-
- // Links to URLs, emails, or dependencies
- .line a {
- color: $solarized-dark-kd;
- }
-
- /* Solarized Dark
-
- For use with Jekyll and Pygments
-
- http://ethanschoonover.com/solarized
-
- SOLARIZED HEX ROLE
- --------- -------- ------------------------------------------
- base03 #002b36 background
- base01 #586e75 comments / secondary content
- base1 #93a1a1 body text / default code / primary content
- orange #cb4b16 constants
- red #dc322f regex, special keywords
- blue #268bd2 reserved keywords
- cyan #2aa198 strings, numbers
- green #859900 operators, other keywords
- */
-
- .c { color: $solarized-dark-c; } /* Comment */
- .err { color: $solarized-dark-err; } /* Error */
- .g { color: $solarized-dark-g; } /* Generic */
- .k { color: $solarized-dark-k; } /* Keyword */
- .l { color: $solarized-dark-l; } /* Literal */
- .n { color: $solarized-dark-n; } /* Name */
- .o { color: $solarized-dark-o; } /* Operator */
- .x { color: $solarized-dark-x; } /* Other */
- .p { color: $solarized-dark-p; } /* Punctuation */
- .cm { color: $solarized-dark-cm; } /* Comment.Multiline */
- .cp { color: $solarized-dark-cp; } /* Comment.Preproc */
- .c1 { color: $solarized-dark-c1; } /* Comment.Single */
- .cs { color: $solarized-dark-cs; } /* Comment.Special */
- .gd { color: $solarized-dark-gd; } /* Generic.Deleted */
- .ge { color: $solarized-dark-ge; font-style: italic; } /* Generic.Emph */
- .gr { color: $solarized-dark-gr; } /* Generic.Error */
- .gh { color: $solarized-dark-gh; } /* Generic.Heading */
- .gi { color: $solarized-dark-gi; } /* Generic.Inserted */
- .go { color: $solarized-dark-go; } /* Generic.Output */
- .gp { color: $solarized-dark-gp; } /* Generic.Prompt */
- .gs { color: $solarized-dark-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */
- .gu { color: $solarized-dark-gu; } /* Generic.Subheading */
- .gt { color: $solarized-dark-gt; } /* Generic.Traceback */
- .kc { color: $solarized-dark-kc; } /* Keyword.Constant */
- .kd { color: $solarized-dark-kd; } /* Keyword.Declaration */
- .kn { color: $solarized-dark-kn; } /* Keyword.Namespace */
- .kp { color: $solarized-dark-kp; } /* Keyword.Pseudo */
- .kr { color: $solarized-dark-kr; } /* Keyword.Reserved */
- .kt { color: $solarized-dark-kt; } /* Keyword.Type */
- .ld { color: $solarized-dark-ld; } /* Literal.Date */
- .m { color: $solarized-dark-m; } /* Literal.Number */
- .s { color: $solarized-dark-s; } /* Literal.String */
- .na { color: $solarized-dark-na; } /* Name.Attribute */
- .nb { color: $solarized-dark-nb; } /* Name.Builtin */
- .nc { color: $solarized-dark-nc; } /* Name.Class */
- .no { color: $solarized-dark-no; } /* Name.Constant */
- .nd { color: $solarized-dark-nd; } /* Name.Decorator */
- .ni { color: $solarized-dark-ni; } /* Name.Entity */
- .ne { color: $solarized-dark-ne; } /* Name.Exception */
- .nf { color: $solarized-dark-nf; } /* Name.Function */
- .nl { color: $solarized-dark-nl; } /* Name.Label */
- .nn { color: $solarized-dark-nn; } /* Name.Namespace */
- .nx { color: $solarized-dark-nx; } /* Name.Other */
- .py { color: $solarized-dark-py; } /* Name.Property */
- .nt { color: $solarized-dark-nt; } /* Name.Tag */
- .nv { color: $solarized-dark-nv; } /* Name.Variable */
- .ow { color: $solarized-dark-ow; } /* Operator.Word */
- .w { color: $solarized-dark-w; } /* Text.Whitespace */
- .mf { color: $solarized-dark-mf; } /* Literal.Number.Float */
- .mh { color: $solarized-dark-mh; } /* Literal.Number.Hex */
- .mi { color: $solarized-dark-mi; } /* Literal.Number.Integer */
- .mo { color: $solarized-dark-mo; } /* Literal.Number.Oct */
- .sb { color: $solarized-dark-sb; } /* Literal.String.Backtick */
- .sc { color: $solarized-dark-sc; } /* Literal.String.Char */
- .sd { color: $solarized-dark-sd; } /* Literal.String.Doc */
- .s2 { color: $solarized-dark-s2; } /* Literal.String.Double */
- .se { color: $solarized-dark-se; } /* Literal.String.Escape */
- .sh { color: $solarized-dark-sh; } /* Literal.String.Heredoc */
- .si { color: $solarized-dark-si; } /* Literal.String.Interpol */
- .sx { color: $solarized-dark-sx; } /* Literal.String.Other */
- .sr { color: $solarized-dark-sr; } /* Literal.String.Regex */
- .s1 { color: $solarized-dark-s1; } /* Literal.String.Single */
- .ss { color: $solarized-dark-ss; } /* Literal.String.Symbol */
- .bp { color: $solarized-dark-bp; } /* Name.Builtin.Pseudo */
- .vc { color: $solarized-dark-vc; } /* Name.Variable.Class */
- .vg { color: $solarized-dark-vg; } /* Name.Variable.Global */
- .vi { color: $solarized-dark-vi; } /* Name.Variable.Instance */
- .il { color: $solarized-dark-il; } /* Literal.Number.Integer.Long */
-}
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
deleted file mode 100644
index 84a92d0320a..00000000000
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ /dev/null
@@ -1,292 +0,0 @@
-/* https://gist.github.com/qguv/7936275 */
-
-/*
-* Solarized light syntax colors
-*/
-$solarized-light-matchline-bg: rgba(255, 255, 255, 0.4);
-$solarized-light-new-bg: rgba(133, 153, 0, 0.2);
-$solarized-light-new-idiff: rgba(133, 153, 0, 0.25);
-$solarized-light-old-bg: rgba(220, 50, 47, 0.2);
-$solarized-light-old-idiff: rgba(220, 50, 47, 0.25);
-$solarized-light-border: #c5d0d4;
-$solarized-light-pre-bg: #002b36;
-$solarized-light-pre-bg: #fdf6e3;
-$solarized-light-pre-color: #586e75;
-$solarized-light-line-bg: #fdf6e3;
-$solarized-light-line-color: rgba(0, 0, 0, 0.3);
-$solarized-light-line-color-new: #a1a080;
-$solarized-light-line-color-old: #ad9186;
-$solarized-light-highlight: #eee8d5;
-$solarized-light-hll-bg: #ddd8c5;
-$solarized-light-over-bg: #ded7fc;
-$solarized-light-expanded-border: #d2cdbd;
-$solarized-light-expanded-bg: #ece6d4;
-$solarized-light-c: #93a1a1;
-$solarized-light-err: #586e75;
-$solarized-light-g: #586e75;
-$solarized-light-k: #859900;
-$solarized-light-l: #586e75;
-$solarized-light-n: #586e75;
-$solarized-light-o: #859900;
-$solarized-light-x: #cb4b16;
-$solarized-light-p: #586e75;
-$solarized-light-cm: #93a1a1;
-$solarized-light-cp: #859900;
-$solarized-light-c1: #93a1a1;
-$solarized-light-cs: #859900;
-$solarized-light-gd: #2aa198;
-$solarized-light-ge: #586e75;
-$solarized-light-gr: #dc322f;
-$solarized-light-gh: #cb4b16;
-$solarized-light-gi: #859900;
-$solarized-light-go: #586e75;
-$solarized-light-gp: #586e75;
-$solarized-light-gs: #586e75;
-$solarized-light-gu: #cb4b16;
-$solarized-light-gt: #586e75;
-$solarized-light-kc: #cb4b16;
-$solarized-light-kd: #268bd2;
-$solarized-light-kn: #859900;
-$solarized-light-kp: #859900;
-$solarized-light-kr: #268bd2;
-$solarized-light-kt: #dc322f;
-$solarized-light-ld: #586e75;
-$solarized-light-m: #2aa198;
-$solarized-light-s: #2aa198;
-$solarized-light-na: #586e75;
-$solarized-light-nb: #b58900;
-$solarized-light-nc: #268bd2;
-$solarized-light-no: #cb4b16;
-$solarized-light-nd: #268bd2;
-$solarized-light-ni: #cb4b16;
-$solarized-light-ne: #cb4b16;
-$solarized-light-nf: #268bd2;
-$solarized-light-nl: #586e75;
-$solarized-light-nn: #586e75;
-$solarized-light-nx: #586e75;
-$solarized-light-py: #586e75;
-$solarized-light-nt: #268bd2;
-$solarized-light-nv: #268bd2;
-$solarized-light-ow: #859900;
-$solarized-light-w: #586e75;
-$solarized-light-mf: #2aa198;
-$solarized-light-mh: #2aa198;
-$solarized-light-mi: #2aa198;
-$solarized-light-mo: #2aa198;
-$solarized-light-sb: #93a1a1;
-$solarized-light-sc: #2aa198;
-$solarized-light-sd: #586e75;
-$solarized-light-s2: #2aa198;
-$solarized-light-se: #cb4b16;
-$solarized-light-sh: #586e75;
-$solarized-light-si: #2aa198;
-$solarized-light-sx: #2aa198;
-$solarized-light-sr: #dc322f;
-$solarized-light-s1: #2aa198;
-$solarized-light-ss: #2aa198;
-$solarized-light-bp: #268bd2;
-$solarized-light-vc: #268bd2;
-$solarized-light-vg: #268bd2;
-$solarized-light-vi: #268bd2;
-$solarized-light-il: #2aa198;
-
-@mixin match-line {
- color: $black-transparent;
- background: $solarized-light-matchline-bg;
-}
-
-.code.solarized-light {
- // Line numbers
- .line-numbers,
- .diff-line-num {
- background-color: $solarized-light-line-bg;
- }
-
- .diff-line-num,
- .diff-line-num a {
- color: $solarized-light-line-color;
- }
-
- // Code itself
- pre.code,
- .diff-line-num {
- border-color: $solarized-light-border;
- }
-
- &,
- pre.code,
- .line_holder .line_content {
- background-color: $solarized-light-pre-bg;
- color: $solarized-light-pre-color;
- }
-
- // Diff line
- .line_holder {
- &.match .line_content,
- &.old-nonewline .line_content,
- &.new-nonewline .line_content {
- @include match-line;
- }
-
- td.diff-line-num.hll:not(.empty-cell),
- td.line_content.hll:not(.empty-cell) {
- background-color: $solarized-light-hll-bg;
- border-color: darken($solarized-light-hll-bg, 15%);
- }
-
- .diff-line-num.new,
- .line_content.new {
- @include diff-background($solarized-light-new-bg,
- $solarized-light-new-idiff, $solarized-light-border);
-
- &::before,
- a {
- color: $solarized-light-line-color-new;
- }
- }
-
- .diff-line-num.old,
- .line_content.old {
- @include diff-background($solarized-light-old-bg, $solarized-light-old-idiff, $solarized-light-border);
-
- &::before,
- a {
- color: $solarized-light-line-color-old;
- }
- }
-
- .diff-line-num {
- &.is-over,
- &.hll:not(.empty-cell).is-over {
- background-color: $solarized-light-over-bg;
- border-color: darken($solarized-light-over-bg, 5%);
-
- a {
- color: darken($solarized-light-over-bg, 15%);
- }
- }
- }
-
- .line_content.match {
- @include match-line;
- }
-
- &:not(.diff-expanded) + .diff-expanded,
- &.diff-expanded + .line_holder:not(.diff-expanded) {
- > .diff-line-num,
- > .line_content {
- border-top: 1px solid $solarized-light-expanded-border;
- }
- }
-
- &.diff-expanded {
- > .diff-line-num,
- > .line_content {
- background: $solarized-light-expanded-bg;
- border-color: $solarized-light-expanded-bg;
- }
- }
- }
-
- // highlight line via anchor
- pre .hll {
- background-color: $solarized-light-hll-bg !important;
- }
-
- // Search result highlight
- span.highlight_word {
- background-color: $solarized-light-highlight !important;
- }
-
- // Links to URLs, emails, or dependencies
- .line a {
- color: $solarized-light-kd;
- }
-
- /* Solarized Light
-
- For use with Jekyll and Pygments
-
- http://ethanschoonover.com/solarized
-
- SOLARIZED HEX ROLE
- --------- -------- ------------------------------------------
- base01 #586e75 body text / default code / primary content
- base1 #93a1a1 comments / secondary content
- base3 #fdf6e3 background
- orange #cb4b16 constants
- red #dc322f regex, special keywords
- blue #268bd2 reserved keywords
- cyan #2aa198 strings, numbers
- green #859900 operators, other keywords
- */
-
- .c { color: $solarized-light-c; } /* Comment */
- .err { color: $solarized-light-err; } /* Error */
- .g { color: $solarized-light-g; } /* Generic */
- .k { color: $solarized-light-k; } /* Keyword */
- .l { color: $solarized-light-l; } /* Literal */
- .n { color: $solarized-light-n; } /* Name */
- .o { color: $solarized-light-o; } /* Operator */
- .x { color: $solarized-light-x; } /* Other */
- .p { color: $solarized-light-p; } /* Punctuation */
- .cm { color: $solarized-light-cm; } /* Comment.Multiline */
- .cp { color: $solarized-light-cp; } /* Comment.Preproc */
- .c1 { color: $solarized-light-c1; } /* Comment.Single */
- .cs { color: $solarized-light-cs; } /* Comment.Special */
- .gd { color: $solarized-light-gd; } /* Generic.Deleted */
- .ge { color: $solarized-light-ge; font-style: italic; } /* Generic.Emph */
- .gr { color: $solarized-light-gr; } /* Generic.Error */
- .gh { color: $solarized-light-gh; } /* Generic.Heading */
- .gi { color: $solarized-light-gi; } /* Generic.Inserted */
- .go { color: $solarized-light-go; } /* Generic.Output */
- .gp { color: $solarized-light-gp; } /* Generic.Prompt */
- .gs { color: $solarized-light-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */
- .gu { color: $solarized-light-gu; } /* Generic.Subheading */
- .gt { color: $solarized-light-gt; } /* Generic.Traceback */
- .kc { color: $solarized-light-kc; } /* Keyword.Constant */
- .kd { color: $solarized-light-kd; } /* Keyword.Declaration */
- .kn { color: $solarized-light-kn; } /* Keyword.Namespace */
- .kp { color: $solarized-light-kp; } /* Keyword.Pseudo */
- .kr { color: $solarized-light-kr; } /* Keyword.Reserved */
- .kt { color: $solarized-light-kt; } /* Keyword.Type */
- .ld { color: $solarized-light-ld; } /* Literal.Date */
- .m { color: $solarized-light-m; } /* Literal.Number */
- .s { color: $solarized-light-s; } /* Literal.String */
- .na { color: $solarized-light-na; } /* Name.Attribute */
- .nb { color: $solarized-light-nb; } /* Name.Builtin */
- .nc { color: $solarized-light-nc; } /* Name.Class */
- .no { color: $solarized-light-no; } /* Name.Constant */
- .nd { color: $solarized-light-nd; } /* Name.Decorator */
- .ni { color: $solarized-light-ni; } /* Name.Entity */
- .ne { color: $solarized-light-ne; } /* Name.Exception */
- .nf { color: $solarized-light-nf; } /* Name.Function */
- .nl { color: $solarized-light-nl; } /* Name.Label */
- .nn { color: $solarized-light-nn; } /* Name.Namespace */
- .nx { color: $solarized-light-nx; } /* Name.Other */
- .py { color: $solarized-light-py; } /* Name.Property */
- .nt { color: $solarized-light-nt; } /* Name.Tag */
- .nv { color: $solarized-light-nv; } /* Name.Variable */
- .ow { color: $solarized-light-ow; } /* Operator.Word */
- .w { color: $solarized-light-w; } /* Text.Whitespace */
- .mf { color: $solarized-light-mf; } /* Literal.Number.Float */
- .mh { color: $solarized-light-mh; } /* Literal.Number.Hex */
- .mi { color: $solarized-light-mi; } /* Literal.Number.Integer */
- .mo { color: $solarized-light-mo; } /* Literal.Number.Oct */
- .sb { color: $solarized-light-sb; } /* Literal.String.Backtick */
- .sc { color: $solarized-light-sc; } /* Literal.String.Char */
- .sd { color: $solarized-light-sd; } /* Literal.String.Doc */
- .s2 { color: $solarized-light-s2; } /* Literal.String.Double */
- .se { color: $solarized-light-se; } /* Literal.String.Escape */
- .sh { color: $solarized-light-sh; } /* Literal.String.Heredoc */
- .si { color: $solarized-light-si; } /* Literal.String.Interpol */
- .sx { color: $solarized-light-sx; } /* Literal.String.Other */
- .sr { color: $solarized-light-sr; } /* Literal.String.Regex */
- .s1 { color: $solarized-light-s1; } /* Literal.String.Single */
- .ss { color: $solarized-light-ss; } /* Literal.String.Symbol */
- .bp { color: $solarized-light-bp; } /* Name.Builtin.Pseudo */
- .vc { color: $solarized-light-vc; } /* Name.Variable.Class */
- .vg { color: $solarized-light-vg; } /* Name.Variable.Global */
- .vi { color: $solarized-light-vi; } /* Name.Variable.Instance */
- .il { color: $solarized-light-il; } /* Literal.Number.Integer.Long */
-}
diff --git a/app/assets/stylesheets/highlight/themes/dark.scss b/app/assets/stylesheets/highlight/themes/dark.scss
new file mode 100644
index 00000000000..16893dd047e
--- /dev/null
+++ b/app/assets/stylesheets/highlight/themes/dark.scss
@@ -0,0 +1,261 @@
+/* https://github.com/MozMorris/tomorrow-pygments */
+
+@import "../common";
+
+/*
+* Dark syntax colors
+*/
+$dark-new-bg: rgba(51, 255, 51, 0.1);
+$dark-new-idiff: rgba(51, 255, 51, 0.2);
+$dark-old-bg: rgba(255, 51, 51, 0.2);
+$dark-old-idiff: rgba(255, 51, 51, 0.25);
+$dark-border: #808080;
+$dark-code-border: #666;
+$dark-main-bg: #1d1f21;
+$dark-main-color: #1d1f21;
+$dark-line-color: #c5c8c6;
+$dark-line-num-color: rgba(255, 255, 255, 0.3);
+$dark-line-num-color-new: #627165;
+$dark-line-num-color-old: #806565;
+$dark-diff-not-empty-bg: #557;
+$dark-highlight-bg: #ffe792;
+$dark-highlight-color: $black;
+$dark-pre-hll-bg: #373b41;
+$dark-hll-bg: #373b41;
+$dark-over-bg: #9f9ab5;
+$dark-expanded-bg: #3e3e3e;
+$dark-c: #969896;
+$dark-err: #c66;
+$dark-k: #b294bb;
+$dark-l: #de935f;
+$dark-n: #c5c8c6;
+$dark-o: #8abeb7;
+$dark-p: #c5c8c6;
+$dark-cm: #969896;
+$dark-cp: #969896;
+$dark-c1: #969896;
+$dark-cs: #969896;
+$dark-gd: #c66;
+$dark-gh: #c5c8c6;
+$dark-gi: #b5bd68;
+$dark-gp: #969896;
+$dark-gu: #8abeb7;
+$dark-kc: #b294bb;
+$dark-kd: #b294bb;
+$dark-kn: #8abeb7;
+$dark-kp: #b294bb;
+$dark-kr: #b294bb;
+$dark-kt: #f0c674;
+$dark-ld: #b5bd68;
+$dark-m: #de935f;
+$dark-s: #b5bd68;
+$dark-na: #81a2be;
+$dark-nb: #c5c8c6;
+$dark-nc: #f0c674;
+$dark-no: #c66;
+$dark-nd: #8abeb7;
+$dark-ni: #c5c8c6;
+$dark-ne: #c66;
+$dark-nf: #81a2be;
+$dark-nl: #c5c8c6;
+$dark-nn: #f0c674;
+$dark-nx: #81a2be;
+$dark-py: #c5c8c6;
+$dark-nt: #8abeb7;
+$dark-nv: #c66;
+$dark-ow: #8abeb7;
+$dark-w: #c5c8c6;
+$dark-mf: #de935f;
+$dark-mh: #de935f;
+$dark-mi: #de935f;
+$dark-mo: #de935f;
+$dark-sb: #b5bd68;
+$dark-sc: #c5c8c6;
+$dark-sd: #969896;
+$dark-s2: #b5bd68;
+$dark-se: #de935f;
+$dark-sh: #b5bd68;
+$dark-si: #de935f;
+$dark-sx: #b5bd68;
+$dark-sr: #b5bd68;
+$dark-s1: #b5bd68;
+$dark-ss: #b5bd68;
+$dark-bp: #c5c8c6;
+$dark-vc: #c66;
+$dark-vg: #c66;
+$dark-vi: #c66;
+$dark-il: #de935f;
+
+.code.dark {
+ // Line numbers
+ .line-numbers,
+ .diff-line-num {
+ background-color: $dark-main-bg;
+ }
+
+ .diff-line-num,
+ .diff-line-num a {
+ color: $dark-line-num-color;
+ }
+
+ // Code itself
+ pre.code,
+ .diff-line-num {
+ border-color: $dark-code-border;
+ }
+
+ &,
+ pre.code,
+ .line_holder .line_content {
+ background-color: $dark-main-bg;
+ color: $dark-line-color;
+ }
+
+ // Diff line
+ .line_holder {
+ &.match .line_content,
+ &.old-nonewline .line_content,
+ &.new-nonewline .line_content {
+ @include dark-diff-match-line;
+ }
+
+ td.diff-line-num.hll:not(.empty-cell),
+ td.line_content.hll:not(.empty-cell) {
+ background-color: $dark-diff-not-empty-bg;
+ border-color: darken($dark-diff-not-empty-bg, 15%);
+ }
+
+ .diff-line-num.new,
+ .line_content.new {
+ @include diff-background($dark-new-bg, $dark-new-idiff, $dark-border);
+
+ &::before,
+ a {
+ color: $dark-line-num-color-new;
+ }
+ }
+
+ .diff-line-num.old,
+ .line_content.old {
+ @include diff-background($dark-old-bg, $dark-old-idiff, $dark-border);
+
+ &::before,
+ a {
+ color: $dark-line-num-color-old;
+ }
+ }
+
+ .diff-line-num {
+ &.is-over,
+ &.hll:not(.empty-cell).is-over {
+ background-color: $dark-over-bg;
+ border-color: darken($dark-over-bg, 5%);
+
+ a {
+ color: darken($dark-over-bg, 15%);
+ }
+ }
+ }
+
+ .line_content.match {
+ @include dark-diff-match-line;
+ }
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $black;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $dark-expanded-bg;
+ border-color: $dark-expanded-bg;
+ }
+ }
+ }
+
+ // highlight line via anchor
+ pre .hll {
+ background-color: $dark-pre-hll-bg !important;
+ }
+
+ // Search result highlight
+ span.highlight_word {
+ background-color: $dark-highlight-bg !important;
+ color: $dark-highlight-color !important;
+ }
+
+ // Links to URLs, emails, or dependencies
+ .line a {
+ color: $dark-na;
+ }
+
+ .hll { background-color: $dark-hll-bg; }
+ .c { color: $dark-c; } /* Comment */
+ .err { color: $dark-err; } /* Error */
+ .k { color: $dark-k; } /* Keyword */
+ .l { color: $dark-l; } /* Literal */
+ .n { color: $dark-n; } /* Name */
+ .o { color: $dark-o; } /* Operator */
+ .p { color: $dark-p; } /* Punctuation */
+ .cm { color: $dark-cm; } /* Comment.Multiline */
+ .cp { color: $dark-cp; } /* Comment.Preproc */
+ .c1 { color: $dark-c1; } /* Comment.Single */
+ .cs { color: $dark-cs; } /* Comment.Special */
+ .gd { color: $dark-gd; } /* Generic.Deleted */
+ .ge { font-style: italic; } /* Generic.Emph */
+ .gh { color: $dark-gh; font-weight: $gl-font-weight-bold; } /* Generic.Heading */
+ .gi { color: $dark-gi; } /* Generic.Inserted */
+ .gp { color: $dark-gp; font-weight: $gl-font-weight-bold; } /* Generic.Prompt */
+ .gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */
+ .gu { color: $dark-gu; font-weight: $gl-font-weight-bold; } /* Generic.Subheading */
+ .kc { color: $dark-kc; } /* Keyword.Constant */
+ .kd { color: $dark-kd; } /* Keyword.Declaration */
+ .kn { color: $dark-kn; } /* Keyword.Namespace */
+ .kp { color: $dark-kp; } /* Keyword.Pseudo */
+ .kr { color: $dark-kr; } /* Keyword.Reserved */
+ .kt { color: $dark-kt; } /* Keyword.Type */
+ .ld { color: $dark-ld; } /* Literal.Date */
+ .m { color: $dark-m; } /* Literal.Number */
+ .s { color: $dark-s; } /* Literal.String */
+ .na { color: $dark-na; } /* Name.Attribute */
+ .nb { color: $dark-nb; } /* Name.Builtin */
+ .nc { color: $dark-nc; } /* Name.Class */
+ .no { color: $dark-no; } /* Name.Constant */
+ .nd { color: $dark-nd; } /* Name.Decorator */
+ .ni { color: $dark-ni; } /* Name.Entity */
+ .ne { color: $dark-ne; } /* Name.Exception */
+ .nf { color: $dark-nf; } /* Name.Function */
+ .nl { color: $dark-nl; } /* Name.Label */
+ .nn { color: $dark-nn; } /* Name.Namespace */
+ .nx { color: $dark-nx; } /* Name.Other */
+ .py { color: $dark-py; } /* Name.Property */
+ .nt { color: $dark-nt; } /* Name.Tag */
+ .nv { color: $dark-nv; } /* Name.Variable */
+ .ow { color: $dark-ow; } /* Operator.Word */
+ .w { color: $dark-w; } /* Text.Whitespace */
+ .mf { color: $dark-mf; } /* Literal.Number.Float */
+ .mh { color: $dark-mh; } /* Literal.Number.Hex */
+ .mi { color: $dark-mi; } /* Literal.Number.Integer */
+ .mo { color: $dark-mo; } /* Literal.Number.Oct */
+ .sb { color: $dark-sb; } /* Literal.String.Backtick */
+ .sc { color: $dark-sc; } /* Literal.String.Char */
+ .sd { color: $dark-sd; } /* Literal.String.Doc */
+ .s2 { color: $dark-s2; } /* Literal.String.Double */
+ .se { color: $dark-se; } /* Literal.String.Escape */
+ .sh { color: $dark-sh; } /* Literal.String.Heredoc */
+ .si { color: $dark-si; } /* Literal.String.Interpol */
+ .sx { color: $dark-sx; } /* Literal.String.Other */
+ .sr { color: $dark-sr; } /* Literal.String.Regex */
+ .s1 { color: $dark-s1; } /* Literal.String.Single */
+ .ss { color: $dark-ss; } /* Literal.String.Symbol */
+ .bp { color: $dark-bp; } /* Name.Builtin.Pseudo */
+ .vc { color: $dark-vc; } /* Name.Variable.Class */
+ .vg { color: $dark-vg; } /* Name.Variable.Global */
+ .vi { color: $dark-vi; } /* Name.Variable.Instance */
+ .il { color: $dark-il; } /* Literal.Number.Integer.Long */
+}
diff --git a/app/assets/stylesheets/highlight/themes/monokai.scss b/app/assets/stylesheets/highlight/themes/monokai.scss
new file mode 100644
index 00000000000..37fe61b925c
--- /dev/null
+++ b/app/assets/stylesheets/highlight/themes/monokai.scss
@@ -0,0 +1,259 @@
+/* https://github.com/richleland/pygments-css/blob/master/monokai.css */
+
+@import "../common";
+
+/*
+* Monokai Colors
+*/
+$monokai-bg: #272822;
+$monokai-border: #555;
+$monokai-text-color: #f8f8f2;
+$monokai-line-num-color: rgba(255, 255, 255, 0.3);
+$monokai-line-num-color-new: #707565;
+$monokai-line-num-color-old: #7e736f;
+$monokai-line-empty-bg: #49483e;
+$monokai-line-empty-border: darken($monokai-line-empty-bg, 15%);
+$monokai-diff-border: #808080;
+$monokai-highlight-bg: #ffe792;
+$monokai-over-bg: #9f9ab5;
+$monokai-expanded-bg: #3e3e3e;
+
+$monokai-new-bg: rgba(166, 226, 46, 0.1);
+$monokai-new-idiff: rgba(166, 226, 46, 0.15);
+
+$monokai-old-bg: rgba(254, 147, 140, 0.15);
+$monokai-old-idiff: rgba(254, 147, 140, 0.2);
+
+$monokai-hll: #49483e;
+$monokai-c: #75715e;
+$monokai-err-color: #960050;
+$monokai-err-bg: #1e0010;
+$monokai-k: #66d9ef;
+$monokai-l: #ae81ff;
+$monokai-n: #f8f8f2;
+$monokai-o: #f92672;
+$monokai-p: #f8f8f2;
+$monokai-cm: #75715e;
+$monokai-cp: #75715e;
+$monokai-c1: #75715e;
+$monokai-cs: #75715e;
+$monokai-kc: #66d9ef;
+$monokai-kd: #66d9ef;
+$monokai-kn: #f92672;
+$monokai-kp: #66d9ef;
+$monokai-kr: #66d9ef;
+$monokai-kt: #66d9ef;
+$monokai-ld: #e6db74;
+$monokai-m: #ae81ff;
+$monokai-s: #e6db74;
+$monokai-na: #a6e22e;
+$monokai-nb: #f8f8f2;
+$monokai-nc: #a6e22e;
+$monokai-no: #66d9ef;
+$monokai-nd: #a6e22e;
+$monokai-ni: #f8f8f2;
+$monokai-ne: #a6e22e;
+$monokai-nf: #a6e22e;
+$monokai-nl: #f8f8f2;
+$monokai-nn: #f8f8f2;
+$monokai-nx: #a6e22e;
+$monokai-py: #f8f8f2;
+$monokai-nt: #f92672;
+$monokai-nv: #f8f8f2;
+$monokai-ow: #f92672;
+$monokai-w: #f8f8f2;
+$monokai-mf: #ae81ff;
+$monokai-mh: #ae81ff;
+$monokai-mi: #ae81ff;
+$monokai-mo: #ae81ff;
+$monokai-sb: #e6db74;
+$monokai-sc: #e6db74;
+$monokai-sd: #e6db74;
+$monokai-s2: #e6db74;
+$monokai-se: #ae81ff;
+$monokai-sh: #e6db74;
+$monokai-si: #e6db74;
+$monokai-sx: #e6db74;
+$monokai-sr: #e6db74;
+$monokai-s1: #e6db74;
+$monokai-ss: #e6db74;
+$monokai-bp: #f8f8f2;
+$monokai-vc: #f8f8f2;
+$monokai-vg: #f8f8f2;
+$monokai-vi: #f8f8f2;
+$monokai-il: #ae81ff;
+$monokai-gu: #75715e;
+$monokai-gd: #f92672;
+$monokai-gi: #a6e22e;
+
+.code.monokai {
+ // Line numbers
+ .line-numbers,
+ .diff-line-num {
+ background-color: $monokai-bg;
+ }
+
+ .diff-line-num,
+ .diff-line-num a {
+ color: $monokai-line-num-color;
+ }
+
+ // Code itself
+ pre.code,
+ .diff-line-num {
+ border-color: $monokai-border;
+ }
+
+ &,
+ pre.code,
+ .line_holder .line_content {
+ background-color: $monokai-bg;
+ color: $monokai-text-color;
+ }
+
+ // Diff line
+ .line_holder {
+ &.match .line_content,
+ &.old-nonewline .line_content,
+ &.new-nonewline .line_content {
+ @include dark-diff-match-line;
+ }
+
+ td.diff-line-num.hll:not(.empty-cell),
+ td.line_content.hll:not(.empty-cell) {
+ background-color: $monokai-line-empty-bg;
+ border-color: $monokai-line-empty-border;
+ }
+
+ .diff-line-num.new,
+ .line_content.new {
+ @include diff-background($monokai-new-bg, $monokai-new-idiff, $monokai-diff-border);
+
+ &::before,
+ a {
+ color: $monokai-line-num-color-new;
+ }
+ }
+
+ .diff-line-num.old,
+ .line_content.old {
+ @include diff-background($monokai-old-bg, $monokai-old-idiff, $monokai-diff-border);
+
+ &::before,
+ a {
+ color: $monokai-line-num-color-old;
+ }
+ }
+
+ .diff-line-num {
+ &.is-over,
+ &.hll:not(.empty-cell).is-over {
+ background-color: $monokai-over-bg;
+ border-color: darken($monokai-over-bg, 5%);
+
+ a {
+ color: darken($monokai-over-bg, 15%);
+ }
+ }
+ }
+
+ .line_content.match {
+ @include dark-diff-match-line;
+ }
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $black;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $monokai-expanded-bg;
+ border-color: $monokai-expanded-bg;
+ }
+ }
+ }
+
+ // highlight line via anchor
+ pre .hll {
+ background-color: $monokai-hll !important;
+ }
+
+ // Search result highlight
+ span.highlight_word {
+ background-color: $monokai-highlight-bg !important;
+ color: $black !important;
+ }
+
+ // Links to URLs, emails, or dependencies
+ .line a {
+ color: $monokai-k;
+ }
+
+ .hll { background-color: $monokai-hll; }
+ .c { color: $monokai-c; } /* Comment */
+ .err { color: $monokai-err-color; background-color: $monokai-err-bg; } /* Error */
+ .k { color: $monokai-k; } /* Keyword */
+ .l { color: $monokai-l; } /* Literal */
+ .n { color: $monokai-n; } /* Name */
+ .o { color: $monokai-o; } /* Operator */
+ .p { color: $monokai-p; } /* Punctuation */
+ .cm { color: $monokai-cm; } /* Comment.Multiline */
+ .cp { color: $monokai-cp; } /* Comment.Preproc */
+ .c1 { color: $monokai-c1; } /* Comment.Single */
+ .cs { color: $monokai-cs; } /* Comment.Special */
+ .ge { font-style: italic; } /* Generic.Emph */
+ .gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */
+ .kc { color: $monokai-kc; } /* Keyword.Constant */
+ .kd { color: $monokai-kd; } /* Keyword.Declaration */
+ .kn { color: $monokai-kn; } /* Keyword.Namespace */
+ .kp { color: $monokai-kp; } /* Keyword.Pseudo */
+ .kr { color: $monokai-kr; } /* Keyword.Reserved */
+ .kt { color: $monokai-kt; } /* Keyword.Type */
+ .ld { color: $monokai-ld; } /* Literal.Date */
+ .m { color: $monokai-m; } /* Literal.Number */
+ .s { color: $monokai-s; } /* Literal.String */
+ .na { color: $monokai-na; } /* Name.Attribute */
+ .nb { color: $monokai-nb; } /* Name.Builtin */
+ .nc { color: $monokai-nc; } /* Name.Class */
+ .no { color: $monokai-no; } /* Name.Constant */
+ .nd { color: $monokai-nd; } /* Name.Decorator */
+ .ni { color: $monokai-ni; } /* Name.Entity */
+ .ne { color: $monokai-ne; } /* Name.Exception */
+ .nf { color: $monokai-nf; } /* Name.Function */
+ .nl { color: $monokai-nl; } /* Name.Label */
+ .nn { color: $monokai-nn; } /* Name.Namespace */
+ .nx { color: $monokai-nx; } /* Name.Other */
+ .py { color: $monokai-py; } /* Name.Property */
+ .nt { color: $monokai-nt; } /* Name.Tag */
+ .nv { color: $monokai-nv; } /* Name.Variable */
+ .ow { color: $monokai-ow; } /* Operator.Word */
+ .w { color: $monokai-w; } /* Text.Whitespace */
+ .mf { color: $monokai-mf; } /* Literal.Number.Float */
+ .mh { color: $monokai-mh; } /* Literal.Number.Hex */
+ .mi { color: $monokai-mi; } /* Literal.Number.Integer */
+ .mo { color: $monokai-mo; } /* Literal.Number.Oct */
+ .sb { color: $monokai-sb; } /* Literal.String.Backtick */
+ .sc { color: $monokai-sc; } /* Literal.String.Char */
+ .sd { color: $monokai-sd; } /* Literal.String.Doc */
+ .s2 { color: $monokai-s2; } /* Literal.String.Double */
+ .se { color: $monokai-se; } /* Literal.String.Escape */
+ .sh { color: $monokai-sh; } /* Literal.String.Heredoc */
+ .si { color: $monokai-si; } /* Literal.String.Interpol */
+ .sx { color: $monokai-sx; } /* Literal.String.Other */
+ .sr { color: $monokai-sr; } /* Literal.String.Regex */
+ .s1 { color: $monokai-s1; } /* Literal.String.Single */
+ .ss { color: $monokai-ss; } /* Literal.String.Symbol */
+ .bp { color: $monokai-bp; } /* Name.Builtin.Pseudo */
+ .vc { color: $monokai-vc; } /* Name.Variable.Class */
+ .vg { color: $monokai-vg; } /* Name.Variable.Global */
+ .vi { color: $monokai-vi; } /* Name.Variable.Instance */
+ .il { color: $monokai-il; } /* Literal.Number.Integer.Long */
+ .gu { color: $monokai-gu; } /* Generic.Subheading & Diff Unified/Comment? */
+ .gd { color: $monokai-gd; } /* Generic.Deleted & Diff Deleted */
+ .gi { color: $monokai-gi; } /* Generic.Inserted & Diff Inserted */
+}
diff --git a/app/assets/stylesheets/highlight/themes/none.scss b/app/assets/stylesheets/highlight/themes/none.scss
new file mode 100644
index 00000000000..b4217aac37a
--- /dev/null
+++ b/app/assets/stylesheets/highlight/themes/none.scss
@@ -0,0 +1,238 @@
+/*
+* None Syntax Colors
+*/
+
+@import "../common";
+
+@mixin match-line {
+ color: $black-transparent;
+ background-color: $white-normal;
+}
+
+.code.none {
+ // Line numbers
+ .line-numbers,
+ .diff-line-num {
+ background-color: $gray-light;
+ }
+
+ .diff-line-num,
+ .diff-line-num a {
+ color: $black-transparent;
+ }
+
+ // Code itself
+ pre.code,
+ .diff-line-num {
+ border-color: $white-normal;
+ }
+
+ &,
+ pre.code,
+ .line_holder .line_content {
+ background-color: $white-light;
+ color: $gl-text-color;
+ }
+
+// Diff line
+
+ $none-over-bg: #ded7fc;
+ $none-expanded-border: #e0e0e0;
+ $none-expanded-bg: #e0e0e0;
+
+ .line_holder {
+
+ &.match .line_content,
+ .new-nonewline.line_content,
+ .old-nonewline.line_content {
+ @include match-line;
+ }
+
+ .diff-line-num {
+ &.old {
+ a {
+ color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%);
+ }
+ }
+
+ &.new {
+ a {
+ color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%);
+ }
+ }
+
+ &.is-over,
+ &.hll:not(.empty-cell).is-over {
+ background-color: $none-over-bg;
+ border-color: darken($none-over-bg, 5%);
+
+ a {
+ color: darken($none-over-bg, 15%);
+ }
+ }
+
+ &.hll:not(.empty-cell) {
+ background-color: $white-light;
+ border-color: $white-normal;
+ }
+ }
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $none-expanded-border;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $none-expanded-bg;
+ border-color: $none-expanded-bg;
+ }
+ }
+
+ .line_content {
+ &.old {
+ background-color: $white-normal;
+
+ &::before {
+ color: $gl-text-color;
+ }
+
+ span.idiff {
+ background-color: $white-normal;
+ text-decoration: underline;
+ }
+ }
+
+ &.new {
+ background-color: $white-normal;
+
+ &::before {
+ color: $gl-text-color;
+ }
+
+ span.idiff {
+ background-color: $white-normal;
+ text-decoration: underline;
+ }
+ }
+
+ &.match {
+ @include match-line;
+ }
+
+ &.hll:not(.empty-cell) {
+ background-color: $white-normal;
+ }
+ }
+ }
+
+ // highlight line via anchor
+ pre .hll {
+ background-color: $white-normal;
+ }
+
+ // Search result highlight
+ span.highlight_word {
+ background-color: $white-normal;
+ }
+
+ // Links to URLs, emails, or dependencies
+ .line a {
+ color: $gl-text-color;
+ text-decoration: underline;
+ }
+
+ .hll { background-color: $white-light; }
+
+ .gd {
+ color: $gl-text-color;
+ background-color: $white-light;
+
+ .x {
+ color: $gl-text-color;
+ background-color: $white-light;
+ }
+ }
+
+ .gi {
+ color: $gl-text-color;
+ background-color: $white-light;
+
+ .x {
+ color: $gl-text-color;
+ background-color: $white-light;
+ }
+ }
+
+ .c { color: $gl-text-color; } /* Comment */
+ .err { color: $gl-text-color; } /* Error */
+ .g { color: $gl-text-color; } /* Generic */
+ .k { color: $gl-text-color; } /* Keyword */
+ .l { color: $gl-text-color; } /* Literal */
+ .n { color: $gl-text-color; } /* Name */
+ .o { color: $gl-text-color; } /* Operator */
+ .x { color: $gl-text-color; } /* Other */
+ .p { color: $gl-text-color; } /* Punctuation */
+ .cm { color: $gl-text-color; } /* Comment.Multiline */
+ .cp { color: $gl-text-color; } /* Comment.Preproc */
+ .c1 { color: $gl-text-color; } /* Comment.Single */
+ .cs { color: $gl-text-color; } /* Comment.Special */
+ .ge { color: $gl-text-color; } /* Generic.Emph */
+ .gr { color: $gl-text-color; } /* Generic.Error */
+ .gh { color: $gl-text-color; } /* Generic.Heading */
+ .go { color: $gl-text-color; } /* Generic.Output */
+ .gp { color: $gl-text-color; } /* Generic.Prompt */
+ .gs { color: $gl-text-color; } /* Generic.Strong */
+ .gu { color: $gl-text-color; } /* Generic.Subheading */
+ .gt { color: $gl-text-color; } /* Generic.Traceback */
+ .kc { color: $gl-text-color; } /* Keyword.Constant */
+ .kd { color: $gl-text-color; } /* Keyword.Declaration */
+ .kn { color: $gl-text-color; } /* Keyword.Namespace */
+ .kp { color: $gl-text-color; } /* Keyword.Pseudo */
+ .kr { color: $gl-text-color; } /* Keyword.Reserved */
+ .kt { color: $gl-text-color; } /* Keyword.Type */
+ .ld { color: $gl-text-color; } /* Literal.Date */
+ .m { color: $gl-text-color; } /* Literal.Number */
+ .s { color: $gl-text-color; } /* Literal.String */
+ .na { color: $gl-text-color; } /* Name.Attribute */
+ .nb { color: $gl-text-color; } /* Name.Builtin */
+ .nc { color: $gl-text-color; } /* Name.Class */
+ .no { color: $gl-text-color; } /* Name.Constant */
+ .nd { color: $gl-text-color; } /* Name.Decorator */
+ .ni { color: $gl-text-color; } /* Name.Entity */
+ .ne { color: $gl-text-color; } /* Name.Exception */
+ .nf { color: $gl-text-color; } /* Name.Function */
+ .nl { color: $gl-text-color; } /* Name.Label */
+ .nn { color: $gl-text-color; } /* Name.Namespace */
+ .nx { color: $gl-text-color; } /* Name.Other */
+ .py { color: $gl-text-color; } /* Name.Property */
+ .nt { color: $gl-text-color; } /* Name.Tag */
+ .nv { color: $gl-text-color; } /* Name.Variable */
+ .ow { color: $gl-text-color; } /* Operator.Word */
+ .w { color: $gl-text-color; } /* Text.Whitespace */
+ .mf { color: $gl-text-color; } /* Literal.Number.Float */
+ .mh { color: $gl-text-color; } /* Literal.Number.Hex */
+ .mi { color: $gl-text-color; } /* Literal.Number.Integer */
+ .mo { color: $gl-text-color; } /* Literal.Number.Oct */
+ .sb { color: $gl-text-color; } /* Literal.String.Backtick */
+ .sc { color: $gl-text-color; } /* Literal.String.Char */
+ .sd { color: $gl-text-color; } /* Literal.String.Doc */
+ .s2 { color: $gl-text-color; } /* Literal.String.Double */
+ .se { color: $gl-text-color; } /* Literal.String.Escape */
+ .sh { color: $gl-text-color; } /* Literal.String.Heredoc */
+ .si { color: $gl-text-color; } /* Literal.String.Interpol */
+ .sx { color: $gl-text-color; } /* Literal.String.Other */
+ .sr { color: $gl-text-color; } /* Literal.String.Regex */
+ .s1 { color: $gl-text-color; } /* Literal.String.Single */
+ .ss { color: $gl-text-color; } /* Literal.String.Symbol */
+ .bp { color: $gl-text-color; } /* Name.Builtin.Pseudo */
+ .vc { color: $gl-text-color; } /* Name.Variable.Class */
+ .vg { color: $gl-text-color; } /* Name.Variable.Global */
+ .vi { color: $gl-text-color; } /* Name.Variable.Instance */
+ .il { color: $gl-text-color; } /* Literal.Number.Integer.Long */
+
+}
diff --git a/app/assets/stylesheets/highlight/themes/solarized-dark.scss b/app/assets/stylesheets/highlight/themes/solarized-dark.scss
new file mode 100644
index 00000000000..a4e9eda22c9
--- /dev/null
+++ b/app/assets/stylesheets/highlight/themes/solarized-dark.scss
@@ -0,0 +1,286 @@
+/* https://gist.github.com/qguv/7936275 */
+
+@import "../common";
+
+/*
+* Solarized dark colors
+*/
+$solarized-dark-new-bg: rgba(133, 153, 0, 0.15);
+$solarized-dark-new-idiff: rgba(133, 153, 0, 0.25);
+$solarized-dark-old-bg: rgba(220, 50, 47, 0.3);
+$solarized-dark-old-idiff: rgba(220, 50, 47, 0.25);
+$solarized-dark-border: #113b46;
+$solarized-dark-pre-bg: #002b36;
+$solarized-dark-pre-color: #93a1a1;
+$solarized-dark-pre-border: #113b46;
+$solarized-dark-line-bg: #002b36;
+$solarized-dark-line-color: rgba(255, 255, 255, 0.3);
+$solarized-dark-line-color-new: #5a766c;
+$solarized-dark-line-color-old: #7a6c71;
+$solarized-dark-highlight: #094554;
+$solarized-dark-hll-bg: #174652;
+$solarized-dark-over-bg: #9f9ab5;
+$solarized-dark-expanded-bg: #010d10;
+$solarized-dark-c: #586e75;
+$solarized-dark-err: #93a1a1;
+$solarized-dark-g: #93a1a1;
+$solarized-dark-k: #859900;
+$solarized-dark-l: #93a1a1;
+$solarized-dark-n: #93a1a1;
+$solarized-dark-o: #859900;
+$solarized-dark-x: #cb4b16;
+$solarized-dark-p: #93a1a1;
+$solarized-dark-cm: #586e75;
+$solarized-dark-cp: #859900;
+$solarized-dark-c1: #586e75;
+$solarized-dark-cs: #859900;
+$solarized-dark-gd: #2aa198;
+$solarized-dark-ge: #93a1a1;
+$solarized-dark-gr: #dc322f;
+$solarized-dark-gh: #cb4b16;
+$solarized-dark-gi: #859900;
+$solarized-dark-go: #93a1a1;
+$solarized-dark-gp: #93a1a1;
+$solarized-dark-gs: #93a1a1;
+$solarized-dark-gu: #cb4b16;
+$solarized-dark-gt: #93a1a1;
+$solarized-dark-kc: #cb4b16;
+$solarized-dark-kd: #268bd2;
+$solarized-dark-kn: #859900;
+$solarized-dark-kp: #859900;
+$solarized-dark-kr: #268bd2;
+$solarized-dark-kt: #dc322f;
+$solarized-dark-ld: #93a1a1;
+$solarized-dark-m: #2aa198;
+$solarized-dark-s: #2aa198;
+$solarized-dark-na: #93a1a1;
+$solarized-dark-nb: #b58900;
+$solarized-dark-nc: #268bd2;
+$solarized-dark-no: #cb4b16;
+$solarized-dark-nd: #268bd2;
+$solarized-dark-ni: #cb4b16;
+$solarized-dark-ne: #cb4b16;
+$solarized-dark-nf: #268bd2;
+$solarized-dark-nl: #93a1a1;
+$solarized-dark-nn: #93a1a1;
+$solarized-dark-nx: #93a1a1;
+$solarized-dark-py: #93a1a1;
+$solarized-dark-nt: #268bd2;
+$solarized-dark-nv: #268bd2;
+$solarized-dark-ow: #859900;
+$solarized-dark-w: #93a1a1;
+$solarized-dark-mf: #2aa198;
+$solarized-dark-mh: #2aa198;
+$solarized-dark-mi: #2aa198;
+$solarized-dark-mo: #2aa198;
+$solarized-dark-sb: #586e75;
+$solarized-dark-sc: #2aa198;
+$solarized-dark-sd: #93a1a1;
+$solarized-dark-s2: #2aa198;
+$solarized-dark-se: #cb4b16;
+$solarized-dark-sh: #93a1a1;
+$solarized-dark-si: #2aa198;
+$solarized-dark-sx: #2aa198;
+$solarized-dark-sr: #dc322f;
+$solarized-dark-s1: #2aa198;
+$solarized-dark-ss: #2aa198;
+$solarized-dark-bp: #268bd2;
+$solarized-dark-vc: #268bd2;
+$solarized-dark-vg: #268bd2;
+$solarized-dark-vi: #268bd2;
+$solarized-dark-il: #2aa198;
+
+.code.solarized-dark {
+ // Line numbers
+ .line-numbers,
+ .diff-line-num {
+ background-color: $solarized-dark-line-bg;
+ }
+
+ .diff-line-num,
+ .diff-line-num a {
+ color: $solarized-dark-line-color;
+ }
+
+ // Code itself
+ pre.code,
+ .diff-line-num {
+ border-color: $solarized-dark-pre-border;
+ }
+
+ &,
+ pre.code,
+ .line_holder .line_content {
+ background-color: $solarized-dark-pre-bg;
+ color: $solarized-dark-pre-color;
+ }
+
+ // Diff line
+ .line_holder {
+ &.match .line_content,
+ &.old-nonewline .line_content,
+ &.new-nonewline .line_content {
+ @include dark-diff-match-line;
+ }
+
+ td.diff-line-num.hll:not(.empty-cell),
+ td.line_content.hll:not(.empty-cell) {
+ background-color: $solarized-dark-hll-bg;
+ border-color: darken($solarized-dark-hll-bg, 15%);
+ }
+
+ .diff-line-num.new,
+ .line_content.new {
+ @include diff-background($solarized-dark-new-bg, $solarized-dark-new-idiff, $solarized-dark-border);
+
+ &::before,
+ a {
+ color: $solarized-dark-line-color-new;
+ }
+ }
+
+ .diff-line-num.old,
+ .line_content.old {
+ @include diff-background($solarized-dark-old-bg, $solarized-dark-old-idiff, $solarized-dark-border);
+
+ &::before,
+ a {
+ color: $solarized-dark-line-color-old;
+ }
+ }
+
+ .diff-line-num {
+ &.is-over,
+ &.hll:not(.empty-cell).is-over {
+ background-color: $solarized-dark-over-bg;
+ border-color: darken($solarized-dark-over-bg, 5%);
+
+ a {
+ color: darken($solarized-dark-over-bg, 15%);
+ }
+ }
+ }
+
+ .line_content.match {
+ @include dark-diff-match-line;
+ }
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $black;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $solarized-dark-expanded-bg;
+ border-color: $solarized-dark-expanded-bg;
+ }
+ }
+ }
+
+ // highlight line via anchor
+ pre .hll {
+ background-color: $solarized-dark-hll-bg !important;
+ }
+
+ // Search result highlight
+ span.highlight_word {
+ background-color: $solarized-dark-highlight !important;
+ }
+
+ // Links to URLs, emails, or dependencies
+ .line a {
+ color: $solarized-dark-kd;
+ }
+
+ /* Solarized Dark
+
+ For use with Jekyll and Pygments
+
+ http://ethanschoonover.com/solarized
+
+ SOLARIZED HEX ROLE
+ --------- -------- ------------------------------------------
+ base03 #002b36 background
+ base01 #586e75 comments / secondary content
+ base1 #93a1a1 body text / default code / primary content
+ orange #cb4b16 constants
+ red #dc322f regex, special keywords
+ blue #268bd2 reserved keywords
+ cyan #2aa198 strings, numbers
+ green #859900 operators, other keywords
+ */
+
+ .c { color: $solarized-dark-c; } /* Comment */
+ .err { color: $solarized-dark-err; } /* Error */
+ .g { color: $solarized-dark-g; } /* Generic */
+ .k { color: $solarized-dark-k; } /* Keyword */
+ .l { color: $solarized-dark-l; } /* Literal */
+ .n { color: $solarized-dark-n; } /* Name */
+ .o { color: $solarized-dark-o; } /* Operator */
+ .x { color: $solarized-dark-x; } /* Other */
+ .p { color: $solarized-dark-p; } /* Punctuation */
+ .cm { color: $solarized-dark-cm; } /* Comment.Multiline */
+ .cp { color: $solarized-dark-cp; } /* Comment.Preproc */
+ .c1 { color: $solarized-dark-c1; } /* Comment.Single */
+ .cs { color: $solarized-dark-cs; } /* Comment.Special */
+ .gd { color: $solarized-dark-gd; } /* Generic.Deleted */
+ .ge { color: $solarized-dark-ge; font-style: italic; } /* Generic.Emph */
+ .gr { color: $solarized-dark-gr; } /* Generic.Error */
+ .gh { color: $solarized-dark-gh; } /* Generic.Heading */
+ .gi { color: $solarized-dark-gi; } /* Generic.Inserted */
+ .go { color: $solarized-dark-go; } /* Generic.Output */
+ .gp { color: $solarized-dark-gp; } /* Generic.Prompt */
+ .gs { color: $solarized-dark-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */
+ .gu { color: $solarized-dark-gu; } /* Generic.Subheading */
+ .gt { color: $solarized-dark-gt; } /* Generic.Traceback */
+ .kc { color: $solarized-dark-kc; } /* Keyword.Constant */
+ .kd { color: $solarized-dark-kd; } /* Keyword.Declaration */
+ .kn { color: $solarized-dark-kn; } /* Keyword.Namespace */
+ .kp { color: $solarized-dark-kp; } /* Keyword.Pseudo */
+ .kr { color: $solarized-dark-kr; } /* Keyword.Reserved */
+ .kt { color: $solarized-dark-kt; } /* Keyword.Type */
+ .ld { color: $solarized-dark-ld; } /* Literal.Date */
+ .m { color: $solarized-dark-m; } /* Literal.Number */
+ .s { color: $solarized-dark-s; } /* Literal.String */
+ .na { color: $solarized-dark-na; } /* Name.Attribute */
+ .nb { color: $solarized-dark-nb; } /* Name.Builtin */
+ .nc { color: $solarized-dark-nc; } /* Name.Class */
+ .no { color: $solarized-dark-no; } /* Name.Constant */
+ .nd { color: $solarized-dark-nd; } /* Name.Decorator */
+ .ni { color: $solarized-dark-ni; } /* Name.Entity */
+ .ne { color: $solarized-dark-ne; } /* Name.Exception */
+ .nf { color: $solarized-dark-nf; } /* Name.Function */
+ .nl { color: $solarized-dark-nl; } /* Name.Label */
+ .nn { color: $solarized-dark-nn; } /* Name.Namespace */
+ .nx { color: $solarized-dark-nx; } /* Name.Other */
+ .py { color: $solarized-dark-py; } /* Name.Property */
+ .nt { color: $solarized-dark-nt; } /* Name.Tag */
+ .nv { color: $solarized-dark-nv; } /* Name.Variable */
+ .ow { color: $solarized-dark-ow; } /* Operator.Word */
+ .w { color: $solarized-dark-w; } /* Text.Whitespace */
+ .mf { color: $solarized-dark-mf; } /* Literal.Number.Float */
+ .mh { color: $solarized-dark-mh; } /* Literal.Number.Hex */
+ .mi { color: $solarized-dark-mi; } /* Literal.Number.Integer */
+ .mo { color: $solarized-dark-mo; } /* Literal.Number.Oct */
+ .sb { color: $solarized-dark-sb; } /* Literal.String.Backtick */
+ .sc { color: $solarized-dark-sc; } /* Literal.String.Char */
+ .sd { color: $solarized-dark-sd; } /* Literal.String.Doc */
+ .s2 { color: $solarized-dark-s2; } /* Literal.String.Double */
+ .se { color: $solarized-dark-se; } /* Literal.String.Escape */
+ .sh { color: $solarized-dark-sh; } /* Literal.String.Heredoc */
+ .si { color: $solarized-dark-si; } /* Literal.String.Interpol */
+ .sx { color: $solarized-dark-sx; } /* Literal.String.Other */
+ .sr { color: $solarized-dark-sr; } /* Literal.String.Regex */
+ .s1 { color: $solarized-dark-s1; } /* Literal.String.Single */
+ .ss { color: $solarized-dark-ss; } /* Literal.String.Symbol */
+ .bp { color: $solarized-dark-bp; } /* Name.Builtin.Pseudo */
+ .vc { color: $solarized-dark-vc; } /* Name.Variable.Class */
+ .vg { color: $solarized-dark-vg; } /* Name.Variable.Global */
+ .vi { color: $solarized-dark-vi; } /* Name.Variable.Instance */
+ .il { color: $solarized-dark-il; } /* Literal.Number.Integer.Long */
+}
diff --git a/app/assets/stylesheets/highlight/themes/solarized-light.scss b/app/assets/stylesheets/highlight/themes/solarized-light.scss
new file mode 100644
index 00000000000..b604d1ccb6c
--- /dev/null
+++ b/app/assets/stylesheets/highlight/themes/solarized-light.scss
@@ -0,0 +1,294 @@
+/* https://gist.github.com/qguv/7936275 */
+
+@import "../common";
+
+/*
+* Solarized light syntax colors
+*/
+$solarized-light-matchline-bg: rgba(255, 255, 255, 0.4);
+$solarized-light-new-bg: rgba(133, 153, 0, 0.2);
+$solarized-light-new-idiff: rgba(133, 153, 0, 0.25);
+$solarized-light-old-bg: rgba(220, 50, 47, 0.2);
+$solarized-light-old-idiff: rgba(220, 50, 47, 0.25);
+$solarized-light-border: #c5d0d4;
+$solarized-light-pre-bg: #002b36;
+$solarized-light-pre-bg: #fdf6e3;
+$solarized-light-pre-color: #586e75;
+$solarized-light-line-bg: #fdf6e3;
+$solarized-light-line-color: rgba(0, 0, 0, 0.3);
+$solarized-light-line-color-new: #a1a080;
+$solarized-light-line-color-old: #ad9186;
+$solarized-light-highlight: #eee8d5;
+$solarized-light-hll-bg: #ddd8c5;
+$solarized-light-over-bg: #ded7fc;
+$solarized-light-expanded-border: #d2cdbd;
+$solarized-light-expanded-bg: #ece6d4;
+$solarized-light-c: #93a1a1;
+$solarized-light-err: #586e75;
+$solarized-light-g: #586e75;
+$solarized-light-k: #859900;
+$solarized-light-l: #586e75;
+$solarized-light-n: #586e75;
+$solarized-light-o: #859900;
+$solarized-light-x: #cb4b16;
+$solarized-light-p: #586e75;
+$solarized-light-cm: #93a1a1;
+$solarized-light-cp: #859900;
+$solarized-light-c1: #93a1a1;
+$solarized-light-cs: #859900;
+$solarized-light-gd: #2aa198;
+$solarized-light-ge: #586e75;
+$solarized-light-gr: #dc322f;
+$solarized-light-gh: #cb4b16;
+$solarized-light-gi: #859900;
+$solarized-light-go: #586e75;
+$solarized-light-gp: #586e75;
+$solarized-light-gs: #586e75;
+$solarized-light-gu: #cb4b16;
+$solarized-light-gt: #586e75;
+$solarized-light-kc: #cb4b16;
+$solarized-light-kd: #268bd2;
+$solarized-light-kn: #859900;
+$solarized-light-kp: #859900;
+$solarized-light-kr: #268bd2;
+$solarized-light-kt: #dc322f;
+$solarized-light-ld: #586e75;
+$solarized-light-m: #2aa198;
+$solarized-light-s: #2aa198;
+$solarized-light-na: #586e75;
+$solarized-light-nb: #b58900;
+$solarized-light-nc: #268bd2;
+$solarized-light-no: #cb4b16;
+$solarized-light-nd: #268bd2;
+$solarized-light-ni: #cb4b16;
+$solarized-light-ne: #cb4b16;
+$solarized-light-nf: #268bd2;
+$solarized-light-nl: #586e75;
+$solarized-light-nn: #586e75;
+$solarized-light-nx: #586e75;
+$solarized-light-py: #586e75;
+$solarized-light-nt: #268bd2;
+$solarized-light-nv: #268bd2;
+$solarized-light-ow: #859900;
+$solarized-light-w: #586e75;
+$solarized-light-mf: #2aa198;
+$solarized-light-mh: #2aa198;
+$solarized-light-mi: #2aa198;
+$solarized-light-mo: #2aa198;
+$solarized-light-sb: #93a1a1;
+$solarized-light-sc: #2aa198;
+$solarized-light-sd: #586e75;
+$solarized-light-s2: #2aa198;
+$solarized-light-se: #cb4b16;
+$solarized-light-sh: #586e75;
+$solarized-light-si: #2aa198;
+$solarized-light-sx: #2aa198;
+$solarized-light-sr: #dc322f;
+$solarized-light-s1: #2aa198;
+$solarized-light-ss: #2aa198;
+$solarized-light-bp: #268bd2;
+$solarized-light-vc: #268bd2;
+$solarized-light-vg: #268bd2;
+$solarized-light-vi: #268bd2;
+$solarized-light-il: #2aa198;
+
+@mixin match-line {
+ color: $black-transparent;
+ background: $solarized-light-matchline-bg;
+}
+
+.code.solarized-light {
+ // Line numbers
+ .line-numbers,
+ .diff-line-num {
+ background-color: $solarized-light-line-bg;
+ }
+
+ .diff-line-num,
+ .diff-line-num a {
+ color: $solarized-light-line-color;
+ }
+
+ // Code itself
+ pre.code,
+ .diff-line-num {
+ border-color: $solarized-light-border;
+ }
+
+ &,
+ pre.code,
+ .line_holder .line_content {
+ background-color: $solarized-light-pre-bg;
+ color: $solarized-light-pre-color;
+ }
+
+ // Diff line
+ .line_holder {
+ &.match .line_content,
+ &.old-nonewline .line_content,
+ &.new-nonewline .line_content {
+ @include match-line;
+ }
+
+ td.diff-line-num.hll:not(.empty-cell),
+ td.line_content.hll:not(.empty-cell) {
+ background-color: $solarized-light-hll-bg;
+ border-color: darken($solarized-light-hll-bg, 15%);
+ }
+
+ .diff-line-num.new,
+ .line_content.new {
+ @include diff-background($solarized-light-new-bg,
+ $solarized-light-new-idiff, $solarized-light-border);
+
+ &::before,
+ a {
+ color: $solarized-light-line-color-new;
+ }
+ }
+
+ .diff-line-num.old,
+ .line_content.old {
+ @include diff-background($solarized-light-old-bg, $solarized-light-old-idiff, $solarized-light-border);
+
+ &::before,
+ a {
+ color: $solarized-light-line-color-old;
+ }
+ }
+
+ .diff-line-num {
+ &.is-over,
+ &.hll:not(.empty-cell).is-over {
+ background-color: $solarized-light-over-bg;
+ border-color: darken($solarized-light-over-bg, 5%);
+
+ a {
+ color: darken($solarized-light-over-bg, 15%);
+ }
+ }
+ }
+
+ .line_content.match {
+ @include match-line;
+ }
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $solarized-light-expanded-border;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $solarized-light-expanded-bg;
+ border-color: $solarized-light-expanded-bg;
+ }
+ }
+ }
+
+ // highlight line via anchor
+ pre .hll {
+ background-color: $solarized-light-hll-bg !important;
+ }
+
+ // Search result highlight
+ span.highlight_word {
+ background-color: $solarized-light-highlight !important;
+ }
+
+ // Links to URLs, emails, or dependencies
+ .line a {
+ color: $solarized-light-kd;
+ }
+
+ /* Solarized Light
+
+ For use with Jekyll and Pygments
+
+ http://ethanschoonover.com/solarized
+
+ SOLARIZED HEX ROLE
+ --------- -------- ------------------------------------------
+ base01 #586e75 body text / default code / primary content
+ base1 #93a1a1 comments / secondary content
+ base3 #fdf6e3 background
+ orange #cb4b16 constants
+ red #dc322f regex, special keywords
+ blue #268bd2 reserved keywords
+ cyan #2aa198 strings, numbers
+ green #859900 operators, other keywords
+ */
+
+ .c { color: $solarized-light-c; } /* Comment */
+ .err { color: $solarized-light-err; } /* Error */
+ .g { color: $solarized-light-g; } /* Generic */
+ .k { color: $solarized-light-k; } /* Keyword */
+ .l { color: $solarized-light-l; } /* Literal */
+ .n { color: $solarized-light-n; } /* Name */
+ .o { color: $solarized-light-o; } /* Operator */
+ .x { color: $solarized-light-x; } /* Other */
+ .p { color: $solarized-light-p; } /* Punctuation */
+ .cm { color: $solarized-light-cm; } /* Comment.Multiline */
+ .cp { color: $solarized-light-cp; } /* Comment.Preproc */
+ .c1 { color: $solarized-light-c1; } /* Comment.Single */
+ .cs { color: $solarized-light-cs; } /* Comment.Special */
+ .gd { color: $solarized-light-gd; } /* Generic.Deleted */
+ .ge { color: $solarized-light-ge; font-style: italic; } /* Generic.Emph */
+ .gr { color: $solarized-light-gr; } /* Generic.Error */
+ .gh { color: $solarized-light-gh; } /* Generic.Heading */
+ .gi { color: $solarized-light-gi; } /* Generic.Inserted */
+ .go { color: $solarized-light-go; } /* Generic.Output */
+ .gp { color: $solarized-light-gp; } /* Generic.Prompt */
+ .gs { color: $solarized-light-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */
+ .gu { color: $solarized-light-gu; } /* Generic.Subheading */
+ .gt { color: $solarized-light-gt; } /* Generic.Traceback */
+ .kc { color: $solarized-light-kc; } /* Keyword.Constant */
+ .kd { color: $solarized-light-kd; } /* Keyword.Declaration */
+ .kn { color: $solarized-light-kn; } /* Keyword.Namespace */
+ .kp { color: $solarized-light-kp; } /* Keyword.Pseudo */
+ .kr { color: $solarized-light-kr; } /* Keyword.Reserved */
+ .kt { color: $solarized-light-kt; } /* Keyword.Type */
+ .ld { color: $solarized-light-ld; } /* Literal.Date */
+ .m { color: $solarized-light-m; } /* Literal.Number */
+ .s { color: $solarized-light-s; } /* Literal.String */
+ .na { color: $solarized-light-na; } /* Name.Attribute */
+ .nb { color: $solarized-light-nb; } /* Name.Builtin */
+ .nc { color: $solarized-light-nc; } /* Name.Class */
+ .no { color: $solarized-light-no; } /* Name.Constant */
+ .nd { color: $solarized-light-nd; } /* Name.Decorator */
+ .ni { color: $solarized-light-ni; } /* Name.Entity */
+ .ne { color: $solarized-light-ne; } /* Name.Exception */
+ .nf { color: $solarized-light-nf; } /* Name.Function */
+ .nl { color: $solarized-light-nl; } /* Name.Label */
+ .nn { color: $solarized-light-nn; } /* Name.Namespace */
+ .nx { color: $solarized-light-nx; } /* Name.Other */
+ .py { color: $solarized-light-py; } /* Name.Property */
+ .nt { color: $solarized-light-nt; } /* Name.Tag */
+ .nv { color: $solarized-light-nv; } /* Name.Variable */
+ .ow { color: $solarized-light-ow; } /* Operator.Word */
+ .w { color: $solarized-light-w; } /* Text.Whitespace */
+ .mf { color: $solarized-light-mf; } /* Literal.Number.Float */
+ .mh { color: $solarized-light-mh; } /* Literal.Number.Hex */
+ .mi { color: $solarized-light-mi; } /* Literal.Number.Integer */
+ .mo { color: $solarized-light-mo; } /* Literal.Number.Oct */
+ .sb { color: $solarized-light-sb; } /* Literal.String.Backtick */
+ .sc { color: $solarized-light-sc; } /* Literal.String.Char */
+ .sd { color: $solarized-light-sd; } /* Literal.String.Doc */
+ .s2 { color: $solarized-light-s2; } /* Literal.String.Double */
+ .se { color: $solarized-light-se; } /* Literal.String.Escape */
+ .sh { color: $solarized-light-sh; } /* Literal.String.Heredoc */
+ .si { color: $solarized-light-si; } /* Literal.String.Interpol */
+ .sx { color: $solarized-light-sx; } /* Literal.String.Other */
+ .sr { color: $solarized-light-sr; } /* Literal.String.Regex */
+ .s1 { color: $solarized-light-s1; } /* Literal.String.Single */
+ .ss { color: $solarized-light-ss; } /* Literal.String.Symbol */
+ .bp { color: $solarized-light-bp; } /* Name.Builtin.Pseudo */
+ .vc { color: $solarized-light-vc; } /* Name.Variable.Class */
+ .vg { color: $solarized-light-vg; } /* Name.Variable.Global */
+ .vi { color: $solarized-light-vi; } /* Name.Variable.Instance */
+ .il { color: $solarized-light-il; } /* Literal.Number.Integer.Long */
+}
diff --git a/app/assets/stylesheets/highlight/themes/white.scss b/app/assets/stylesheets/highlight/themes/white.scss
new file mode 100644
index 00000000000..7239086f649
--- /dev/null
+++ b/app/assets/stylesheets/highlight/themes/white.scss
@@ -0,0 +1,3 @@
+.code.white {
+ @import "../white_base";
+}
diff --git a/app/assets/stylesheets/highlight/white.scss b/app/assets/stylesheets/highlight/white.scss
deleted file mode 100644
index 355c8d223f7..00000000000
--- a/app/assets/stylesheets/highlight/white.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.code.white {
- @import "white_base";
-}
diff --git a/app/assets/stylesheets/highlight/white_base.scss b/app/assets/stylesheets/highlight/white_base.scss
index c636abbdfad..ee0ec94c636 100644
--- a/app/assets/stylesheets/highlight/white_base.scss
+++ b/app/assets/stylesheets/highlight/white_base.scss
@@ -1,5 +1,7 @@
/* https://github.com/aahan/pygments-github-style */
+@import './common';
+
/*
* White Syntax Colors
*/
@@ -35,16 +37,16 @@ $white-kt: #458;
$white-m: #099;
$white-s: #d14;
$white-n: #333;
-$white-na: teal;
+$white-na: #008080;
$white-nb: #0086b3;
$white-nc: #458;
-$white-no: teal;
-$white-ni: purple;
+$white-no: #008080;
+$white-ni: #800080;
$white-ne: #900;
$white-nf: #900;
$white-nn: #555;
-$white-nt: navy;
-$white-nv: teal;
+$white-nt: #000080;
+$white-nv: #008080;
$white-w: #bbb;
$white-mf: #099;
$white-mh: #099;
@@ -62,9 +64,9 @@ $white-sr: #009926;
$white-s1: #d14;
$white-ss: #990073;
$white-bp: #999;
-$white-vc: teal;
-$white-vg: teal;
-$white-vi: teal;
+$white-vc: #008080;
+$white-vg: #008080;
+$white-vi: #008080;
$white-il: #099;
$white-gc-color: #999;
$white-gc-bg: #eaf2f5;
@@ -75,7 +77,7 @@ $white-gc-bg: #eaf2f5;
background-color: $gray-light;
}
- // Line numbers
+// Line numbers
.line-numbers,
.diff-line-num {
background-color: $gray-light;
@@ -101,7 +103,6 @@ pre.code,
// Diff line
.line_holder {
-
&.match .line_content,
.new-nonewline.line_content,
.old-nonewline.line_content {
@@ -199,25 +200,38 @@ pre .hll {
background-color: $white-pre-hll-bg !important;
}
- // Search result highlight
+// Search result highlight
span.highlight_word {
background-color: $white-highlight !important;
}
- // Links to URLs, emails, or dependencies
+// Links to URLs, emails, or dependencies
.line a {
color: $white-nb;
}
.hll { background-color: $white-hll-bg; }
-.c { color: $white-c; font-style: italic; }
-.err { color: $white-err; background-color: $white-err-bg; }
+
+.c { color: $white-c;
+ font-style: italic; }
+
+.err { color: $white-err;
+ background-color: $white-err-bg; }
.k { font-weight: $gl-font-weight-bold; }
.o { font-weight: $gl-font-weight-bold; }
-.cm { color: $white-cm; font-style: italic; }
-.cp { color: $white-cp; font-weight: $gl-font-weight-bold; }
-.c1 { color: $white-c1; font-style: italic; }
-.cs { color: $white-cs; font-weight: $gl-font-weight-bold; font-style: italic; }
+
+.cm { color: $white-cm;
+ font-style: italic; }
+
+.cp { color: $white-cp;
+ font-weight: $gl-font-weight-bold; }
+
+.c1 { color: $white-c1;
+ font-style: italic; }
+
+.cs { color: $white-cs;
+ font-weight: $gl-font-weight-bold;
+ font-style: italic; }
.gd {
color: $white-gd;
@@ -246,24 +260,34 @@ span.highlight_word {
.go { color: $white-go; }
.gp { color: $white-gp; }
.gs { font-weight: $gl-font-weight-bold; }
-.gu { color: $white-gu; font-weight: $gl-font-weight-bold; }
+
+.gu { color: $white-gu;
+ font-weight: $gl-font-weight-bold; }
.gt { color: $white-gt; }
.kc { font-weight: $gl-font-weight-bold; }
.kd { font-weight: $gl-font-weight-bold; }
.kn { font-weight: $gl-font-weight-bold; }
.kp { font-weight: $gl-font-weight-bold; }
.kr { font-weight: $gl-font-weight-bold; }
-.kt { color: $white-kt; font-weight: $gl-font-weight-bold; }
+
+.kt { color: $white-kt;
+ font-weight: $gl-font-weight-bold; }
.m { color: $white-m; }
.s { color: $white-s; }
.n { color: $white-n; }
.na { color: $white-na; }
.nb { color: $white-nb; }
-.nc { color: $white-nc; font-weight: $gl-font-weight-bold; }
+
+.nc { color: $white-nc;
+ font-weight: $gl-font-weight-bold; }
.no { color: $white-no; }
.ni { color: $white-ni; }
-.ne { color: $white-ne; font-weight: $gl-font-weight-bold; }
-.nf { color: $white-nf; font-weight: $gl-font-weight-bold; }
+
+.ne { color: $white-ne;
+ font-weight: $gl-font-weight-bold; }
+
+.nf { color: $white-nf;
+ font-weight: $gl-font-weight-bold; }
.nn { color: $white-nn; }
.nt { color: $white-nt; }
.nv { color: $white-nv; }
@@ -289,4 +313,6 @@ span.highlight_word {
.vg { color: $white-vg; }
.vi { color: $white-vi; }
.il { color: $white-il; }
-.gc { color: $white-gc-color; background-color: $white-gc-bg; }
+
+.gc { color: $white-gc-color;
+ background-color: $white-gc-bg; }
diff --git a/app/assets/stylesheets/mailers/highlighted_diff_email.scss b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
index 8b234a5a656..33c114838c2 100644
--- a/app/assets/stylesheets/mailers/highlighted_diff_email.scss
+++ b/app/assets/stylesheets/mailers/highlighted_diff_email.scss
@@ -1,4 +1,4 @@
-@import "framework/variables";
+@import 'framework/variables';
// This file is largely copied from `highlight/white.scss`, but modified to
// avoid all descendant selectors (`table td`). This is because the CSS inlining
@@ -40,16 +40,16 @@ $highlighted-kt: #458;
$highlighted-m: #099;
$highlighted-s: #d14;
$highlighted-n: #333;
-$highlighted-na: teal;
+$highlighted-na: #008080;
$highlighted-nb: #0086b3;
$highlighted-nc: #458;
-$highlighted-no: teal;
-$highlighted-ni: purple;
+$highlighted-no: #008080;
+$highlighted-ni: #800080;
$highlighted-ne: #900;
$highlighted-nf: #900;
$highlighted-nn: #555;
-$highlighted-nt: navy;
-$highlighted-nv: teal;
+$highlighted-nt: #000080;
+$highlighted-nv: #008080;
$highlighted-w: #bbb;
$highlighted-mf: #099;
$highlighted-mh: #099;
@@ -67,9 +67,9 @@ $highlighted-sr: #009926;
$highlighted-s1: #d14;
$highlighted-ss: #990073;
$highlighted-bp: #999;
-$highlighted-vc: teal;
-$highlighted-vg: teal;
-$highlighted-vi: teal;
+$highlighted-vc: #008080;
+$highlighted-vg: #008080;
+$highlighted-vi: #008080;
$highlighted-il: #099;
$highlighted-gc: #999;
$highlighted-gc-bg: #eaf2f5;
@@ -151,14 +151,27 @@ span.highlight_word {
}
.hll { background-color: $highlighted-hll-bg; }
-.c { color: $highlighted-c; font-style: italic; }
-.err { color: $highlighted-err; background-color: $highlighted-err-bg; }
+
+.c { color: $highlighted-c;
+ font-style: italic; }
+
+.err { color: $highlighted-err;
+ background-color: $highlighted-err-bg; }
.k { font-weight: $gl-font-weight-bold; }
.o { font-weight: $gl-font-weight-bold; }
-.cm { color: $highlighted-cm; font-style: italic; }
-.cp { color: $highlighted-cp; font-weight: $gl-font-weight-bold; }
-.c1 { color: $highlighted-c1; font-style: italic; }
-.cs { color: $highlighted-cs; font-weight: $gl-font-weight-bold; font-style: italic; }
+
+.cm { color: $highlighted-cm;
+ font-style: italic; }
+
+.cp { color: $highlighted-cp;
+ font-weight: $gl-font-weight-bold; }
+
+.c1 { color: $highlighted-c1;
+ font-style: italic; }
+
+.cs { color: $highlighted-cs;
+ font-weight: $gl-font-weight-bold;
+ font-style: italic; }
.gd {
color: $highlighted-gd;
@@ -187,24 +200,34 @@ span.highlight_word {
.go { color: $highlighted-go; }
.gp { color: $highlighted-gp; }
.gs { font-weight: $gl-font-weight-bold; }
-.gu { color: $highlighted-gu; font-weight: $gl-font-weight-bold; }
+
+.gu { color: $highlighted-gu;
+ font-weight: $gl-font-weight-bold; }
.gt { color: $highlighted-gt; }
.kc { font-weight: $gl-font-weight-bold; }
.kd { font-weight: $gl-font-weight-bold; }
.kn { font-weight: $gl-font-weight-bold; }
.kp { font-weight: $gl-font-weight-bold; }
.kr { font-weight: $gl-font-weight-bold; }
-.kt { color: $highlighted-kt; font-weight: $gl-font-weight-bold; }
+
+.kt { color: $highlighted-kt;
+ font-weight: $gl-font-weight-bold; }
.m { color: $highlighted-m; }
.s { color: $highlighted-s; }
.n { color: $highlighted-n; }
.na { color: $highlighted-na; }
.nb { color: $highlighted-nb; }
-.nc { color: $highlighted-nc; font-weight: $gl-font-weight-bold; }
+
+.nc { color: $highlighted-nc;
+ font-weight: $gl-font-weight-bold; }
.no { color: $highlighted-no; }
.ni { color: $highlighted-ni; }
-.ne { color: $highlighted-ne; font-weight: $gl-font-weight-bold; }
-.nf { color: $highlighted-nf; font-weight: $gl-font-weight-bold; }
+
+.ne { color: $highlighted-ne;
+ font-weight: $gl-font-weight-bold; }
+
+.nf { color: $highlighted-nf;
+ font-weight: $gl-font-weight-bold; }
.nn { color: $highlighted-nn; }
.nt { color: $highlighted-nt; }
.nv { color: $highlighted-nv; }
@@ -230,4 +253,6 @@ span.highlight_word {
.vg { color: $highlighted-vg; }
.vi { color: $highlighted-vi; }
.il { color: $highlighted-il; }
-.gc { color: $highlighted-gc; background-color: $highlighted-gc-bg; }
+
+.gc { color: $highlighted-gc;
+ background-color: $highlighted-gc-bg; }
diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss
index f24c80bd81c..d77b7dfad68 100644
--- a/app/assets/stylesheets/notify.scss
+++ b/app/assets/stylesheets/notify.scss
@@ -1,4 +1,4 @@
-@import "framework/variables";
+@import 'framework/variables';
img {
max-width: 100%;
diff --git a/app/assets/stylesheets/page_bundles/xterm.scss b/app/assets/stylesheets/page_bundles/xterm.scss
index 7f040ac9b96..de3f2a1177d 100644
--- a/app/assets/stylesheets/page_bundles/xterm.scss
+++ b/app/assets/stylesheets/page_bundles/xterm.scss
@@ -6,11 +6,11 @@
$black: #000;
$red: #ea1010;
- $green: #009900;
- $yellow: #999900;
+ $green: #090;
+ $yellow: #990;
$blue: #0073e6;
$magenta: #d411d4;
- $cyan: #009999;
+ $cyan: #099;
$white: #ccc;
$l-black: #373b41;
$l-red: #ff6161;
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index a9324ba2ed0..81216b2b98e 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -3,7 +3,6 @@
}
.user-can-drag {
- cursor: -webkit-grab;
cursor: grab;
}
@@ -12,12 +11,8 @@
opacity: 1 !important;
* {
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
user-select: none;
// !important to make sure no style can override this when dragging
- cursor: -webkit-grabbing !important;
cursor: grabbing !important;
}
}
@@ -501,7 +496,6 @@
}
.add-issues-modal {
- display: -webkit-flex;
display: flex;
position: fixed;
top: 0;
@@ -513,9 +507,7 @@
}
.add-issues-container {
- display: -webkit-flex;
display: flex;
- -webkit-flex-direction: column;
flex-direction: column;
width: 90vw;
height: 85vh;
@@ -528,16 +520,12 @@
box-shadow: 0 2px 12px rgba($black, 0.5);
.empty-state {
- display: -webkit-flex;
display: flex;
- -webkit-flex: 1;
flex: 1;
margin-top: 0;
&.add-issues-empty-state-filter {
- -webkit-flex-direction: column;
flex-direction: column;
- -webkit-justify-content: center;
justify-content: center;
}
@@ -566,11 +554,9 @@
}
.add-issues-search {
- display: -webkit-flex;
display: flex;
.issues-filters {
- -webkit-flex: 1;
flex: 1;
}
}
@@ -588,9 +574,7 @@
}
.add-issues-list {
- display: -webkit-flex;
display: flex;
- -webkit-flex: 1;
flex: 1;
padding-top: 3px;
margin-left: -$gl-vert-padding;
@@ -609,7 +593,6 @@
}
.add-issues-list-loading {
- -webkit-align-self: center;
align-self: center;
width: 100%;
padding-left: $gl-vert-padding;
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 65f46e3852a..916f6cd3137 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -50,7 +50,6 @@
position: relative;
}
-
.build-trace {
@include build-trace();
}
@@ -75,7 +74,11 @@
@include build-trace-top-bar(35px);
&.has-archived-block {
- top: $header-height + $performance-bar-height + 28px;
+ top: $header-height + 28px;
+
+ .with-performance-bar & {
+ top: $header-height + $performance-bar-height + 28px;
+ }
}
&.affix {
@@ -388,3 +391,14 @@
right: 0;
margin-top: -17px;
}
+
+@include media-breakpoint-down(sm) {
+ .top-bar {
+ .truncated-info {
+ white-space: nowrap;
+ overflow: hidden;
+ max-width: 220px;
+ text-overflow: ellipsis;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 11966931a6c..670e320dbc2 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -342,11 +342,11 @@
}
&.invalid {
- @include status-color($gray-dark, color("gray"), $gray-darkest);
+ @include status-color($gray-dark, color('gray'), $gray-darkest);
border-color: $gray-darkest;
&:not(span):hover {
- color: color("gray");
+ color: color('gray');
}
}
}
diff --git a/app/assets/stylesheets/pages/cycle_analytics.scss b/app/assets/stylesheets/pages/cycle_analytics.scss
index ec2108b15be..2b932d164a5 100644
--- a/app/assets/stylesheets/pages/cycle_analytics.scss
+++ b/app/assets/stylesheets/pages/cycle_analytics.scss
@@ -330,7 +330,6 @@
// Custom Styles for stage items
.item-build-component {
-
.item-title {
.icon-build-status {
float: left;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index ae0768592e0..1fedbd8bddb 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -54,6 +54,10 @@
background-color: $gray-normal;
}
+ a {
+ color: $gray-700;
+ }
+
svg {
vertical-align: middle;
top: -1px;
@@ -85,138 +89,6 @@
}
}
- .note-text {
- table {
- font-family: $font-family-sans-serif;
- }
- }
-
- table {
- width: 100%;
- font-family: $monospace-font;
- border: 0;
- border-collapse: separate;
- margin: 0;
- padding: 0;
- table-layout: fixed;
- border-radius: 0 0 $border-radius-default $border-radius-default;
-
- .diff-line-num {
- width: 50px;
- position: relative;
-
- a {
- transition: none;
- }
- }
-
- .line_holder td {
- line-height: $code-line-height;
- font-size: $code-font-size;
- vertical-align: top;
-
- &.noteable_line {
- position: relative;
- }
-
- span {
- white-space: pre-wrap;
-
- &.context-cell {
- display: inline-block;
- width: 100%;
- height: 100%;
- }
- }
-
- .line {
- word-wrap: break-word;
- }
- }
-
- &.left-side-selected {
- td.line_content.parallel.right-side {
- user-select: none;
- }
- }
-
- &.right-side-selected {
- td.line_content.parallel.left-side {
- user-select: none;
- }
- }
- }
-
- tr.line_holder.parallel {
- td.line_content.parallel {
- width: 46%;
- }
-
- .add-diff-note {
- margin-left: -55px;
- }
- }
-
- .old_line,
- .new_line {
- user-select: none;
- margin: 0;
- border: 0;
- padding: 0 5px;
- border-right: 1px solid;
- text-align: right;
- min-width: 35px;
- max-width: 50px;
- width: 35px;
-
- a {
- float: left;
- width: 35px;
- font-weight: $gl-font-weight-normal;
-
- &[disabled] {
- cursor: default;
-
- &:hover,
- &:active {
- text-decoration: none;
- }
- }
- }
- }
-
- .line_content {
- display: block;
- margin: 0;
- padding: 0 1.5em;
- border: 0;
- position: relative;
-
- &.parallel {
- display: table-cell;
-
- span {
- word-break: break-all;
- }
- }
-
- &.old {
- &::before {
- content: '-';
- position: absolute;
- left: 0.5em;
- }
- }
-
- &.new {
- &::before {
- content: '+';
- position: absolute;
- left: 0.5em;
- }
- }
- }
-
.diff-loading-error-block {
padding: $gl-padding * 2 $gl-padding;
text-align: center;
@@ -239,22 +111,18 @@
img {
border: 1px solid $white-light;
- background-image: linear-gradient(
- 45deg,
- $border-color 25%,
- transparent 25%,
- transparent 75%,
- $border-color 75%,
- $border-color 100%
- ),
- linear-gradient(
- 45deg,
- $border-color 25%,
- transparent 25%,
- transparent 75%,
- $border-color 75%,
- $border-color 100%
- );
+ background-image: linear-gradient(45deg,
+ $border-color 25%,
+ transparent 25%,
+ transparent 75%,
+ $border-color 75%,
+ $border-color 100%),
+ linear-gradient(45deg,
+ $border-color 25%,
+ transparent 25%,
+ transparent 75%,
+ $border-color 75%,
+ $border-color 100%);
background-size: 10px 10px;
background-position: 0 0, 5px 5px;
max-width: 100%;
@@ -443,10 +311,6 @@
}
}
- .line_content {
- white-space: pre-wrap;
- }
-
.diff-file-container {
.frame.deleted {
border: 1px solid $deleted;
@@ -508,12 +372,120 @@
}
}
+table.code {
+ width: 100%;
+ font-family: $monospace-font;
+ border: 0;
+ border-collapse: separate;
+ margin: 0;
+ padding: 0;
+ table-layout: fixed;
+ border-radius: 0 0 $border-radius-default $border-radius-default;
+
+ tr.line_holder td {
+ line-height: $code-line-height;
+ font-size: $code-font-size;
+ vertical-align: top;
+
+ span {
+ white-space: pre-wrap;
+
+ &.context-cell {
+ display: inline-block;
+ width: 100%;
+ height: 100%;
+ }
+
+ &.line {
+ word-wrap: break-word;
+ }
+ }
+
+ &.diff-line-num {
+ user-select: none;
+ margin: 0;
+ border: 0;
+ padding: 0 10px 0 5px;
+ border-right: 1px solid;
+ text-align: right;
+ width: 50px;
+ position: relative;
+
+ a {
+ transition: none;
+ float: left;
+ width: 100%;
+ font-weight: $gl-font-weight-normal;
+
+ &[disabled] {
+ cursor: default;
+
+ &:hover,
+ &:active {
+ text-decoration: none;
+ }
+ }
+ }
+
+ &:not(.js-unfold-bottom) a::before {
+ content: attr(data-linenumber);
+ }
+ }
+
+ &.line_content {
+ display: block;
+ margin: 0;
+ padding: 0 1.5em;
+ border: 0;
+ position: relative;
+ white-space: pre-wrap;
+
+ &.parallel {
+ display: table-cell;
+ width: 46%;
+
+ span {
+ word-break: break-all;
+ }
+ }
+
+ &.old {
+ &::before {
+ content: '-';
+ position: absolute;
+ left: 0.5em;
+ }
+ }
+
+ &.new {
+ &::before {
+ content: '+';
+ position: absolute;
+ left: 0.5em;
+ }
+ }
+ }
+ }
+
+ &.left-side-selected {
+ td.line_content.parallel.right-side {
+ user-select: none;
+ }
+ }
+
+ &.right-side-selected {
+ td.line_content.parallel.left-side {
+ user-select: none;
+ }
+ }
+}
+
.diff-stats {
align-items: center;
- padding: 0 .25rem;
+ padding: 0 0.25rem;
.diff-stats-group {
- padding: 0 .25rem;
+ padding: 0 0.25rem;
}
svg.diff-stats-icon {
@@ -522,7 +494,7 @@
&.is-compare-versions-header {
.diff-stats-group {
- padding: 0 .5rem;
+ padding: 0 0.5rem;
}
}
}
@@ -602,34 +574,12 @@
}
}
-@mixin diff-background($background, $idiff, $border) {
- background: $background;
-
- &.line_content span.idiff {
- background: $idiff;
- }
-
- &.diff-line-num {
- border-color: $border;
- }
-}
-
.files {
.diff-file:last-child {
margin-bottom: 0;
}
}
-.file-holder {
- .diff-line-num:not(.js-unfold-bottom) {
- a {
- &::before {
- content: attr(data-linenumber);
- }
- }
- }
-}
-
.diff-comment-avatar-holders {
position: absolute;
height: 19px;
@@ -846,34 +796,26 @@
width: 100%;
height: 10px;
background-color: $white-light;
- background-image: linear-gradient(
- 45deg,
- transparent,
- transparent 73%,
- $diff-jagged-border-gradient-color 75%,
- $white-light 80%
- ),
- linear-gradient(
- 225deg,
- transparent,
- transparent 73%,
- $diff-jagged-border-gradient-color 75%,
- $white-light 80%
- ),
- linear-gradient(
- 135deg,
- transparent,
- transparent 73%,
- $diff-jagged-border-gradient-color 75%,
- $white-light 80%
- ),
- linear-gradient(
- -45deg,
- transparent,
- transparent 73%,
- $diff-jagged-border-gradient-color 75%,
- $white-light 80%
- );
+ background-image: linear-gradient(45deg,
+ transparent,
+ transparent 73%,
+ $diff-jagged-border-gradient-color 75%,
+ $white-light 80%),
+ linear-gradient(225deg,
+ transparent,
+ transparent 73%,
+ $diff-jagged-border-gradient-color 75%,
+ $white-light 80%),
+ linear-gradient(135deg,
+ transparent,
+ transparent 73%,
+ $diff-jagged-border-gradient-color 75%,
+ $white-light 80%),
+ linear-gradient(-45deg,
+ transparent,
+ transparent 73%,
+ $diff-jagged-border-gradient-color 75%,
+ $white-light 80%);
background-position: 5px 5px, 0 5px, 0 5px, 5px 5px;
background-size: 10px 10px;
background-repeat: repeat;
@@ -916,7 +858,7 @@
}
}
-.files:not([data-can-create-note="true"]) .frame {
+.files:not([data-can-create-note='true']) .frame {
cursor: auto;
}
@@ -925,15 +867,14 @@
.btn-transparent.image-diff-overlay-add-comment {
position: relative;
cursor: image-url('illustrations/image_comment_light_cursor.svg')
- $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
+ $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
auto;
// Retina cursor
- cursor: -webkit-image-set(
- image-url('illustrations/image_comment_light_cursor.svg') 1x,
- image-url('illustrations/image_comment_light_cursor@2x.svg') 2x
- )
- $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
+ // scss-lint:disable DuplicateProperty
+ cursor: image-set(image-url('illustrations/image_comment_light_cursor.svg') 1x,
+ image-url('illustrations/image_comment_light_cursor@2x.svg') 2x)
+ $image-comment-cursor-left-offset $image-comment-cursor-top-offset,
auto;
.comment-indicator {
diff --git a/app/assets/stylesheets/pages/environments.scss b/app/assets/stylesheets/pages/environments.scss
index 61ecf133b02..0eb854ecf98 100644
--- a/app/assets/stylesheets/pages/environments.scss
+++ b/app/assets/stylesheets/pages/environments.scss
@@ -346,7 +346,9 @@
}
> .popover-title,
- > .popover-content {
+ > .popover-content,
+ > .popover-header,
+ > .popover-body {
padding: 8px;
font-size: 12px;
white-space: nowrap;
diff --git a/app/assets/stylesheets/pages/graph.scss b/app/assets/stylesheets/pages/graph.scss
index 83b1680512d..3febf4cf826 100644
--- a/app/assets/stylesheets/pages/graph.scss
+++ b/app/assets/stylesheets/pages/graph.scss
@@ -71,12 +71,10 @@
.svg-graph-container-with-grab {
cursor: grab;
- cursor: -webkit-grab;
}
.svg-graph-container-grabbed {
cursor: grabbing;
- cursor: -webkit-grabbing;
}
@keyframes flickerAnimation {
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 8ade995525a..0a07747e0d4 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -15,6 +15,11 @@
word-wrap: nowrap;
}
+.content-list .group-name {
+ font-weight: $gl-font-weight-bold;
+ color: $pages-group-name-color;
+}
+
.group-row {
@include basic-list-stats;
@@ -172,6 +177,50 @@
}
}
+.card {
+ .shared_runners_limit_under_quota {
+ color: $green-500;
+ }
+
+ .shared_runners_limit_over_quota {
+ color: $red-500;
+ }
+}
+
+.pipeline-quota {
+ border-top: 1px solid $table-border-color;
+ border-bottom: 1px solid $table-border-color;
+ margin: 0 0 $gl-padding;
+
+ .row {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ }
+
+ .right {
+ text-align: right;
+ }
+
+ .progress {
+ height: 6px;
+ width: 100%;
+ margin-bottom: 0;
+ margin-top: 4px;
+ }
+}
+
+.user-settings-pipeline-quota {
+ margin-top: $gl-padding;
+
+ .pipeline-quota {
+ border-top: 0;
+ }
+}
+
+table.pipeline-project-metrics tr td {
+ padding: $gl-padding;
+}
+
.mattermost-icon svg {
width: 16px;
height: 16px;
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index 2c23f31c240..7610c5cf6f3 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -30,19 +30,11 @@
.key {
@extend .badge.badge-pill;
background-color: $label-inverse-bg;
- font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
+ font: 11px Consolas, 'Liberation Mono', Menlo, Courier, monospace;
padding: 3px 5px;
}
}
.documentation {
padding: 7px;
-
- // Border around images in the help pages.
- img:not(.emoji) {
- border: 1px solid $white-normal;
- padding: 5px;
- margin: 5px;
- max-height: calc(100vh - 100px);
- }
}
diff --git a/app/assets/stylesheets/pages/import.scss b/app/assets/stylesheets/pages/import.scss
index 7f800367cad..20240835fda 100644
--- a/app/assets/stylesheets/pages/import.scss
+++ b/app/assets/stylesheets/pages/import.scss
@@ -49,3 +49,15 @@
.import-projects-loading-icon {
margin-top: $gl-padding-32;
}
+
+.btn-import {
+ .loading-icon {
+ display: none;
+ }
+
+ &.is-loading {
+ .loading-icon {
+ display: inline-block;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index e0bdc1341b1..6415d902ca6 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -22,6 +22,7 @@
.detail-page-header,
.page-content-header,
.commit-box,
+ .info-well,
.commit-ci-menu,
.files-changed-inner,
.limited-header-width,
@@ -71,14 +72,6 @@
height: $gl-padding * 2;
}
- // Border around images in issue and MR descriptions.
- .description img:not(.emoji) {
- border: 1px solid $white-normal;
- padding: 5px;
- max-height: calc(100vh - 100px);
- max-width: 100%;
- }
-
.emoji-block {
padding: 10px 0;
}
@@ -136,7 +129,7 @@
color: $blue-800;
.avatar {
- border-color: rgba($gray-normal, .2);
+ border-color: rgba($gray-normal, 0.2);
}
}
@@ -223,7 +216,7 @@
}
a.edit-link:not([href]):hover {
- color: rgba($gray-normal, .2);
+ color: rgba($gray-normal, 0.2);
}
.lock-edit, // uses same style, different js behaviour
@@ -263,6 +256,10 @@
.selectbox {
display: none;
+
+ &.show {
+ display: block;
+ }
}
.btn-clipboard:hover {
@@ -316,6 +313,7 @@
}
.no-value,
+ .btn-default-hover-link,
.btn-secondary-hover-link {
color: $gl-text-color-secondary;
}
@@ -599,7 +597,6 @@
margin: -7px;
}
-
.user-list {
display: flex;
flex-wrap: wrap;
@@ -711,14 +708,11 @@
.issuable-list {
li {
-
.issue-box {
- display: -webkit-flex;
display: flex;
}
.issuable-info-container {
- -webkit-flex: 1;
flex: 1;
display: flex;
padding-right: $gl-padding;
@@ -726,6 +720,7 @@
.issuable-main-info {
flex: 1 auto;
margin-right: 10px;
+ min-width: 0;
.issue-weight-icon {
vertical-align: sub;
@@ -787,6 +782,7 @@
@media(max-width: map-get($grid-breakpoints, lg)-1) {
.task-status,
.issuable-due-date,
+ .issuable-weight,
.project-ref-path {
display: none;
}
@@ -813,7 +809,6 @@
.sidebar-collapsed-icon {
-
> .stopwatch-svg {
display: inline-block;
}
@@ -871,11 +866,11 @@
}
.help-state-toggle-enter-active {
- transition: all .8s ease;
+ transition: all 0.8s ease;
}
.help-state-toggle-leave-active {
- transition: all .5s ease;
+ transition: all 0.5s ease;
}
.help-state-toggle-enter,
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 0037364978c..c7d2369a6b8 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -58,8 +58,6 @@ form.edit-issue {
}
ul.related-merge-requests > li {
- display: -ms-flexbox;
- display: -webkit-flex;
display: flex;
align-items: center;
@@ -147,6 +145,11 @@ ul.related-merge-requests > li {
}
}
+.issues-footer {
+ padding-top: $gl-padding;
+ padding-bottom: 37px;
+}
+
.issues-nav-controls {
font-size: 0;
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 2372640277e..6f98b4f7f13 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -34,7 +34,7 @@
.dropdown-new-label {
.dropdown-content {
- max-height: 136px;
+ max-height: initial;
}
}
@@ -95,13 +95,11 @@
.prioritized-labels & {
box-shadow: 0 1px 2px $issue-boards-card-shadow;
cursor: move;
- cursor: -webkit-grab;
- cursor: -moz-grab;
+ cursor: grab;
border: 0;
&:active {
- cursor: -webkit-grabbing;
- cursor: -moz-grabbing;
+ cursor: grabbing;
}
}
}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 67d7a8175ac..22a515cbdaa 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -120,7 +120,6 @@
}
.new-session-tabs {
- display: -webkit-flex;
display: flex;
box-shadow: 0 0 0 1px $border-color;
border-top-right-radius: $border-radius-default;
@@ -190,7 +189,7 @@
margin-top: 16px;
}
- input[type="submit"] {
+ input[type='submit'] {
@extend .btn-block;
margin-bottom: 0;
}
diff --git a/app/assets/stylesheets/pages/members.scss b/app/assets/stylesheets/pages/members.scss
index 99609a96976..e0b84e0f92d 100644
--- a/app/assets/stylesheets/pages/members.scss
+++ b/app/assets/stylesheets/pages/members.scss
@@ -14,6 +14,12 @@
}
.member {
+ &.is-overriden {
+ .btn-ldap-override {
+ display: none !important;
+ }
+ }
+
.list-item-name {
@include media-breakpoint-up(sm) {
float: left;
@@ -27,7 +33,6 @@
.controls {
@include media-breakpoint-up(sm) {
- display: -webkit-flex;
display: flex;
}
@@ -123,6 +128,49 @@
outline: 0;
}
+.members-ldap {
+ align-self: center;
+ height: 100%;
+ margin-right: 10px;
+ margin-left: -49px;
+}
+
+.alert-member-ldap {
+ background-color: $orange-50;
+
+ @include media-breakpoint-up(sm) {
+ line-height: 40px;
+ }
+
+ > p {
+ float: left;
+ margin-bottom: 10px;
+ color: $orange-600;
+
+ @include media-breakpoint-up(sm) {
+ padding-left: 55px;
+ margin-bottom: 0;
+ }
+ }
+
+ .controls {
+ width: 100%;
+
+ @include media-breakpoint-up(sm) {
+ width: auto;
+ }
+ }
+}
+
+.btn-ldap-override {
+ width: 100%;
+
+ @include media-breakpoint-up(sm) {
+ margin-left: 10px;
+ width: auto;
+ }
+}
+
.flex-project-members-panel {
display: flex;
flex-direction: row;
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index e0f7d075fc7..278a9014458 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -20,81 +20,81 @@ $colors: (
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-header-head-neutral : rgba(#3f3, 0.2),
+ dark-line-head-neutral : rgba(#3f3, 0.1),
dark-button-head-neutral : #40874f,
- dark-header-head-chosen : rgba(#3f3, .33),
- dark-line-head-chosen : rgba(#3f3, .2),
+ dark-header-head-chosen : rgba(#3f3, 0.33),
+ dark-line-head-chosen : rgba(#3f3, 0.2),
dark-button-head-chosen : #258537,
- dark-header-origin-neutral : rgba(#2878c9, .4),
- dark-line-origin-neutral : rgba(#2878c9, .3),
+ dark-header-origin-neutral : rgba(#2878c9, 0.4),
+ dark-line-origin-neutral : rgba(#2878c9, 0.3),
dark-button-origin-neutral : #2a5c8c,
- dark-header-origin-chosen : rgba(#2878c9, .6),
- dark-line-origin-chosen : rgba(#2878c9, .4),
+ dark-header-origin-chosen : rgba(#2878c9, 0.6),
+ dark-line-origin-chosen : rgba(#2878c9, 0.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, 0.25),
+ dark-line-not-chosen : rgba(#fff, 0.1),
- monokai-header-head-neutral : rgba(#a6e22e, .25),
- monokai-line-head-neutral : rgba(#a6e22e, .1),
+ monokai-header-head-neutral : rgba(#a6e22e, 0.25),
+ monokai-line-head-neutral : rgba(#a6e22e, 0.1),
monokai-button-head-neutral : #376b20,
- monokai-header-head-chosen : rgba(#a6e22e, .4),
- monokai-line-head-chosen : rgba(#a6e22e, .25),
+ monokai-header-head-chosen : rgba(#a6e22e, 0.4),
+ monokai-line-head-chosen : rgba(#a6e22e, 0.25),
monokai-button-head-chosen : #39800d,
- monokai-header-origin-neutral : rgba(#60d9f1, .35),
- monokai-line-origin-neutral : rgba(#60d9f1, .15),
+ monokai-header-origin-neutral : rgba(#60d9f1, 0.35),
+ monokai-line-origin-neutral : rgba(#60d9f1, 0.15),
monokai-button-origin-neutral : #38848c,
- monokai-header-origin-chosen : rgba(#60d9f1, .5),
- monokai-line-origin-chosen : rgba(#60d9f1, .35),
+ monokai-header-origin-chosen : rgba(#60d9f1, 0.5),
+ monokai-line-origin-chosen : rgba(#60d9f1, 0.35),
monokai-button-origin-chosen : #3ea4b2,
- monokai-header-not-chosen : rgba(#76715d, .24),
- monokai-line-not-chosen : rgba(#76715d, .1),
+ monokai-header-not-chosen : rgba(#76715d, 0.24),
+ monokai-line-not-chosen : rgba(#76715d, 0.1),
- solarized-light-header-head-neutral : rgba(#859900, .37),
- solarized-light-line-head-neutral : rgba(#859900, .2),
+ solarized-light-header-head-neutral : rgba(#859900, 0.37),
+ solarized-light-line-head-neutral : rgba(#859900, 0.2),
solarized-light-button-head-neutral : #afb262,
- solarized-light-header-head-chosen : rgba(#859900, .5),
- solarized-light-line-head-chosen : rgba(#859900, .37),
+ solarized-light-header-head-chosen : rgba(#859900, 0.5),
+ solarized-light-line-head-chosen : rgba(#859900, 0.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-header-origin-neutral : rgba(#2878c9, 0.37),
+ solarized-light-line-origin-neutral : rgba(#2878c9, 0.15),
solarized-light-button-origin-neutral : #60a1bf,
- solarized-light-header-origin-chosen : rgba(#2878c9, .6),
- solarized-light-line-origin-chosen : rgba(#2878c9, .37),
+ solarized-light-header-origin-chosen : rgba(#2878c9, 0.6),
+ solarized-light-line-origin-chosen : rgba(#2878c9, 0.37),
solarized-light-button-origin-chosen : #2482b2,
- solarized-light-header-not-chosen : rgba(#839496, .37),
- solarized-light-line-not-chosen : rgba(#839496, .2),
+ solarized-light-header-not-chosen : rgba(#839496, 0.37),
+ solarized-light-line-not-chosen : rgba(#839496, 0.2),
- solarized-dark-header-head-neutral : rgba(#859900, .35),
- solarized-dark-line-head-neutral : rgba(#859900, .15),
+ solarized-dark-header-head-neutral : rgba(#859900, 0.35),
+ solarized-dark-line-head-neutral : rgba(#859900, 0.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-header-head-chosen : rgba(#859900, 0.5),
+ solarized-dark-line-head-chosen : rgba(#859900, 0.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-header-origin-neutral : rgba(#2878c9, 0.35),
+ solarized-dark-line-origin-neutral : rgba(#2878c9, 0.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-header-origin-chosen : rgba(#2878c9, 0.6),
+ solarized-dark-line-origin-chosen : rgba(#2878c9, 0.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, 0.25),
+ solarized_dark_line_not_chosen : rgba(#839496, 0.15),
none_header_head_neutral : $gray-normal,
none_line_head_neutral : $gray-normal,
@@ -210,26 +210,20 @@ $colors: (
}
#conflicts {
-
.white {
- @include color-scheme('white')
- }
+ @include color-scheme('white'); }
.dark {
- @include color-scheme('dark')
- }
+ @include color-scheme('dark'); }
.monokai {
- @include color-scheme('monokai')
- }
+ @include color-scheme('monokai'); }
.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 {
white-space: normal;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 883c856870f..7f8b8ea8100 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -82,7 +82,6 @@
}
.mr-widget-body,
-.mr-widget-section,
.mr-widget-content,
.mr-widget-footer {
padding: $gl-padding;
@@ -167,6 +166,7 @@
float: left;
.accept-merge-request {
+ &.ci-preparing,
&.ci-pending,
&.ci-running {
@include btn-blue;
@@ -492,11 +492,9 @@
.merge-request {
padding: 10px 0 10px 15px;
position: relative;
- display: -webkit-flex;
display: flex;
.issuable-info-container {
- -webkit-flex: 1;
flex: 1;
}
@@ -809,13 +807,13 @@
.merge-request-tabs-holder {
top: $header-height;
- z-index: 300;
+ z-index: 250;
background-color: $white-light;
border-bottom: 1px solid $border-color;
@include media-breakpoint-up(sm) {
- position: sticky;
position: -webkit-sticky;
+ position: sticky;
}
&.affix {
@@ -1022,3 +1020,13 @@
padding-left: 50px;
padding-bottom: $gl-padding;
}
+
+.mr-compare {
+ .diff-file .file-title-flex-parent {
+ top: $header-height + 51px;
+
+ .with-performance-bar & {
+ top: $performance-bar-height + $header-height + 51px;
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/milestone.scss b/app/assets/stylesheets/pages/milestone.scss
index 15f3a2ef4a8..49608a3964f 100644
--- a/app/assets/stylesheets/pages/milestone.scss
+++ b/app/assets/stylesheets/pages/milestone.scss
@@ -67,18 +67,14 @@ $status-box-line-height: 26px;
.card-header {
line-height: $line-height-base;
padding: 14px 16px;
- display: -webkit-flex;
display: flex;
.title {
- -webkit-flex: 1;
- -webkit-flex-grow: 1;
flex: 1;
flex-grow: 2;
}
.counter {
- -webkit-flex: 1;
flex: 0;
padding-left: 16px;
}
@@ -239,6 +235,7 @@ $status-box-line-height: 26px;
padding: 0;
}
+ .popover-body,
.popover-content {
padding: 0;
}
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 51f755c67af..3343b55d24b 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -58,7 +58,7 @@
border: 1px solid $border-color;
border-radius: $border-radius-base;
transition: border-color ease-in-out 0.15s,
- box-shadow ease-in-out 0.15s;
+ box-shadow ease-in-out 0.15s;
&.is-focused {
@extend .form-control:focus;
@@ -72,7 +72,7 @@
&.is-dropzone-hover {
border-color: $green-500;
box-shadow: 0 0 2px $black-transparent,
- 0 0 4px $green-500-focus;
+ 0 0 4px $green-500-focus;
.comment-toolbar,
.nav-links {
@@ -84,9 +84,7 @@
.md-header .nav-links {
display: flex;
- display: -webkit-flex;
flex-flow: row wrap;
- -webkit-flex-flow: row wrap;
width: 100%;
.float-right {
@@ -444,7 +442,7 @@ table {
.uploading-error-message {
@include media-breakpoint-down(xs) {
&::after {
- content: "\a";
+ content: '\a';
white-space: pre;
}
}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 7e7eff1346a..0c334e919de 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -44,6 +44,7 @@ $note-form-margin-left: 72px;
border: 1px solid $border-color;
border-radius: $border-radius-default;
margin: $gl-padding 0;
+ overflow: auto;
&.system-note,
&.note-form {
@@ -224,14 +225,7 @@ $note-form-margin-left: 72px;
overflow-y: hidden;
.note-text {
- @include md-typography;
- // Reset ul style types since we're nested inside a ul already
- @include bulleted-list;
word-wrap: break-word;
-
- table {
- @include markdown-table;
- }
}
}
@@ -283,8 +277,6 @@ $note-form-margin-left: 72px;
}
.system-note-message {
- display: inline;
-
&::first-letter {
text-transform: lowercase;
}
@@ -303,26 +295,6 @@ $note-form-margin-left: 72px;
}
}
- .timeline-icon {
- float: left;
- display: flex;
- align-items: center;
- background-color: $white-light;
- width: $system-note-icon-size;
- height: $system-note-icon-size;
- border: 1px solid $border-color;
- border-radius: $system-note-icon-size;
- margin: -6px $gl-padding 0 0;
-
- svg {
- width: $system-note-svg-size;
- height: $system-note-svg-size;
- fill: $gray-darkest;
- display: block;
- margin: 0 auto;
- }
- }
-
.timeline-content {
@include notes-media('min', map-get($grid-breakpoints, sm)) {
margin-left: 30px;
@@ -380,6 +352,37 @@ $note-form-margin-left: 72px;
}
}
}
+
+ .system-note,
+ .discussion-filter-note {
+ .timeline-icon {
+ float: left;
+ display: flex;
+ align-items: center;
+ background-color: $white-light;
+ width: $system-note-icon-size;
+ height: $system-note-icon-size;
+ border: 1px solid $border-color;
+ border-radius: $system-note-icon-size;
+ margin: -6px $gl-padding 0 0;
+
+ svg {
+ width: $system-note-svg-size;
+ height: $system-note-svg-size;
+ fill: $gray-darkest;
+ display: block;
+ margin: 0 auto;
+ }
+ }
+ }
+
+ .discussion-filter-note {
+ .timeline-icon {
+ width: $system-note-icon-size + 6;
+ height: $system-note-icon-size + 6;
+ margin-top: -8px;
+ }
+ }
}
// Diff code in discussion view
@@ -596,12 +599,6 @@ $note-form-margin-left: 72px;
}
.note-headline-meta {
- display: inline-block;
-
- .system-note-message {
- white-space: normal;
- }
-
.system-note-separator {
color: $gl-text-color-disabled;
}
@@ -739,7 +736,7 @@ $note-form-margin-left: 72px;
.add-diff-note {
@include btn-comment-icon;
opacity: 0;
- margin-left: -55px;
+ margin-left: -50px;
position: absolute;
top: 50%;
transform: translateY(-50%);
@@ -905,7 +902,6 @@ $note-form-margin-left: 72px;
}
.discussion-filter-container {
-
.btn > svg {
width: $gl-col-padding;
height: $gl-col-padding;
@@ -927,7 +923,6 @@ $note-form-margin-left: 72px;
//This needs to be deleted when Snippet/Commit comments are convered to Vue
// See https://gitlab.com/gitlab-org/gitlab-ce/issues/53918#note_117038785
.unstyled-comments {
-
.discussion-header {
padding: $gl-padding;
border-bottom: 1px solid $border-color;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index e676d48c1f4..bb08440fda8 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -341,13 +341,15 @@
&.builds .ci-table tr {
height: 71px;
}
-}
-.build-failures {
- th {
- border-top: 0;
+ .ci-table {
+ thead th {
+ border-top: 0;
+ }
}
+}
+.build-failures {
.build-state {
padding: 20px 2px;
@@ -496,7 +498,8 @@
list-style: none;
}
- &:last-child {
+ // when downstream pipelines are present, the last stage isn't the last column
+ &:last-child:not(.has-downstream) {
.build {
// Remove right connecting horizontal line from first build in last stage
&:first-child::after {
@@ -513,7 +516,8 @@
}
}
- &:first-child {
+ // when upstream pipelines are present, the first stage isn't the first column
+ &:first-child:not(.has-upstream) {
.build {
// Remove left curved connectors from all builds in first stage
&:not(:first-child)::before {
@@ -791,6 +795,7 @@
@include mini-pipeline-graph-color($white, $orange-100, $orange-200, $orange-500, $orange-600, $orange-700);
}
+ &.ci-status-icon-preparing,
&.ci-status-icon-running {
@include mini-pipeline-graph-color($white, $blue-100, $blue-200, $blue-500, $blue-600, $blue-700);
}
@@ -994,7 +999,6 @@ button.mini-pipeline-graph-dropdown-toggle {
* Top arrow in the dropdown in the mini pipeline graph
*/
.mini-pipeline-graph-dropdown-menu {
-
&::before,
&::after {
content: '';
diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss
index a1e847009fc..87cef43b923 100644
--- a/app/assets/stylesheets/pages/profile.scss
+++ b/app/assets/stylesheets/pages/profile.scss
@@ -266,23 +266,6 @@
padding-top: 20px;
}
- .cover-controls {
- position: static;
- padding: 0 16px;
- margin-bottom: 20px;
- display: -webkit-flex;
- display: flex;
-
- .btn {
- -webkit-flex-grow: 1;
- flex-grow: 1;
-
- &:first-child {
- margin-left: 0;
- }
- }
- }
-
.user-profile-nav {
a {
margin-right: 0;
@@ -322,6 +305,7 @@ table.u2f-registrations {
margin: 20px -5px 0;
.bordered-box {
+ padding: 32px;
border: 1px solid $blue-300;
border-radius: $border-radius-default;
background-color: $blue-50;
@@ -455,8 +439,17 @@ table.u2f-registrations {
}
}
+ .form-group > label {
+ font-weight: $gl-font-weight-bold;
+ }
+
+ .form-group > .form-text {
+ font-size: $gl-font-size;
+ }
+
.emoji-menu-toggle-button {
@include emoji-menu-toggle-button;
+ padding: 6px 10px;
.no-emoji-placeholder {
position: relative;
@@ -478,3 +471,41 @@ table.u2f-registrations {
.help-block {
color: $gl-text-color-secondary;
}
+
+.gitlab-slack-gif {
+ width: 100%;
+ max-width: $add-to-slack-gif-max-width;
+}
+
+.gitlab-slack-well {
+ background-color: $white-light;
+ box-shadow: none;
+ max-width: $add-to-slack-well-max-width;
+}
+
+.gitlab-slack-logo {
+ width: $add-to-slack-logo-size;
+ height: $add-to-slack-logo-size;
+}
+
+.gitlab-slack-popup {
+ width: 100%;
+ max-width: $add-to-slack-popup-max-width;
+}
+
+.gitlab-slack-right-arrow svg {
+ fill: $white-dark;
+ width: $right-arrow-size;
+ height: $right-arrow-size;
+ vertical-align: text-bottom;
+}
+
+.gitlab-slack-double-headed-arrow {
+ vertical-align: text-top;
+
+ svg {
+ fill: $gray-darker;
+ width: $double-headed-arrow-width;
+ height: $double-headed-arrow-height;
+ }
+}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 277030ad3af..bcb306d97d5 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -18,12 +18,9 @@
}
.input-group {
- display: flex;
-
.select2-container {
display: unset;
max-width: unset;
- width: unset !important;
flex-grow: 1;
}
@@ -571,9 +568,7 @@
.import-buttons {
padding-left: 0;
- display: -webkit-flex;
display: flex;
- -webkit-flex-wrap: wrap;
flex-wrap: wrap;
.btn {
@@ -695,10 +690,6 @@
}
}
-.project-empty-note-panel {
- border-bottom: 1px solid $border-color;
-}
-
.project-stats,
.project-buttons {
.scrolling-tabs-container {
diff --git a/app/assets/stylesheets/pages/search.scss b/app/assets/stylesheets/pages/search.scss
index 149c3254d84..20bdc6596e9 100644
--- a/app/assets/stylesheets/pages/search.scss
+++ b/app/assets/stylesheets/pages/search.scss
@@ -84,9 +84,7 @@ input[type='checkbox']:hover {
.search-icon {
transition: color $default-transition-duration;
- -webkit-user-select: none;
- -moz-user-select: none;
- -ms-user-select: none;
+ user-select: none;
}
.clear-icon {
@@ -185,13 +183,11 @@ input[type='checkbox']:hover {
.search-holder {
@include media-breakpoint-up(sm) {
- display: -webkit-flex;
display: flex;
}
.search-field-holder,
.project-filter-form {
- -webkit-flex: 1 0 auto;
flex: 1 0 auto;
position: relative;
margin-right: 0;
diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss
index 811cc310a8f..e4ed685bd1b 100644
--- a/app/assets/stylesheets/pages/settings.scss
+++ b/app/assets/stylesheets/pages/settings.scss
@@ -23,7 +23,10 @@
}
.settings {
- border-bottom: 1px solid $gray-darker;
+ // border-top for each item except the top one
+ + .settings {
+ border-top: 1px solid $border-color;
+ }
&:first-of-type {
margin-top: 10px;
@@ -213,6 +216,31 @@
}
}
+.nested-settings {
+ padding-left: 20px;
+}
+
+.input-btn-group {
+ display: flex;
+
+ .input-large {
+ flex: 1;
+ }
+
+ .btn {
+ margin-left: 10px;
+ }
+}
+
+.settings-flex-row {
+ display: flex;
+ align-items: center;
+
+ .float-right {
+ margin-left: auto;
+ }
+}
+
.prometheus-metrics-monitoring {
.card {
.card-toggle {
@@ -243,6 +271,27 @@
}
}
+ .custom-monitored-metrics {
+ .card-title {
+ display: flex;
+ align-items: center;
+
+ > .btn-success {
+ margin-left: auto;
+ }
+ }
+
+ .custom-metric {
+ display: flex;
+ align-items: center;
+ }
+
+ .custom-metric-link-bold {
+ font-weight: $gl-font-weight-bold;
+ text-decoration: none;
+ }
+ }
+
.loading-metrics,
.empty-metrics {
padding: 30px 10px;
@@ -277,6 +326,12 @@
}
}
+.saml-settings.info-well {
+ .form-control[readonly] {
+ background: $white-light;
+ }
+}
+
.modal-doorkeepr-auth {
.modal-body {
padding: $gl-padding;
@@ -316,8 +371,4 @@
.push-pull-table {
margin-top: 1em;
-
- .mirror-action-buttons {
- padding-right: 0;
- }
}
diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss
index d331edaa302..79186480605 100644
--- a/app/assets/stylesheets/pages/stat_graph.scss
+++ b/app/assets/stylesheets/pages/stat_graph.scss
@@ -25,6 +25,8 @@
}
#contributors {
+ flex: 1;
+
.contributors-list {
margin: 0 0 10px;
list-style: none;
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 7d59dd3b5d1..a59bb31bdcb 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -33,7 +33,7 @@
border-color: $gl-text-color;
&:not(span):hover {
- background-color: rgba($gl-text-color, .07);
+ background-color: rgba($gl-text-color, 0.07);
}
}
@@ -44,6 +44,7 @@
}
&.ci-info,
+ &.ci-preparing,
&.ci-running {
@include status-color($blue-100, $blue-500, $blue-600);
}
@@ -54,7 +55,7 @@
border-color: $gl-text-color-secondary;
&:not(span):hover {
- background-color: rgba($gl-text-color-secondary, .07);
+ background-color: rgba($gl-text-color-secondary, 0.07);
}
}
}
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index 3fc37e20c36..2a1e8345755 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -6,9 +6,7 @@
.todos-list > .todo {
// workaround because we cannot use border-colapse
border-top: 1px solid transparent;
- display: -webkit-flex;
display: flex;
- -webkit-flex-direction: row;
flex-direction: row;
&:hover {
@@ -29,23 +27,18 @@
.todo-avatar,
.todo-actions {
@include transition(opacity);
- -webkit-flex: 0 0 auto;
flex: 0 0 auto;
}
.todo-actions {
- display: -webkit-flex;
display: flex;
- -webkit-justify-content: center;
justify-content: center;
- -webkit-flex-direction: column;
flex-direction: column;
margin-left: 10px;
min-width: 55px;
}
.todo-item {
- -webkit-flex: 0 1 100%;
flex: 0 1 100%;
min-width: 0;
}
@@ -60,13 +53,13 @@
.todo-avatar,
.todo-item {
- opacity: .6;
+ opacity: 0.6;
}
}
.todo-avatar,
.todo-item {
- opacity: .2;
+ opacity: 0.2;
}
.btn {
@@ -82,7 +75,6 @@
display: flex;
> .title-item {
- -webkit-flex: 0 0 auto;
flex: 0 0 auto;
margin: 0 2px;
@@ -96,7 +88,6 @@
}
.todo-label {
- -webkit-flex: 0 1 auto;
flex: 0 1 auto;
overflow: hidden;
text-overflow: ellipsis;
@@ -222,23 +213,19 @@
}
.todos-empty {
- display: -webkit-flex;
display: flex;
- -webkit-flex-direction: column;
flex-direction: column;
max-width: 900px;
margin-left: auto;
margin-right: auto;
@include media-breakpoint-up(sm) {
- -webkit-flex-direction: row;
flex-direction: row;
padding-top: 80px;
}
}
.todos-empty-content {
- -webkit-align-self: center;
align-self: center;
max-width: 480px;
margin-right: 20px;
@@ -252,7 +239,6 @@
@include media-breakpoint-up(sm) {
width: 300px;
margin-right: 0;
- -webkit-order: 2;
order: 2;
}
}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index a46b8679a42..5664f46484e 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -172,26 +172,6 @@
text-decoration: inherit;
}
}
-
- .tree_commit {
- max-width: 320px;
-
- .str-truncated {
- max-width: 100%;
- }
- }
-
- .tree_time_ago {
- min-width: 135px;
- }
- }
-
- .tree_author {
- padding-right: 8px;
-
- .commit-author-name {
- color: $gl-text-color;
- }
}
.tree-truncated-warning {
diff --git a/app/assets/stylesheets/pages/ui_dev_kit.scss b/app/assets/stylesheets/pages/ui_dev_kit.scss
index 84c617c7ec0..7744fd814d0 100644
--- a/app/assets/stylesheets/pages/ui_dev_kit.scss
+++ b/app/assets/stylesheets/pages/ui_dev_kit.scss
@@ -10,7 +10,7 @@
margin-bottom: 15px;
&::before {
- content: "Example";
+ content: 'Example';
color: $ui-dev-kit-example-color;
}
}
diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss
index 82e887aa62a..3260aed143e 100644
--- a/app/assets/stylesheets/pages/wiki.scss
+++ b/app/assets/stylesheets/pages/wiki.scss
@@ -179,9 +179,3 @@ ul.wiki-pages-list.content-list {
}
}
}
-
-.wiki:not(.use-csslab) {
- table {
- @include markdown-table;
- }
-}
diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss
index bb10928a037..9ed1600419d 100644
--- a/app/assets/stylesheets/print.scss
+++ b/app/assets/stylesheets/print.scss
@@ -1,21 +1,21 @@
-.wiki h1,
-.wiki h2,
-.wiki h3,
-.wiki h4,
-.wiki h5,
-.wiki h6 {
+.md h1,
+.md h2,
+.md h3,
+.md h4,
+.md h5,
+.md h6 {
margin-top: 17px;
}
-.wiki h1 {
+.md h1 {
font-size: 30px;
}
-.wiki h2 {
+.md h2 {
font-size: 22px;
}
-.wiki h3 {
+.md h3 {
font-size: 18px;
font-weight: 600;
}
diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb
index e3226c86b0b..383ec2a7d16 100644
--- a/app/controllers/admin/appearances_controller.rb
+++ b/app/controllers/admin/appearances_controller.rb
@@ -14,7 +14,7 @@ class Admin::AppearancesController < Admin::ApplicationController
@appearance = Appearance.new(appearance_params)
if @appearance.save
- redirect_to admin_appearances_path, notice: 'Appearance was successfully created.'
+ redirect_to admin_appearances_path, notice: _('Appearance was successfully created.')
else
render action: 'show'
end
@@ -22,7 +22,7 @@ class Admin::AppearancesController < Admin::ApplicationController
def update
if @appearance.update(appearance_params)
- redirect_to admin_appearances_path, notice: 'Appearance was successfully updated.'
+ redirect_to admin_appearances_path, notice: _('Appearance was successfully updated.')
else
render action: 'show'
end
@@ -33,21 +33,21 @@ class Admin::AppearancesController < Admin::ApplicationController
@appearance.save
- redirect_to admin_appearances_path, notice: 'Logo was successfully removed.'
+ redirect_to admin_appearances_path, notice: _('Logo was successfully removed.')
end
def header_logos
@appearance.remove_header_logo!
@appearance.save
- redirect_to admin_appearances_path, notice: 'Header logo was successfully removed.'
+ redirect_to admin_appearances_path, notice: _('Header logo was successfully removed.')
end
def favicon
@appearance.remove_favicon!
@appearance.save
- redirect_to admin_appearances_path, notice: 'Favicon was successfully removed.'
+ redirect_to admin_appearances_path, notice: _('Favicon was successfully removed.')
end
private
@@ -74,6 +74,11 @@ class Admin::AppearancesController < Admin::ApplicationController
favicon_cache
new_project_guidelines
updated_by
+ header_message
+ footer_message
+ message_background_color
+ message_font_color
+ email_header_and_footer_enabled
]
end
end
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 8f267eccc8a..ab792cf7403 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -48,7 +48,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
respond_to do |format|
if successful
format.json { head :ok }
- format.html { redirect_to redirect_path, notice: 'Application settings saved successfully' }
+ format.html { redirect_to redirect_path, notice: _('Application settings saved successfully') }
else
format.json { head :bad_request }
format.html { render :show }
@@ -70,13 +70,13 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
def reset_registration_token
@application_setting.reset_runners_registration_token!
- flash[:notice] = 'New runners registration token has been generated!'
+ flash[:notice] = _('New runners registration token has been generated!')
redirect_to admin_runners_path
end
def reset_health_check_token
@application_setting.reset_health_check_access_token!
- flash[:notice] = 'New health check access token has been generated!'
+ flash[:notice] = _('New health check access token has been generated!')
redirect_back_or_default
end
@@ -85,7 +85,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
redirect_to(
admin_application_settings_path,
- notice: 'Started asynchronous removal of all repository check states.'
+ notice: _('Started asynchronous removal of all repository check states.')
)
end
diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb
index 6fc336714b6..3648c8be426 100644
--- a/app/controllers/admin/applications_controller.rb
+++ b/app/controllers/admin/applications_controller.rb
@@ -34,7 +34,7 @@ class Admin::ApplicationsController < Admin::ApplicationController
def update
if @application.update(application_params)
- redirect_to admin_application_path(@application), notice: 'Application was successfully updated.'
+ redirect_to admin_application_path(@application), notice: _('Application was successfully updated.')
else
render :edit
end
@@ -42,7 +42,7 @@ class Admin::ApplicationsController < Admin::ApplicationController
def destroy
@application.destroy
- redirect_to admin_applications_url, status: 302, notice: 'Application was successfully destroyed.'
+ redirect_to admin_applications_url, status: 302, notice: _('Application was successfully destroyed.')
end
private
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index a91d9a534cd..6e5dd1a1f55 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -19,7 +19,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
@broadcast_message = BroadcastMessage.new(broadcast_message_params)
if @broadcast_message.save
- redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully created.'
+ redirect_to admin_broadcast_messages_path, notice: _('Broadcast Message was successfully created.')
else
render :index
end
@@ -27,7 +27,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
def update
if @broadcast_message.update(broadcast_message_params)
- redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully updated.'
+ redirect_to admin_broadcast_messages_path, notice: _('Broadcast Message was successfully updated.')
else
render :edit
end
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index 49ce275ad14..180f7d4c803 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -25,7 +25,7 @@ class Admin::DeployKeysController < Admin::ApplicationController
def update
if deploy_key.update(update_params)
- flash[:notice] = 'Deploy key was successfully updated.'
+ flash[:notice] = _('Deploy key was successfully updated.')
redirect_to admin_deploy_keys_path
else
render 'edit'
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 46e85e1424f..e0ecdb0c0e9 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -36,7 +36,7 @@ class Admin::GroupsController < Admin::ApplicationController
if @group.save
@group.add_owner(current_user)
- redirect_to [:admin, @group], notice: "Group '#{@group.name}' was successfully created."
+ redirect_to [:admin, @group], notice: _('Group %{group_name} was successfully created.') % { group_name: @group.name }
else
render "new"
end
@@ -44,7 +44,7 @@ class Admin::GroupsController < Admin::ApplicationController
def update
if @group.update(group_params)
- redirect_to [:admin, @group], notice: 'Group was successfully updated.'
+ redirect_to [:admin, @group], notice: _('Group was successfully updated.')
else
render "edit"
end
@@ -55,7 +55,7 @@ class Admin::GroupsController < Admin::ApplicationController
result = Members::CreateService.new(current_user, member_params.merge(limit: -1)).execute(@group)
if result[:status] == :success
- redirect_to [:admin, @group], notice: 'Users were successfully added.'
+ redirect_to [:admin, @group], notice: _('Users were successfully added.')
else
redirect_to [:admin, @group], alert: result[:message]
end
@@ -66,7 +66,7 @@ class Admin::GroupsController < Admin::ApplicationController
redirect_to admin_groups_path,
status: 302,
- alert: "Group '#{@group.name}' was scheduled for deletion."
+ alert: _('Group %{group_name} was scheduled for deletion.') % { group_name: @group.name }
end
private
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index d0abdec50ae..51b0f45c5be 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -14,7 +14,7 @@ class Admin::HooksController < Admin::ApplicationController
@hook = SystemHook.new(hook_params.to_h)
if @hook.save
- redirect_to admin_hooks_path, notice: 'Hook was successfully created.'
+ redirect_to admin_hooks_path, notice: _('Hook was successfully created.')
else
@hooks = SystemHook.all
render :index
@@ -26,7 +26,7 @@ class Admin::HooksController < Admin::ApplicationController
def update
if hook.update(hook_params)
- flash[:notice] = 'System hook was successfully updated.'
+ flash[:notice] = _('System hook was successfully updated.')
redirect_to admin_hooks_path
else
render 'edit'
diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb
index b51c2f678ca..f518f7a657f 100644
--- a/app/controllers/admin/identities_controller.rb
+++ b/app/controllers/admin/identities_controller.rb
@@ -13,7 +13,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
@identity.user_id = user.id
if @identity.save
- redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully created.'
+ redirect_to admin_user_identities_path(@user), notice: _('User identity was successfully created.')
else
render :new
end
@@ -29,7 +29,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
def update
if @identity.update(identity_params)
RepairLdapBlockedUserService.new(@user).execute
- redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully updated.'
+ redirect_to admin_user_identities_path(@user), notice: _('User identity was successfully updated.')
else
render :edit
end
@@ -38,9 +38,9 @@ class Admin::IdentitiesController < Admin::ApplicationController
def destroy
if @identity.destroy
RepairLdapBlockedUserService.new(@user).execute
- redirect_to admin_user_identities_path(@user), status: 302, notice: 'User identity was successfully removed.'
+ redirect_to admin_user_identities_path(@user), status: 302, notice: _('User identity was successfully removed.')
else
- redirect_to admin_user_identities_path(@user), status: 302, alert: 'Failed to remove user identity.'
+ redirect_to admin_user_identities_path(@user), status: 302, alert: _('Failed to remove user identity.')
end
end
diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb
index 706bcc1e549..cfe29d734b7 100644
--- a/app/controllers/admin/impersonation_tokens_controller.rb
+++ b/app/controllers/admin/impersonation_tokens_controller.rb
@@ -12,7 +12,7 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
if @impersonation_token.save
PersonalAccessToken.redis_store!(current_user.id, @impersonation_token.token)
- redirect_to admin_user_impersonation_tokens_path, notice: "A new impersonation token has been created."
+ redirect_to admin_user_impersonation_tokens_path, notice: _("A new impersonation token has been created.")
else
set_index_vars
render :index
@@ -23,9 +23,9 @@ class Admin::ImpersonationTokensController < Admin::ApplicationController
@impersonation_token = finder.find(params[:id])
if @impersonation_token.revoke!
- flash[:notice] = "Revoked impersonation token #{@impersonation_token.name}!"
+ flash[:notice] = _("Revoked impersonation token %{token_name}!") % { token_name: @impersonation_token.name }
else
- flash[:alert] = "Could not revoke impersonation token #{@impersonation_token.name}."
+ flash[:alert] = _("Could not revoke impersonation token %{token_name}.") % { token_name: @impersonation_token.name }
end
redirect_to admin_user_impersonation_tokens_path
diff --git a/app/controllers/admin/keys_controller.rb b/app/controllers/admin/keys_controller.rb
index 4e9262ccc96..340eecd7632 100644
--- a/app/controllers/admin/keys_controller.rb
+++ b/app/controllers/admin/keys_controller.rb
@@ -17,9 +17,9 @@ class Admin::KeysController < Admin::ApplicationController
respond_to do |format|
if key.destroy
- format.html { redirect_to keys_admin_user_path(user), status: 302, notice: 'User key was successfully removed.' }
+ format.html { redirect_to keys_admin_user_path(user), status: 302, notice: _('User key was successfully removed.') }
else
- format.html { redirect_to keys_admin_user_path(user), status: 302, alert: 'Failed to remove user key.' }
+ format.html { redirect_to keys_admin_user_path(user), status: 302, alert: _('Failed to remove user key.') }
end
end
end
diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb
index aa5eae7a474..90c1694fd2e 100644
--- a/app/controllers/admin/labels_controller.rb
+++ b/app/controllers/admin/labels_controller.rb
@@ -21,7 +21,7 @@ class Admin::LabelsController < Admin::ApplicationController
@label = Labels::CreateService.new(label_params).execute(template: true)
if @label.persisted?
- redirect_to admin_labels_url, notice: "Label was created"
+ redirect_to admin_labels_url, notice: _("Label was created")
else
render :new
end
@@ -31,7 +31,7 @@ class Admin::LabelsController < Admin::ApplicationController
@label = Labels::UpdateService.new(label_params).execute(@label)
if @label.valid?
- redirect_to admin_labels_path, notice: 'Label was successfully updated.'
+ redirect_to admin_labels_path, notice: _('Label was successfully updated.')
else
render :edit
end
@@ -43,7 +43,7 @@ class Admin::LabelsController < Admin::ApplicationController
respond_to do |format|
format.html do
- redirect_to admin_labels_path, status: 302, notice: 'Label was removed'
+ redirect_to admin_labels_path, status: 302, notice: _('Label was removed')
end
format.js
end
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 550f29a58d2..fb135d1a32c 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -15,7 +15,7 @@ class Admin::ProjectsController < Admin::ApplicationController
format.html
format.json do
render json: {
- html: view_to_html_string("admin/projects/_projects", locals: { projects: @projects })
+ html: view_to_html_string("admin/projects/_projects", projects: @projects)
}
end
end
@@ -50,7 +50,7 @@ class Admin::ProjectsController < Admin::ApplicationController
redirect_to(
admin_project_path(@project),
- notice: 'Repository check was triggered.'
+ notice: _('Repository check was triggered.')
)
end
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 0b6ff491c66..783c59822f1 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class Admin::RunnersController < Admin::ApplicationController
- before_action :runner, except: :index
+ before_action :runner, except: [:index, :tag_list]
def index
finder = Admin::RunnersFinder.new(params: params)
@@ -34,20 +34,26 @@ class Admin::RunnersController < Admin::ApplicationController
def resume
if Ci::UpdateRunnerService.new(@runner).update(active: true)
- redirect_to admin_runners_path, notice: 'Runner was successfully updated.'
+ redirect_to admin_runners_path, notice: _('Runner was successfully updated.')
else
- redirect_to admin_runners_path, alert: 'Runner was not updated.'
+ redirect_to admin_runners_path, alert: _('Runner was not updated.')
end
end
def pause
if Ci::UpdateRunnerService.new(@runner).update(active: false)
- redirect_to admin_runners_path, notice: 'Runner was successfully updated.'
+ redirect_to admin_runners_path, notice: _('Runner was successfully updated.')
else
- redirect_to admin_runners_path, alert: 'Runner was not updated.'
+ redirect_to admin_runners_path, alert: _('Runner was not updated.')
end
end
+ def tag_list
+ tags = Autocomplete::ActsAsTaggableOn::TagsFinder.new(params: params).execute
+
+ render json: ActsAsTaggableOn::TagSerializer.new.represent(tags)
+ end
+
private
def runner
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index 18d22c95b61..45cf0d3207e 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -14,7 +14,7 @@ class Admin::SpamLogsController < Admin::ApplicationController
spam_log.remove_user(deleted_by: current_user)
redirect_to admin_spam_logs_path,
status: 302,
- notice: "User #{spam_log.user.username} was successfully removed."
+ notice: _('User %{username} was successfully removed.') % { username: spam_log.user.username }
else
spam_log.destroy
head :ok
@@ -25,9 +25,9 @@ class Admin::SpamLogsController < Admin::ApplicationController
spam_log = SpamLog.find(params[:id])
if HamService.new(spam_log).mark_as_ham!
- redirect_to admin_spam_logs_path, notice: 'Spam log successfully submitted as ham.'
+ redirect_to admin_spam_logs_path, notice: _('Spam log successfully submitted as ham.')
else
- redirect_to admin_spam_logs_path, alert: 'Error with Akismet. Please check the logs for more info.'
+ redirect_to admin_spam_logs_path, alert: _('Error with Akismet. Please check the logs for more info.')
end
end
end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 0eae007715a..a02d0843615 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -7,7 +7,7 @@ class Admin::UsersController < Admin::ApplicationController
before_action :check_impersonation_availability, only: :impersonate
def index
- @users = User.order_name_asc.filter(params[:filter])
+ @users = User.filter_items(params[:filter]).order_name_asc
@users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
@users = @users.sort_by_attribute(@sort = params[:sort])
@users = @users.page(params[:page])
@@ -39,19 +39,19 @@ class Admin::UsersController < Admin::ApplicationController
warden.set_user(user, scope: :user)
- Gitlab::AppLogger.info("User #{current_user.username} has started impersonating #{user.username}")
+ Gitlab::AppLogger.info(_("User %{current_user_username} has started impersonating %{username}") % { current_user_username: current_user.username, username: user.username })
- flash[:alert] = "You are now impersonating #{user.username}"
+ flash[:alert] = _("You are now impersonating %{username}") % { username: user.username }
redirect_to root_path
else
flash[:alert] =
if user.blocked?
- "You cannot impersonate a blocked user"
+ _("You cannot impersonate a blocked user")
elsif user.internal?
- "You cannot impersonate an internal user"
+ _("You cannot impersonate an internal user")
else
- "You cannot impersonate a user who cannot log in"
+ _("You cannot impersonate a user who cannot log in")
end
redirect_to admin_user_path(user)
@@ -60,35 +60,35 @@ class Admin::UsersController < Admin::ApplicationController
def block
if update_user { |user| user.block }
- redirect_back_or_admin_user(notice: "Successfully blocked")
+ redirect_back_or_admin_user(notice: _("Successfully blocked"))
else
- redirect_back_or_admin_user(alert: "Error occurred. User was not blocked")
+ redirect_back_or_admin_user(alert: _("Error occurred. User was not blocked"))
end
end
def unblock
if user.ldap_blocked?
- redirect_back_or_admin_user(alert: "This user cannot be unlocked manually from GitLab")
+ redirect_back_or_admin_user(alert: _("This user cannot be unlocked manually from GitLab"))
elsif update_user { |user| user.activate }
- redirect_back_or_admin_user(notice: "Successfully unblocked")
+ redirect_back_or_admin_user(notice: _("Successfully unblocked"))
else
- redirect_back_or_admin_user(alert: "Error occurred. User was not unblocked")
+ redirect_back_or_admin_user(alert: _("Error occurred. User was not unblocked"))
end
end
def unlock
if update_user { |user| user.unlock_access! }
- redirect_back_or_admin_user(alert: "Successfully unlocked")
+ redirect_back_or_admin_user(alert: _("Successfully unlocked"))
else
- redirect_back_or_admin_user(alert: "Error occurred. User was not unlocked")
+ redirect_back_or_admin_user(alert: _("Error occurred. User was not unlocked"))
end
end
def confirm
if update_user { |user| user.confirm }
- redirect_back_or_admin_user(notice: "Successfully confirmed")
+ redirect_back_or_admin_user(notice: _("Successfully confirmed"))
else
- redirect_back_or_admin_user(alert: "Error occurred. User was not confirmed")
+ redirect_back_or_admin_user(alert: _("Error occurred. User was not confirmed"))
end
end
@@ -96,7 +96,7 @@ class Admin::UsersController < Admin::ApplicationController
update_user { |user| user.disable_two_factor! }
redirect_to admin_user_path(user),
- notice: 'Two-factor Authentication has been disabled for this user'
+ notice: _('Two-factor Authentication has been disabled for this user')
end
def create
@@ -109,7 +109,7 @@ class Admin::UsersController < Admin::ApplicationController
respond_to do |format|
if @user.persisted?
- format.html { redirect_to [:admin, @user], notice: 'User was successfully created.' }
+ format.html { redirect_to [:admin, @user], notice: _('User was successfully created.') }
format.json { render json: @user, status: :created, location: @user }
else
format.html { render "new" }
@@ -138,7 +138,7 @@ class Admin::UsersController < Admin::ApplicationController
end
if result[:status] == :success
- format.html { redirect_to [:admin, user], notice: 'User was successfully updated.' }
+ format.html { redirect_to [:admin, user], notice: _('User was successfully updated.') }
format.json { head :ok }
else
# restore username to keep form action url.
@@ -153,7 +153,7 @@ class Admin::UsersController < Admin::ApplicationController
user.delete_async(deleted_by: current_user, params: params.permit(:hard_delete))
respond_to do |format|
- format.html { redirect_to admin_users_path, status: 302, notice: "The user is being deleted." }
+ format.html { redirect_to admin_users_path, status: 302, notice: _("The user is being deleted.") }
format.json { head :ok }
end
end
@@ -164,11 +164,11 @@ class Admin::UsersController < Admin::ApplicationController
respond_to do |format|
if success
- format.html { redirect_back_or_admin_user(notice: 'Successfully removed email.') }
+ format.html { redirect_back_or_admin_user(notice: _('Successfully removed email.')) }
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: :bad_request }
+ 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: :bad_request }
end
end
end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index af0b0c64814..b7eb6af6d67 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -43,7 +43,10 @@ class ApplicationController < ActionController::Base
:git_import_enabled?, :gitlab_project_import_enabled?,
:manifest_import_enabled?
+ # Adds `no-store` to the DEFAULT_CACHE_CONTROL, to prevent security
+ # concerns due to caching private data.
DEFAULT_GITLAB_CACHE_CONTROL = "#{ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL}, no-store".freeze
+ DEFAULT_GITLAB_CONTROL_NO_CACHE = "#{DEFAULT_GITLAB_CACHE_CONTROL}, no-cache".freeze
rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception)
@@ -235,9 +238,9 @@ class ApplicationController < ActionController::Base
end
def no_cache_headers
- response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
- response.headers["Pragma"] = "no-cache"
- response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
+ headers['Cache-Control'] = DEFAULT_GITLAB_CONTROL_NO_CACHE
+ headers['Pragma'] = 'no-cache' # HTTP 1.0 compatibility
+ headers['Expires'] = 'Fri, 01 Jan 1990 00:00:00 GMT'
end
def default_headers
@@ -247,10 +250,16 @@ class ApplicationController < ActionController::Base
headers['X-Content-Type-Options'] = 'nosniff'
if current_user
- # Adds `no-store` to the DEFAULT_CACHE_CONTROL, to prevent security
- # concerns due to caching private data.
- headers['Cache-Control'] = DEFAULT_GITLAB_CACHE_CONTROL
- headers["Pragma"] = "no-cache" # HTTP 1.0 compatibility
+ headers['Cache-Control'] = default_cache_control
+ headers['Pragma'] = 'no-cache' # HTTP 1.0 compatibility
+ end
+ end
+
+ def default_cache_control
+ if request.xhr?
+ ActionDispatch::Http::Cache::Response::DEFAULT_CACHE_CONTROL
+ else
+ DEFAULT_GITLAB_CACHE_CONTROL
end
end
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index 0d5c8657c9e..091327931c2 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class AutocompleteController < ApplicationController
- skip_before_action :authenticate_user!, only: [:users, :award_emojis]
+ skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches]
def users
project = Autocomplete::ProjectFinder
@@ -38,4 +38,11 @@ class AutocompleteController < ApplicationController
def award_emojis
render json: AwardedEmojiFinder.new(current_user).execute
end
+
+ def merge_request_target_branches
+ merge_requests = MergeRequestsFinder.new(current_user, params).execute
+ target_branches = merge_requests.recent_target_branches
+
+ render json: target_branches.map { |target_branch| { title: target_branch } }
+ end
end
diff --git a/app/controllers/clusters/applications_controller.rb b/app/controllers/clusters/applications_controller.rb
index c4e7fc950f9..73c744efeba 100644
--- a/app/controllers/clusters/applications_controller.rb
+++ b/app/controllers/clusters/applications_controller.rb
@@ -3,26 +3,41 @@
class Clusters::ApplicationsController < Clusters::BaseController
before_action :cluster
before_action :authorize_create_cluster!, only: [:create]
+ before_action :authorize_update_cluster!, only: [:update]
def create
- Clusters::Applications::CreateService
- .new(@cluster, current_user, create_cluster_application_params)
- .execute(request)
+ request_handler do
+ Clusters::Applications::CreateService
+ .new(@cluster, current_user, cluster_application_params)
+ .execute(request)
+ end
+ end
+
+ def update
+ request_handler do
+ Clusters::Applications::UpdateService
+ .new(@cluster, current_user, cluster_application_params)
+ .execute(request)
+ end
+ end
+
+ private
+
+ def request_handler
+ yield
head :no_content
- rescue Clusters::Applications::CreateService::InvalidApplicationError
+ rescue Clusters::Applications::BaseService::InvalidApplicationError
render_404
rescue StandardError
head :bad_request
end
- private
-
def cluster
@cluster ||= clusterable.clusters.find(params[:id]) || render_404
end
- def create_cluster_application_params
+ def cluster_application_params
params.permit(:application, :hostname, :email)
end
end
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index 3bd91b71d92..68a2a83f0de 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -24,7 +24,7 @@ class Clusters::ClustersController < Clusters::BaseController
# Note: We are paginating through an array here but this should OK as:
#
# In CE, we can have a maximum group nesting depth of 21, so including
- # project cluster, we can have max 22 clusters for a group hierachy.
+ # project cluster, we can have max 22 clusters for a group hierarchy.
# In EE (Premium) we can have any number, as multiple clusters are
# supported, but the number of clusters are fairly low currently.
#
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 5507328f8ae..d5c4712bd78 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -36,7 +36,7 @@ module AuthenticatesWithTwoFactor
end
def locked_user_redirect(user)
- flash.now[:alert] = 'Invalid Login or password'
+ flash.now[:alert] = _('Invalid Login or password')
render 'devise/sessions/new'
end
@@ -66,7 +66,7 @@ module AuthenticatesWithTwoFactor
else
user.increment_failed_attempts!
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP")
- flash.now[:alert] = 'Invalid two-factor code.'
+ flash.now[:alert] = _('Invalid two-factor code.')
prompt_for_two_factor(user)
end
end
@@ -83,7 +83,7 @@ module AuthenticatesWithTwoFactor
else
user.increment_failed_attempts!
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=U2F")
- flash.now[:alert] = 'Authentication via U2F device failed.'
+ flash.now[:alert] = _('Authentication via U2F device failed.')
prompt_for_two_factor(user)
end
end
diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb
new file mode 100644
index 00000000000..ed7ea2f0e04
--- /dev/null
+++ b/app/controllers/concerns/boards_actions.rb
@@ -0,0 +1,38 @@
+# frozen_string_literal: true
+
+module BoardsActions
+ include Gitlab::Utils::StrongMemoize
+ extend ActiveSupport::Concern
+
+ included do
+ include BoardsResponses
+
+ before_action :boards, only: :index
+ before_action :board, only: :show
+ end
+
+ def index
+ respond_with_boards
+ end
+
+ def show
+ # Add / update the board in the recent visits table
+ Boards::Visits::CreateService.new(parent, current_user).execute(board) if request.format.html?
+
+ respond_with_board
+ end
+
+ private
+
+ def boards
+ strong_memoize(:boards) do
+ Boards::ListService.new(parent, current_user).execute
+ end
+ end
+
+ def board
+ strong_memoize(:board) do
+ boards.find(params[:id])
+ end
+ end
+end
diff --git a/app/controllers/concerns/continue_params.rb b/app/controllers/concerns/continue_params.rb
index f0e6adf4dec..54c0510497f 100644
--- a/app/controllers/concerns/continue_params.rb
+++ b/app/controllers/concerns/continue_params.rb
@@ -6,7 +6,7 @@ module ContinueParams
def continue_params
continue_params = params[:continue]
- return nil unless continue_params
+ return unless continue_params
continue_params = continue_params.permit(:to, :notice, :notice_now)
continue_params[:to] = safe_redirect_path(continue_params[:to])
diff --git a/app/controllers/concerns/creates_commit.rb b/app/controllers/concerns/creates_commit.rb
index b3777fd2b0f..e8e681ce649 100644
--- a/app/controllers/concerns/creates_commit.rb
+++ b/app/controllers/concerns/creates_commit.rb
@@ -31,7 +31,7 @@ module CreatesCommit
respond_to do |format|
format.html { redirect_to success_path }
- format.json { render json: { message: "success", filePath: success_path } }
+ format.json { render json: { message: _("success"), filePath: success_path } }
end
else
flash[:alert] = result[:message]
@@ -45,7 +45,7 @@ module CreatesCommit
redirect_to failure_path
end
end
- format.json { render json: { message: "failed", filePath: failure_path } }
+ format.json { render json: { message: _("failed"), filePath: failure_path } }
end
end
end
@@ -60,15 +60,22 @@ module CreatesCommit
private
def update_flash_notice(success_notice)
- flash[:notice] = success_notice || "Your changes have been successfully committed."
+ flash[:notice] = success_notice || _("Your changes have been successfully committed.")
if create_merge_request?
- if merge_request_exists?
- flash[:notice] = nil
- else
- target = different_project? ? "project" : "branch"
- flash[:notice] = flash[:notice] + " You can now submit a merge request to get this change into the original #{target}."
- end
+ flash[:notice] =
+ if merge_request_exists?
+ nil
+ else
+ mr_message =
+ if different_project?
+ _("You can now submit a merge request to get this change into the original project.")
+ else
+ _("You can now submit a merge request to get this change into the original branch.")
+ end
+
+ flash[:notice] += " " + mr_message
+ end
end
end
diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb
index cd3fa641e89..05d88429cfe 100644
--- a/app/controllers/concerns/issuable_actions.rb
+++ b/app/controllers/concerns/issuable_actions.rb
@@ -8,7 +8,7 @@ module IssuableActions
before_action :authorize_destroy_issuable!, only: :destroy
before_action :authorize_admin_issuable!, only: :bulk_update
before_action only: :show do
- push_frontend_feature_flag(:reply_to_individual_notes)
+ push_frontend_feature_flag(:reply_to_individual_notes, default_enabled: true)
end
end
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 07d0bf16d93..c529aabf797 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -91,6 +91,7 @@ module IssuableCollections
options = {
scope: params[:scope],
state: params[:state],
+ confidential: Gitlab::Utils.to_boolean(params[:confidential]),
sort: set_sort_order
}
diff --git a/app/controllers/concerns/lfs_request.rb b/app/controllers/concerns/lfs_request.rb
index 5572c3cee2d..f7137a04437 100644
--- a/app/controllers/concerns/lfs_request.rb
+++ b/app/controllers/concerns/lfs_request.rb
@@ -26,7 +26,7 @@ module LfsRequest
render(
json: {
- message: 'Git LFS is not enabled on this GitLab server, contact your admin.',
+ message: _('Git LFS is not enabled on this GitLab server, contact your admin.'),
documentation_url: help_url
},
status: :not_implemented
@@ -51,7 +51,7 @@ module LfsRequest
def render_lfs_forbidden
render(
json: {
- message: 'Access forbidden. Check your access level.',
+ message: _('Access forbidden. Check your access level.'),
documentation_url: help_url
},
content_type: CONTENT_TYPE,
@@ -62,7 +62,7 @@ module LfsRequest
def render_lfs_not_found
render(
json: {
- message: 'Not found.',
+ message: _('Not found.'),
documentation_url: help_url
},
content_type: CONTENT_TYPE,
@@ -123,7 +123,7 @@ module LfsRequest
(authentication_abilities || []).include?(capability)
end
- # Overriden in EE
+ # Overridden in EE
def limit_exceeded?
false
end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 6402e01ddc0..0b2756c0c6a 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -9,7 +9,7 @@ module MembershipActions
result = Members::CreateService.new(current_user, create_params).execute(membershipable)
if result[:status] == :success
- redirect_to members_page_url, notice: 'Users were successfully added.'
+ redirect_to members_page_url, notice: _('Users were successfully added.')
else
redirect_to members_page_url, alert: result[:message]
end
@@ -35,9 +35,16 @@ module MembershipActions
respond_to do |format|
format.html do
- source = source_type == 'group' ? 'group and any subresources' : source_type
+ message =
+ begin
+ case membershipable
+ when Namespace
+ _("User was successfully removed from group and any subresources.")
+ else
+ _("User was successfully removed from project.")
+ end
+ end
- message = "User was successfully removed from #{source}."
redirect_to members_page_url, notice: message
end
@@ -49,7 +56,7 @@ module MembershipActions
membershipable.request_access(current_user)
redirect_to polymorphic_path(membershipable),
- notice: 'Your request for access has been queued for review.'
+ notice: _('Your request for access has been queued for review.')
end
def approve_access_request
@@ -68,9 +75,9 @@ module MembershipActions
notice =
if member.request?
- "Your access request to the #{source_type} has been withdrawn."
+ _("Your access request to the %{source_type} has been withdrawn.") % { source_type: source_type }
else
- "You left the \"#{membershipable.human_name}\" #{source_type}."
+ _("You left the \"%{membershipable_human_name}\" %{source_type}.") % { membershipable_human_name: membershipable.human_name, source_type: source_type }
end
respond_to do |format|
@@ -90,9 +97,9 @@ module MembershipActions
if member.invite?
member.resend_invite
- redirect_to members_page_url, notice: 'The invitation was successfully resent.'
+ redirect_to members_page_url, notice: _('The invitation was successfully resent.')
else
- redirect_to members_page_url, alert: 'The invitation has already been accepted.'
+ redirect_to members_page_url, alert: _('The invitation has already been accepted.')
end
end
@@ -125,6 +132,16 @@ module MembershipActions
end
def source_type
- @source_type ||= membershipable.class.to_s.humanize(capitalize: false)
+ @source_type ||=
+ begin
+ case membershipable
+ when Namespace
+ _("group")
+ when Project
+ _("project")
+ else
+ raise "Unknown membershipable type: #{membershipable}!"
+ end
+ end
end
end
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index eccbe35577b..c0c0160a827 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -8,7 +8,7 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_merge_requests_tab", {
- merge_requests: @milestone.sorted_merge_requests, # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ merge_requests: @milestone.sorted_merge_requests(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables
show_project_name: true
})
end
diff --git a/app/controllers/concerns/notes_actions.rb b/app/controllers/concerns/notes_actions.rb
index 0319948a12f..f96d1821095 100644
--- a/app/controllers/concerns/notes_actions.rb
+++ b/app/controllers/concerns/notes_actions.rb
@@ -6,7 +6,6 @@ module NotesActions
extend ActiveSupport::Concern
included do
- prepend_before_action :normalize_create_params, only: [:create]
before_action :set_polling_interval_header, only: [:index]
before_action :require_noteable!, only: [:index, :create]
before_action :authorize_admin_note!, only: [:update, :destroy]
@@ -44,17 +43,12 @@ module NotesActions
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def create
- create_params = note_params.merge(
- merge_request_diff_head_sha: params[:merge_request_diff_head_sha],
- in_reply_to_discussion_id: params[:in_reply_to_discussion_id]
- )
-
- @note = Notes::CreateService.new(note_project, current_user, create_params).execute
+ @note = Notes::CreateService.new(note_project, current_user, create_note_params).execute
respond_to do |format|
format.json do
json = {
- commands_changes: @note.commands_changes
+ commands_changes: @note.commands_changes&.slice(:emoji_award, :time_estimate, :spend_time)
}
if @note.persisted? && return_discussion?
@@ -78,7 +72,7 @@ module NotesActions
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def update
- @note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
+ @note = Notes::UpdateService.new(project, current_user, update_note_params).execute(note)
prepare_notes_for_rendering([@note])
respond_to do |format|
@@ -196,24 +190,36 @@ module NotesActions
return access_denied! unless can?(current_user, :admin_note, note)
end
- def note_params
+ def create_note_params
params.require(:note).permit(
- :project_id,
- :noteable_type,
- :noteable_id,
- :commit_id,
- :noteable,
:type,
-
:note,
- :attachment,
+ :line_code, # LegacyDiffNote
+ :position # DiffNote
+ ).tap do |create_params|
+ create_params.merge!(
+ params.permit(:merge_request_diff_head_sha, :in_reply_to_discussion_id)
+ )
- # LegacyDiffNote
- :line_code,
+ # These params are also sent by the client but we need to set these based on
+ # target_type and target_id because we're checking permissions based on that
+ create_params[:noteable_type] = params[:target_type].classify
+
+ case params[:target_type]
+ when 'commit'
+ create_params[:commit_id] = params[:target_id]
+ when 'merge_request'
+ create_params[:noteable_id] = params[:target_id]
+ # Notes on MergeRequest can have an extra `commit_id` context
+ create_params[:commit_id] = params.dig(:note, :commit_id)
+ else
+ create_params[:noteable_id] = params[:target_id]
+ end
+ end
+ end
- # DiffNote
- :position
- )
+ def update_note_params
+ params.require(:note).permit(:note)
end
def set_polling_interval_header
@@ -248,15 +254,6 @@ module NotesActions
DiscussionSerializer.new(project: project, noteable: noteable, current_user: current_user, note_entity: ProjectNoteEntity)
end
- # Avoids checking permissions in the wrong object - this ensures that the object we checked permissions for
- # is the object we're actually creating a note in.
- def normalize_create_params
- params[:note].try do |note|
- note[:noteable_id] = params[:target_id]
- note[:noteable_type] = params[:target_type].classify
- end
- end
-
def note_project
strong_memoize(:note_project) do
next nil unless project
diff --git a/app/controllers/concerns/renders_notes.rb b/app/controllers/concerns/renders_notes.rb
index ce36da6b715..18015b1de88 100644
--- a/app/controllers/concerns/renders_notes.rb
+++ b/app/controllers/concerns/renders_notes.rb
@@ -16,7 +16,7 @@ module RendersNotes
private
def preload_max_access_for_authors(notes, project)
- return nil unless project
+ return unless project
user_ids = notes.map(&:author_id)
project.team.max_member_access_for_user_ids(user_ids)
diff --git a/app/controllers/concerns/spammable_actions.rb b/app/controllers/concerns/spammable_actions.rb
index c3a1b12af84..a8ffa33f1c7 100644
--- a/app/controllers/concerns/spammable_actions.rb
+++ b/app/controllers/concerns/spammable_actions.rb
@@ -12,9 +12,9 @@ module SpammableActions
def mark_as_spam
if SpamService.new(spammable).mark_as_spam!
- redirect_to spammable_path, notice: "#{spammable.spammable_entity_type.titlecase} was submitted to Akismet successfully."
+ redirect_to spammable_path, notice: _("%{spammable_titlecase} was submitted to Akismet successfully.") % { spammable_titlecase: spammable.spammable_entity_type.titlecase }
else
- redirect_to spammable_path, alert: 'Error with Akismet. Please check the logs for more info.'
+ redirect_to spammable_path, alert: _('Error with Akismet. Please check the logs for more info.')
end
end
@@ -33,7 +33,7 @@ module SpammableActions
ensure_spam_config_loaded!
if params[:recaptcha_verification]
- flash[:alert] = 'There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.'
+ flash[:alert] = _('There was an error with the reCAPTCHA. Please solve the reCAPTCHA again.')
end
respond_to do |format|
diff --git a/app/controllers/concerns/uploads_actions.rb b/app/controllers/concerns/uploads_actions.rb
index 4ec0e94df9a..59f6d3452a3 100644
--- a/app/controllers/concerns/uploads_actions.rb
+++ b/app/controllers/concerns/uploads_actions.rb
@@ -16,7 +16,7 @@ module UploadsActions
end
else
format.json do
- render json: 'Invalid file.', status: :unprocessable_entity
+ render json: _('Invalid file.'), status: :unprocessable_entity
end
end
end
@@ -57,7 +57,7 @@ module UploadsActions
render json: authorized
rescue SocketError
- render json: "Error uploading file", status: :internal_server_error
+ render json: _("Error uploading file"), status: :internal_server_error
end
private
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index b1d224d026f..0a47736cad8 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -13,14 +13,20 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
@projects = load_projects(params.merge(non_public: true))
respond_to do |format|
- format.html
+ format.html do
+ # n+1: https://gitlab.com/gitlab-org/gitlab-ce/issues/37434
+ # Also https://gitlab.com/gitlab-org/gitlab-ce/issues/40260
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ render
+ end
+ end
format.atom do
load_events
render layout: 'xml.atom'
end
format.json do
render json: {
- html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ html: view_to_html_string("dashboard/projects/_projects", projects: @projects)
}
end
end
@@ -37,7 +43,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
format.html
format.json do
render json: {
- html: view_to_html_string("dashboard/projects/_projects", locals: { projects: @projects })
+ html: view_to_html_string("dashboard/projects/_projects", projects: @projects)
}
end
end
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 3fa582cf25b..f173c263474 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -21,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
format.html do
redirect_to dashboard_todos_path,
status: 302,
- notice: 'Todo was successfully marked as done.'
+ notice: _('Todo was successfully marked as done.')
end
format.js { head :ok }
format.json { render json: todos_counts }
@@ -32,7 +32,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
updated_ids = TodoService.new.mark_todos_as_done(@todos, current_user)
respond_to do |format|
- format.html { redirect_to dashboard_todos_path, status: 302, notice: 'All todos were marked as done.' }
+ format.html { redirect_to dashboard_todos_path, status: 302, notice: _('All todos were marked as done.') }
format.js { head :ok }
format.json { render json: todos_counts.merge(updated_ids: updated_ids) }
end
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index f3d76c5a478..ef86d5f981a 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -15,7 +15,7 @@ class Explore::ProjectsController < Explore::ApplicationController
format.html
format.json do
render json: {
- html: view_to_html_string("explore/projects/_projects", locals: { projects: @projects })
+ html: view_to_html_string("explore/projects/_projects", projects: @projects)
}
end
end
@@ -30,7 +30,7 @@ class Explore::ProjectsController < Explore::ApplicationController
format.html
format.json do
render json: {
- html: view_to_html_string("explore/projects/_projects", locals: { projects: @projects })
+ html: view_to_html_string("explore/projects/_projects", projects: @projects)
}
end
end
@@ -44,7 +44,7 @@ class Explore::ProjectsController < Explore::ApplicationController
format.html
format.json do
render json: {
- html: view_to_html_string("explore/projects/_projects", locals: { projects: @projects })
+ html: view_to_html_string("explore/projects/_projects", projects: @projects)
}
end
end
diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb
index dd9f5af61b3..ed0995e7ffd 100644
--- a/app/controllers/google_api/authorizations_controller.rb
+++ b/app/controllers/google_api/authorizations_controller.rb
@@ -2,6 +2,10 @@
module GoogleApi
class AuthorizationsController < ApplicationController
+ include Gitlab::Utils::StrongMemoize
+
+ before_action :validate_session_key!
+
def callback
token, expires_at = GoogleApi::CloudPlatform::Client
.new(nil, callback_google_api_auth_url)
@@ -11,21 +15,27 @@ module GoogleApi
session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] =
expires_at.to_s
- state_redirect_uri = redirect_uri_from_session_key(params[:state])
-
- if state_redirect_uri
- redirect_to state_redirect_uri
- else
- redirect_to root_path
- end
+ redirect_to redirect_uri_from_session
end
private
- def redirect_uri_from_session_key(state)
- key = GoogleApi::CloudPlatform::Client
- .session_key_for_redirect_uri(params[:state])
- session[key] if key
+ def validate_session_key!
+ access_denied! unless redirect_uri_from_session.present?
+ end
+
+ def redirect_uri_from_session
+ strong_memoize(:redirect_uri_from_session) do
+ if params[:state].present?
+ session[session_key_for_redirect_uri(params[:state])]
+ else
+ nil
+ end
+ end
+ end
+
+ def session_key_for_redirect_uri(state)
+ GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(state)
end
end
end
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index 3ef03bc9622..7b5dc22815c 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -3,9 +3,17 @@
class GraphqlController < ApplicationController
# Unauthenticated users have access to the API for public data
skip_before_action :authenticate_user!
- prepend_before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
+
+ # Allow missing CSRF tokens, this would mean that if a CSRF is invalid or missing,
+ # the user won't be authenticated but can proceed as an anonymous user.
+ #
+ # If a CSRF is valid, the user is authenticated. This makes it easier to play
+ # around in GraphiQL.
+ protect_from_forgery with: :null_session, only: :execute
before_action :check_graphql_feature_flag!
+ before_action :authorize_access_api!
+ before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
def execute
variables = Gitlab::Graphql::Variables.new(params[:variables]).to_h
@@ -30,6 +38,10 @@ class GraphqlController < ApplicationController
private
+ def authorize_access_api!
+ access_denied!("API not accessible for user.") unless can?(current_user, :access_api)
+ end
+
# Overridden from the ApplicationController to make the response look like
# a GraphQL response. That is nicely picked up in Graphiql.
def render_404
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index 51fdb6c05fb..40b8d5ed72c 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -1,53 +1,16 @@
# frozen_string_literal: true
class Groups::BoardsController < Groups::ApplicationController
- include BoardsResponses
+ include BoardsActions
include RecordUserLastActivity
before_action :assign_endpoint_vars
- before_action :boards, only: :index
- before_action :redirect_to_recent_board, only: :index
-
- def index
- respond_with_boards
- end
-
- def show
- @board = boards.find(params[:id])
-
- # add/update the board in the recent visited table
- Boards::Visits::CreateService.new(@board.group, current_user).execute(@board) if request.format.html?
-
- respond_with_board
- end
private
- def boards
- @boards ||= Boards::ListService.new(group, current_user).execute
- end
-
def assign_endpoint_vars
@boards_endpoint = group_boards_url(group)
@namespace_path = group.to_param
@labels_endpoint = group_labels_url(group)
end
-
- def serialize_as_json(resource)
- resource.as_json(only: [:id])
- end
-
- def includes_board?(board_id)
- boards.any? { |board| board.id == board_id }
- end
-
- def redirect_to_recent_board
- return if request.format.json?
-
- recently_visited = Boards::Visits::LatestService.new(group, current_user).execute
-
- if recently_visited && includes_board?(recently_visited.board_id)
- redirect_to(group_board_path(id: recently_visited.board_id), status: :found)
- end
- end
end
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 0bc082246a1..f1d6fb00cfc 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -12,6 +12,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
# Authorize
before_action :authorize_admin_group_member!, except: admin_not_required_endpoints
+ skip_before_action :check_two_factor_requirement, only: :leave
skip_cross_project_access_check :index, :create, :update, :destroy, :request_access,
:approve_access_request, :leave, :resend_invite,
:override
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index dd8fbf7a029..f8e32451b02 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -16,7 +16,7 @@ class Groups::RunnersController < Groups::ApplicationController
def update
if Ci::UpdateRunnerService.new(@runner).update(runner_params)
- redirect_to group_runner_path(@group, @runner), notice: 'Runner was successfully updated.'
+ redirect_to group_runner_path(@group, @runner), notice: _('Runner was successfully updated.')
else
render 'edit'
end
@@ -30,17 +30,17 @@ class Groups::RunnersController < Groups::ApplicationController
def resume
if Ci::UpdateRunnerService.new(@runner).update(active: true)
- redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), notice: 'Runner was successfully updated.'
+ redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), notice: _('Runner was successfully updated.')
else
- redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), alert: 'Runner was not updated.'
+ redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), alert: _('Runner was not updated.')
end
end
def pause
if Ci::UpdateRunnerService.new(@runner).update(active: false)
- redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), notice: 'Runner was successfully updated.'
+ redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), notice: _('Runner was successfully updated.')
else
- redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), alert: 'Runner was not updated.'
+ redirect_to group_settings_ci_cd_path(@group, anchor: 'runners-settings'), alert: _('Runner was not updated.')
end
end
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index f476f428fdb..c465e622de0 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -13,7 +13,17 @@ module Groups
def reset_registration_token
@group.reset_runners_token!
- flash[:notice] = 'New runners registration token has been generated!'
+ flash[:notice] = _('GroupSettings|New runners registration token has been generated!')
+ redirect_to group_settings_ci_cd_path
+ end
+
+ def update_auto_devops
+ if auto_devops_service.execute
+ flash[:notice] = s_('GroupSettings|Auto DevOps pipeline was updated for the group')
+ else
+ flash[:alert] = s_("GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}." % { error_messages: group.errors.full_messages })
+ end
+
redirect_to group_settings_ci_cd_path
end
@@ -29,6 +39,14 @@ module Groups
def authorize_admin_group!
return render_404 unless can?(current_user, :admin_group, group)
end
+
+ def auto_devops_params
+ params.require(:group).permit(:auto_devops_enabled)
+ end
+
+ def auto_devops_service
+ Groups::AutoDevopsService.new(group, current_user, auto_devops_params)
+ end
end
end
end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 4e50106398a..0192b1c253e 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -58,11 +58,24 @@ class GroupsController < Groups::ApplicationController
def show
respond_to do |format|
- format.html
+ format.html do
+ render_show_html
+ end
format.atom do
- load_events
- render layout: 'xml.atom'
+ render_details_view_atom
+ end
+ end
+ end
+
+ def details
+ respond_to do |format|
+ format.html do
+ render_details_html
+ end
+
+ format.atom do
+ render_details_view_atom
end
end
end
@@ -119,6 +132,19 @@ class GroupsController < Groups::ApplicationController
protected
+ def render_show_html
+ render 'groups/show'
+ end
+
+ def render_details_html
+ render 'groups/show'
+ end
+
+ def render_details_view_atom
+ load_events
+ render layout: 'xml.atom', template: 'groups/show'
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def authorize_create_group!
allowed = if params[:parent_id].present?
@@ -178,8 +204,8 @@ class GroupsController < Groups::ApplicationController
.includes(:namespace)
@events = EventCollection
- .new(@projects, offset: params[:offset].to_i, filter: event_filter)
- .to_a
+ .new(@projects, offset: params[:offset].to_i, filter: event_filter)
+ .to_a
Events::RenderService
.new(current_user)
diff --git a/app/controllers/import/bitbucket_controller.rb b/app/controllers/import/bitbucket_controller.rb
index 2b1395f364f..293d76ea765 100644
--- a/app/controllers/import/bitbucket_controller.rb
+++ b/app/controllers/import/bitbucket_controller.rb
@@ -62,7 +62,7 @@ class Import::BitbucketController < Import::BaseController
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
+ render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity
end
end
diff --git a/app/controllers/import/bitbucket_server_controller.rb b/app/controllers/import/bitbucket_server_controller.rb
index f333e43b892..643a3bfed1f 100644
--- a/app/controllers/import/bitbucket_server_controller.rb
+++ b/app/controllers/import/bitbucket_server_controller.rb
@@ -25,7 +25,7 @@ class Import::BitbucketServerController < Import::BaseController
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
+ return render json: { errors: _("Project %{project_repo} could not be found") % { project_repo: "#{@project_key}/#{@repo_slug}" } }, status: :unprocessable_entity
end
project_name = params[:new_name].presence || repo.name
@@ -41,10 +41,10 @@ class Import::BitbucketServerController < Import::BaseController
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
+ render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity
end
- rescue BitbucketServer::Connection::ConnectionError => e
- render json: { errors: "Unable to connect to server: #{e}" }, status: :unprocessable_entity
+ rescue BitbucketServer::Connection::ConnectionError => error
+ render json: { errors: _("Unable to connect to server: %{error}") % { error: error } }, status: :unprocessable_entity
end
def configure
@@ -65,8 +65,8 @@ class Import::BitbucketServerController < Import::BaseController
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.reject! { |repo| already_added_projects_names.include?(repo.browse_url) }
- rescue BitbucketServer::Connection::ConnectionError => e
- flash[:alert] = "Unable to connect to server: #{e}"
+ rescue BitbucketServer::Connection::ConnectionError => error
+ flash[:alert] = _("Unable to connect to server: %{error}") % { error: error }
clear_session_data
redirect_to new_import_bitbucket_server_path
end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index 5a439e6de78..a37ba682b91 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -14,7 +14,7 @@ class Import::FogbugzController < Import::BaseController
res = Gitlab::FogbugzImport::Client.new(import_params.symbolize_keys)
rescue
# If the URI is invalid various errors can occur
- return redirect_to new_import_fogbugz_path, alert: 'Could not connect to FogBugz, check your URL'
+ return redirect_to new_import_fogbugz_path, alert: _('Could not connect to FogBugz, check your URL')
end
session[:fogbugz_token] = res.get_token
session[:fogbugz_uri] = params[:uri]
@@ -29,14 +29,14 @@ class Import::FogbugzController < Import::BaseController
user_map = params[:users]
unless user_map.is_a?(Hash) && user_map.all? { |k, v| !v[:name].blank? }
- flash.now[:alert] = 'All users must have a name.'
+ flash.now[:alert] = _('All users must have a name.')
return render 'new_user_map'
end
session[:fogbugz_user_map] = user_map
- flash[:notice] = 'The user map has been saved. Continue by selecting the projects you want to import.'
+ flash[:notice] = _('The user map has been saved. Continue by selecting the projects you want to import.')
redirect_to status_import_fogbugz_path
end
diff --git a/app/controllers/import/gitea_controller.rb b/app/controllers/import/gitea_controller.rb
index 68ad8650dba..a23b2f8139e 100644
--- a/app/controllers/import/gitea_controller.rb
+++ b/app/controllers/import/gitea_controller.rb
@@ -46,7 +46,7 @@ class Import::GiteaController < Import::GithubController
def provider_auth
if session[access_token_key].blank? || provider_url.blank?
redirect_to new_import_gitea_url,
- alert: 'You need to specify both an Access Token and a Host URL.'
+ alert: _('You need to specify both an Access Token and a Host URL.')
end
end
diff --git a/app/controllers/import/gitlab_controller.rb b/app/controllers/import/gitlab_controller.rb
index 498de0b07b8..5ec8e9e6fc5 100644
--- a/app/controllers/import/gitlab_controller.rb
+++ b/app/controllers/import/gitlab_controller.rb
@@ -42,7 +42,7 @@ class Import::GitlabController < Import::BaseController
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
+ render json: { errors: _('This namespace has already been taken! Please choose another one.') }, status: :unprocessable_entity
end
end
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index 354fba5d204..89889141be6 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -13,7 +13,7 @@ class Import::GitlabProjectsController < Import::BaseController
def create
unless file_is_valid?
- return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive (ending in .gz)." })
+ return redirect_back_or_default(options: { alert: _("You need to upload a GitLab project export archive (ending in .gz).") })
end
@project = ::Projects::GitlabProjectsImportService.new(current_user, project_params).execute
@@ -21,7 +21,7 @@ class Import::GitlabProjectsController < Import::BaseController
if @project.saved?
redirect_to(
project_path(@project),
- notice: "Project '#{@project.name}' is being imported."
+ notice: _("Project '%{project_name}' is being imported.") % { project_name: @project.name }
)
else
redirect_back_or_default(options: { alert: "Project could not be imported: #{@project.errors.full_messages.join(', ')}" })
diff --git a/app/controllers/import/google_code_controller.rb b/app/controllers/import/google_code_controller.rb
index 331f06c3dd6..4dddfbcd20d 100644
--- a/app/controllers/import/google_code_controller.rb
+++ b/app/controllers/import/google_code_controller.rb
@@ -11,18 +11,18 @@ class Import::GoogleCodeController < Import::BaseController
dump_file = params[:dump_file]
unless dump_file.respond_to?(:read)
- return redirect_back_or_default(options: { alert: "You need to upload a Google Takeout archive." })
+ return redirect_back_or_default(options: { alert: _("You need to upload a Google Takeout archive.") })
end
begin
dump = JSON.parse(dump_file.read)
rescue
- return redirect_back_or_default(options: { alert: "The uploaded file is not a valid Google Takeout archive." })
+ return redirect_back_or_default(options: { alert: _("The uploaded file is not a valid Google Takeout archive.") })
end
client = Gitlab::GoogleCodeImport::Client.new(dump)
unless client.valid?
- return redirect_back_or_default(options: { alert: "The uploaded file is not a valid Google Takeout archive." })
+ return redirect_back_or_default(options: { alert: _("The uploaded file is not a valid Google Takeout archive.") })
end
session[:google_code_dump] = dump
@@ -44,13 +44,13 @@ class Import::GoogleCodeController < Import::BaseController
begin
user_map = JSON.parse(user_map_json)
rescue
- flash.now[:alert] = "The entered user map is not a valid JSON user map."
+ flash.now[:alert] = _("The entered user map is not a valid JSON user map.")
return render "new_user_map"
end
unless user_map.is_a?(Hash) && user_map.all? { |k, v| k.is_a?(String) && v.is_a?(String) }
- flash.now[:alert] = "The entered user map is not a valid JSON user map."
+ flash.now[:alert] = _("The entered user map is not a valid JSON user map.")
return render "new_user_map"
end
@@ -62,7 +62,7 @@ class Import::GoogleCodeController < Import::BaseController
session[:google_code_user_map] = user_map
- flash[:notice] = "The user map has been saved. Continue by selecting the projects you want to import."
+ flash[:notice] = _("The user map has been saved. Continue by selecting the projects you want to import.")
redirect_to status_import_google_code_path
end
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index cc2bb99f55b..e90e8278c13 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -3,6 +3,7 @@
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
include AuthenticatesWithTwoFactor
include Devise::Controllers::Rememberable
+ include AuthHelper
protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true
@@ -80,10 +81,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
end
if current_user
+ return render_403 unless link_provider_allowed?(oauth['provider'])
+
log_audit_event(current_user, with: oauth['provider'])
identity_linker ||= auth_module::IdentityLinker.new(current_user, oauth)
-
identity_linker.link
if identity_linker.changed?
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index b0d65f284af..0d2a6145d0e 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -14,7 +14,7 @@ class Profiles::AccountsController < Profiles::ApplicationController
return render_404 unless identity
- if unlink_allowed?(provider)
+ if unlink_provider_allowed?(provider)
identity.destroy
else
flash[:alert] = "You are not allowed to unlink your primary login account"
diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb
index efe7ede5efa..c473023cacb 100644
--- a/app/controllers/profiles/active_sessions_controller.rb
+++ b/app/controllers/profiles/active_sessions_controller.rb
@@ -2,15 +2,6 @@
class Profiles::ActiveSessionsController < Profiles::ApplicationController
def index
- @sessions = ActiveSession.list(current_user)
- end
-
- def destroy
- ActiveSession.destroy(current_user, params[:id])
-
- respond_to do |format|
- format.html { redirect_to profile_active_sessions_url, status: :found }
- format.js { head :ok }
- end
+ @sessions = ActiveSession.list(current_user).reject(&:is_impersonated)
end
end
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index 94002095739..0227af2c266 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -37,6 +37,14 @@ class Profiles::PreferencesController < Profiles::ApplicationController
end
def preferences_param_names
- [:color_scheme_id, :layout, :dashboard, :project_view, :theme_id, :first_day_of_week]
+ [
+ :color_scheme_id,
+ :layout,
+ :dashboard,
+ :project_view,
+ :theme_id,
+ :first_day_of_week,
+ :preferred_language
+ ]
end
end
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index ba94196b2f9..83e14275a8b 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -18,21 +18,16 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
two_factor_authentication_reason(
global: lambda do
flash.now[:alert] =
- 'The global settings require you to enable Two-Factor Authentication for your account.'
+ s_('The global settings require you to enable Two-Factor Authentication for your account.')
end,
group: lambda do |groups|
- group_links = groups.map { |group| view_context.link_to group.full_name, group_path(group) }.to_sentence
-
- flash.now[:alert] = %{
- The group settings for #{group_links} require you to enable
- Two-Factor Authentication for your account.
- }.html_safe
+ flash.now[:alert] = groups_notification(groups)
end
)
unless two_factor_grace_period_expired?
grace_period_deadline = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
- flash.now[:alert] = flash.now[:alert] + " You need to do this before #{l(grace_period_deadline)}."
+ flash.now[:alert] = flash.now[:alert] + s_(" You need to do this before %{grace_period_deadline}.") % { grace_period_deadline: l(grace_period_deadline) }
end
end
@@ -49,7 +44,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
render 'create'
else
- @error = 'Invalid pin code'
+ @error = s_('Invalid pin code')
@qr_code = build_qr_code
setup_u2f_registration
render 'show'
@@ -63,7 +58,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
if @u2f_registration.persisted?
session.delete(:challenges)
- redirect_to profile_two_factor_auth_path, notice: "Your U2F device was registered!"
+ redirect_to profile_two_factor_auth_path, notice: s_("Your U2F device was registered!")
else
@qr_code = build_qr_code
setup_u2f_registration
@@ -85,7 +80,7 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def skip
if two_factor_grace_period_expired?
- redirect_to new_profile_two_factor_auth_path, alert: 'Cannot skip two factor authentication setup'
+ redirect_to new_profile_two_factor_auth_path, alert: s_('Cannot skip two factor authentication setup')
else
session[:skip_two_factor] = current_user.otp_grace_period_started_at + two_factor_grace_period.hours
redirect_to root_path
@@ -126,4 +121,12 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
def u2f_registration_params
params.require(:u2f_registration).permit(:device_response, :name)
end
+
+ def groups_notification(groups)
+ group_links = groups.map { |group| view_context.link_to group.full_name, group_path(group) }.to_sentence
+ leave_group_links = groups.map { |group| view_context.link_to (s_("leave %{group_name}") % { group_name: group.full_name }), leave_group_members_path(group), remote: false, method: :delete}.to_sentence
+
+ s_(%{The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}.})
+ .html_safe % { group_links: group_links.html_safe, leave_group_links: leave_group_links.html_safe }
+ end
end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 15248d2d08f..b9c52618d4b 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -104,7 +104,6 @@ class ProfilesController < Profiles::ApplicationController
:username,
:website_url,
:organization,
- :preferred_language,
:private_profile,
:include_private_contributions,
status: [:emoji, :message]
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index e0677ce3fbc..6504fd6c08a 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -17,7 +17,7 @@ class Projects::ApplicationController < ApplicationController
def project
return @project if @project
- return nil unless params[:project_id] || params[:id]
+ return unless params[:project_id] || params[:id]
path = File.join(params[:namespace_id], params[:project_id] || params[:id])
auth_proc = ->(project) { !project.pending_delete? }
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 9c130af8394..0e3f13045ce 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Projects::AutocompleteSourcesController < Projects::ApplicationController
+ before_action :authorize_read_milestone!, only: :milestones
+
def members
render json: ::Projects::ParticipantsService.new(@project, current_user).execute(target)
end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 77672e7d9fc..909b17e9c8d 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -29,7 +29,7 @@ class Projects::BlobController < Projects::ApplicationController
end
def create
- create_commit(Files::CreateService, success_notice: "The file has been successfully created.",
+ create_commit(Files::CreateService, success_notice: _("The file has been successfully created."),
success_path: -> { project_blob_path(@project, File.join(@branch_name, @file_path)) },
failure_view: :new,
failure_path: project_new_blob_path(@project, @ref))
@@ -81,7 +81,7 @@ class Projects::BlobController < Projects::ApplicationController
end
def destroy
- create_commit(Files::DeleteService, success_notice: "The file has been successfully deleted.",
+ create_commit(Files::DeleteService, success_notice: _("The file has been successfully deleted."),
success_path: -> { after_delete_path },
failure_view: :show,
failure_path: project_blob_path(@project, @id))
@@ -90,65 +90,21 @@ class Projects::BlobController < Projects::ApplicationController
def diff
apply_diff_view_cookie!
- @blob.load_all_data!
- @lines = @blob.present.highlight.lines
-
- @form = UnfoldForm.new(params.to_unsafe_h)
-
- @lines = @lines[@form.since - 1..@form.to - 1].map(&:html_safe)
-
- if @form.bottom?
- @match_line = ''
- else
- lines_length = @lines.length - 1
- line = [@form.since, lines_length].join(',')
- @match_line = "@@ -#{line}+#{line} @@"
- end
+ @form = Blobs::UnfoldPresenter.new(blob, params.to_unsafe_h)
- # We can keep only 'render_diff_lines' from this conditional when
+ # keep only json rendering when
# https://gitlab.com/gitlab-org/gitlab-ce/issues/44988 is done
if rendered_for_merge_request?
- render_diff_lines
+ render json: DiffLineSerializer.new.represent(@form.diff_lines)
else
+ @lines = @form.lines
+ @match_line = @form.match_line_text
render layout: false
end
end
private
- # Converts a String array to Gitlab::Diff::Line array
- def render_diff_lines
- @lines.map! do |line|
- # These are marked as context lines but are loaded from blobs.
- # We also have context lines loaded from diffs in other places.
- diff_line = Gitlab::Diff::Line.new(line, nil, nil, nil, nil)
- diff_line.rich_text = line
- diff_line
- end
-
- add_match_line
-
- render json: DiffLineSerializer.new.represent(@lines)
- end
-
- def add_match_line
- return unless @form.unfold?
-
- if @form.bottom? && @form.to < @blob.lines.size
- old_pos = @form.to - @form.offset
- new_pos = @form.to
- elsif @form.since != 1
- old_pos = new_pos = @form.since
- end
-
- # Match line is not needed when it reaches the top limit or bottom limit of the file.
- return unless new_pos
-
- @match_line = Gitlab::Diff::Line.new(@match_line, 'match', nil, old_pos, new_pos)
-
- @form.bottom? ? @lines.push(@match_line) : @lines.unshift(@match_line)
- end
-
def blob
@blob ||= @repository.blob_at(@commit.id, @path)
@@ -231,6 +187,8 @@ class Projects::BlobController < Projects::ApplicationController
end
def validate_diff_params
+ return if params[:full]
+
if [:since, :to, :offset].any? { |key| params[key].blank? }
head :ok
end
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 8189b5d182a..95897aaf980 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -1,34 +1,15 @@
# frozen_string_literal: true
class Projects::BoardsController < Projects::ApplicationController
- include BoardsResponses
+ include BoardsActions
include IssuableCollections
before_action :check_issues_available!
before_action :authorize_read_board!, only: [:index, :show]
- before_action :boards, only: :index
before_action :assign_endpoint_vars
- before_action :redirect_to_recent_board, only: :index
-
- def index
- respond_with_boards
- end
-
- def show
- @board = boards.find(params[:id])
-
- # add/update the board in the recent visited table
- Boards::Visits::CreateService.new(@board.project, current_user).execute(@board) if request.format.html?
-
- respond_with_board
- end
private
- def boards
- @boards ||= Boards::ListService.new(project, current_user).execute
- end
-
def assign_endpoint_vars
@boards_endpoint = project_boards_path(project)
@bulk_issues_path = bulk_update_project_issues_path(project)
@@ -39,22 +20,4 @@ class Projects::BoardsController < Projects::ApplicationController
def authorize_read_board!
access_denied! unless can?(current_user, :read_board, project)
end
-
- def serialize_as_json(resource)
- resource.as_json(only: [:id])
- end
-
- def includes_board?(board_id)
- boards.any? { |board| board.id == board_id }
- end
-
- def redirect_to_recent_board
- return if request.format.json?
-
- recently_visited = Boards::Visits::LatestService.new(project, current_user).execute
-
- if recently_visited && includes_board?(recently_visited.board_id)
- redirect_to(namespace_project_board_path(id: recently_visited.board_id), status: :found)
- end
- end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 32b7f3207ef..6ff2e222489 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -115,7 +115,7 @@ class Projects::BranchesController < Projects::ApplicationController
DeleteMergedBranchesService.new(@project, current_user).async_execute
redirect_to project_branches_path(@project),
- notice: 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
+ notice: _('Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.')
end
private
@@ -143,7 +143,7 @@ class Projects::BranchesController < Projects::ApplicationController
def redirect_for_legacy_index_sort_or_search
# Normalize a legacy URL with redirect
if request.format != :json && !params[:state].presence && [:sort, :search, :page].any? { |key| params[key].presence }
- redirect_to project_branches_filtered_path(@project, state: 'all'), notice: 'Update your bookmarked URLs as filtered/sorted branches URL has been changed.'
+ redirect_to project_branches_filtered_path(@project, state: 'all'), notice: _('Update your bookmarked URLs as filtered/sorted branches URL has been changed.')
end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index b13c0ae3967..939a09d4fd2 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -65,7 +65,11 @@ class Projects::CommitController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
def merge_requests
- @merge_requests = @commit.merge_requests.map do |mr|
+ @merge_requests = MergeRequestsFinder.new(
+ current_user,
+ project_id: @project.id,
+ commit_sha: @commit.sha
+ ).execute.map do |mr|
{ iid: mr.iid, path: merge_request_path(mr), title: mr.title }
end
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 6824a07dc76..514b03e23b5 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -38,7 +38,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
def update
if deploy_key.update(update_params)
- flash[:notice] = 'Deploy key was successfully updated.'
+ flash[:notice] = _('Deploy key was successfully updated.')
redirect_to_repository_settings(@project, anchor: 'js-deploy-keys-settings')
else
render 'edit'
diff --git a/app/controllers/projects/git_http_client_controller.rb b/app/controllers/projects/git_http_client_controller.rb
index d439db97252..55d5fce9214 100644
--- a/app/controllers/projects/git_http_client_controller.rb
+++ b/app/controllers/projects/git_http_client_controller.rb
@@ -78,7 +78,7 @@ class Projects::GitHttpClientController < Projects::ApplicationController
end
def parse_repo_path
- @project, @wiki, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:project_id]}")
+ @project, @repo_type, @redirected_path = Gitlab::RepoPath.parse("#{params[:namespace_id]}/#{params[:project_id]}")
end
def render_missing_personal_access_token
@@ -89,13 +89,19 @@ class Projects::GitHttpClientController < Projects::ApplicationController
end
def repository
- wiki? ? project.wiki.repository : project.repository
+ repo_type.repository_for(project)
end
def wiki?
- parse_repo_path unless defined?(@wiki)
+ repo_type.wiki?
+ end
- @wiki
+ def repo_type
+ parse_repo_path unless defined?(@repo_type)
+ # When there a project did not exist, the parsed repo_type would be empty.
+ # In that case, we want to continue with a regular project repository. As we
+ # could create the project if the user pushing is allowed to do so.
+ @repo_type || Gitlab::GlRepository::PROJECT
end
def handle_basic_authentication(login, password)
diff --git a/app/controllers/projects/git_http_controller.rb b/app/controllers/projects/git_http_controller.rb
index 30e436365de..e519cc1f158 100644
--- a/app/controllers/projects/git_http_controller.rb
+++ b/app/controllers/projects/git_http_controller.rb
@@ -4,6 +4,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
include WorkhorseRequest
before_action :access_check
+ prepend_before_action :deny_head_requests, only: [:info_refs]
rescue_from Gitlab::GitAccess::UnauthorizedError, with: :render_403
rescue_from Gitlab::GitAccess::NotFoundError, with: :render_404
@@ -20,6 +21,8 @@ class Projects::GitHttpController < Projects::GitHttpClientController
# POST /foo/bar.git/git-upload-pack (git pull)
def git_upload_pack
+ enqueue_fetch_statistics_update
+
render_ok
end
@@ -30,6 +33,10 @@ class Projects::GitHttpController < Projects::GitHttpClientController
private
+ def deny_head_requests
+ head :forbidden if request.head?
+ end
+
def download_request?
upload_pack?
end
@@ -48,7 +55,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
def render_ok
set_workhorse_internal_api_content_type
- render json: Gitlab::Workhorse.git_http_ok(repository, wiki?, user, action_name)
+ render json: Gitlab::Workhorse.git_http_ok(repository, repo_type, user, action_name)
end
def render_403(exception)
@@ -67,6 +74,13 @@ class Projects::GitHttpController < Projects::GitHttpClientController
render plain: exception.message, status: :service_unavailable
end
+ def enqueue_fetch_statistics_update
+ return if wiki?
+ return unless project.daily_statistics_enabled?
+
+ ProjectDailyStatisticsWorker.perform_async(project.id)
+ end
+
def access
@access ||= access_klass.new(access_actor, project,
'http', authentication_abilities: authentication_abilities,
@@ -85,7 +99,7 @@ class Projects::GitHttpController < Projects::GitHttpClientController
end
def access_klass
- @access_klass ||= wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess
+ @access_klass ||= repo_type.access_checker_class
end
def project_path
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 925b6ed9bfd..c80fce513f6 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -45,7 +45,14 @@ class Projects::GraphsController < Projects::ApplicationController
end
def get_languages
- @languages = @project.repository.languages
+ @languages =
+ if @project.repository_languages.present?
+ @project.repository_languages.map do |lang|
+ { value: lang.share, label: lang.name, color: lang.color, highlight: lang.color }
+ end
+ else
+ @project.repository.languages
+ end
end
def fetch_graph
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index 7c713c19762..dc65f9959db 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -13,11 +13,12 @@ class Projects::GroupLinksController < Projects::ApplicationController
group = Group.find(params[:link_group_id]) if params[:link_group_id].present?
if group
- return render_404 unless can?(current_user, :read_group, group)
+ result = Projects::GroupLinks::CreateService.new(project, current_user, group_link_create_params).execute(group)
+ return render_404 if result[:http_status] == 404
- Projects::GroupLinks::CreateService.new(project, current_user, group_link_create_params).execute(group)
+ flash[:alert] = result[:message] if result[:http_status] == 409
else
- flash[:alert] = 'Please select a group.'
+ flash[:alert] = _('Please select a group.')
end
redirect_to project_project_members_path(project)
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index bc84418b79f..5fa0339f44d 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -32,7 +32,7 @@ class Projects::HooksController < Projects::ApplicationController
def update
if hook.update(hook_params)
- flash[:notice] = 'Hook was successfully updated.'
+ flash[:notice] = _('Hook was successfully updated.')
redirect_to project_settings_integrations_path(@project)
else
render 'edit'
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index 8b33fa85c1e..8ee0bd26daf 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -42,9 +42,9 @@ class Projects::ImportsController < Projects::ApplicationController
def finished_notice
if @project.forked?
- 'The project was successfully forked.'
+ _('The project was successfully forked.')
else
- 'The project was successfully imported.'
+ _('The project was successfully imported.')
end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index b9d02a62fc3..e9ed5554ab4 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -95,9 +95,9 @@ class Projects::IssuesController < Projects::ApplicationController
if service.discussions_to_resolve.count(&:resolved?) > 0
flash[:notice] = if service.discussion_to_resolve_id
- "Resolved 1 discussion."
+ _("Resolved 1 discussion.")
else
- "Resolved all discussions."
+ _("Resolved all discussions.")
end
end
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index d5ce790e2d9..35cc32d3e63 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -122,7 +122,7 @@ class Projects::JobsController < Projects::ApplicationController
def erase
if @build.erase(erased_by: current_user)
redirect_to project_job_path(project, @build),
- notice: "Job has been successfully erased!"
+ notice: _("Job has been successfully erased!")
else
respond_422
end
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index 640038818f2..386a1f00bd2 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -132,7 +132,7 @@ class Projects::LabelsController < Projects::ApplicationController
respond_to do |format|
format.html do
redirect_to(project_labels_path(@project),
- notice: 'Failed to promote label due to internal error. Please contact administrators.')
+ notice: _('Failed to promote label due to internal error. Please contact administrators.'))
end
format.js
end
diff --git a/app/controllers/projects/lfs_api_controller.rb b/app/controllers/projects/lfs_api_controller.rb
index be40077d389..42c415757f9 100644
--- a/app/controllers/projects/lfs_api_controller.rb
+++ b/app/controllers/projects/lfs_api_controller.rb
@@ -26,7 +26,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController
def deprecated
render(
json: {
- message: 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
+ 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: :not_implemented
@@ -62,7 +62,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController
else
object[:error] = {
code: 404,
- message: "Object does not exist on the server or you don't have permissions to access it"
+ message: _("Object does not exist on the server or you don't have permissions to access it")
}
end
end
diff --git a/app/controllers/projects/merge_requests/conflicts_controller.rb b/app/controllers/projects/merge_requests/conflicts_controller.rb
index 045a4e974fe..011ac9a42f8 100644
--- a/app/controllers/projects/merge_requests/conflicts_controller.rb
+++ b/app/controllers/projects/merge_requests/conflicts_controller.rb
@@ -16,12 +16,12 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap
render json: @conflicts_list
elsif @merge_request.can_be_merged?
render json: {
- message: 'The merge conflicts for this merge request have already been resolved. Please return to the merge request.',
+ message: _('The merge conflicts for this merge request have already been resolved. Please return to the merge request.'),
type: 'error'
}
else
render json: {
- message: 'The merge conflicts for this merge request cannot be resolved through GitLab. Please try to resolve them locally.',
+ message: _('The merge conflicts for this merge request cannot be resolved through GitLab. Please try to resolve them locally.'),
type: 'error'
}
end
@@ -43,7 +43,7 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap
return render_404 unless @conflicts_list.can_be_resolved_in_ui?
if @merge_request.can_be_merged?
- render status: :bad_request, json: { message: 'The merge conflicts for this merge request have already been resolved.' }
+ render status: :bad_request, json: { message: _('The merge conflicts for this merge request have already been resolved.') }
return
end
@@ -52,7 +52,7 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap
.new(merge_request)
.execute(current_user, params)
- flash[:notice] = 'All merge conflicts were resolved. The merge request can now be merged.'
+ flash[:notice] = _('All merge conflicts were resolved. The merge request can now be merged.')
render json: { redirect_to: project_merge_request_url(@project, @merge_request, resolved_conflicts: true) }
rescue Gitlab::Git::Conflict::Resolver::ResolutionError => e
diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb
index 5639402a1e9..32cefe54613 100644
--- a/app/controllers/projects/merge_requests/creations_controller.rb
+++ b/app/controllers/projects/merge_requests/creations_controller.rb
@@ -89,7 +89,11 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
def build_merge_request
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
- @merge_request = ::MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute
+
+ # Gitaly N+1 issue: https://gitlab.com/gitlab-org/gitlab-ce/issues/58096
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ @merge_request = ::MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute
+ end
end
def define_new_vars
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index 518d41bd3fb..456d2c34768 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -46,8 +46,8 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
# rubocop: disable CodeReuse/ActiveRecord
def commit
- return nil unless commit_id = params[:commit_id].presence
- return nil unless @merge_request.all_commits.exists?(sha: commit_id)
+ return unless commit_id = params[:commit_id].presence
+ return unless @merge_request.all_commits.exists?(sha: commit_id)
@commit ||= @project.commit(commit_id)
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 46a44841c31..5cf7fa3422d 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -16,10 +16,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action :authenticate_user!, only: [:assign_related_issues]
before_action :check_user_can_push_to_source_branch!, only: [:rebase]
- before_action only: [:show] do
- push_frontend_feature_flag(:diff_tree_filtering, default_enabled: true)
- end
-
def index
@merge_requests = @issuables
diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb
index ab7ab13657a..ef330ae00f4 100644
--- a/app/controllers/projects/mirrors_controller.rb
+++ b/app/controllers/projects/mirrors_controller.rb
@@ -18,7 +18,7 @@ class Projects::MirrorsController < Projects::ApplicationController
result = ::Projects::UpdateService.new(project, current_user, mirror_params).execute
if result[:status] == :success
- flash[:notice] = 'Mirroring settings were successfully updated.'
+ flash[:notice] = _('Mirroring settings were successfully updated.')
else
flash[:alert] = project.errors.full_messages.join(', ').html_safe
end
@@ -38,7 +38,7 @@ class Projects::MirrorsController < Projects::ApplicationController
def update_now
if params[:sync_remote]
project.update_remote_mirrors
- flash[:notice] = "The remote repository is being updated..."
+ flash[:notice] = _("The remote repository is being updated...")
end
redirect_to_repository_settings(project, anchor: 'js-push-remote-settings')
diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb
index d0e35bee986..73e629ab7c3 100644
--- a/app/controllers/projects/pages_controller.rb
+++ b/app/controllers/projects/pages_controller.rb
@@ -5,7 +5,8 @@ class Projects::PagesController < Projects::ApplicationController
before_action :require_pages_enabled!
before_action :authorize_read_pages!, only: [:show]
- before_action :authorize_update_pages!, except: [:show]
+ before_action :authorize_update_pages!, except: [:show, :destroy]
+ before_action :authorize_remove_pages!, only: [:destroy]
# rubocop: disable CodeReuse/ActiveRecord
def show
diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb
index acf56f0eb6a..fd5b89298a7 100644
--- a/app/controllers/projects/pipeline_schedules_controller.rb
+++ b/app/controllers/projects/pipeline_schedules_controller.rb
@@ -50,9 +50,11 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
job_id = RunPipelineScheduleWorker.perform_async(schedule.id, current_user.id)
if job_id
- flash[:notice] = "Successfully scheduled a pipeline to run. Go to the <a href=\"#{project_pipelines_path(@project)}\">Pipelines page</a> for details.".html_safe
+ link_to_pipelines = view_context.link_to(_('Pipelines page'), project_pipelines_path(@project))
+ message = _("Successfully scheduled a pipeline to run. Go to the %{link_to_pipelines} for details.").html_safe % { link_to_pipelines: link_to_pipelines }
+ flash[:notice] = message.html_safe
else
- flash[:alert] = 'Unable to schedule a pipeline to run immediately'
+ flash[:alert] = _('Unable to schedule a pipeline to run immediately')
end
redirect_to pipeline_schedules_path(@project)
@@ -85,7 +87,7 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
return unless limiter.throttled?([current_user, schedule], 1)
- flash[:alert] = 'You cannot play this scheduled pipeline at the moment. Please wait a minute.'
+ flash[:alert] = _('You cannot play this scheduled pipeline at the moment. Please wait a minute.')
redirect_to pipeline_schedules_path(@project)
end
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 91f40b90aa8..ca62f54813b 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -15,7 +15,7 @@ class Projects::RunnersController < Projects::ApplicationController
def update
if Ci::UpdateRunnerService.new(@runner).update(runner_params)
- redirect_to project_runner_path(@project, @runner), notice: 'Runner was successfully updated.'
+ redirect_to project_runner_path(@project, @runner), notice: _('Runner was successfully updated.')
else
render 'edit'
end
@@ -31,17 +31,17 @@ class Projects::RunnersController < Projects::ApplicationController
def resume
if Ci::UpdateRunnerService.new(@runner).update(active: true)
- redirect_to project_runners_path(@project), notice: 'Runner was successfully updated.'
+ redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.')
else
- redirect_to project_runners_path(@project), alert: 'Runner was not updated.'
+ redirect_to project_runners_path(@project), alert: _('Runner was not updated.')
end
end
def pause
if Ci::UpdateRunnerService.new(@runner).update(active: false)
- redirect_to project_runners_path(@project), notice: 'Runner was successfully updated.'
+ redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.')
else
- redirect_to project_runners_path(@project), alert: 'Runner was not updated.'
+ redirect_to project_runners_path(@project), alert: _('Runner was not updated.')
end
end
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index f1c9d0d0f77..e0df51590ae 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -43,20 +43,20 @@ class Projects::ServicesController < Projects::ApplicationController
if outcome[:success]
{}
else
- { error: true, message: 'Test failed.', service_response: outcome[:result].to_s, test_failed: true }
+ { error: true, message: _('Test failed.'), service_response: outcome[:result].to_s, test_failed: true }
end
else
- { error: true, message: 'Validations failed.', service_response: @service.errors.full_messages.join(','), test_failed: false }
+ { error: true, message: _('Validations failed.'), service_response: @service.errors.full_messages.join(','), test_failed: false }
end
rescue Gitlab::HTTP::BlockedUrlError => e
- { error: true, message: 'Test failed.', service_response: e.message, test_failed: true }
+ { error: true, message: _('Test failed.'), service_response: e.message, test_failed: true }
end
def success_message
if @service.active?
- "#{@service.title} activated."
+ _("%{service_title} activated.") % { service_title: @service.title }
else
- "#{@service.title} settings saved, but not activated."
+ _("%{service_title} settings saved, but not activated.") % { service_title: @service.title }
end
end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index f2f63e986bb..d1c5cef76fa 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -13,7 +13,7 @@ module Projects
Projects::UpdateService.new(project, current_user, update_params).tap do |service|
result = service.execute
if result[:status] == :success
- flash[:notice] = "Pipelines settings for '#{@project.name}' were successfully updated."
+ flash[:notice] = _("Pipelines settings for '%{project_name}' were successfully updated.") % { project_name: @project.name }
run_autodevops_pipeline(service)
@@ -39,7 +39,7 @@ module Projects
def reset_registration_token
@project.reset_runners_token!
- flash[:notice] = 'New runners registration token has been generated!'
+ flash[:notice] = _('New runners registration token has been generated!')
redirect_to namespace_project_settings_ci_cd_path
end
@@ -58,7 +58,7 @@ module Projects
return unless service.run_auto_devops_pipeline?
if @project.empty_repo?
- flash[:warning] = "This repository is currently empty. A new Auto DevOps pipeline will be created after a new file has been pushed to a branch."
+ flash[:warning] = _("This repository is currently empty. A new Auto DevOps pipeline will be created after a new file has been pushed to a branch.")
return
end
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index 521ec2acebb..1fafc33e917 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -3,7 +3,6 @@
module Projects
module Settings
class OperationsController < Projects::ApplicationController
- before_action :check_license
before_action :authorize_update_environment!
helper_method :error_tracking_setting
@@ -14,16 +13,37 @@ module Projects
def update
result = ::Projects::Operations::UpdateService.new(project, current_user, update_params).execute
+ render_update_response(result)
+ end
+
+ private
+
+ # overridden in EE
+ def render_update_response(result)
+ respond_to do |format|
+ format.json do
+ render_update_json_response(result)
+ end
+ end
+ end
+
+ def render_update_json_response(result)
if result[:status] == :success
flash[:notice] = _('Your changes have been saved')
- redirect_to project_settings_operations_path(@project)
+ render json: {
+ status: result[:status]
+ }
else
- render 'show'
+ render(
+ status: result[:http_status] || :bad_request,
+ json: {
+ status: result[:status],
+ message: result[:message]
+ }
+ )
end
end
- private
-
def error_tracking_setting
@error_tracking_setting ||= project.error_tracking_setting ||
project.build_error_tracking_setting
@@ -35,11 +55,14 @@ module Projects
# overridden in EE
def permitted_project_params
- { error_tracking_setting_attributes: [:enabled, :api_url, :token] }
- end
-
- def check_license
- render_404 unless helpers.settings_operations_available?
+ {
+ error_tracking_setting_attributes: [
+ :enabled,
+ :api_host,
+ :token,
+ project: [:slug, :name, :organization_slug, :organization_name]
+ ]
+ }
end
end
end
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index edebfc55c17..90d53aa08ea 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -37,7 +37,7 @@ class Projects::TreeController < Projects::ApplicationController
def create_dir
return render_404 unless @commit_params.values.all?
- create_commit(Files::CreateDirService, success_notice: "The directory has been successfully created.",
+ create_commit(Files::CreateDirService, success_notice: _("The directory has been successfully created."),
success_path: project_tree_path(@project, File.join(@branch_name, @dir_name)),
failure_path: project_tree_path(@project, @ref))
end
diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb
index c7b4ebb2b24..284e119ca06 100644
--- a/app/controllers/projects/triggers_controller.rb
+++ b/app/controllers/projects/triggers_controller.rb
@@ -16,9 +16,9 @@ class Projects::TriggersController < Projects::ApplicationController
@trigger = project.triggers.create(trigger_params.merge(owner: current_user))
if @trigger.valid?
- flash[:notice] = 'Trigger was created successfully.'
+ flash[:notice] = _('Trigger was created successfully.')
else
- flash[:alert] = 'You could not create a new trigger.'
+ flash[:alert] = _('You could not create a new trigger.')
end
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
@@ -26,9 +26,9 @@ class Projects::TriggersController < Projects::ApplicationController
def take_ownership
if trigger.update(owner: current_user)
- flash[:notice] = 'Trigger was re-assigned.'
+ flash[:notice] = _('Trigger was re-assigned.')
else
- flash[:alert] = 'You could not take ownership of trigger.'
+ flash[:alert] = _('You could not take ownership of trigger.')
end
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
@@ -39,7 +39,7 @@ class Projects::TriggersController < Projects::ApplicationController
def update
if trigger.update(trigger_params)
- redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers'), 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
@@ -47,9 +47,9 @@ class Projects::TriggersController < Projects::ApplicationController
def destroy
if trigger.destroy
- flash[:notice] = "Trigger removed."
+ flash[:notice] = _("Trigger removed.")
else
- flash[:alert] = "Could not remove the trigger."
+ flash[:alert] = _("Could not remove the trigger.")
end
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers'), status: :found
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index 88dd111132b..da2420633ef 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -49,7 +49,7 @@ class Projects::WikisController < Projects::ApplicationController
if @page.valid?
redirect_to(
project_wiki_path(@project, @page),
- notice: 'Wiki was successfully updated.'
+ notice: _('Wiki was successfully updated.')
)
else
render 'edit'
@@ -65,7 +65,7 @@ class Projects::WikisController < Projects::ApplicationController
if @page.persisted?
redirect_to(
project_wiki_path(@project, @page),
- notice: 'Wiki was successfully updated.'
+ notice: _('Wiki was successfully updated.')
)
else
render action: "edit"
@@ -85,7 +85,7 @@ class Projects::WikisController < Projects::ApplicationController
else
redirect_to(
project_wiki_path(@project, :home),
- notice: "Page not found"
+ notice: _("Page not found")
)
end
end
@@ -95,7 +95,7 @@ class Projects::WikisController < Projects::ApplicationController
redirect_to project_wiki_path(@project, :home),
status: 302,
- notice: "Page was successfully deleted"
+ notice: _("Page was successfully deleted")
rescue Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
@@ -118,7 +118,7 @@ class Projects::WikisController < Projects::ApplicationController
@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."
+ flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
redirect_to project_path(@project)
false
end
@@ -155,7 +155,7 @@ class Projects::WikisController < Projects::ApplicationController
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."
+ flash.now[:notice] = _("The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.")
end
def file_blob
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 1b22907c10f..90d4bc674d9 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -29,6 +29,7 @@ class SearchController < ApplicationController
@search_objects = search_service.search_objects
render_commits if @scope == 'commits'
+ eager_load_user_status if @scope == 'users'
check_single_commit_result
end
@@ -54,6 +55,12 @@ class SearchController < ApplicationController
@search_objects = prepare_commits_for_rendering(@search_objects)
end
+ def eager_load_user_status
+ return if Feature.disabled?(:users_search, default_enabled: true)
+
+ @search_objects = @search_objects.eager_load(:status) # rubocop:disable CodeReuse/ActiveRecord
+ end
+
def check_single_commit_result
if @search_results.single_commit_result?
only_commit = @search_results.objects('commits').first
diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb
index 091bcb1253d..eee14b0faf4 100644
--- a/app/controllers/snippets/notes_controller.rb
+++ b/app/controllers/snippets/notes_controller.rb
@@ -26,10 +26,6 @@ class Snippets::NotesController < ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
alias_method :noteable, :snippet
- def note_params
- super.merge(noteable_id: params[:snippet_id])
- end
-
def finder_params
params.merge(last_fetched_at: last_fetched_at, target_id: snippet.id, target_type: 'personal_snippet')
end
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index 519e7439205..568c6e2a852 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -28,13 +28,13 @@ class UploadsController < ApplicationController
end
def find_model
- return nil unless params[:id]
+ return unless params[:id]
upload_model_class.find(params[:id])
end
def authorize_access!
- return nil unless model
+ return unless model
authorized =
case model
@@ -54,7 +54,7 @@ class UploadsController < ApplicationController
end
def authorize_create_access!
- return nil unless model
+ return unless model
# for now we support only personal snippets comments
authorized = can?(current_user, :comment_personal_snippet, model)
diff --git a/app/finders/admin/runners_finder.rb b/app/finders/admin/runners_finder.rb
index fbb1cfc5c66..b2799565f57 100644
--- a/app/finders/admin/runners_finder.rb
+++ b/app/finders/admin/runners_finder.rb
@@ -11,10 +11,11 @@ class Admin::RunnersFinder < UnionFinder
search!
filter_by_status!
filter_by_runner_type!
+ filter_by_tag_list!
sort!
paginate!
- @runners
+ @runners.with_tags
end
def sort_key
@@ -44,6 +45,14 @@ class Admin::RunnersFinder < UnionFinder
filter_by!(:type_type, Ci::Runner::AVAILABLE_TYPES)
end
+ def filter_by_tag_list!
+ tag_list = @params[:tag_name].presence
+
+ if tag_list
+ @runners = @runners.tagged_with(tag_list)
+ end
+ end
+
def sort!
@runners = @runners.order_by(sort_key)
end
diff --git a/app/finders/autocomplete/acts_as_taggable_on/tags_finder.rb b/app/finders/autocomplete/acts_as_taggable_on/tags_finder.rb
new file mode 100644
index 00000000000..f38c187799c
--- /dev/null
+++ b/app/finders/autocomplete/acts_as_taggable_on/tags_finder.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Autocomplete
+ module ActsAsTaggableOn
+ class TagsFinder
+ LIMIT = 20
+
+ def initialize(params:)
+ @params = params
+ end
+
+ def execute
+ tags = all_tags
+ tags = filter_by_name(tags)
+ limit(tags)
+ end
+
+ private
+
+ def all_tags
+ ::ActsAsTaggableOn::Tag.all
+ end
+
+ def filter_by_name(tags)
+ return tags unless search
+ return tags.none if search.empty?
+
+ if search.length >= Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING
+ tags.named_like(search)
+ else
+ tags.named(search)
+ end
+ end
+
+ def limit(tags)
+ tags.limit(LIMIT) # rubocop: disable CodeReuse/ActiveRecord
+ end
+
+ def search
+ @params[:search]
+ end
+ end
+ end
+end
diff --git a/app/finders/concerns/finder_methods.rb b/app/finders/concerns/finder_methods.rb
index 5290313585f..8de3276184d 100644
--- a/app/finders/concerns/finder_methods.rb
+++ b/app/finders/concerns/finder_methods.rb
@@ -3,13 +3,13 @@
module FinderMethods
# rubocop: disable CodeReuse/ActiveRecord
def find_by!(*args)
- raise_not_found_unless_authorized execute.find_by!(*args)
+ raise_not_found_unless_authorized execute.reorder(nil).find_by!(*args)
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def find_by(*args)
- if_authorized execute.find_by(*args)
+ if_authorized execute.reorder(nil).find_by(*args)
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb
index 96a36db7ec8..ec340f38450 100644
--- a/app/finders/group_descendants_finder.rb
+++ b/app/finders/group_descendants_finder.rb
@@ -134,7 +134,7 @@ class GroupDescendantsFinder
def subgroups
return Group.none unless Group.supports_nested_objects?
- # When filtering subgroups, we want to find all matches withing the tree of
+ # When filtering subgroups, we want to find all matches within the tree of
# descendants to show to the user
groups = if params[:filter]
subgroups_matching_filter
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 23af2e0521c..6eab8c5ee51 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -78,13 +78,15 @@ class IssuableFinder
items = init_collection
items = filter_items(items)
- # This has to be last as we may use a CTE as an optimization fence
- # by passing the attempt_group_search_optimizations param and
- # enabling the use_cte_for_group_issues_search feature flag
+ # This has to be last as we use a CTE as an optimization fence
+ # for counts by passing the force_cte param and enabling the
+ # attempt_group_search_optimizations feature flag
# https://www.postgresql.org/docs/current/static/queries-with.html
items = by_search(items)
- sort(items)
+ items = sort(items) unless use_cte_for_count?
+
+ items
end
def filter_items(items)
@@ -94,6 +96,7 @@ class IssuableFinder
items = by_scope(items)
items = by_created_at(items)
items = by_updated_at(items)
+ items = by_closed_at(items)
items = by_state(items)
items = by_group(items)
items = by_assignee(items)
@@ -116,8 +119,9 @@ class IssuableFinder
#
# rubocop: disable CodeReuse/ActiveRecord
def count_by_state
- count_params = params.merge(state: nil, sort: nil)
+ count_params = params.merge(state: nil, sort: nil, force_cte: true)
finder = self.class.new(current_user, count_params)
+
counts = Hash.new(0)
# Searching by label includes a GROUP BY in the query, but ours will be last
@@ -127,8 +131,11 @@ class IssuableFinder
#
# This does not apply when we are using a CTE for the search, as the labels
# GROUP BY is inside the subquery in that case, so we set labels_count to 1.
+ #
+ # We always use CTE when searching in Groups if the feature flag is enabled,
+ # but never when searching in Projects.
labels_count = label_names.any? ? label_names.count : 1
- labels_count = 1 if use_cte_for_search?
+ labels_count = 1 if use_cte_for_count?
finder.execute.reorder(nil).group(:state).count.each do |key, value|
counts[count_key(key)] += value / labels_count
@@ -304,27 +311,31 @@ class IssuableFinder
def use_subquery_for_search?
strong_memoize(:use_subquery_for_search) do
- attempt_group_search_optimizations? &&
- Feature.enabled?(:use_subquery_for_group_issues_search, default_enabled: true)
+ !force_cte? && attempt_group_search_optimizations?
end
end
- def use_cte_for_search?
- strong_memoize(:use_cte_for_search) do
- attempt_group_search_optimizations? &&
- !use_subquery_for_search? &&
- Feature.enabled?(:use_cte_for_group_issues_search, default_enabled: true)
+ def use_cte_for_count?
+ strong_memoize(:use_cte_for_count) do
+ force_cte? && attempt_group_search_optimizations?
end
end
private
+ def force_cte?
+ !!params[:force_cte]
+ end
+
def init_collection
klass.all
end
def attempt_group_search_optimizations?
- search && Gitlab::Database.postgresql? && params[:attempt_group_search_optimizations]
+ search &&
+ Gitlab::Database.postgresql? &&
+ params[:attempt_group_search_optimizations] &&
+ Feature.enabled?(:attempt_group_search_optimizations, default_enabled: true)
end
def count_key(value)
@@ -353,6 +364,13 @@ class IssuableFinder
items
end
+ def by_closed_at(items)
+ items = items.closed_after(params[:closed_after]) if params[:closed_after].present?
+ items = items.closed_before(params[:closed_before]) if params[:closed_before].present?
+
+ items
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def by_state(items)
case params[:state].to_s
@@ -403,7 +421,7 @@ class IssuableFinder
def by_search(items)
return items unless search
- if use_cte_for_search?
+ if use_cte_for_count?
cte = Gitlab::SQL::RecursiveCTE.new(klass.table_name)
cte << items
@@ -478,7 +496,7 @@ class IssuableFinder
upcoming_ids = Milestone.upcoming_ids(projects, related_groups)
items = items.left_joins_milestones.where(milestone_id: upcoming_ids)
elsif filter_by_started_milestone?
- items = items.left_joins_milestones.where('milestones.start_date <= NOW()')
+ items = items.left_joins_milestones.merge(Milestone.started)
else
items = items.with_milestone(params[:milestone_title])
end
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index a0504ca0879..cb44575d6f1 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -69,7 +69,16 @@ class IssuesFinder < IssuableFinder
end
def filter_items(items)
- by_due_date(super)
+ issues = super
+ issues = by_due_date(issues)
+ issues = by_confidential(issues)
+ issues
+ end
+
+ def by_confidential(items)
+ return items if params[:confidential].nil?
+
+ params[:confidential] ? items.confidential_only : items.public_only
end
def by_due_date(items)
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index b645011a3c5..84689ff5dc7 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -29,7 +29,7 @@
#
class MergeRequestsFinder < IssuableFinder
def self.scalar_params
- @scalar_params ||= super + [:wip]
+ @scalar_params ||= super + [:wip, :target_branch]
end
def klass
@@ -37,13 +37,20 @@ class MergeRequestsFinder < IssuableFinder
end
def filter_items(_items)
- items = by_source_branch(super)
+ items = by_commit(super)
+ items = by_source_branch(items)
items = by_wip(items)
by_target_branch(items)
end
private
+ def by_commit(items)
+ return items unless params[:commit_sha].presence
+
+ items.by_commit_sha(params[:commit_sha])
+ end
+
def source_branch
@source_branch ||= params[:source_branch].presence
end
diff --git a/app/finders/projects/daily_statistics_finder.rb b/app/finders/projects/daily_statistics_finder.rb
new file mode 100644
index 00000000000..912c23107bc
--- /dev/null
+++ b/app/finders/projects/daily_statistics_finder.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module Projects
+ class DailyStatisticsFinder
+ attr_reader :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def fetches
+ ProjectDailyStatistic.of_project(project)
+ .of_last_30_days
+ .sorted_by_date_desc
+ end
+
+ def total_fetch_count
+ fetches.sum_fetch_count
+ end
+ end
+end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index 93d3c991846..0319e95d439 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -81,7 +81,7 @@ class ProjectsFinder < UnionFinder
if private_only?
current_user.authorized_projects
else
- Project.public_or_visible_to_user(current_user)
+ Project.public_or_visible_to_user(current_user, params[:visibility_level])
end
end
end
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index d3774746cb8..bf29f15642d 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -69,6 +69,8 @@ class SnippetsFinder < UnionFinder
base.with_optional_visibility(visibility_from_scope).fresh
end
+ private
+
# Produces a query that retrieves snippets from multiple projects.
#
# The resulting query will, depending on the user's permissions, include the
diff --git a/app/graphql/resolvers/issues_resolver.rb b/app/graphql/resolvers/issues_resolver.rb
index fd1b46ba860..b98d8bd1fff 100644
--- a/app/graphql/resolvers/issues_resolver.rb
+++ b/app/graphql/resolvers/issues_resolver.rb
@@ -9,7 +9,30 @@ module Resolvers
argument :iids, [GraphQL::ID_TYPE],
required: false,
description: 'The list of IIDs of issues, e.g., [1, 2]'
-
+ argument :state, Types::IssuableStateEnum,
+ required: false,
+ description: "Current state of Issue"
+ argument :label_name, GraphQL::STRING_TYPE.to_list_type,
+ required: false,
+ description: "Labels applied to the Issue"
+ argument :created_before, Types::TimeType,
+ required: false,
+ description: "Issues created before this date"
+ argument :created_after, Types::TimeType,
+ required: false,
+ description: "Issues created after this date"
+ argument :updated_before, Types::TimeType,
+ required: false,
+ description: "Issues updated before this date"
+ argument :updated_after, Types::TimeType,
+ required: false,
+ description: "Issues updated after this date"
+ argument :closed_before, Types::TimeType,
+ required: false,
+ description: "Issues closed before this date"
+ argument :closed_after, Types::TimeType,
+ required: false,
+ description: "Issues closed after this date"
argument :search, GraphQL::STRING_TYPE,
required: false
argument :sort, Types::Sort,
diff --git a/app/graphql/resolvers/metadata_resolver.rb b/app/graphql/resolvers/metadata_resolver.rb
new file mode 100644
index 00000000000..3a79e6434fb
--- /dev/null
+++ b/app/graphql/resolvers/metadata_resolver.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Resolvers
+ class MetadataResolver < BaseResolver
+ type Types::MetadataType, null: false
+
+ def resolve(**args)
+ { version: Gitlab::VERSION, revision: Gitlab.revision }
+ end
+ end
+end
diff --git a/app/graphql/types/ci/detailed_status_type.rb b/app/graphql/types/ci/detailed_status_type.rb
new file mode 100644
index 00000000000..2987354b556
--- /dev/null
+++ b/app/graphql/types/ci/detailed_status_type.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+module Types
+ module Ci
+ class DetailedStatusType < BaseObject
+ graphql_name 'DetailedStatus'
+
+ field :group, GraphQL::STRING_TYPE, null: false
+ field :icon, GraphQL::STRING_TYPE, null: false
+ field :favicon, GraphQL::STRING_TYPE, null: false
+ field :details_path, GraphQL::STRING_TYPE, null: false
+ field :has_details, GraphQL::BOOLEAN_TYPE, null: false, method: :has_details?
+ field :label, GraphQL::STRING_TYPE, null: false
+ field :text, GraphQL::STRING_TYPE, null: false
+ field :tooltip, GraphQL::STRING_TYPE, null: false, method: :status_tooltip
+ end
+ end
+end
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index 2bbffad4563..18696293b97 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -13,6 +13,10 @@ module Types
field :sha, GraphQL::STRING_TYPE, null: false
field :before_sha, GraphQL::STRING_TYPE, null: true
field :status, PipelineStatusEnum, null: false
+ field :detailed_status,
+ Types::Ci::DetailedStatusType,
+ null: false,
+ resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
field :duration,
GraphQL::INT_TYPE,
null: true,
diff --git a/app/graphql/types/issuable_state_enum.rb b/app/graphql/types/issuable_state_enum.rb
new file mode 100644
index 00000000000..f2f6d6c6cab
--- /dev/null
+++ b/app/graphql/types/issuable_state_enum.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module Types
+ class IssuableStateEnum < BaseEnum
+ graphql_name 'IssuableState'
+ description 'State of a GitLab issue or merge request'
+
+ value 'opened'
+ value 'closed'
+ value 'locked'
+ end
+end
diff --git a/app/graphql/types/issue_state_enum.rb b/app/graphql/types/issue_state_enum.rb
new file mode 100644
index 00000000000..6521407fc9d
--- /dev/null
+++ b/app/graphql/types/issue_state_enum.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+module Types
+ class IssueStateEnum < IssuableStateEnum
+ graphql_name 'IssueState'
+ description 'State of a GitLab issue'
+ end
+end
diff --git a/app/graphql/types/issue_type.rb b/app/graphql/types/issue_type.rb
index a8f2f7914a8..5ad3ea52930 100644
--- a/app/graphql/types/issue_type.rb
+++ b/app/graphql/types/issue_type.rb
@@ -11,22 +11,20 @@ module Types
field :iid, GraphQL::ID_TYPE, null: false
field :title, GraphQL::STRING_TYPE, null: false
field :description, GraphQL::STRING_TYPE, null: true
- field :state, GraphQL::STRING_TYPE, null: false
+ field :state, IssueStateEnum, null: false
field :author, Types::UserType,
null: false,
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find } do
- authorize :read_user
- end
+ resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(User, obj.author_id).find },
+ authorize: :read_user
field :assignees, Types::UserType.connection_type, null: true
field :labels, Types::LabelType.connection_type, null: true
field :milestone, Types::MilestoneType,
null: true,
- resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find } do
- authorize :read_milestone
- end
+ resolve: -> (obj, _args, _ctx) { Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, obj.milestone_id).find },
+ authorize: :read_milestone
field :due_date, Types::TimeType, null: true
field :confidential, GraphQL::BOOLEAN_TYPE, null: false
diff --git a/app/graphql/types/merge_request_state_enum.rb b/app/graphql/types/merge_request_state_enum.rb
new file mode 100644
index 00000000000..92f52726ab3
--- /dev/null
+++ b/app/graphql/types/merge_request_state_enum.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Types
+ class MergeRequestStateEnum < IssuableStateEnum
+ graphql_name 'MergeRequestState'
+ description 'State of a GitLab merge request'
+
+ value 'merged'
+ end
+end
diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb
index 7e63d4022b1..1ed27a14e33 100644
--- a/app/graphql/types/merge_request_type.rb
+++ b/app/graphql/types/merge_request_type.rb
@@ -12,7 +12,7 @@ module Types
field :iid, GraphQL::ID_TYPE, null: false
field :title, GraphQL::STRING_TYPE, null: false
field :description, GraphQL::STRING_TYPE, null: true
- field :state, GraphQL::STRING_TYPE, null: true
+ field :state, MergeRequestStateEnum, null: false
field :created_at, Types::TimeType, null: false
field :updated_at, Types::TimeType, null: false
field :source_project, Types::ProjectType, null: true
@@ -48,9 +48,7 @@ module Types
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 :head_pipeline, Types::Ci::PipelineType, null: true, method: :actual_head_pipeline, authorize: :read_pipeline
field :pipelines, Types::Ci::PipelineType.connection_type,
resolver: Resolvers::MergeRequestPipelinesResolver
end
diff --git a/app/graphql/types/metadata_type.rb b/app/graphql/types/metadata_type.rb
new file mode 100644
index 00000000000..2d8bad0614b
--- /dev/null
+++ b/app/graphql/types/metadata_type.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Types
+ class MetadataType < ::Types::BaseObject
+ graphql_name 'Metadata'
+
+ field :version, GraphQL::STRING_TYPE, null: false
+ field :revision, GraphQL::STRING_TYPE, null: false
+ end
+end
diff --git a/app/graphql/types/project_type.rb b/app/graphql/types/project_type.rb
index d25c8c8bd90..b96c2f3afb2 100644
--- a/app/graphql/types/project_type.rb
+++ b/app/graphql/types/project_type.rb
@@ -16,7 +16,6 @@ module Types
field :description, GraphQL::STRING_TYPE, null: true
- field :default_branch, GraphQL::STRING_TYPE, null: true
field :tag_list, GraphQL::STRING_TYPE, null: true
field :ssh_url_to_repo, GraphQL::STRING_TYPE, null: true
@@ -59,7 +58,6 @@ module Types
end
field :import_status, GraphQL::STRING_TYPE, null: true
- field :ci_config_path, GraphQL::STRING_TYPE, null: true
field :only_allow_merge_if_pipeline_succeeds, GraphQL::BOOLEAN_TYPE, null: true
field :request_access_enabled, GraphQL::BOOLEAN_TYPE, null: true
@@ -69,16 +67,14 @@ module Types
field :merge_requests,
Types::MergeRequestType.connection_type,
null: true,
- resolver: Resolvers::MergeRequestsResolver do
- authorize :read_merge_request
- end
+ resolver: Resolvers::MergeRequestsResolver,
+ authorize: :read_merge_request
field :merge_request,
Types::MergeRequestType,
null: true,
- resolver: Resolvers::MergeRequestsResolver.single do
- authorize :read_merge_request
- end
+ resolver: Resolvers::MergeRequestsResolver.single,
+ authorize: :read_merge_request
field :issues,
Types::IssueType.connection_type,
diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb
index 7c41716b82a..472fe5d6ec2 100644
--- a/app/graphql/types/query_type.rb
+++ b/app/graphql/types/query_type.rb
@@ -1,14 +1,21 @@
# frozen_string_literal: true
module Types
- class QueryType < BaseObject
+ class QueryType < ::Types::BaseObject
graphql_name 'Query'
field :project, Types::ProjectType,
null: true,
resolver: Resolvers::ProjectResolver,
- description: "Find a project" do
- authorize :read_project
+ description: "Find a project",
+ authorize: :read_project
+
+ field :metadata, Types::MetadataType,
+ null: true,
+ resolver: Resolvers::MetadataResolver,
+ description: 'Metadata about GitLab' do |*args|
+
+ authorize :read_instance_metadata
end
field :echo, GraphQL::STRING_TYPE, null: false, function: Functions::Echo.new
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index 7fbbbb04154..c0db9910143 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
module AppearancesHelper
+ include MarkupHelper
+
def brand_title
current_appearance&.title.presence || default_brand_title
end
@@ -40,4 +42,36 @@ module AppearancesHelper
render 'shared/logo_type.svg'
end
end
+
+ def header_message
+ return unless current_appearance&.show_header?
+
+ class_names = []
+ class_names << 'with-performance-bar' if performance_bar_enabled?
+
+ render_message(:header_message, class_names: class_names)
+ end
+
+ def footer_message
+ return unless current_appearance&.show_footer?
+
+ render_message(:footer_message)
+ end
+
+ private
+
+ def render_message(field_sym, class_names: [], style: message_style)
+ class_names << field_sym.to_s.dasherize
+
+ content_tag :div, class: class_names, style: style do
+ markdown_field(current_appearance, field_sym)
+ end
+ end
+
+ def message_style
+ style = []
+ style << "background-color: #{current_appearance.message_background_color};"
+ style << "color: #{current_appearance.message_font_color}"
+ style.join
+ end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 9efa84b02f0..ffa5719fefb 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -214,12 +214,19 @@ module ApplicationHelper
class_names = []
class_names << 'issue-boards-page' if current_controller?(:boards)
class_names << 'with-performance-bar' if performance_bar_enabled?
-
+ class_names << system_message_class
class_names
end
- # EE feature: System header and footer, unavailable in CE
def system_message_class
+ class_names = []
+
+ return class_names unless appearance
+
+ class_names << 'with-system-header' if appearance.show_header?
+ class_names << 'with-system-footer' if appearance.show_footer?
+
+ class_names
end
# Returns active css class when condition returns true
@@ -292,4 +299,10 @@ module ApplicationHelper
snippets: snippets_project_autocomplete_sources_path(object)
}
end
+
+ private
+
+ def appearance
+ ::Appearance.current
+ end
end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index 2b1d6f49878..b4ee648361c 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -100,8 +100,12 @@ module AuthHelper
end
# rubocop: enable CodeReuse/ActiveRecord
- def unlink_allowed?(provider)
- %w(saml cas3).exclude?(provider.to_s)
+ def unlink_provider_allowed?(provider)
+ IdentityProviderPolicy.new(current_user, provider).can?(:unlink)
+ end
+
+ def link_provider_allowed?(provider)
+ IdentityProviderPolicy.new(current_user, provider).can?(:link)
end
extend self
diff --git a/app/helpers/auto_devops_helper.rb b/app/helpers/auto_devops_helper.rb
index 67e7e475920..0f0d5350df6 100644
--- a/app/helpers/auto_devops_helper.rb
+++ b/app/helpers/auto_devops_helper.rb
@@ -9,4 +9,17 @@ module AutoDevopsHelper
!project.repository.gitlab_ci_yml &&
!project.ci_service
end
+
+ def badge_for_auto_devops_scope(auto_devops_receiver)
+ return unless auto_devops_receiver.auto_devops_enabled?
+
+ case auto_devops_receiver.first_auto_devops_config[:scope]
+ when :project
+ nil
+ when :group
+ s_('CICD|group enabled')
+ when :instance
+ s_('CICD|instance enabled')
+ end
+ end
end
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 23d6684a8e6..3e1bb9af5cc 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -31,12 +31,13 @@ module BlobHelper
edit_button_tag(blob,
common_classes,
_('Edit'),
- edit_blob_path(project, ref, path, options),
+ Feature.enabled?(:web_ide_default) ? ide_edit_path(project, ref, path, options) : edit_blob_path(project, ref, path, options),
project,
ref)
end
def ide_edit_button(project = @project, ref = @ref, path = @path, options = {})
+ return if Feature.enabled?(:web_ide_default)
return unless blob = readable_blob(options, path, project, ref)
edit_button_tag(blob,
diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb
index 923a06a0512..355b91a8661 100644
--- a/app/helpers/ci_status_helper.rb
+++ b/app/helpers/ci_status_helper.rb
@@ -100,17 +100,6 @@ module CiStatusHelper
"pipeline-status/#{pipeline_status.sha}-#{pipeline_status.status}"
end
- def render_project_pipeline_status(pipeline_status, tooltip_placement: 'left')
- project = pipeline_status.project
- path = pipelines_project_commit_path(project, pipeline_status.sha, ref: pipeline_status.ref)
-
- render_status_with_link(
- 'commit',
- pipeline_status.status,
- path,
- tooltip_placement: tooltip_placement)
- end
-
def render_commit_status(commit, ref: nil, tooltip_placement: 'left')
project = commit.project
path = pipelines_project_commit_path(project, commit, ref: ref)
@@ -123,12 +112,6 @@ module CiStatusHelper
icon_size: 24)
end
- def render_pipeline_status(pipeline, tooltip_placement: 'left')
- project = pipeline.project
- path = project_pipeline_path(project, pipeline)
- render_status_with_link('pipeline', pipeline.status, path, tooltip_placement: tooltip_placement)
- end
-
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)}"
diff --git a/app/helpers/clusters_helper.rb b/app/helpers/clusters_helper.rb
index 916dcb1a308..769f75f57c4 100644
--- a/app/helpers/clusters_helper.rb
+++ b/app/helpers/clusters_helper.rb
@@ -14,4 +14,10 @@ module ClustersHelper
render 'clusters/clusters/gcp_signup_offer_banner'
end
end
+
+ def has_rbac_enabled?(cluster)
+ return cluster.platform_kubernetes_rbac? if cluster.platform_kubernetes
+
+ !cluster.provider.legacy_abac?
+ end
end
diff --git a/app/helpers/count_helper.rb b/app/helpers/count_helper.rb
index 13839474e1f..62bb2e4da23 100644
--- a/app/helpers/count_helper.rb
+++ b/app/helpers/count_helper.rb
@@ -13,7 +13,7 @@ module CountHelper
# memberships, and deducting 1 for each root of the fork network.
# This might be inacurate as the root of the fork network might have been deleted.
#
- # This makes querying this information a lot more effecient and it should be
+ # This makes querying this information a lot more efficient and it should be
# accurate enough for the instance wide statistics
def approximate_fork_count_with_delimiters(count_data)
fork_network_count = count_data[ForkNetwork]
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index dedc58f482b..96471d15aac 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -131,4 +131,42 @@ module EmailsHelper
project.id.to_s + "." + project_path_as_domain + "." + Gitlab.config.gitlab.host
end
+
+ def html_header_message
+ return unless show_header?
+
+ render_message(:header_message, style: '')
+ end
+
+ def html_footer_message
+ return unless show_footer?
+
+ render_message(:footer_message, style: '')
+ end
+
+ def text_header_message
+ return unless show_header?
+
+ strip_tags(render_message(:header_message, style: ''))
+ end
+
+ def text_footer_message
+ return unless show_footer?
+
+ strip_tags(render_message(:footer_message, style: ''))
+ end
+
+ private
+
+ def show_footer?
+ email_header_and_footer_enabled? && current_appearance&.show_footer?
+ end
+
+ def show_header?
+ email_header_and_footer_enabled? && current_appearance&.show_header?
+ end
+
+ def email_header_and_footer_enabled?
+ current_appearance&.email_header_and_footer_enabled?
+ end
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 4a9ed123161..9d028dccad7 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -4,6 +4,7 @@ module GroupsHelper
def group_overview_nav_link_paths
%w[
groups#show
+ groups#details
groups#activity
groups#subgroups
analytics#show
diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb
index 1a471034972..0622cdfc196 100644
--- a/app/helpers/issuables_helper.rb
+++ b/app/helpers/issuables_helper.rb
@@ -191,7 +191,7 @@ module IssuablesHelper
output << content_tag(:strong) do
author_output = link_to_member(project, issuable.author, size: 24, mobile_classes: "d-none d-sm-inline")
- author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "d-block d-sm-none")
+ author_output << link_to_member(project, issuable.author, size: 24, by_username: true, avatar: false, mobile_classes: "d-inline d-sm-none")
if status = user_status(issuable.author)
author_output << "#{status}".html_safe
@@ -200,7 +200,7 @@ module IssuablesHelper
author_output
end
- output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip', title: _('1st contribution!'))
+ output << content_tag(:span, (issuable_first_contribution_icon if issuable.first_contribution?), class: 'has-tooltip prepend-left-4', title: _('1st contribution!'))
output << content_tag(:span, (issuable.task_status if issuable.tasks?), id: "task_status", class: "d-none d-sm-none d-md-inline-block prepend-left-8")
output << content_tag(:span, (issuable.task_status_short if issuable.tasks?), id: "task_status_short", class: "d-md-none")
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index 39f661b5f0c..bd53add80ca 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -227,6 +227,10 @@ module LabelsHelper
"#{action} at #{level} level"
end
+ def labels_sorted_by_title(labels)
+ labels.sort_by(&:title)
+ end
+
# Required for Banzai::Filter::LabelReferenceFilter
module_function :render_colored_label, :text_color_for_bg, :escape_once
end
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index 66f4b7b3f30..d83c69603a9 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -74,7 +74,7 @@ module MarkupHelper
# the tag contents are truncated without removing the closing tag.
def first_line_in_markdown(object, attribute, max_chars = nil, options = {})
md = markdown_field(object, attribute, options)
- return nil unless md.present?
+ return unless md.present?
tags = %w(a gl-emoji b pre code p span)
tags << 'img' if options[:allow_images]
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 23d7aa427bb..991ca42c445 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -29,7 +29,7 @@ module MergeRequestsHelper
def ci_build_details_path(merge_request)
build_url = merge_request.source_project.ci_service.build_page(merge_request.diff_head_sha, merge_request.source_branch)
- return nil unless build_url
+ return unless build_url
parsed_url = URI.parse(build_url)
@@ -92,7 +92,7 @@ module MergeRequestsHelper
end
def version_index(merge_request_diff)
- return nil if @merge_request_diffs.empty?
+ return if @merge_request_diffs.empty?
@merge_request_diffs.size - @merge_request_diffs.index(merge_request_diff)
end
@@ -149,7 +149,7 @@ module MergeRequestsHelper
def merge_request_source_project_for_project(project = @project)
unless can?(current_user, :create_merge_request_in, project)
- return nil
+ return
end
if can?(current_user, :create_merge_request_from, project)
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index aaf38cbfe70..a50137bea3d 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -122,7 +122,7 @@ module NotesHelper
end
def new_form_url
- return nil unless @snippet.is_a?(PersonalSnippet)
+ return unless @snippet.is_a?(PersonalSnippet)
snippet_notes_path(@snippet)
end
diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb
index bc1742e8167..766508b6609 100644
--- a/app/helpers/preferences_helper.rb
+++ b/app/helpers/preferences_helper.rb
@@ -46,7 +46,8 @@ module PreferencesHelper
def first_day_of_week_choices
[
[_('Sunday'), 0],
- [_('Monday'), 1]
+ [_('Monday'), 1],
+ [_('Saturday'), 6]
]
end
@@ -62,6 +63,10 @@ module PreferencesHelper
Gitlab::ColorSchemes.for_user(current_user).css_class
end
+ def language_choices
+ Gitlab::I18n::AVAILABLE_LANGUAGES.map { |value, label| [label, value] }
+ end
+
private
# Ensure that anyone adding new options updates `DASHBOARD_CHOICES` too
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index c400302cda3..f2abb241753 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -169,7 +169,7 @@ module ProjectsHelper
translation.html_safe
end
- def project_list_cache_key(project)
+ def project_list_cache_key(project, pipeline_status: true)
key = [
project.route.cache_key,
project.cache_key,
@@ -179,10 +179,11 @@ module ProjectsHelper
Gitlab::CurrentSettings.cache_key,
"cross-project:#{can?(current_user, :read_cross_project)}",
max_project_member_access_cache_key(project),
+ pipeline_status,
'v2.6'
]
- key << pipeline_status_cache_key(project.pipeline_status) if project.pipeline_status.has_status?
+ key << pipeline_status_cache_key(project.pipeline_status) if pipeline_status && project.pipeline_status.has_status?
key
end
@@ -284,6 +285,20 @@ module ProjectsHelper
can?(current_user, :read_environment, @project)
end
+ def error_tracking_setting_project_json
+ setting = @project.error_tracking_setting
+
+ return if setting.blank? || setting.project_slug.blank? ||
+ setting.organization_slug.blank?
+
+ {
+ name: setting.project_name,
+ organization_name: setting.organization_name,
+ organization_slug: setting.organization_slug,
+ slug: setting.project_slug
+ }.to_json
+ end
+
private
def get_project_nav_tabs(project, current_user)
@@ -350,7 +365,8 @@ module ProjectsHelper
blobs: :download_code,
commits: :download_code,
merge_requests: :read_merge_request,
- notes: [:read_merge_request, :download_code, :read_issue, :read_project_snippet]
+ notes: [:read_merge_request, :download_code, :read_issue, :read_project_snippet],
+ members: :read_project_member
)
end
diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb
index 0ee76a51f7d..09165979b26 100644
--- a/app/helpers/search_helper.rb
+++ b/app/helpers/search_helper.rb
@@ -33,10 +33,15 @@ module SearchHelper
"Showing #{from} - #{to} of #{count} #{scope.humanize(capitalize: false)} for \"#{term}\""
end
- def find_project_for_result_blob(result)
+ def find_project_for_result_blob(projects, result)
@project
end
+ # Used in EE
+ def blob_projects(results)
+ nil
+ end
+
def parse_search_result(result)
result
end
@@ -201,4 +206,14 @@ module SearchHelper
def limited_count(count, limit = 1000)
count > limit ? "#{limit}+" : count
end
+
+ def search_tabs?(tab)
+ return false if Feature.disabled?(:users_search, default_enabled: true)
+
+ if @project
+ project_search_tabs?(:members)
+ else
+ can?(current_user, :read_users_list)
+ end
+ end
end
diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index e2879bfdcf1..c5bab877c00 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -136,18 +136,9 @@ module TreeHelper
end
# returns the relative path of the first subdir that doesn't have only one directory descendant
- # rubocop: disable CodeReuse/ActiveRecord
def flatten_tree(root_path, tree)
- return tree.flat_path.sub(%r{\A#{Regexp.escape(root_path)}/}, '') if tree.flat_path.present?
-
- subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
- if subtree.count == 1 && subtree.first.dir?
- return tree_join(tree.name, flatten_tree(root_path, subtree.first))
- else
- return tree.name
- end
+ tree.flat_path.sub(%r{\A#{Regexp.escape(root_path)}/}, '')
end
- # rubocop: enable CodeReuse/ActiveRecord
def selected_branch
@branch_name || tree_edit_branch
diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb
index 1ad7bb81784..5d658d35107 100644
--- a/app/helpers/user_callouts_helper.rb
+++ b/app/helpers/user_callouts_helper.rb
@@ -17,6 +17,9 @@ module UserCalloutsHelper
render 'shared/flash_user_callout', flash_type: flash_type, message: message, feature_name: feature_name
end
+ def render_dashboard_gold_trial(user)
+ end
+
private
def user_dismissed?(feature_name)
diff --git a/app/mailers/abuse_report_mailer.rb b/app/mailers/abuse_report_mailer.rb
index e032f568913..e0aa66e6de3 100644
--- a/app/mailers/abuse_report_mailer.rb
+++ b/app/mailers/abuse_report_mailer.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
class AbuseReportMailer < BaseMailer
+ layout 'empty_mailer'
+
+ helper EmailsHelper
+
def notify(abuse_report_id)
return unless deliverable?
diff --git a/app/mailers/email_rejection_mailer.rb b/app/mailers/email_rejection_mailer.rb
index 45fc5a6c383..d743533b1bc 100644
--- a/app/mailers/email_rejection_mailer.rb
+++ b/app/mailers/email_rejection_mailer.rb
@@ -1,6 +1,10 @@
# frozen_string_literal: true
class EmailRejectionMailer < BaseMailer
+ layout 'empty_mailer'
+
+ helper EmailsHelper
+
def rejection(reason, original_raw, can_retry = false)
@reason = reason
@original_message = Mail::Message.new(original_raw)
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index 654ae211310..d2e334fb856 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -74,6 +74,7 @@ module Emails
@new_issue = new_issue
@new_project = new_issue.project
+ @can_access_project = recipient.can?(:read_project, @new_project)
mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id, reason))
end
diff --git a/app/mailers/emails/pipelines.rb b/app/mailers/emails/pipelines.rb
index 31e183640ad..fb57c0da34d 100644
--- a/app/mailers/emails/pipelines.rb
+++ b/app/mailers/emails/pipelines.rb
@@ -15,7 +15,7 @@ module Emails
def pipeline_mail(pipeline, recipients, status)
@project = pipeline.project
@pipeline = pipeline
- @merge_request = pipeline.merge_requests.first
+ @merge_request = pipeline.merge_requests_as_head_pipeline.first
add_headers
# We use bcc here because we don't want to generate this emails for a
diff --git a/app/mailers/repository_check_mailer.rb b/app/mailers/repository_check_mailer.rb
index 145169be8a6..a24d3476d0e 100644
--- a/app/mailers/repository_check_mailer.rb
+++ b/app/mailers/repository_check_mailer.rb
@@ -2,6 +2,10 @@
class RepositoryCheckMailer < BaseMailer
# rubocop: disable CodeReuse/ActiveRecord
+ layout 'empty_mailer'
+
+ helper EmailsHelper
+
def notify(failed_count)
@message =
if failed_count == 1
diff --git a/app/models/active_session.rb b/app/models/active_session.rb
index 0d9c6a4a1f0..1e01f1d17e6 100644
--- a/app/models/active_session.rb
+++ b/app/models/active_session.rb
@@ -5,7 +5,8 @@ class ActiveSession
attr_accessor :created_at, :updated_at,
:session_id, :ip_address,
- :browser, :os, :device_name, :device_type
+ :browser, :os, :device_name, :device_type,
+ :is_impersonated
def current?(session)
return false if session_id.nil? || session.id.nil?
@@ -31,7 +32,8 @@ class ActiveSession
device_type: client.device_type,
created_at: user.current_sign_in_at || timestamp,
updated_at: timestamp,
- session_id: session_id
+ session_id: session_id,
+ is_impersonated: request.session[:impersonator_id].present?
)
redis.pipelined do
diff --git a/app/models/appearance.rb b/app/models/appearance.rb
index ff1ecfda684..bdee9b2b73c 100644
--- a/app/models/appearance.rb
+++ b/app/models/appearance.rb
@@ -8,12 +8,20 @@ class Appearance < ActiveRecord::Base
cache_markdown_field :description
cache_markdown_field :new_project_guidelines
+ cache_markdown_field :header_message, pipeline: :broadcast_message
+ cache_markdown_field :footer_message, pipeline: :broadcast_message
validates :logo, file_size: { maximum: 1.megabyte }
validates :header_logo, file_size: { maximum: 1.megabyte }
+ validates :message_background_color, allow_blank: true, color: true
+ validates :message_font_color, allow_blank: true, color: true
validate :single_appearance_row, on: :create
+ default_value_for :message_background_color, '#E75E40'
+ default_value_for :message_font_color, '#FFFFFF'
+ default_value_for :email_header_and_footer_enabled, false
+
mount_uploader :logo, AttachmentUploader
mount_uploader :header_logo, AttachmentUploader
mount_uploader :favicon, FaviconUploader
@@ -41,6 +49,14 @@ class Appearance < ActiveRecord::Base
logo_system_path(favicon, 'favicon')
end
+ def show_header?
+ header_message.present?
+ end
+
+ def show_footer?
+ footer_message.present?
+ end
+
private
def logo_system_path(logo, mount_type)
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index daadf9427ba..9cc7c0a1b97 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -7,20 +7,14 @@ class ApplicationSetting < ActiveRecord::Base
include IgnorableColumn
include ChronicDurationAttribute
- add_authentication_token_field :runners_registration_token, encrypted: true, fallback: true
+ add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
add_authentication_token_field :health_check_access_token
- DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
- | # or
- \s # any whitespace character
- | # or
- [\r\n] # any number of newline characters
- }x
-
- # Setting a key restriction to `-1` means that all keys of this type are
- # forbidden.
- FORBIDDEN_KEY_VALUE = KeyRestrictionValidator::FORBIDDEN
- SUPPORTED_KEY_TYPES = %i[rsa dsa ecdsa ed25519].freeze
+ # Include here so it can override methods from
+ # `add_authentication_token_field`
+ # We don't prepend for now because otherwise we'll need to
+ # fix a lot of tests using allow_any_instance_of
+ include ApplicationSettingImplementation
serialize :restricted_visibility_levels # rubocop:disable Cop/ActiveRecordSerialize
serialize :import_sources # rubocop:disable Cop/ActiveRecordSerialize
@@ -42,8 +36,6 @@ class ApplicationSetting < ActiveRecord::Base
cache_markdown_field :shared_runners_text, pipeline: :plain_markdown
cache_markdown_field :after_sign_up_text
- attr_accessor :domain_whitelist_raw, :domain_blacklist_raw
-
default_value_for :id, 1
chronic_duration_attr_writer :archive_builds_in_human_readable, :archive_builds_in_seconds
@@ -69,7 +61,7 @@ class ApplicationSetting < ActiveRecord::Base
url: true
validates :admin_notification_email,
- email: true,
+ devise_email: true,
allow_blank: true
validates :two_factor_grace_period,
@@ -231,266 +223,4 @@ class ApplicationSetting < ActiveRecord::Base
reset_memoized_terms
end
after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') }
-
- def self.defaults
- {
- 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'],
- disabled_oauth_sign_in_sources: [],
- domain_whitelist: Settings.gitlab['domain_whitelist'],
- dsa_key_restriction: 0,
- ecdsa_key_restriction: 0,
- ed25519_key_restriction: 0,
- first_day_of_week: 0,
- gitaly_timeout_default: 55,
- gitaly_timeout_fast: 10,
- gitaly_timeout_medium: 30,
- gravatar_enabled: Settings.gravatar['enabled'],
- help_page_hide_commercial_content: false,
- help_page_text: nil,
- hide_third_party_offers: false,
- housekeeping_bitmaps_enabled: true,
- housekeeping_enabled: true,
- housekeeping_full_repack_period: 50,
- housekeeping_gc_period: 200,
- housekeeping_incremental_repack_period: 10,
- import_sources: Settings.gitlab['import_sources'],
- max_artifacts_size: Settings.artifacts['max_size'],
- max_attachment_size: Settings.gitlab['max_attachment_size'],
- 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,
- repository_storages: ['default'],
- require_two_factor_authentication: false,
- restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
- session_expire_delay: Settings.gitlab['session_expire_delay'],
- send_user_confirmation_email: false,
- shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
- shared_runners_text: nil,
- sign_in_text: nil,
- signup_enabled: Settings.gitlab['signup_enabled'],
- terminal_max_session_time: 0,
- throttle_authenticated_api_enabled: false,
- 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,
- 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'],
- instance_statistics_visibility_private: false,
- user_default_external: false,
- user_default_internal_regex: nil,
- user_show_add_ssh_key_message: true,
- usage_stats_set_by_user_id: nil,
- diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
- commit_email_hostname: default_commit_email_hostname,
- protected_ci_variables: false,
- local_markdown_version: 0
- }
- end
-
- def self.default_commit_email_hostname
- "users.noreply.#{Gitlab.config.gitlab.host}"
- end
-
- def self.create_from_defaults
- build_from_defaults.tap(&:save)
- end
-
- def self.human_attribute_name(attr, _options = {})
- if attr == :default_artifacts_expire_in
- 'Default artifacts expiration'
- else
- super
- end
- end
-
- def home_page_url_column_exists?
- ::Gitlab::Database.cached_column_exists?(:application_settings, :home_page_url)
- end
-
- def help_page_support_url_column_exists?
- ::Gitlab::Database.cached_column_exists?(:application_settings, :help_page_support_url)
- end
-
- def disabled_oauth_sign_in_sources=(sources)
- sources = (sources || []).map(&:to_s) & Devise.omniauth_providers.map(&:to_s)
- super(sources)
- end
-
- def domain_whitelist_raw
- self.domain_whitelist&.join("\n")
- end
-
- def domain_blacklist_raw
- self.domain_blacklist&.join("\n")
- end
-
- def domain_whitelist_raw=(values)
- self.domain_whitelist = []
- self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR)
- self.domain_whitelist.reject! { |d| d.empty? }
- self.domain_whitelist
- end
-
- def domain_blacklist_raw=(values)
- self.domain_blacklist = []
- self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR)
- self.domain_blacklist.reject! { |d| d.empty? }
- self.domain_blacklist
- end
-
- def domain_blacklist_file=(file)
- self.domain_blacklist_raw = file.read
- end
-
- def repository_storages
- Array(read_attribute(:repository_storages))
- end
-
- def commit_email_hostname
- super.presence || self.class.default_commit_email_hostname
- end
-
- def default_project_visibility=(level)
- super(Gitlab::VisibilityLevel.level_value(level))
- end
-
- def default_snippet_visibility=(level)
- super(Gitlab::VisibilityLevel.level_value(level))
- end
-
- def default_group_visibility=(level)
- super(Gitlab::VisibilityLevel.level_value(level))
- end
-
- def restricted_visibility_levels=(levels)
- super(levels&.map { |level| Gitlab::VisibilityLevel.level_value(level) })
- end
-
- def strip_sentry_values
- sentry_dsn.strip! if sentry_dsn.present?
- clientside_sentry_dsn.strip! if clientside_sentry_dsn.present?
- end
-
- def performance_bar_allowed_group
- Group.find_by_id(performance_bar_allowed_group_id)
- end
-
- # Return true if the Performance Bar is enabled for a given group
- def performance_bar_enabled
- performance_bar_allowed_group_id.present?
- end
-
- # Choose one of the available repository storage options. Currently all have
- # equal weighting.
- def pick_repository_storage
- repository_storages.sample
- end
-
- def runners_registration_token
- ensure_runners_registration_token!
- end
-
- def health_check_access_token
- ensure_health_check_access_token!
- end
-
- def usage_ping_can_be_configured?
- Settings.gitlab.usage_ping_enabled
- end
-
- def usage_ping_enabled
- usage_ping_can_be_configured? && super
- end
-
- def allowed_key_types
- SUPPORTED_KEY_TYPES.select do |type|
- key_restriction_for(type) != FORBIDDEN_KEY_VALUE
- end
- end
-
- def key_restriction_for(type)
- attr_name = "#{type}_key_restriction"
-
- has_attribute?(attr_name) ? public_send(attr_name) : FORBIDDEN_KEY_VALUE # rubocop:disable GitlabSecurity/PublicSend
- end
-
- def allow_signup?
- signup_enabled? && password_authentication_enabled_for_web?
- end
-
- def password_authentication_enabled?
- password_authentication_enabled_for_web? || password_authentication_enabled_for_git?
- end
-
- def user_default_internal_regex_enabled?
- user_default_external? && user_default_internal_regex.present?
- end
-
- def user_default_internal_regex_instance
- Regexp.new(user_default_internal_regex, Regexp::IGNORECASE)
- end
-
- delegate :terms, to: :latest_terms, allow_nil: true
- def latest_terms
- @latest_terms ||= Term.latest
- end
-
- def reset_memoized_terms
- @latest_terms = nil
- latest_terms
- end
-
- def archive_builds_older_than
- archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds
- end
-
- private
-
- def ensure_uuid!
- return if uuid?
-
- self.uuid = SecureRandom.uuid
- end
-
- def check_repository_storages
- invalid = repository_storages - Gitlab.config.repositories.storages.keys
- errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless
- invalid.empty?
- end
-
- def terms_exist
- return unless enforce_terms?
-
- errors.add(:terms, "You need to set terms to be enforced") unless terms.present?
- end
-
- def expire_performance_bar_allowed_user_ids_cache
- Gitlab::PerformanceBar.expire_allowed_user_ids_cache
- end
end
diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb
new file mode 100644
index 00000000000..265aa1d4965
--- /dev/null
+++ b/app/models/application_setting_implementation.rb
@@ -0,0 +1,281 @@
+# frozen_string_literal: true
+
+module ApplicationSettingImplementation
+ extend ActiveSupport::Concern
+
+ DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace
+ | # or
+ \s # any whitespace character
+ | # or
+ [\r\n] # any number of newline characters
+ }x
+
+ # Setting a key restriction to `-1` means that all keys of this type are
+ # forbidden.
+ FORBIDDEN_KEY_VALUE = KeyRestrictionValidator::FORBIDDEN
+ SUPPORTED_KEY_TYPES = %i[rsa dsa ecdsa ed25519].freeze
+
+ class_methods do
+ def defaults
+ {
+ 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'],
+ disabled_oauth_sign_in_sources: [],
+ domain_whitelist: Settings.gitlab['domain_whitelist'],
+ dsa_key_restriction: 0,
+ ecdsa_key_restriction: 0,
+ ed25519_key_restriction: 0,
+ first_day_of_week: 0,
+ gitaly_timeout_default: 55,
+ gitaly_timeout_fast: 10,
+ gitaly_timeout_medium: 30,
+ gravatar_enabled: Settings.gravatar['enabled'],
+ help_page_hide_commercial_content: false,
+ help_page_text: nil,
+ hide_third_party_offers: false,
+ housekeeping_bitmaps_enabled: true,
+ housekeeping_enabled: true,
+ housekeeping_full_repack_period: 50,
+ housekeeping_gc_period: 200,
+ housekeeping_incremental_repack_period: 10,
+ import_sources: Settings.gitlab['import_sources'],
+ max_artifacts_size: Settings.artifacts['max_size'],
+ max_attachment_size: Settings.gitlab['max_attachment_size'],
+ 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,
+ repository_storages: ['default'],
+ require_two_factor_authentication: false,
+ restricted_visibility_levels: Settings.gitlab['restricted_visibility_levels'],
+ session_expire_delay: Settings.gitlab['session_expire_delay'],
+ send_user_confirmation_email: false,
+ shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
+ shared_runners_text: nil,
+ sign_in_text: nil,
+ signup_enabled: Settings.gitlab['signup_enabled'],
+ terminal_max_session_time: 0,
+ throttle_authenticated_api_enabled: false,
+ 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,
+ 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'],
+ instance_statistics_visibility_private: false,
+ user_default_external: false,
+ user_default_internal_regex: nil,
+ user_show_add_ssh_key_message: true,
+ usage_stats_set_by_user_id: nil,
+ diff_max_patch_bytes: Gitlab::Git::Diff::DEFAULT_MAX_PATCH_BYTES,
+ commit_email_hostname: default_commit_email_hostname,
+ protected_ci_variables: false,
+ local_markdown_version: 0
+ }
+ end
+
+ def default_commit_email_hostname
+ "users.noreply.#{Gitlab.config.gitlab.host}"
+ end
+
+ def create_from_defaults
+ build_from_defaults.tap(&:save)
+ end
+
+ def human_attribute_name(attr, _options = {})
+ if attr == :default_artifacts_expire_in
+ 'Default artifacts expiration'
+ else
+ super
+ end
+ end
+ end
+
+ def home_page_url_column_exists?
+ ::Gitlab::Database.cached_column_exists?(:application_settings, :home_page_url)
+ end
+
+ def help_page_support_url_column_exists?
+ ::Gitlab::Database.cached_column_exists?(:application_settings, :help_page_support_url)
+ end
+
+ def disabled_oauth_sign_in_sources=(sources)
+ sources = (sources || []).map(&:to_s) & Devise.omniauth_providers.map(&:to_s)
+ super(sources)
+ end
+
+ def domain_whitelist_raw
+ self.domain_whitelist&.join("\n")
+ end
+
+ def domain_blacklist_raw
+ self.domain_blacklist&.join("\n")
+ end
+
+ def domain_whitelist_raw=(values)
+ self.domain_whitelist = []
+ self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR)
+ self.domain_whitelist.reject! { |d| d.empty? }
+ self.domain_whitelist
+ end
+
+ def domain_blacklist_raw=(values)
+ self.domain_blacklist = []
+ self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR)
+ self.domain_blacklist.reject! { |d| d.empty? }
+ self.domain_blacklist
+ end
+
+ def domain_blacklist_file=(file)
+ self.domain_blacklist_raw = file.read
+ end
+
+ def repository_storages
+ Array(read_attribute(:repository_storages))
+ end
+
+ def commit_email_hostname
+ super.presence || self.class.default_commit_email_hostname
+ end
+
+ def default_project_visibility=(level)
+ super(Gitlab::VisibilityLevel.level_value(level))
+ end
+
+ def default_snippet_visibility=(level)
+ super(Gitlab::VisibilityLevel.level_value(level))
+ end
+
+ def default_group_visibility=(level)
+ super(Gitlab::VisibilityLevel.level_value(level))
+ end
+
+ def restricted_visibility_levels=(levels)
+ super(levels&.map { |level| Gitlab::VisibilityLevel.level_value(level) })
+ end
+
+ def strip_sentry_values
+ sentry_dsn.strip! if sentry_dsn.present?
+ clientside_sentry_dsn.strip! if clientside_sentry_dsn.present?
+ end
+
+ def performance_bar_allowed_group
+ Group.find_by_id(performance_bar_allowed_group_id)
+ end
+
+ # Return true if the Performance Bar is enabled for a given group
+ def performance_bar_enabled
+ performance_bar_allowed_group_id.present?
+ end
+
+ # Choose one of the available repository storage options. Currently all have
+ # equal weighting.
+ def pick_repository_storage
+ repository_storages.sample
+ end
+
+ def runners_registration_token
+ ensure_runners_registration_token!
+ end
+
+ def health_check_access_token
+ ensure_health_check_access_token!
+ end
+
+ def usage_ping_can_be_configured?
+ Settings.gitlab.usage_ping_enabled
+ end
+
+ def usage_ping_enabled
+ usage_ping_can_be_configured? && super
+ end
+
+ def allowed_key_types
+ SUPPORTED_KEY_TYPES.select do |type|
+ key_restriction_for(type) != FORBIDDEN_KEY_VALUE
+ end
+ end
+
+ def key_restriction_for(type)
+ attr_name = "#{type}_key_restriction"
+
+ has_attribute?(attr_name) ? public_send(attr_name) : FORBIDDEN_KEY_VALUE # rubocop:disable GitlabSecurity/PublicSend
+ end
+
+ def allow_signup?
+ signup_enabled? && password_authentication_enabled_for_web?
+ end
+
+ def password_authentication_enabled?
+ password_authentication_enabled_for_web? || password_authentication_enabled_for_git?
+ end
+
+ def user_default_internal_regex_enabled?
+ user_default_external? && user_default_internal_regex.present?
+ end
+
+ def user_default_internal_regex_instance
+ Regexp.new(user_default_internal_regex, Regexp::IGNORECASE)
+ end
+
+ delegate :terms, to: :latest_terms, allow_nil: true
+ def latest_terms
+ @latest_terms ||= ApplicationSetting::Term.latest
+ end
+
+ def reset_memoized_terms
+ @latest_terms = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ latest_terms
+ end
+
+ def archive_builds_older_than
+ archive_builds_in_seconds.seconds.ago if archive_builds_in_seconds
+ end
+
+ private
+
+ def ensure_uuid!
+ return if uuid?
+
+ self.uuid = SecureRandom.uuid
+ end
+
+ def check_repository_storages
+ invalid = repository_storages - Gitlab.config.repositories.storages.keys
+ errors.add(:repository_storages, "can't include: #{invalid.join(", ")}") unless
+ invalid.empty?
+ end
+
+ def terms_exist
+ return unless enforce_terms?
+
+ errors.add(:terms, "You need to set terms to be enforced") unless terms.present?
+ end
+
+ def expire_performance_bar_allowed_user_ids_cache
+ Gitlab::PerformanceBar.expire_allowed_user_ids_cache
+ end
+end
diff --git a/app/models/board_group_recent_visit.rb b/app/models/board_group_recent_visit.rb
index 92abbb67222..f5b75270595 100644
--- a/app/models/board_group_recent_visit.rb
+++ b/app/models/board_group_recent_visit.rb
@@ -10,7 +10,7 @@ class BoardGroupRecentVisit < ActiveRecord::Base
validates :group, presence: true
validates :board, presence: true
- scope :by_user_group, -> (user, group) { where(user: user, group: group).order(:updated_at) }
+ scope :by_user_group, -> (user, group) { where(user: user, group: group) }
def self.visited!(user, board)
visit = find_or_create_by(user: user, group: board.group, board: board)
@@ -19,7 +19,10 @@ class BoardGroupRecentVisit < ActiveRecord::Base
retry
end
- def self.latest(user, group)
- by_user_group(user, group).last
+ def self.latest(user, group, count: nil)
+ visits = by_user_group(user, group).order(updated_at: :desc)
+ visits = visits.preload(:board) if count && count > 1
+
+ visits.first(count)
end
end
diff --git a/app/models/board_project_recent_visit.rb b/app/models/board_project_recent_visit.rb
index 7cffff906d8..2a1b14b3ae0 100644
--- a/app/models/board_project_recent_visit.rb
+++ b/app/models/board_project_recent_visit.rb
@@ -10,7 +10,7 @@ class BoardProjectRecentVisit < ActiveRecord::Base
validates :project, presence: true
validates :board, presence: true
- scope :by_user_project, -> (user, project) { where(user: user, project: project).order(:updated_at) }
+ scope :by_user_project, -> (user, project) { where(user: user, project: project) }
def self.visited!(user, board)
visit = find_or_create_by(user: user, project: board.project, board: board)
@@ -19,7 +19,10 @@ class BoardProjectRecentVisit < ActiveRecord::Base
retry
end
- def self.latest(user, project)
- by_user_project(user, project).last
+ def self.latest(user, project, count: nil)
+ visits = by_user_project(user, project).order(updated_at: :desc)
+ visits = visits.preload(:board) if count && count > 1
+
+ visits.first(count)
end
end
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index 2d237383e60..1c95abdd9ee 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -4,7 +4,7 @@ class BroadcastMessage < ActiveRecord::Base
include CacheMarkdownField
include Sortable
- cache_markdown_field :message, pipeline: :broadcast_message
+ cache_markdown_field :message, pipeline: :broadcast_message, whitelisted: true
validates :message, presence: true
validates :starts_at, presence: true
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index 5450d40ea95..0d8d7d95791 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -3,14 +3,18 @@
module Ci
class Bridge < CommitStatus
include Ci::Processable
+ include Ci::Contextable
include Importable
include AfterCommitQueue
+ include HasRef
include Gitlab::Utils::StrongMemoize
belongs_to :project
belongs_to :trigger_request
validates :ref, presence: true
+ delegate :merge_request_event?, to: :pipeline
+
def self.retry(bridge, current_user)
raise NotImplementedError
end
@@ -37,11 +41,11 @@ module Ci
false
end
- def expanded_environment_name
+ def runnable?
+ false
end
- def predefined_variables
- raise NotImplementedError
+ def expanded_environment_name
end
def execute_hooks
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 6b2b7e77180..59f47effff7 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -5,6 +5,7 @@ module Ci
prepend ArtifactMigratable
include Ci::Processable
include Ci::Metadatable
+ include Ci::Contextable
include TokenAuthenticatable
include AfterCommitQueue
include ObjectStorage::BackgroundMove
@@ -46,6 +47,7 @@ module Ci
delegate :terminal_specification, to: :runner_session, allow_nil: true
delegate :gitlab_deploy_token, to: :project
delegate :trigger_short_token, to: :trigger_request, allow_nil: true
+ delegate :merge_request_event?, to: :pipeline
##
# Since Gitlab 11.5, deployments records started being created right after
@@ -136,7 +138,7 @@ module Ci
acts_as_taggable
- add_authentication_token_field :token, encrypted: true, fallback: true
+ add_authentication_token_field :token, encrypted: :optional
before_save :update_artifacts_size, if: :artifacts_file_changed?
before_save :ensure_token
@@ -170,6 +172,10 @@ module Ci
end
state_machine :status do
+ event :enqueue do
+ transition [:created, :skipped, :manual, :scheduled] => :preparing, if: :any_unmet_prerequisites?
+ end
+
event :actionize do
transition created: :manual
end
@@ -183,8 +189,12 @@ module Ci
end
event :enqueue_scheduled do
+ transition scheduled: :preparing, if: ->(build) do
+ build.scheduled_at&.past? && build.any_unmet_prerequisites?
+ end
+
transition scheduled: :pending, if: ->(build) do
- build.scheduled_at && build.scheduled_at < Time.now
+ build.scheduled_at&.past? && !build.any_unmet_prerequisites?
end
end
@@ -202,6 +212,12 @@ module Ci
end
end
+ after_transition any => [:preparing] do |build|
+ build.run_after_commit do
+ Ci::BuildPrepareWorker.perform_async(id)
+ end
+ end
+
after_transition any => [:pending] do |build|
build.run_after_commit do
BuildQueueWorker.perform_async(id)
@@ -288,6 +304,10 @@ module Ci
self.name == 'pages'
end
+ def runnable?
+ true
+ end
+
def archived?
return true if degenerated?
@@ -349,6 +369,16 @@ module Ci
!retried?
end
+ def any_unmet_prerequisites?
+ return false unless Feature.enabled?(:ci_preparing_state, default_enabled: true)
+
+ prerequisites.present?
+ end
+
+ def prerequisites
+ Gitlab::Ci::Build::Prerequisite::Factory.new(self).unmet
+ end
+
def expanded_environment_name
return unless has_environment?
@@ -397,63 +427,59 @@ module Ci
options&.dig(:environment, :on_stop)
end
- # A slugified version of the build ref, suitable for inclusion in URLs and
- # domain names. Rules:
+ ##
+ # All variables, including persisted environment variables.
#
- # * Lowercased
- # * Anything not matching [a-z0-9-] is replaced with a -
- # * Maximum length is 63 bytes
- # * First/Last Character is not a hyphen
- def ref_slug
- Gitlab::Utils.slugify(ref.to_s)
+ def variables
+ strong_memoize(:variables) do
+ Gitlab::Ci::Variables::Collection.new
+ .concat(persisted_variables)
+ .concat(scoped_variables)
+ .concat(persisted_environment_variables)
+ .to_runner_variables
+ end
end
- ##
- # Variables in the environment name scope.
- #
- def scoped_variables(environment: expanded_environment_name)
+ CI_REGISTRY_USER = 'gitlab-ci-token'.freeze
+
+ def persisted_variables
Gitlab::Ci::Variables::Collection.new.tap do |variables|
- variables.concat(predefined_variables)
- variables.concat(project.predefined_variables)
- variables.concat(pipeline.predefined_variables)
- variables.concat(runner.predefined_variables) if runner
- variables.concat(project.deployment_variables(environment: environment)) if environment
- variables.concat(yaml_variables)
- variables.concat(user_variables)
- variables.concat(secret_group_variables)
- variables.concat(secret_project_variables(environment: environment))
- variables.concat(trigger_request.user_variables) if trigger_request
- variables.concat(pipeline.variables)
- variables.concat(pipeline.pipeline_schedule.job_variables) if pipeline.pipeline_schedule
+ break variables unless persisted?
+
+ variables
+ .concat(pipeline.persisted_variables)
+ .append(key: 'CI_JOB_ID', value: id.to_s)
+ .append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self))
+ .append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false, masked: true)
+ .append(key: 'CI_BUILD_ID', value: id.to_s)
+ .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false, masked: true)
+ .append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER)
+ .append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false, masked: true)
+ .append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false)
+ .concat(deploy_token_variables)
end
end
- ##
- # Variables that do not depend on the environment name.
- #
- def simple_variables
- strong_memoize(:simple_variables) do
- scoped_variables(environment: nil).to_runner_variables
+ def persisted_environment_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ break variables unless persisted? && persisted_environment.present?
+
+ variables.concat(persisted_environment.predefined_variables)
+
+ # Here we're passing unexpanded environment_url for runner to expand,
+ # and we need to make sure that CI_ENVIRONMENT_NAME and
+ # CI_ENVIRONMENT_SLUG so on are available for the URL be expanded.
+ variables.append(key: 'CI_ENVIRONMENT_URL', value: environment_url) if environment_url
end
end
- ##
- # All variables, including persisted environment variables.
- #
- def variables
- Gitlab::Ci::Variables::Collection.new
- .concat(persisted_variables)
- .concat(scoped_variables)
- .concat(persisted_environment_variables)
- .to_runner_variables
- end
+ def deploy_token_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ break variables unless gitlab_deploy_token
- ##
- # Regular Ruby hash of scoped variables, without duplicates that are
- # possible to be present in an array of hashes returned from `variables`.
- #
- def scoped_variables_hash
- scoped_variables.to_hash
+ variables.append(key: 'CI_DEPLOY_USER', value: gitlab_deploy_token.username)
+ variables.append(key: 'CI_DEPLOY_PASSWORD', value: gitlab_deploy_token.token, public: false, masked: true)
+ end
end
def features
@@ -631,27 +657,6 @@ module Ci
super || project.try(:build_coverage_regex)
end
- def user_variables
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- break variables if user.blank?
-
- variables.append(key: 'GITLAB_USER_ID', value: user.id.to_s)
- variables.append(key: 'GITLAB_USER_EMAIL', value: user.email)
- variables.append(key: 'GITLAB_USER_LOGIN', value: user.username)
- variables.append(key: 'GITLAB_USER_NAME', value: user.name)
- end
- end
-
- def secret_group_variables
- return [] unless project.group
-
- project.group.ci_variables_for(git_ref, project)
- end
-
- def secret_project_variables(environment: persisted_environment)
- project.ci_variables_for(ref: git_ref, environment: environment)
- end
-
def steps
[Gitlab::Ci::Build::Step.from_commands(self),
Gitlab::Ci::Build::Step.from_after_script(self)].compact
@@ -754,7 +759,7 @@ module Ci
# Virtual deployment status depending on the environment status.
def deployment_status
- return nil unless starts_environment?
+ return unless starts_environment?
if success?
return successful_deployment_status
@@ -811,89 +816,6 @@ module Ci
@unscoped_project ||= Project.unscoped.find_by(id: project_id)
end
- CI_REGISTRY_USER = 'gitlab-ci-token'.freeze
-
- def persisted_variables
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- break variables unless persisted?
-
- variables
- .concat(pipeline.persisted_variables)
- .append(key: 'CI_JOB_ID', value: id.to_s)
- .append(key: 'CI_JOB_URL', value: Gitlab::Routing.url_helpers.project_job_url(project, self))
- .append(key: 'CI_JOB_TOKEN', value: token.to_s, public: false)
- .append(key: 'CI_BUILD_ID', value: id.to_s)
- .append(key: 'CI_BUILD_TOKEN', value: token.to_s, public: false)
- .append(key: 'CI_REGISTRY_USER', value: CI_REGISTRY_USER)
- .append(key: 'CI_REGISTRY_PASSWORD', value: token.to_s, public: false)
- .append(key: 'CI_REPOSITORY_URL', value: repo_url.to_s, public: false)
- .concat(deploy_token_variables)
- end
- end
-
- def predefined_variables # rubocop:disable Metrics/AbcSize
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- variables.append(key: 'CI', value: 'true')
- variables.append(key: 'GITLAB_CI', value: 'true')
- variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(','))
- variables.append(key: 'CI_SERVER_NAME', value: 'GitLab')
- variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
- variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s)
- variables.append(key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s)
- variables.append(key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s)
- variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision)
- 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_SHORT_SHA', value: short_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?
- variables.append(key: "CI_PIPELINE_TRIGGERED", value: 'true') if trigger_request
- variables.append(key: "CI_JOB_MANUAL", value: 'true') if action?
- variables.append(key: "CI_NODE_INDEX", value: self.options[:instance].to_s) if self.options&.include?(:instance)
- variables.append(key: "CI_NODE_TOTAL", value: (self.options&.dig(:parallel) || 1).to_s)
- variables.concat(legacy_variables)
- end
- end
-
- def legacy_variables
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- variables.append(key: 'CI_BUILD_REF', value: sha)
- variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha)
- variables.append(key: 'CI_BUILD_REF_NAME', value: ref)
- variables.append(key: 'CI_BUILD_REF_SLUG', value: ref_slug)
- variables.append(key: 'CI_BUILD_NAME', value: name)
- variables.append(key: 'CI_BUILD_STAGE', value: stage)
- variables.append(key: "CI_BUILD_TAG", value: ref) if tag?
- variables.append(key: "CI_BUILD_TRIGGERED", value: 'true') if trigger_request
- variables.append(key: "CI_BUILD_MANUAL", value: 'true') if action?
- end
- end
-
- def persisted_environment_variables
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- break variables unless persisted? && persisted_environment.present?
-
- variables.concat(persisted_environment.predefined_variables)
-
- # Here we're passing unexpanded environment_url for runner to expand,
- # and we need to make sure that CI_ENVIRONMENT_NAME and
- # CI_ENVIRONMENT_SLUG so on are available for the URL be expanded.
- variables.append(key: 'CI_ENVIRONMENT_URL', value: environment_url) if environment_url
- end
- end
-
- def deploy_token_variables
- Gitlab::Ci::Variables::Collection.new.tap do |variables|
- break variables unless gitlab_deploy_token
-
- variables.append(key: 'CI_DEPLOY_USER', value: gitlab_deploy_token.username)
- variables.append(key: 'CI_DEPLOY_PASSWORD', value: gitlab_deploy_token.token, public: false)
- end
- end
-
def environment_url
options&.dig(:environment, :url) || persisted_environment&.external_url
end
diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb
index da08214963f..75017f224a0 100644
--- a/app/models/ci/build_trace_chunk.rb
+++ b/app/models/ci/build_trace_chunk.rb
@@ -18,7 +18,7 @@ module Ci
FailedToPersistDataError = Class.new(StandardError)
# 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.
+ # The bottom item takes the highest precedence, and the top item takes the lowest precedence.
enum data_store: {
redis: 1,
database: 2,
@@ -115,7 +115,7 @@ module Ci
current_data = get_data
unless current_data&.bytesize.to_i == CHUNK_SIZE
- raise FailedToPersistDataError, 'Data is not fullfilled in a bucket'
+ raise FailedToPersistDataError, 'Data is not fulfilled in a bucket'
end
old_store_class = self.class.get_store_class(data_store)
diff --git a/app/models/ci/group_variable.rb b/app/models/ci/group_variable.rb
index 492d1d0329e..323ff560564 100644
--- a/app/models/ci/group_variable.rb
+++ b/app/models/ci/group_variable.rb
@@ -5,6 +5,7 @@ module Ci
extend Gitlab::Ci::Model
include HasVariable
include Presentable
+ include Maskable
belongs_to :group, class_name: "::Group"
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index f0ae516a2f8..826b3f82bbf 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -12,6 +12,11 @@ module Ci
include AtomicInternalId
include EnumWithNil
include HasRef
+ include ShaAttribute
+ include FromUnion
+
+ sha_attribute :source_sha
+ sha_attribute :target_sha
belongs_to :project, inverse_of: :all_pipelines
belongs_to :user
@@ -35,7 +40,7 @@ module Ci
# Merge requests for which the current pipeline is running against
# the merge request's latest commit.
- has_many :merge_requests, foreign_key: "head_pipeline_id"
+ has_many :merge_requests_as_head_pipeline, foreign_key: "head_pipeline_id", class_name: 'MergeRequest'
has_many :pending_builds, -> { pending }, foreign_key: :commit_id, class_name: 'Ci::Build'
has_many :retryable_builds, -> { latest.failed_or_canceled.includes(:project) }, foreign_key: :commit_id, class_name: 'Ci::Build'
@@ -47,6 +52,8 @@ module Ci
has_many :auto_canceled_pipelines, class_name: 'Ci::Pipeline', foreign_key: 'auto_canceled_by_id'
has_many :auto_canceled_jobs, class_name: 'CommitStatus', foreign_key: 'auto_canceled_by_id'
+ has_one :chat_data, class_name: 'Ci::PipelineChatData'
+
accepts_nested_attributes_for :variables, reject_if: :persisted?
delegate :id, to: :project, prefix: true
@@ -54,9 +61,9 @@ module Ci
validates :sha, presence: { unless: :importing? }
validates :ref, presence: { unless: :importing? }
- validates :merge_request, presence: { if: :merge_request? }
- validates :merge_request, absence: { unless: :merge_request? }
- validates :tag, inclusion: { in: [false], if: :merge_request? }
+ validates :merge_request, presence: { if: :merge_request_event? }
+ validates :merge_request, absence: { unless: :merge_request_event? }
+ validates :tag, inclusion: { in: [false], if: :merge_request_event? }
validates :status, presence: { unless: :importing? }
validate :valid_commit_sha, unless: :importing?
validates :source, exclusion: { in: %w(unknown), unless: :importing? }, on: :create
@@ -75,10 +82,14 @@ module Ci
state_machine :status, initial: :created do
event :enqueue do
- transition [:created, :skipped, :scheduled] => :pending
+ transition [:created, :preparing, :skipped, :scheduled] => :pending
transition [:success, :failed, :canceled] => :running
end
+ event :prepare do
+ transition any - [:preparing] => :preparing
+ end
+
event :run do
transition any - [:running] => :running
end
@@ -111,7 +122,7 @@ module Ci
# Do not add any operations to this state_machine
# Create a separate worker for each new operation
- before_transition [:created, :pending] => :running do |pipeline|
+ before_transition [:created, :preparing, :pending] => :running do |pipeline|
pipeline.started_at = Time.now
end
@@ -134,7 +145,7 @@ module Ci
end
end
- after_transition [:created, :pending] => :running do |pipeline|
+ after_transition [:created, :preparing, :pending] => :running do |pipeline|
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
@@ -142,7 +153,7 @@ module Ci
pipeline.run_after_commit { PipelineMetricsWorker.perform_async(pipeline.id) }
end
- after_transition [:created, :pending, :running] => :success do |pipeline|
+ after_transition [:created, :preparing, :pending, :running] => :success do |pipeline|
pipeline.run_after_commit { PipelineSuccessWorker.perform_async(pipeline.id) }
end
@@ -173,20 +184,34 @@ module Ci
scope :sort_by_merge_request_pipelines, -> do
sql = 'CASE ci_pipelines.source WHEN (?) THEN 0 ELSE 1 END, ci_pipelines.id DESC'
- query = ActiveRecord::Base.send(:sanitize_sql_array, [sql, sources[:merge_request]]) # rubocop:disable GitlabSecurity/PublicSend
+ query = ActiveRecord::Base.send(:sanitize_sql_array, [sql, sources[:merge_request_event]]) # rubocop:disable GitlabSecurity/PublicSend
order(query)
end
scope :for_user, -> (user) { where(user: user) }
+ scope :for_sha, -> (sha) { where(sha: sha) }
+ scope :for_source_sha, -> (source_sha) { where(source_sha: source_sha) }
+ scope :for_sha_or_source_sha, -> (sha) { for_sha(sha).or(for_source_sha(sha)) }
+
+ scope :triggered_by_merge_request, -> (merge_request) do
+ where(source: :merge_request_event, merge_request: merge_request)
+ end
+
+ scope :detached_merge_request_pipelines, -> (merge_request, sha) do
+ triggered_by_merge_request(merge_request).for_sha(sha)
+ end
+
+ scope :merge_request_pipelines, -> (merge_request, source_sha) do
+ triggered_by_merge_request(merge_request).for_source_sha(source_sha)
+ end
+
+ scope :mergeable_merge_request_pipelines, -> (merge_request) do
+ triggered_by_merge_request(merge_request).where(target_sha: merge_request.target_branch_sha)
+ end
- scope :for_merge_request, -> (merge_request, ref, sha) do
- ##
- # We have to filter out unrelated MR pipelines.
- # When merge request is empty, it selects general pipelines, such as push sourced pipelines.
- # When merge request is matched, it selects MR pipelines.
- where(merge_request: [nil, merge_request], ref: ref, sha: sha)
- .sort_by_merge_request_pipelines
+ scope :triggered_for_branch, -> (ref) do
+ where(source: branch_pipeline_sources).where(ref: ref, tag: false)
end
# Returns the pipelines in descending order (= newest first), optionally
@@ -276,8 +301,8 @@ module Ci
sources.reject { |source| source == "external" }.values
end
- def self.latest_for_merge_request(merge_request, ref, sha)
- for_merge_request(merge_request, ref, sha).first
+ def self.branch_pipeline_sources
+ @branch_pipeline_sources ||= sources.reject { |source| source == 'merge_request_event' }.values
end
def self.ci_sources_values
@@ -395,10 +420,6 @@ module Ci
@commit ||= Commit.lazy(project, sha)
end
- def branch?
- super && !merge_request?
- end
-
def stuck?
pending_builds.any?(&:stuck?)
end
@@ -580,6 +601,7 @@ module Ci
retry_optimistic_lock(self) do
case latest_builds_status.to_s
when 'created' then nil
+ when 'preparing' then prepare
when 'pending' then enqueue
when 'running' then run
when 'success' then succeed
@@ -621,7 +643,9 @@ module Ci
variables.append(key: 'CI_COMMIT_TITLE', value: git_commit_full_title.to_s)
variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s)
- if merge_request? && merge_request
+ if merge_request_event? && merge_request
+ variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s)
+ variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA', value: target_sha.to_s)
variables.concat(merge_request.predefined_variables)
end
end
@@ -649,10 +673,10 @@ module Ci
# All the merge requests for which the current pipeline runs/ran against
def all_merge_requests
@all_merge_requests ||=
- if merge_request?
- project.merge_requests.where(id: merge_request_id)
+ if merge_request_event?
+ MergeRequest.where(id: merge_request_id)
else
- project.merge_requests.where(source_branch: ref)
+ MergeRequest.where(source_project_id: project_id, source_branch: ref)
end
end
@@ -694,7 +718,7 @@ module Ci
# * nil: Modified path can not be evaluated
def modified_paths
strong_memoize(:modified_paths) do
- if merge_request?
+ if merge_request_event?
merge_request.modified_paths
elsif branch_updated?
push_details.modified_paths
@@ -706,6 +730,26 @@ module Ci
ref == project.default_branch
end
+ def triggered_by_merge_request?
+ merge_request_event? && merge_request_id.present?
+ end
+
+ def detached_merge_request_pipeline?
+ triggered_by_merge_request? && target_sha.nil?
+ end
+
+ def merge_request_pipeline?
+ triggered_by_merge_request? && target_sha.present?
+ end
+
+ def mergeable_merge_request_pipeline?
+ triggered_by_merge_request? && target_sha == merge_request.target_branch_sha
+ end
+
+ def matches_sha_or_source_sha?(sha)
+ self.sha == sha || self.source_sha == sha
+ end
+
private
def ci_yaml_from_repo
@@ -737,7 +781,7 @@ module Ci
end
def git_ref
- if merge_request?
+ if merge_request_event?
##
# In the future, we're going to change this ref to
# merge request's merged reference, such as "refs/merge-requests/:iid/merge".
diff --git a/app/models/ci/pipeline_chat_data.rb b/app/models/ci/pipeline_chat_data.rb
new file mode 100644
index 00000000000..8d37500fec5
--- /dev/null
+++ b/app/models/ci/pipeline_chat_data.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module Ci
+ class PipelineChatData < ActiveRecord::Base
+ self.table_name = 'ci_pipeline_chat_data'
+
+ belongs_to :chat_name
+
+ validates :pipeline_id, presence: true
+ validates :chat_name_id, presence: true
+ validates :response_url, presence: true
+ end
+end
diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb
index 2994aaae4aa..571c4271475 100644
--- a/app/models/ci/pipeline_enums.rb
+++ b/app/models/ci/pipeline_enums.rb
@@ -22,7 +22,8 @@ module Ci
schedule: 4,
api: 5,
external: 6,
- merge_request: 10
+ chat: 8,
+ merge_request_event: 10
}
end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 5aae31de6e2..43f040a91ae 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -10,7 +10,7 @@ module Ci
include FromUnion
include TokenAuthenticatable
- add_authentication_token_field :token, encrypted: true, migrating: true
+ add_authentication_token_field :token, encrypted: -> { Feature.enabled?(:ci_runners_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
enum access_level: {
not_protected: 0,
@@ -97,6 +97,7 @@ module Ci
scope :order_contacted_at_asc, -> { order(contacted_at: :asc) }
scope :order_created_at_desc, -> { order(created_at: :desc) }
+ scope :with_tags, -> { preload(:tags) }
validate :tag_constraints
validates :access_level, presence: true
diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb
index 0389945191e..098f5189517 100644
--- a/app/models/ci/stage.rb
+++ b/app/models/ci/stage.rb
@@ -39,10 +39,14 @@ module Ci
state_machine :status, initial: :created do
event :enqueue do
- transition created: :pending
+ transition [:created, :preparing] => :pending
transition [:success, :failed, :canceled, :skipped] => :running
end
+ event :prepare do
+ transition any - [:preparing] => :preparing
+ end
+
event :run do
transition any - [:running] => :running
end
@@ -76,6 +80,7 @@ module Ci
retry_optimistic_lock(self) do
case statuses.latest.status
when 'created' then nil
+ when 'preparing' then prepare
when 'pending' then enqueue
when 'running' then run
when 'success' then succeed
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 524d79014f8..64836ea4fa4 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -5,6 +5,7 @@ module Ci
extend Gitlab::Ci::Model
include HasVariable
include Presentable
+ include Maskable
belongs_to :project
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index 7c15aaa4825..567f1a2267f 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -48,6 +48,7 @@ module Clusters
def schedule_status_update
return unless installed?
return if external_ip
+ return if external_hostname
ClusterWaitForIngressIpAddressWorker.perform_async(name, id)
end
diff --git a/app/models/clusters/applications/jupyter.rb b/app/models/clusters/applications/jupyter.rb
index 421a923d386..7efcc175f9f 100644
--- a/app/models/clusters/applications/jupyter.rb
+++ b/app/models/clusters/applications/jupyter.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Jupyter < ActiveRecord::Base
- VERSION = 'v0.6'.freeze
+ VERSION = '0.9-174bbd5'.freeze
self.table_name = 'clusters_applications_jupyter'
@@ -18,8 +18,10 @@ module Clusters
def set_initial_status
return unless not_installable?
+ return unless cluster&.application_ingress_available?
- if cluster&.application_ingress_available? && cluster.application_ingress.external_ip
+ ingress = cluster.application_ingress
+ if ingress.external_ip || ingress.external_hostname
self.status = 'installable'
end
end
@@ -75,17 +77,22 @@ module Clusters
"gitlab" => {
"clientId" => oauth_application.uid,
"clientSecret" => oauth_application.secret,
- "callbackUrl" => callback_url
+ "callbackUrl" => callback_url,
+ "gitlabProjectIdWhitelist" => [project_id]
}
},
"singleuser" => {
"extraEnv" => {
- "GITLAB_CLUSTER_ID" => cluster.id
+ "GITLAB_CLUSTER_ID" => cluster.id.to_s
}
}
}
end
+ def project_id
+ cluster&.project&.id
+ end
+
def gitlab_url
Gitlab.config.gitlab.url
end
diff --git a/app/models/clusters/applications/knative.rb b/app/models/clusters/applications/knative.rb
index 8d79b041b64..347c3c8c37f 100644
--- a/app/models/clusters/applications/knative.rb
+++ b/app/models/clusters/applications/knative.rb
@@ -66,6 +66,7 @@ module Clusters
def schedule_status_update
return unless installed?
return if external_ip
+ return if external_hostname
ClusterWaitForIngressIpAddressWorker.perform_async(name, id)
end
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 52c440ffb2f..fa7ce363531 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -6,7 +6,6 @@ module Clusters
include PrometheusAdapter
VERSION = '6.7.3'
- READY_STATUS = [:installed, :updating, :updated, :update_errored].freeze
self.table_name = 'clusters_applications_prometheus'
@@ -25,10 +24,6 @@ module Clusters
end
end
- def ready?
- READY_STATUS.include?(status_name)
- end
-
def chart
'stable/prometheus'
end
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index f17da0bb7b1..ec8f5cc40c0 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ActiveRecord::Base
- VERSION = '0.1.45'.freeze
+ VERSION = '0.3.0'.freeze
self.table_name = 'clusters_applications_runners'
@@ -13,7 +13,7 @@ module Clusters
include ::Clusters::Concerns::ApplicationData
belongs_to :runner, class_name: 'Ci::Runner', foreign_key: :runner_id
- delegate :project, to: :cluster
+ delegate :project, :group, to: :cluster
default_value_for :version, VERSION
@@ -55,12 +55,17 @@ module Clusters
end
def runner_create_params
- {
+ attributes = {
name: 'kubernetes-cluster',
- runner_type: :project_type,
- tag_list: %w(kubernetes cluster),
- projects: [project]
+ runner_type: cluster.cluster_type,
+ tag_list: %w[kubernetes cluster]
}
+
+ if cluster.group_type?
+ attributes.merge(groups: [group])
+ elsif cluster.project_type?
+ attributes.merge(projects: [project])
+ end
end
def gitlab_url
diff --git a/app/models/clusters/cluster.rb b/app/models/clusters/cluster.rb
index be3e6a05e1e..5156c7d7514 100644
--- a/app/models/clusters/cluster.rb
+++ b/app/models/clusters/cluster.rb
@@ -67,6 +67,7 @@ module Clusters
delegate :available?, to: :application_prometheus, prefix: true, allow_nil: true
delegate :available?, to: :application_knative, prefix: true, allow_nil: true
delegate :external_ip, to: :application_ingress, prefix: true, allow_nil: true
+ delegate :external_hostname, to: :application_ingress, prefix: true, allow_nil: true
alias_attribute :base_domain, :domain
diff --git a/app/models/clusters/concerns/application_core.rb b/app/models/clusters/concerns/application_core.rb
index 683b45331f6..ee964fb7c93 100644
--- a/app/models/clusters/concerns/application_core.rb
+++ b/app/models/clusters/concerns/application_core.rb
@@ -30,6 +30,12 @@ module Clusters
# Override if you need extra data synchronized
# from K8s after installation
end
+
+ def update_command
+ install_command.tap do |command|
+ command.version = version
+ end
+ end
end
end
end
diff --git a/app/models/clusters/kubernetes_namespace.rb b/app/models/clusters/kubernetes_namespace.rb
index 73da6cb37d7..7fc75e00cd0 100644
--- a/app/models/clusters/kubernetes_namespace.rb
+++ b/app/models/clusters/kubernetes_namespace.rb
@@ -37,7 +37,7 @@ module Clusters
variables
.append(key: 'KUBE_SERVICE_ACCOUNT', value: service_account_name.to_s)
.append(key: 'KUBE_NAMESPACE', value: namespace.to_s)
- .append(key: 'KUBE_TOKEN', value: service_account_token.to_s, public: false)
+ .append(key: 'KUBE_TOKEN', value: service_account_token.to_s, public: false, masked: true)
.append(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true)
end
end
diff --git a/app/models/clusters/platforms/kubernetes.rb b/app/models/clusters/platforms/kubernetes.rb
index 46d0898014e..7786b48429c 100644
--- a/app/models/clusters/platforms/kubernetes.rb
+++ b/app/models/clusters/platforms/kubernetes.rb
@@ -41,7 +41,7 @@ module Clusters
validate :no_namespace, unless: :allow_user_defined_namespace?
# We expect to be `active?` only when enabled and cluster is created (the api_url is assigned)
- validates :api_url, url: true, presence: true
+ validates :api_url, public_url: true, presence: true
validates :token, presence: true
validates :ca_cert, certificate: true, allow_blank: true, if: :ca_cert_changed?
@@ -95,7 +95,7 @@ module Clusters
# https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22433
variables
.append(key: 'KUBE_URL', value: api_url)
- .append(key: 'KUBE_TOKEN', value: token, public: false)
+ .append(key: 'KUBE_TOKEN', value: token, public: false, masked: true)
.append(key: 'KUBE_NAMESPACE', value: actual_namespace)
.append(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true)
end
@@ -110,7 +110,7 @@ module Clusters
# short time later
def terminals(environment)
with_reactive_cache do |data|
- pods = filter_by_label(data[:pods], app: environment.slug)
+ pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug)
terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }.compact
terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
end
diff --git a/app/models/commit_collection.rb b/app/models/commit_collection.rb
index a9a2e9c81eb..52524456439 100644
--- a/app/models/commit_collection.rb
+++ b/app/models/commit_collection.rb
@@ -28,10 +28,43 @@ class CommitCollection
def without_merge_commits
strong_memoize(:without_merge_commits) do
- commits.reject(&:merge_commit?)
+ # `#enrich!` the collection to ensure all commits contain
+ # the necessary parent data
+ enrich!.commits.reject(&:merge_commit?)
end
end
+ def unenriched
+ commits.reject(&:gitaly_commit?)
+ end
+
+ def fully_enriched?
+ unenriched.empty?
+ end
+
+ # Batch load any commits that are not backed by full gitaly data, and
+ # replace them in the collection.
+ def enrich!
+ # A project is needed in order to fetch data from gitaly. Projects
+ # can be absent from commits in certain rare situations (like when
+ # viewing a MR of a deleted fork). In these cases, assume that the
+ # enriched data is not needed.
+ return self if project.blank? || fully_enriched?
+
+ # Batch load full Commits from the repository
+ # and map to a Hash of id => Commit
+ replacements = Hash[unenriched.map do |c|
+ [c.id, Commit.lazy(project, c.id)]
+ end.compact]
+
+ # Replace the commits, keeping the same order
+ @commits = @commits.map do |c|
+ replacements.fetch(c.id, c)
+ end
+
+ self
+ end
+
# Sets the pipeline status for every commit.
#
# Setting this status ahead of time removes the need for running a query for
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
index 094747ee48d..920b1d092dd 100644
--- a/app/models/commit_range.rb
+++ b/app/models/commit_range.rb
@@ -134,25 +134,25 @@ class CommitRange
end
def sha_from
- return nil unless @commit_from
+ return unless @commit_from
@commit_from.id
end
def sha_to
- return nil unless @commit_to
+ return unless @commit_to
@commit_to.id
end
def sha_start
- return nil unless sha_from
+ return unless sha_from
exclude_start? ? sha_from + '^' : sha_from
end
def commit_start
- return nil unless sha_start
+ return unless sha_start
if exclude_start?
@commit_start ||= project.commit(sha_start)
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index 7f6562b63e5..5f66a661324 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -66,7 +66,10 @@ class CommitStatus < ActiveRecord::Base
end
event :enqueue do
- transition [:created, :skipped, :manual, :scheduled] => :pending
+ # A CommitStatus will never have prerequisites, but this event
+ # is shared by Ci::Build, which cannot progress unless prerequisites
+ # are satisfied.
+ transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending, unless: :any_unmet_prerequisites?
end
event :run do
@@ -74,26 +77,26 @@ class CommitStatus < ActiveRecord::Base
end
event :skip do
- transition [:created, :pending] => :skipped
+ transition [:created, :preparing, :pending] => :skipped
end
event :drop do
- transition [:created, :pending, :running, :scheduled] => :failed
+ transition [:created, :preparing, :pending, :running, :scheduled] => :failed
end
event :success do
- transition [:created, :pending, :running] => :success
+ transition [:created, :preparing, :pending, :running] => :success
end
event :cancel do
- transition [:created, :pending, :running, :manual, :scheduled] => :canceled
+ transition [:created, :preparing, :pending, :running, :manual, :scheduled] => :canceled
end
- before_transition [:created, :skipped, :manual, :scheduled] => :pending do |commit_status|
+ before_transition [:created, :preparing, :skipped, :manual, :scheduled] => :pending do |commit_status|
commit_status.queued_at = Time.now
end
- before_transition [:created, :pending] => :running do |commit_status|
+ before_transition [:created, :preparing, :pending] => :running do |commit_status|
commit_status.started_at = Time.now
end
@@ -180,6 +183,10 @@ class CommitStatus < ActiveRecord::Base
false
end
+ def any_unmet_prerequisites?
+ false
+ end
+
def auto_canceled?
canceled? && auto_canceled_by_id?
end
diff --git a/app/models/commit_status_enums.rb b/app/models/commit_status_enums.rb
index 152105d9429..45e08fa18fe 100644
--- a/app/models/commit_status_enums.rb
+++ b/app/models/commit_status_enums.rb
@@ -14,7 +14,8 @@ module CommitStatusEnums
runner_unsupported: 6,
stale_schedule: 7,
job_execution_timeout: 8,
- archived_failure: 9
+ archived_failure: 9,
+ unmet_prerequisites: 10
}
end
end
diff --git a/app/models/concerns/blob_language_from_git_attributes.rb b/app/models/concerns/blob_language_from_git_attributes.rb
index 70213d22147..56e1276a220 100644
--- a/app/models/concerns/blob_language_from_git_attributes.rb
+++ b/app/models/concerns/blob_language_from_git_attributes.rb
@@ -5,7 +5,7 @@ module BlobLanguageFromGitAttributes
extend ActiveSupport::Concern
def language_from_gitattributes
- return nil unless project
+ return unless project
repository = project.repository
repository.gitattribute(path, 'gitlab-language')
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 1a8570b80c3..28ea51d6769 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -7,6 +7,7 @@
# cache_markdown_field :foo
# cache_markdown_field :bar
# cache_markdown_field :baz, pipeline: :single_line
+# cache_markdown_field :baz, whitelisted: true
#
# Corresponding foo_html, bar_html and baz_html fields should exist.
module CacheMarkdownField
@@ -14,7 +15,7 @@ module CacheMarkdownField
# Increment this number every time the renderer changes its output
CACHE_COMMONMARK_VERSION_START = 10
- CACHE_COMMONMARK_VERSION = 14
+ CACHE_COMMONMARK_VERSION = 15
# changes to these attributes cause the cache to be invalidates
INVALIDATED_BY = %w[author project].freeze
@@ -37,7 +38,15 @@ module CacheMarkdownField
end
def html_fields
- markdown_fields.map {|field| html_field(field) }
+ markdown_fields.map { |field| html_field(field) }
+ end
+
+ def html_fields_whitelisted
+ markdown_fields.each_with_object([]) do |field, fields|
+ if @data[field].fetch(:whitelisted, false)
+ fields << html_field(field)
+ end
+ end
end
end
@@ -149,13 +158,18 @@ module CacheMarkdownField
alias_method :attributes_before_markdown_cache, :attributes
def attributes
attrs = attributes_before_markdown_cache
+ html_fields = cached_markdown_fields.html_fields
+ whitelisted = cached_markdown_fields.html_fields_whitelisted
+ exclude_fields = html_fields - whitelisted
- attrs.delete('cached_markdown_version')
-
- cached_markdown_fields.html_fields.each do |field|
+ exclude_fields.each do |field|
attrs.delete(field)
end
+ if whitelisted.empty?
+ attrs.delete('cached_markdown_version')
+ end
+
attrs
end
diff --git a/app/models/concerns/ci/contextable.rb b/app/models/concerns/ci/contextable.rb
new file mode 100644
index 00000000000..4986a42dbd2
--- /dev/null
+++ b/app/models/concerns/ci/contextable.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+module Ci
+ ##
+ # This module implements methods that provide context in form of
+ # essential CI/CD variables that can be used by a build / bridge job.
+ #
+ module Contextable
+ ##
+ # Variables in the environment name scope.
+ #
+ def scoped_variables(environment: expanded_environment_name)
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ variables.concat(predefined_variables)
+ variables.concat(project.predefined_variables)
+ variables.concat(pipeline.predefined_variables)
+ variables.concat(runner.predefined_variables) if runnable? && runner
+ variables.concat(project.deployment_variables(environment: environment)) if environment
+ variables.concat(yaml_variables)
+ variables.concat(user_variables)
+ variables.concat(secret_group_variables)
+ variables.concat(secret_project_variables(environment: environment))
+ variables.concat(trigger_request.user_variables) if trigger_request
+ variables.concat(pipeline.variables)
+ variables.concat(pipeline.pipeline_schedule.job_variables) if pipeline.pipeline_schedule
+ end
+ end
+
+ ##
+ # Regular Ruby hash of scoped variables, without duplicates that are
+ # possible to be present in an array of hashes returned from `variables`.
+ #
+ def scoped_variables_hash
+ scoped_variables.to_hash
+ end
+
+ ##
+ # Variables that do not depend on the environment name.
+ #
+ def simple_variables
+ strong_memoize(:simple_variables) do
+ scoped_variables(environment: nil).to_runner_variables
+ end
+ end
+
+ def user_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ break variables if user.blank?
+
+ variables.append(key: 'GITLAB_USER_ID', value: user.id.to_s)
+ variables.append(key: 'GITLAB_USER_EMAIL', value: user.email)
+ variables.append(key: 'GITLAB_USER_LOGIN', value: user.username)
+ variables.append(key: 'GITLAB_USER_NAME', value: user.name)
+ end
+ end
+
+ def predefined_variables # rubocop:disable Metrics/AbcSize
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ variables.append(key: 'CI', value: 'true')
+ variables.append(key: 'GITLAB_CI', value: 'true')
+ variables.append(key: 'GITLAB_FEATURES', value: project.licensed_features.join(','))
+ variables.append(key: 'CI_SERVER_NAME', value: 'GitLab')
+ variables.append(key: 'CI_SERVER_VERSION', value: Gitlab::VERSION)
+ variables.append(key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s)
+ variables.append(key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s)
+ variables.append(key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s)
+ variables.append(key: 'CI_SERVER_REVISION', value: Gitlab.revision)
+ 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_SHORT_SHA', value: short_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?
+ variables.append(key: "CI_PIPELINE_TRIGGERED", value: 'true') if trigger_request
+ variables.append(key: "CI_JOB_MANUAL", value: 'true') if action?
+ variables.append(key: "CI_NODE_INDEX", value: self.options[:instance].to_s) if self.options&.include?(:instance)
+ variables.append(key: "CI_NODE_TOTAL", value: (self.options&.dig(:parallel) || 1).to_s)
+ variables.concat(legacy_variables)
+ end
+ end
+
+ def legacy_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ variables.append(key: 'CI_BUILD_REF', value: sha)
+ variables.append(key: 'CI_BUILD_BEFORE_SHA', value: before_sha)
+ variables.append(key: 'CI_BUILD_REF_NAME', value: ref)
+ variables.append(key: 'CI_BUILD_REF_SLUG', value: ref_slug)
+ variables.append(key: 'CI_BUILD_NAME', value: name)
+ variables.append(key: 'CI_BUILD_STAGE', value: stage)
+ variables.append(key: "CI_BUILD_TAG", value: ref) if tag?
+ variables.append(key: "CI_BUILD_TRIGGERED", value: 'true') if trigger_request
+ variables.append(key: "CI_BUILD_MANUAL", value: 'true') if action?
+ end
+ end
+
+ def secret_group_variables
+ return [] unless project.group
+
+ project.group.ci_variables_for(git_ref, project)
+ end
+
+ def secret_project_variables(environment: persisted_environment)
+ project.ci_variables_for(ref: git_ref, environment: environment)
+ end
+ end
+end
diff --git a/app/models/concerns/ci/processable.rb b/app/models/concerns/ci/processable.rb
index 1c78b1413a8..268fa8ec692 100644
--- a/app/models/concerns/ci/processable.rb
+++ b/app/models/concerns/ci/processable.rb
@@ -23,5 +23,9 @@ module Ci
def expanded_environment_name
raise NotImplementedError
end
+
+ def scoped_variables_hash
+ raise NotImplementedError
+ end
end
end
diff --git a/app/models/concerns/closed_at_filterable.rb b/app/models/concerns/closed_at_filterable.rb
new file mode 100644
index 00000000000..239c2e47611
--- /dev/null
+++ b/app/models/concerns/closed_at_filterable.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+module ClosedAtFilterable
+ extend ActiveSupport::Concern
+
+ included do
+ scope :closed_before, ->(date) { where(scoped_table[:closed_at].lteq(date)) }
+ scope :closed_after, ->(date) { where(scoped_table[:closed_at].gteq(date)) }
+
+ def self.scoped_table
+ arel_table.alias(table_name)
+ end
+ end
+end
diff --git a/app/models/concerns/fast_destroy_all.rb b/app/models/concerns/fast_destroy_all.rb
index 1e3afd641ed..f862031bce0 100644
--- a/app/models/concerns/fast_destroy_all.rb
+++ b/app/models/concerns/fast_destroy_all.rb
@@ -11,7 +11,7 @@
# it is difficult to accomplish it.
#
# This module defines a format to use `delete_all` and delete associated external data.
-# Here is an exmaple
+# Here is an example
#
# Situation
# - `Project` has many `Ci::BuildTraceChunk` through `Ci::Build`
diff --git a/app/models/concerns/feature_gate.rb b/app/models/concerns/feature_gate.rb
index 3f84de54ad5..bb095f113e2 100644
--- a/app/models/concerns/feature_gate.rb
+++ b/app/models/concerns/feature_gate.rb
@@ -2,7 +2,7 @@
module FeatureGate
def flipper_id
- return nil if new_record?
+ return if new_record?
"#{self.class.name}:#{id}"
end
diff --git a/app/models/concerns/has_ref.rb b/app/models/concerns/has_ref.rb
index d7089294efc..413cd36dcaa 100644
--- a/app/models/concerns/has_ref.rb
+++ b/app/models/concerns/has_ref.rb
@@ -4,7 +4,7 @@ module HasRef
extend ActiveSupport::Concern
def branch?
- !tag?
+ !tag? && !merge_request_event?
end
def git_ref
@@ -14,4 +14,15 @@ module HasRef
Gitlab::Git::TAG_REF_PREFIX + ref.to_s
end
end
+
+ # A slugified version of the build ref, suitable for inclusion in URLs and
+ # domain names. Rules:
+ #
+ # * Lowercased
+ # * Anything not matching [a-z0-9-] is replaced with a -
+ # * Maximum length is 63 bytes
+ # * First/Last Character is not a hyphen
+ def ref_slug
+ Gitlab::Utils.slugify(ref.to_s)
+ end
end
diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb
index 0d2be4c61ab..8882f48c281 100644
--- a/app/models/concerns/has_status.rb
+++ b/app/models/concerns/has_status.rb
@@ -5,14 +5,14 @@ module HasStatus
DEFAULT_STATUS = 'created'.freeze
BLOCKED_STATUS = %w[manual scheduled].freeze
- AVAILABLE_STATUSES = %w[created pending running success failed canceled skipped manual scheduled].freeze
+ AVAILABLE_STATUSES = %w[created preparing pending running success failed canceled skipped manual scheduled].freeze
STARTED_STATUSES = %w[running success failed skipped manual scheduled].freeze
- ACTIVE_STATUSES = %w[pending running].freeze
+ ACTIVE_STATUSES = %w[preparing pending running].freeze
COMPLETED_STATUSES = %w[success failed canceled skipped].freeze
- ORDERED_STATUSES = %w[failed pending running manual scheduled canceled success skipped created].freeze
+ ORDERED_STATUSES = %w[failed preparing pending running manual scheduled canceled success skipped created].freeze
STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3,
failed: 4, canceled: 5, skipped: 6, manual: 7,
- scheduled: 8 }.freeze
+ scheduled: 8, preparing: 9 }.freeze
UnknownStatusError = Class.new(StandardError)
@@ -26,6 +26,7 @@ module HasStatus
success = scope_relevant.success.select('count(*)').to_sql
manual = scope_relevant.manual.select('count(*)').to_sql
scheduled = scope_relevant.scheduled.select('count(*)').to_sql
+ preparing = scope_relevant.preparing.select('count(*)').to_sql
pending = scope_relevant.pending.select('count(*)').to_sql
running = scope_relevant.running.select('count(*)').to_sql
skipped = scope_relevant.skipped.select('count(*)').to_sql
@@ -37,12 +38,14 @@ module HasStatus
WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{builds})=(#{success}) THEN 'success'
WHEN (#{builds})=(#{created}) THEN 'created'
+ WHEN (#{builds})=(#{preparing}) THEN 'preparing'
WHEN (#{builds})=(#{success})+(#{skipped}) THEN 'success'
WHEN (#{builds})=(#{success})+(#{skipped})+(#{canceled}) THEN 'canceled'
WHEN (#{builds})=(#{created})+(#{skipped})+(#{pending}) THEN 'pending'
WHEN (#{running})+(#{pending})>0 THEN 'running'
WHEN (#{manual})>0 THEN 'manual'
WHEN (#{scheduled})>0 THEN 'scheduled'
+ WHEN (#{preparing})>0 THEN 'preparing'
WHEN (#{created})>0 THEN 'running'
ELSE 'failed'
END)"
@@ -70,6 +73,7 @@ module HasStatus
state_machine :status, initial: :created do
state :created, value: 'created'
+ state :preparing, value: 'preparing'
state :pending, value: 'pending'
state :running, value: 'running'
state :failed, value: 'failed'
@@ -81,6 +85,7 @@ module HasStatus
end
scope :created, -> { where(status: 'created') }
+ scope :preparing, -> { where(status: 'preparing') }
scope :relevant, -> { where(status: AVAILABLE_STATUSES - ['created']) }
scope :running, -> { where(status: 'running') }
scope :pending, -> { where(status: 'pending') }
@@ -90,14 +95,14 @@ module HasStatus
scope :skipped, -> { where(status: 'skipped') }
scope :manual, -> { where(status: 'manual') }
scope :scheduled, -> { where(status: 'scheduled') }
- scope :alive, -> { where(status: [:created, :pending, :running]) }
+ scope :alive, -> { where(status: [:created, :preparing, :pending, :running]) }
scope :created_or_pending, -> { where(status: [:created, :pending]) }
scope :running_or_pending, -> { where(status: [:running, :pending]) }
scope :finished, -> { where(status: [:success, :failed, :canceled]) }
scope :failed_or_canceled, -> { where(status: [:failed, :canceled]) }
scope :cancelable, -> do
- where(status: [:running, :pending, :created, :scheduled])
+ where(status: [:running, :preparing, :pending, :created, :scheduled])
end
end
diff --git a/app/models/concerns/has_variable.rb b/app/models/concerns/has_variable.rb
index dfbe413a878..2ec42a1029b 100644
--- a/app/models/concerns/has_variable.rb
+++ b/app/models/concerns/has_variable.rb
@@ -21,9 +21,9 @@ module HasVariable
def key=(new_key)
super(new_key.to_s.strip)
end
+ end
- def to_runner_variable
- { key: key, value: value, public: false }
- end
+ def to_runner_variable
+ { key: key, value: value, public: false }
end
end
diff --git a/app/models/concerns/iid_routes.rb b/app/models/concerns/iid_routes.rb
index b7f99e845ca..3eeb29b6595 100644
--- a/app/models/concerns/iid_routes.rb
+++ b/app/models/concerns/iid_routes.rb
@@ -4,7 +4,7 @@ module IidRoutes
##
# This automagically enforces all related routes to use `iid` instead of `id`
# If you want to use `iid` for some routes and `id` for other routes, this module should not to be included,
- # instead you should define `iid` or `id` explictly at each route generators. e.g. pipeline_path(project.id, pipeline.iid)
+ # instead you should define `iid` or `id` explicitly at each route generators. e.g. pipeline_path(project.id, pipeline.iid)
def to_param
iid.to_s
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 83bd8cc6478..51a8395c013 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -24,11 +24,12 @@ module Issuable
include CreatedAtFilterable
include UpdatedAtFilterable
include IssuableStates
+ include ClosedAtFilterable
# This object is used to gather issuable meta data for displaying
# upvotes, downvotes, notes and closing merge requests count for issues and merge requests
# lists avoiding n+1 queries and improving performance.
- IssuableMeta = Struct.new(:upvotes, :downvotes, :notes_count, :merge_requests_count)
+ IssuableMeta = Struct.new(:upvotes, :downvotes, :user_notes_count, :merge_requests_count)
included do
cache_markdown_field :title, pipeline: :single_line
@@ -36,8 +37,8 @@ module Issuable
redact_field :description
- belongs_to :author, class_name: "User"
- belongs_to :updated_by, class_name: "User"
+ belongs_to :author, class_name: 'User'
+ belongs_to :updated_by, class_name: 'User'
belongs_to :last_edited_by, class_name: 'User'
belongs_to :milestone
@@ -75,6 +76,7 @@ module Issuable
validates :author, presence: true
validates :title, presence: true, length: { maximum: 255 }
+ validate :milestone_is_valid
scope :authored, ->(user) { where(author_id: user) }
scope :recent, -> { reorder(id: :desc) }
@@ -118,6 +120,16 @@ module Issuable
def has_multiple_assignees?
assignees.count > 1
end
+
+ def milestone_available?
+ project_id == milestone&.project_id || project.ancestors_upto.compact.include?(milestone&.group)
+ end
+
+ private
+
+ def milestone_is_valid
+ errors.add(:milestone_id, message: "is invalid") if milestone_id.present? && !milestone_available?
+ end
end
class_methods do
diff --git a/app/models/concerns/maskable.rb b/app/models/concerns/maskable.rb
new file mode 100644
index 00000000000..8793f0ec965
--- /dev/null
+++ b/app/models/concerns/maskable.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Maskable
+ extend ActiveSupport::Concern
+
+ # * Single line
+ # * No escape characters
+ # * No variables
+ # * No spaces
+ # * Minimal length of 8 characters
+ # * Absolutely no fun is allowed
+ REGEX = /\A\w{8,}\z/
+
+ included do
+ validates :masked, inclusion: { in: [true, false] }
+ validates :value, format: { with: REGEX }, if: :masked?
+ end
+
+ def to_runner_variable
+ super.merge(masked: masked?)
+ end
+end
diff --git a/app/models/concerns/milestoneish.rb b/app/models/concerns/milestoneish.rb
index 055ffe04646..e65bbb8ca07 100644
--- a/app/models/concerns/milestoneish.rb
+++ b/app/models/concerns/milestoneish.rb
@@ -46,12 +46,31 @@ module Milestoneish
end
end
+ def issue_participants_visible_by_user(user)
+ User.joins(:issue_assignees)
+ .where('issue_assignees.issue_id' => issues_visible_to_user(user).select(:id))
+ .distinct
+ end
+
+ def issue_labels_visible_by_user(user)
+ Label.joins(:label_links)
+ .where('label_links.target_id' => issues_visible_to_user(user).select(:id), 'label_links.target_type' => 'Issue')
+ .distinct
+ end
+
def sorted_issues(user)
issues_visible_to_user(user).preload_associations.sort_by_attribute('label_priority')
end
- def sorted_merge_requests
- merge_requests.sort_by_attribute('label_priority')
+ def sorted_merge_requests(user)
+ merge_requests_visible_to_user(user).sort_by_attribute('label_priority')
+ end
+
+ def merge_requests_visible_to_user(user)
+ memoize_per_user(user, :merge_requests_visible_to_user) do
+ MergeRequestsFinder.new(user, issues_finder_params)
+ .execute.where(milestone_id: milestoneish_id)
+ end
end
def upcoming?
diff --git a/app/models/concerns/mirror_authentication.rb b/app/models/concerns/mirror_authentication.rb
index e3e1a0441f8..948094221e5 100644
--- a/app/models/concerns/mirror_authentication.rb
+++ b/app/models/concerns/mirror_authentication.rb
@@ -79,7 +79,7 @@ module MirrorAuthentication
end
def ssh_public_key
- return nil if ssh_private_key.blank?
+ return if ssh_private_key.blank?
comment = "git@#{::Gitlab.config.gitlab.host}"
::SSHKey.new(ssh_private_key, comment: comment).ssh_public_key
diff --git a/app/models/concerns/reactive_caching.rb b/app/models/concerns/reactive_caching.rb
index de77ca3e963..d2ead7130e5 100644
--- a/app/models/concerns/reactive_caching.rb
+++ b/app/models/concerns/reactive_caching.rb
@@ -69,7 +69,7 @@ module ReactiveCaching
def with_reactive_cache(*args, &blk)
unless within_reactive_cache_lifetime?(*args)
refresh_reactive_cache!(*args)
- return nil
+ return
end
keep_alive_reactive_cache!(*args)
diff --git a/app/models/concerns/sha_attribute.rb b/app/models/concerns/sha_attribute.rb
index e51b4e22c96..a479bef993c 100644
--- a/app/models/concerns/sha_attribute.rb
+++ b/app/models/concerns/sha_attribute.rb
@@ -16,6 +16,8 @@ module ShaAttribute
# the column is the correct type. In production it should behave like any other attribute.
# See https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/5502 for more discussion
def validate_binary_column_exists!(name)
+ return unless database_exists?
+
unless table_exists?
warn "WARNING: sha_attribute #{name.inspect} is invalid since the table doesn't exist - you may need to run database migrations"
return
@@ -35,5 +37,13 @@ module ShaAttribute
Gitlab::AppLogger.error "ShaAttribute initialization: #{error.message}"
raise
end
+
+ def database_exists?
+ ActiveRecord::Base.connection
+
+ true
+ rescue
+ false
+ end
end
end
diff --git a/app/models/concerns/token_authenticatable.rb b/app/models/concerns/token_authenticatable.rb
index f5bb559ceda..8c769be0489 100644
--- a/app/models/concerns/token_authenticatable.rb
+++ b/app/models/concerns/token_authenticatable.rb
@@ -26,34 +26,41 @@ module TokenAuthenticatable
end
end
- define_method(token_field) do
+ mod = token_authenticatable_module
+
+ mod.define_method(token_field) do
strategy.get_token(self)
end
- define_method("set_#{token_field}") do |token|
+ mod.define_method("set_#{token_field}") do |token|
strategy.set_token(self, token)
end
- define_method("ensure_#{token_field}") do
+ mod.define_method("ensure_#{token_field}") do
strategy.ensure_token(self)
end
# Returns a token, but only saves when the database is in read & write mode
- define_method("ensure_#{token_field}!") do
+ mod.define_method("ensure_#{token_field}!") do
strategy.ensure_token!(self)
end
# Resets the token, but only saves when the database is in read & write mode
- define_method("reset_#{token_field}!") do
+ mod.define_method("reset_#{token_field}!") do
strategy.reset_token!(self)
end
- define_method("#{token_field}_matches?") do |other_token|
+ mod.define_method("#{token_field}_matches?") do |other_token|
token = read_attribute(token_field)
token.present? && ActiveSupport::SecurityUtils.variable_size_secure_compare(other_token, token)
end
end
+ def token_authenticatable_module
+ @token_authenticatable_module ||=
+ const_set(:TokenAuthenticatable, Module.new).tap(&method(:include))
+ end
+
def token_authenticatable_fields
@token_authenticatable_fields ||= []
end
diff --git a/app/models/concerns/token_authenticatable_strategies/base.rb b/app/models/concerns/token_authenticatable_strategies/base.rb
index 01fb194281a..df14e6e4754 100644
--- a/app/models/concerns/token_authenticatable_strategies/base.rb
+++ b/app/models/concerns/token_authenticatable_strategies/base.rb
@@ -39,22 +39,6 @@ module TokenAuthenticatableStrategies
instance.save! if Gitlab::Database.read_write?
end
- def fallback?
- unless options[:fallback].in?([true, false, nil])
- raise ArgumentError, 'fallback: needs to be a boolean value!'
- end
-
- options[:fallback] == true
- end
-
- def migrating?
- unless options[:migrating].in?([true, false, nil])
- raise ArgumentError, 'migrating: needs to be a boolean value!'
- end
-
- options[:migrating] == true
- end
-
def self.fabricate(model, field, options)
if options[:digest] && options[:encrypted]
raise ArgumentError, 'Incompatible options set!'
diff --git a/app/models/concerns/token_authenticatable_strategies/encrypted.rb b/app/models/concerns/token_authenticatable_strategies/encrypted.rb
index 152491aa6e9..2c7fa2c5b3c 100644
--- a/app/models/concerns/token_authenticatable_strategies/encrypted.rb
+++ b/app/models/concerns/token_authenticatable_strategies/encrypted.rb
@@ -2,28 +2,18 @@
module TokenAuthenticatableStrategies
class Encrypted < Base
- def initialize(*)
- super
-
- if migrating? && fallback?
- raise ArgumentError, '`fallback` and `migrating` options are not compatible!'
- end
- end
-
def find_token_authenticatable(token, unscoped = false)
return if token.blank?
- if fully_encrypted?
- return find_by_encrypted_token(token, unscoped)
- end
-
- if fallback?
+ if required?
+ find_by_encrypted_token(token, unscoped)
+ elsif optional?
find_by_encrypted_token(token, unscoped) ||
find_by_plaintext_token(token, unscoped)
elsif migrating?
find_by_plaintext_token(token, unscoped)
else
- raise ArgumentError, 'Unknown encryption phase!'
+ raise ArgumentError, "Unknown encryption strategy: #{encrypted_strategy}!"
end
end
@@ -41,8 +31,8 @@ module TokenAuthenticatableStrategies
return super if instance.has_attribute?(encrypted_field)
- if fully_encrypted?
- raise ArgumentError, 'Using encrypted strategy when encrypted field is missing!'
+ if required?
+ raise ArgumentError, 'Using required encryption strategy when encrypted field is missing!'
else
insecure_strategy.ensure_token(instance)
end
@@ -53,8 +43,7 @@ module TokenAuthenticatableStrategies
encrypted_token = instance.read_attribute(encrypted_field)
token = Gitlab::CryptoHelper.aes256_gcm_decrypt(encrypted_token)
-
- token || (insecure_strategy.get_token(instance) if fallback?)
+ token || (insecure_strategy.get_token(instance) if optional?)
end
def set_token(instance, token)
@@ -62,16 +51,35 @@ module TokenAuthenticatableStrategies
instance[encrypted_field] = Gitlab::CryptoHelper.aes256_gcm_encrypt(token)
instance[token_field] = token if migrating?
- instance[token_field] = nil if fallback?
+ instance[token_field] = nil if optional?
token
end
- def fully_encrypted?
- !migrating? && !fallback?
+ def required?
+ encrypted_strategy == :required
+ end
+
+ def migrating?
+ encrypted_strategy == :migrating
+ end
+
+ def optional?
+ encrypted_strategy == :optional
end
protected
+ def encrypted_strategy
+ value = options[:encrypted]
+ value = value.call if value.is_a?(Proc)
+
+ unless value.in?([:required, :optional, :migrating])
+ raise ArgumentError, 'encrypted: needs to be a :required, :optional or :migrating!'
+ end
+
+ value
+ end
+
def find_by_plaintext_token(token, unscoped)
insecure_strategy.find_token_authenticatable(token, unscoped)
end
@@ -89,7 +97,7 @@ module TokenAuthenticatableStrategies
def token_set?(instance)
raw_token = instance.read_attribute(encrypted_field)
- unless fully_encrypted?
+ unless required?
raw_token ||= insecure_strategy.get_token(instance)
end
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 811e623b7f7..428edfd88de 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -78,6 +78,10 @@ class Deployment < ActiveRecord::Base
Commit.truncate_sha(sha)
end
+ def cluster
+ project.deployment_platform(environment: environment.name)&.cluster
+ end
+
def last?
self == environment.last_deployment
end
diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb
index 279603496b0..feabea9b8ba 100644
--- a/app/models/diff_note.rb
+++ b/app/models/diff_note.rb
@@ -41,6 +41,14 @@ class DiffNote < Note
create_note_diff_file(creation_params)
end
+ # Returns the diff file from `position`
+ def latest_diff_file
+ strong_memoize(:latest_diff_file) do
+ position.diff_file(project.repository)
+ end
+ end
+
+ # Returns the diff file from `original_position`
def diff_file
strong_memoize(:diff_file) do
enqueue_diff_file_creation_job if should_create_diff_file?
@@ -69,8 +77,8 @@ class DiffNote < Note
def supports_suggestion?
return false unless noteable.supports_suggestion? && on_text?
# We don't want to trigger side-effects of `diff_file` call.
- return false unless file = fetch_diff_file
- return false unless line = file.line_for_position(self.original_position)
+ return false unless file = latest_diff_file
+ return false unless line = file.line_for_position(self.position)
line&.suggestible?
end
@@ -80,7 +88,7 @@ class DiffNote < Note
end
def banzai_render_context(field)
- super.merge(suggestions_filter_enabled: supports_suggestion?)
+ super.merge(project: project, suggestions_filter_enabled: supports_suggestion?)
end
private
diff --git a/app/models/email.rb b/app/models/email.rb
index 3ce6e792fa8..7c33c5c7e64 100644
--- a/app/models/email.rb
+++ b/app/models/email.rb
@@ -7,7 +7,7 @@ class Email < ActiveRecord::Base
belongs_to :user
validates :user_id, presence: true
- validates :email, presence: true, uniqueness: true, email: true
+ validates :email, presence: true, uniqueness: true, devise_email: true
validate :unique_email, if: ->(email) { email.email_changed? }
scope :confirmed, -> { where.not(confirmed_at: nil) }
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 1fc088b12ae..3d909cc8e5c 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -119,7 +119,7 @@ class Environment < ActiveRecord::Base
def first_deployment_for(commit_sha)
ref = project.repository.ref_name_for_sha(ref_path, commit_sha)
- return nil unless ref
+ return unless ref
deployment_iid = ref.split('/').last
deployments.find_by(iid: deployment_iid)
@@ -130,7 +130,7 @@ class Environment < ActiveRecord::Base
end
def formatted_external_url
- return nil unless external_url
+ return unless external_url
external_url.gsub(%r{\A.*?://}, '')
end
@@ -243,6 +243,10 @@ class Environment < ActiveRecord::Base
self.environment_type || self.name
end
+ def name_without_type
+ @name_without_type ||= name.delete_prefix("#{environment_type}/")
+ end
+
def deployment_platform
strong_memoize(:deployment_platform) do
project.deployment_platform(environment: self.name)
diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb
index 31084c54bdc..1e2bd3bda7f 100644
--- a/app/models/error_tracking/project_error_tracking_setting.rb
+++ b/app/models/error_tracking/project_error_tracking_setting.rb
@@ -2,19 +2,30 @@
module ErrorTracking
class ProjectErrorTrackingSetting < ActiveRecord::Base
+ include Gitlab::Utils::StrongMemoize
include ReactiveCaching
+ API_URL_PATH_REGEXP = %r{
+ \A
+ (?<prefix>/api/0/projects/+)
+ (?:
+ (?<organization>[^/]+)/+
+ (?<project>[^/]+)/*
+ )?
+ \z
+ }x
+
self.reactive_cache_key = ->(setting) { [setting.class.model_name.singular, setting.project_id] }
belongs_to :project
validates :api_url, length: { maximum: 255 }, public_url: true, url: { enforce_sanitization: true, ascii_only: true }, allow_nil: true
- validates :api_url, presence: true, if: :enabled
+ validates :api_url, presence: { message: 'is a required field' }, if: :enabled
validate :validate_api_url_path, if: :enabled
- validates :token, presence: true, if: :enabled
+ validates :token, presence: { message: 'is a required field' }, if: :enabled
attr_encrypted :token,
mode: :per_attribute_iv,
@@ -23,6 +34,11 @@ module ErrorTracking
after_save :clear_reactive_cache!
+ def api_url=(value)
+ super
+ clear_memoization(:api_url_slugs)
+ end
+
def project_name
super || project_name_from_slug
end
@@ -40,6 +56,8 @@ module ErrorTracking
end
def self.build_api_url_from(api_host:, project_slug:, organization_slug:)
+ return if api_host.blank?
+
uri = Addressable::URI.parse("#{api_host}/api/0/projects/#{organization_slug}/#{project_slug}/")
uri.path = uri.path.squeeze('/')
@@ -58,7 +76,7 @@ module ErrorTracking
def list_sentry_issues(opts = {})
with_reactive_cache('list_issues', opts.stringify_keys) do |result|
- { issues: result }
+ result
end
end
@@ -69,8 +87,10 @@ module ErrorTracking
def calculate_reactive_cache(request, opts)
case request
when 'list_issues'
- sentry_client.list_issues(**opts.symbolize_keys)
+ { issues: sentry_client.list_issues(**opts.symbolize_keys) }
end
+ rescue Sentry::Client::Error => e
+ { error: e.message }
end
# http://HOST/api/0/projects/ORG/PROJECT
@@ -98,34 +118,39 @@ module ErrorTracking
end
def project_slug_from_api_url
- extract_slug(:project)
+ api_url_slug(:project)
end
def organization_slug_from_api_url
- extract_slug(:organization)
+ api_url_slug(:organization)
+ end
+
+ def api_url_slug(capture)
+ slugs = strong_memoize(:api_url_slugs) { extract_api_url_slugs || {} }
+ slugs[capture]
end
- def extract_slug(capture)
+ def extract_api_url_slugs
return if api_url.blank?
begin
url = Addressable::URI.parse(api_url)
rescue Addressable::URI::InvalidURIError
- return nil
+ return
end
- @slug_match ||= url.path.match(%r{^/api/0/projects/+(?<organization>[^/]+)/+(?<project>[^/|$]+)}) || {}
- @slug_match[capture]
+ url.path.match(API_URL_PATH_REGEXP)
end
def validate_api_url_path
return if api_url.blank?
- begin
- unless Addressable::URI.parse(api_url).path.starts_with?('/api/0/projects')
- errors.add(:api_url, 'path needs to start with /api/0/projects')
- end
- rescue Addressable::URI::InvalidURIError
+ unless api_url_slug(:prefix)
+ return errors.add(:api_url, 'is invalid')
+ end
+
+ unless api_url_slug(:organization)
+ errors.add(:project, 'is a required field')
end
end
end
diff --git a/app/models/group.rb b/app/models/group.rb
index 52f503404af..c77586c4cdc 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -56,7 +56,7 @@ class Group < Namespace
validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 }
- add_authentication_token_field :runners_token, encrypted: true, migrating: true
+ add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
after_create :post_create_hook
after_destroy :post_destroy_hook
diff --git a/app/models/individual_note_discussion.rb b/app/models/individual_note_discussion.rb
index b4a661ae5b4..3b6b68a9c5f 100644
--- a/app/models/individual_note_discussion.rb
+++ b/app/models/individual_note_discussion.rb
@@ -14,7 +14,7 @@ class IndividualNoteDiscussion < Discussion
end
def can_convert_to_discussion?
- noteable.supports_replying_to_individual_notes? && Feature.enabled?(:reply_to_individual_notes)
+ noteable.supports_replying_to_individual_notes? && Feature.enabled?(:reply_to_individual_notes, default_enabled: true)
end
def convert_to_discussion!(save: false)
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 182c5d3d4b0..deab53d25e7 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -64,8 +64,10 @@ class Issue < ActiveRecord::Base
scope :order_closest_future_date, -> { reorder('CASE WHEN issues.due_date >= CURRENT_DATE THEN 0 ELSE 1 END ASC, ABS(CURRENT_DATE - issues.due_date) ASC') }
scope :preload_associations, -> { preload(:labels, project: :namespace) }
+ scope :with_api_entity_associations, -> { preload(:timelogs, :assignees, :author, :notes, :labels, project: [:route, { namespace: :route }] ) }
scope :public_only, -> { where(confidential: false) }
+ scope :confidential_only, -> { where(confidential: true) }
after_save :expire_etag_cache
after_save :ensure_metrics, unless: :imported?
@@ -262,6 +264,10 @@ class Issue < ActiveRecord::Base
end
# rubocop: enable CodeReuse/ServiceClass
+ def merge_requests_count
+ merge_requests_closing_issues.count
+ end
+
private
def ensure_metrics
diff --git a/app/models/label.rb b/app/models/label.rb
index 1c3db3eb35d..96bdb7f17c5 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -126,6 +126,13 @@ class Label < ActiveRecord::Base
fuzzy_search(query, [:title, :description])
end
+ # Override Gitlab::SQL::Pattern.min_chars_for_partial_matching as
+ # label queries are never global, and so will not use a trigram
+ # index. That means we can have just one character in the LIKE.
+ def self.min_chars_for_partial_matching
+ 1
+ end
+
def open_issues_count(user = nil)
issues_count(user, state: 'opened')
end
diff --git a/app/models/label_note.rb b/app/models/label_note.rb
index 680952cf421..d6814f4a948 100644
--- a/app/models/label_note.rb
+++ b/app/models/label_note.rb
@@ -81,7 +81,7 @@ class LabelNote < Note
deleted = label_refs.count - existing_refs.count
deleted_str = deleted == 0 ? nil : "#{deleted} deleted"
- return nil unless refs_str || deleted_str
+ return unless refs_str || deleted_str
label_list_str = [refs_str, deleted_str].compact.join(' + ')
suffix = 'label'.pluralize(deleted > 0 ? deleted : existing_refs.count)
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 00dec6bb92b..e2c75bc7ee9 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -73,7 +73,7 @@ class LegacyDiffNote < Note
private
def find_diff
- return nil unless noteable
+ return unless noteable
return @diff if defined?(@diff)
@diff = noteable.raw_diffs(Commit.max_diff_options).find do |d|
diff --git a/app/models/member.rb b/app/models/member.rb
index 8e071a8ff21..5dbc0c2eec9 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -28,7 +28,7 @@ class Member < ActiveRecord::Base
presence: {
if: :invite?
},
- email: {
+ devise_email: {
allow_nil: true
},
uniqueness: {
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 09c5a1c7449..9067c8e8282 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -66,16 +66,23 @@ class MergeRequest < ActiveRecord::Base
has_many :cached_closes_issues, through: :merge_requests_closing_issues, source: :issue
has_many :merge_request_pipelines, foreign_key: 'merge_request_id', class_name: 'Ci::Pipeline'
+ has_many :suggestions, through: :notes
+ has_many :merge_request_assignees
+ # Will be deprecated at https://gitlab.com/gitlab-org/gitlab-ce/issues/59457
belongs_to :assignee, class_name: "User"
serialize :merge_params, Hash # rubocop:disable Cop/ActiveRecordSerialize
- after_create :ensure_merge_request_diff, unless: :importing?
+ after_create :ensure_merge_request_diff
after_update :clear_memoized_shas
after_update :reload_diff_if_branch_changed
after_save :ensure_metrics
+ # Required until the codebase starts using this relation for single or multiple assignees.
+ # TODO: Remove at gitlab-ee#2004 implementation.
+ after_save :refresh_merge_request_assignees, if: :assignee_id_changed?
+
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests
attr_accessor :allow_broken
@@ -184,11 +191,21 @@ class MergeRequest < ActiveRecord::Base
scope :assigned, -> { where("assignee_id IS NOT NULL") }
scope :unassigned, -> { where("assignee_id IS NULL") }
scope :assigned_to, ->(u) { where(assignee_id: u.id)}
+ scope :with_api_entity_associations, -> {
+ preload(:author, :assignee, :notes, :labels, :milestone, :timelogs,
+ latest_merge_request_diff: [:merge_request_diff_commits],
+ metrics: [:latest_closed_by, :merged_by],
+ target_project: [:route, { namespace: :route }],
+ source_project: [:route, { namespace: :route }])
+ }
participant :assignee
after_save :keep_around_commit
+ alias_attribute :project, :target_project
+ alias_attribute :project_id, :target_project_id
+
def self.reference_prefix
'!'
end
@@ -197,6 +214,22 @@ class MergeRequest < ActiveRecord::Base
@available_states ||= super.merge(merged: 3, locked: 4)
end
+ # Returns the top 100 target branches
+ #
+ # The returned value is a Array containing branch names
+ # sort by updated_at of merge request:
+ #
+ # ['master', 'develop', 'production']
+ #
+ # limit - The maximum number of target branch to return.
+ def self.recent_target_branches(limit: 100)
+ group(:target_branch)
+ .select(:target_branch)
+ .reorder('MAX(merge_requests.updated_at) DESC')
+ .limit(limit)
+ .pluck(:target_branch)
+ end
+
def rebase_in_progress?
strong_memoize(:rebase_in_progress) do
# The source project can be deleted
@@ -210,7 +243,7 @@ class MergeRequest < ActiveRecord::Base
# branch head commit, for example checking if a merge request can be merged.
# For more information check: https://gitlab.com/gitlab-org/gitlab-ce/issues/40004
def actual_head_pipeline
- head_pipeline&.sha == diff_head_sha ? head_pipeline : nil
+ head_pipeline&.matches_sha_or_source_sha?(diff_head_sha) ? head_pipeline : nil
end
def merge_pipeline
@@ -653,6 +686,15 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff || create_merge_request_diff
end
+ def refresh_merge_request_assignees
+ transaction do
+ # Using it instead relation.delete_all in order to avoid adding a
+ # dependent: :delete_all (we already have foreign key cascade deletion).
+ MergeRequestAssignee.where(merge_request_id: self).delete_all
+ merge_request_assignees.create(user_id: assignee_id) if assignee_id
+ end
+ end
+
def create_merge_request_diff
fetch_ref!
@@ -768,6 +810,16 @@ class MergeRequest < ActiveRecord::Base
true
end
+ def mergeable_to_ref?
+ return false if merged?
+ return false if broken?
+
+ # Given the `merge_ref_path` will have the same
+ # state the `target_branch` would have. Ideally
+ # we need to check if it can be merged to it.
+ project.repository.can_be_merged?(diff_head_sha, target_branch)
+ end
+
def ff_merge_possible?
project.repository.ancestor?(target_branch_sha, diff_head_sha)
end
@@ -841,10 +893,6 @@ class MergeRequest < ActiveRecord::Base
target_project != source_project
end
- def project
- target_project
- end
-
# If the merge request closes any issues, save this information in the
# `MergeRequestsClosingIssues` model. This is a performance optimization.
# Calculating this information for a number of merge requests requires
@@ -1081,6 +1129,10 @@ class MergeRequest < ActiveRecord::Base
"refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/head"
end
+ def merge_ref_path
+ "refs/#{Repository::REF_MERGE_REQUEST}/#{iid}/merge"
+ end
+
def in_locked_state
begin
lock_mr
@@ -1117,12 +1169,18 @@ class MergeRequest < ActiveRecord::Base
diverged_commits_count > 0
end
- def all_pipelines(shas: all_commit_shas)
+ def all_pipelines
return Ci::Pipeline.none unless source_project
- @all_pipelines ||=
- source_project.ci_pipelines
- .for_merge_request(self, source_branch, all_commit_shas)
+ shas = all_commit_shas
+
+ strong_memoize(:all_pipelines) do
+ Ci::Pipeline.from_union(
+ [source_project.ci_pipelines.merge_request_pipelines(self, shas),
+ source_project.ci_pipelines.detached_merge_request_pipelines(self, shas),
+ source_project.ci_pipelines.triggered_for_branch(source_branch).for_sha(shas)],
+ remove_duplicates: false).sort_by_merge_request_pipelines
+ end
end
def update_head_pipeline
@@ -1144,35 +1202,16 @@ class MergeRequest < ActiveRecord::Base
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables.append(key: 'CI_MERGE_REQUEST_ID', value: id.to_s)
variables.append(key: 'CI_MERGE_REQUEST_IID', value: iid.to_s)
-
- variables.append(key: 'CI_MERGE_REQUEST_REF_PATH',
- value: ref_path.to_s)
-
- variables.append(key: 'CI_MERGE_REQUEST_PROJECT_ID',
- value: project.id.to_s)
-
- variables.append(key: 'CI_MERGE_REQUEST_PROJECT_PATH',
- value: project.full_path)
-
- variables.append(key: 'CI_MERGE_REQUEST_PROJECT_URL',
- value: project.web_url)
-
- variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME',
- value: target_branch.to_s)
-
- if source_project
- variables.append(key: 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID',
- value: source_project.id.to_s)
-
- variables.append(key: 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH',
- value: source_project.full_path)
-
- variables.append(key: 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL',
- value: source_project.web_url)
-
- variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME',
- value: source_branch.to_s)
- end
+ variables.append(key: 'CI_MERGE_REQUEST_REF_PATH', value: ref_path.to_s)
+ variables.append(key: 'CI_MERGE_REQUEST_PROJECT_ID', value: project.id.to_s)
+ variables.append(key: 'CI_MERGE_REQUEST_PROJECT_PATH', value: project.full_path)
+ variables.append(key: 'CI_MERGE_REQUEST_PROJECT_URL', value: project.web_url)
+ variables.append(key: 'CI_MERGE_REQUEST_TARGET_BRANCH_NAME', value: target_branch.to_s)
+ variables.append(key: 'CI_MERGE_REQUEST_TITLE', value: title)
+ variables.append(key: 'CI_MERGE_REQUEST_ASSIGNEES', value: assignee.username) if assignee
+ variables.append(key: 'CI_MERGE_REQUEST_MILESTONE', value: milestone.title) if milestone
+ variables.append(key: 'CI_MERGE_REQUEST_LABELS', value: label_names.join(',')) if labels.present?
+ variables.concat(source_project_variables)
end
end
@@ -1376,7 +1415,17 @@ class MergeRequest < ActiveRecord::Base
private
def find_actual_head_pipeline
- source_project&.ci_pipelines
- &.latest_for_merge_request(self, source_branch, diff_head_sha)
+ all_pipelines.for_sha_or_source_sha(diff_head_sha).first
+ end
+
+ def source_project_variables
+ Gitlab::Ci::Variables::Collection.new.tap do |variables|
+ break variables unless source_project
+
+ variables.append(key: 'CI_MERGE_REQUEST_SOURCE_PROJECT_ID', value: source_project.id.to_s)
+ variables.append(key: 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH', value: source_project.full_path)
+ variables.append(key: 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL', value: source_project.web_url)
+ variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME', value: source_branch.to_s)
+ end
end
end
diff --git a/app/models/merge_request_assignee.rb b/app/models/merge_request_assignee.rb
new file mode 100644
index 00000000000..f0e6be51b7f
--- /dev/null
+++ b/app/models/merge_request_assignee.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class MergeRequestAssignee < ApplicationRecord
+ belongs_to :merge_request
+ belongs_to :assignee, class_name: "User", foreign_key: :user_id
+end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index e286a4e57f2..98db1bf7de7 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -22,6 +22,8 @@ class MergeRequestDiff < ActiveRecord::Base
has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }
+ validates :base_commit_sha, :head_commit_sha, :start_commit_sha, sha: true
+
state_machine :state, initial: :empty do
event :clean do
transition any => :without_files
@@ -284,18 +286,21 @@ class MergeRequestDiff < ActiveRecord::Base
return yield(@external_diff_file) if @external_diff_file
external_diff.open do |file|
- begin
- @external_diff_file = file
+ @external_diff_file = file
- yield(@external_diff_file)
- ensure
- @external_diff_file = nil
- end
+ yield(@external_diff_file)
+ ensure
+ @external_diff_file = nil
end
end
private
+ def encode_in_base64?(diff_text)
+ (diff_text.encoding == Encoding::BINARY && !diff_text.ascii_only?) ||
+ diff_text.include?("\0")
+ end
+
def create_merge_request_diff_files(diffs)
rows =
if has_attribute?(:external_diff) && Gitlab.config.external_diffs.enabled
@@ -348,7 +353,7 @@ class MergeRequestDiff < ActiveRecord::Base
diff_hash.tap do |hash|
diff_text = hash[:diff]
- if diff_text.encoding == Encoding::BINARY && !diff_text.ascii_only?
+ if encode_in_base64?(diff_text)
hash[:binary] = true
hash[:diff] = [diff_text].pack('m0')
end
diff --git a/app/models/merge_request_diff_file.rb b/app/models/merge_request_diff_file.rb
index e8d936e265c..16ec4ed470f 100644
--- a/app/models/merge_request_diff_file.rb
+++ b/app/models/merge_request_diff_file.rb
@@ -23,6 +23,6 @@ class MergeRequestDiffFile < ActiveRecord::Base
super
end
- binary? ? content.unpack('m0').first : content
+ binary? ? content.unpack1('m0') : content
end
end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 26cfdc5ef30..a3831ae3fa8 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -37,6 +37,7 @@ class Milestone < ActiveRecord::Base
scope :active, -> { with_state(:active) }
scope :closed, -> { with_state(:closed) }
scope :for_projects, -> { where(group: nil).includes(:project) }
+ scope :started, -> { active.where('milestones.start_date <= CURRENT_DATE') }
scope :for_projects_and_groups, -> (projects, groups) do
projects = projects.compact if projects.is_a? Array
@@ -149,7 +150,7 @@ class Milestone < ActiveRecord::Base
def self.upcoming_ids(projects, groups)
rel = unscoped
.for_projects_and_groups(projects, groups)
- .active.where('milestones.due_date > NOW()')
+ .active.where('milestones.due_date > CURRENT_DATE')
if Gitlab::Database.postgresql?
rel.order(:project_id, :group_id, :due_date).select('DISTINCT ON (project_id, group_id) id')
@@ -161,7 +162,7 @@ class Milestone < ActiveRecord::Base
ON milestones.project_id <=> earlier_milestones.project_id
AND milestones.group_id <=> earlier_milestones.group_id
AND milestones.due_date > earlier_milestones.due_date
- AND earlier_milestones.due_date > NOW()
+ AND earlier_milestones.due_date > CURRENT_DATE
AND earlier_milestones.state = 'active'
HEREDOC
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index f7592532c5b..dea34e812ca 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -11,6 +11,7 @@ class Namespace < ApplicationRecord
include IgnorableColumn
include FeatureGate
include FromUnion
+ include Gitlab::Utils::StrongMemoize
ignore_column :deleted_at
@@ -149,7 +150,7 @@ class Namespace < ApplicationRecord
end
def find_fork_of(project)
- return nil unless project.fork_network
+ return unless project.fork_network
if Gitlab::SafeRequestStore.active?
forks_in_namespace = Gitlab::SafeRequestStore.fetch("namespaces:#{id}:forked_projects") do
@@ -267,6 +268,22 @@ class Namespace < ApplicationRecord
owner.refresh_authorized_projects
end
+ def auto_devops_enabled?
+ first_auto_devops_config[:status]
+ end
+
+ def first_auto_devops_config
+ return { scope: :group, status: auto_devops_enabled } unless auto_devops_enabled.nil?
+
+ strong_memoize(:first_auto_devops_config) do
+ if has_parent?
+ parent.first_auto_devops_config
+ else
+ { scope: :instance, status: Gitlab::CurrentSettings.auto_devops_enabled? }
+ end
+ end
+ end
+
private
def path_or_parent_changed?
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 6da3bb7bfb7..ecbeb24ee0a 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -40,9 +40,12 @@ module Network
# Get commits from repository
#
def collect_commits
- find_commits(count_to_display_commit_in_center).map do |commit|
- # Decorate with app/model/network/commit.rb
- Network::Commit.new(commit)
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58013
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ find_commits(count_to_display_commit_in_center).map do |commit|
+ # Decorate with app/model/network/commit.rb
+ Network::Commit.new(commit)
+ end
end
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 1578ae9c4cc..2c9980b1a0d 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -313,6 +313,14 @@ class Note < ActiveRecord::Base
!system?
end
+ # Since we're using `updated_at` as `last_edited_at`, it could be touched by transforming / resolving a note.
+ # This makes sure it is only marked as edited when the note body is updated.
+ def edited?
+ return false if updated_by.blank?
+
+ super
+ end
+
def cross_reference_not_visible_for?(user)
cross_reference? && !all_referenced_mentionables_allowed?(user)
end
diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb
index 9f16eefe074..793cce191fa 100644
--- a/app/models/notification_recipient.rb
+++ b/app/models/notification_recipient.rb
@@ -119,7 +119,7 @@ class NotificationRecipient
private
def read_ability
- return nil if @skip_read_ability
+ return if @skip_read_ability
return @read_ability if instance_variable_defined?(:@read_ability)
@read_ability =
@@ -136,7 +136,7 @@ class NotificationRecipient
end
def default_project
- return nil if @target.nil?
+ return if @target.nil?
return @target if @target.is_a?(Project)
return @target.project if @target.respond_to?(:project)
end
@@ -153,7 +153,7 @@ class NotificationRecipient
user.global_notification_setting
end
- # Returns the notificaton_setting of the lowest group in hierarchy with non global level
+ # Returns the notification_setting of the lowest group in hierarchy with non global level
def closest_non_global_group_notification_settting
return unless @group
return if indexed_group_notification_settings.empty?
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 73a58f2420e..ed78a46eaf3 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -2,8 +2,11 @@
class PersonalAccessToken < ActiveRecord::Base
include Expirable
+ include IgnorableColumn
include TokenAuthenticatable
- add_authentication_token_field :token, digest: true, fallback: true
+
+ add_authentication_token_field :token, digest: true
+ ignore_column :token
REDIS_EXPIRY_TIME = 3.minutes
diff --git a/app/models/project.rb b/app/models/project.rb
index c72d3a3b725..06010409574 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -38,7 +38,6 @@ 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:
# nil: legacy
@@ -85,7 +84,7 @@ class Project < ActiveRecord::Base
default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
- add_authentication_token_field :runners_token, encrypted: true, migrating: true
+ add_authentication_token_field :runners_token, encrypted: -> { Feature.enabled?(:projects_tokens_optional_encryption, default_enabled: true) ? :optional : :required }
before_validation :mark_remote_mirrors_for_removal, if: -> { RemoteMirror.table_exists? }
@@ -137,7 +136,7 @@ class Project < ActiveRecord::Base
alias_attribute :parent_id, :namespace_id
has_one :last_event, -> {order 'events.created_at DESC'}, class_name: 'Event'
- has_many :boards, before_add: :validate_board_limit
+ has_many :boards
# Project services
has_one :campfire_service
@@ -160,6 +159,7 @@ class Project < ActiveRecord::Base
has_one :pushover_service
has_one :jira_service
has_one :redmine_service
+ has_one :youtrack_service
has_one :custom_issue_tracker_service
has_one :bugzilla_service
has_one :gitlab_issue_tracker_service, inverse_of: :project
@@ -248,10 +248,10 @@ class Project < ActiveRecord::Base
has_many :container_repositories, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :commit_statuses
- # The relation :all_pipelines is intented to be used when we want to get the
+ # The relation :all_pipelines is intended to be used when we want to get the
# whole list of pipelines associated to the project
has_many :all_pipelines, class_name: 'Ci::Pipeline', inverse_of: :project
- # The relation :ci_pipelines is intented to be used when we want to get only
+ # The relation :ci_pipelines is intended to be used when we want to get only
# those pipeline which are directly related to CI. There are
# other pipelines, like webide ones, that we won't retrieve
# if we use this relation.
@@ -305,6 +305,7 @@ class Project < ActiveRecord::Base
delegate :group_runners_enabled, :group_runners_enabled=, :group_runners_enabled?, to: :ci_cd_settings
delegate :group_clusters_enabled?, to: :group, allow_nil: true
delegate :root_ancestor, to: :namespace, allow_nil: true
+ delegate :last_pipeline, to: :commit, allow_nil: true
# Validations
validates :creator, presence: true, on: :create
@@ -458,14 +459,41 @@ class Project < ActiveRecord::Base
# Returns a collection of projects that is either public or visible to the
# logged in user.
- def self.public_or_visible_to_user(user = nil)
- if user
- where('EXISTS (?) OR projects.visibility_level IN (?)',
- user.authorizations_for_projects,
- Gitlab::VisibilityLevel.levels_for_user(user))
- else
- public_to_user
- end
+ #
+ # requested_visiblity_levels: Normally all projects that are visible
+ # to the user (e.g. internal and public) are queried, but this
+ # parameter allows the caller to narrow the search space to optimize
+ # database queries. For instance, a caller may only want to see
+ # internal projects. Instead of querying for internal and public
+ # projects and throwing away public projects, this parameter allows
+ # the query to be targeted for only internal projects.
+ def self.public_or_visible_to_user(user = nil, requested_visibility_levels = [])
+ return public_to_user unless user
+
+ visible_levels = Gitlab::VisibilityLevel.levels_for_user(user)
+ include_private = true
+ requested_visibility_levels = Array(requested_visibility_levels)
+
+ if requested_visibility_levels.present?
+ visible_levels &= requested_visibility_levels
+ include_private = requested_visibility_levels.include?(Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ public_or_internal_rel =
+ if visible_levels.present?
+ where('projects.visibility_level IN (?)', visible_levels)
+ else
+ Project.none
+ end
+
+ private_rel =
+ if include_private
+ where('EXISTS (?)', user.authorizations_for_projects)
+ else
+ Project.none
+ end
+
+ public_or_internal_rel.or(private_rel)
end
# project features may be "disabled", "internal", "enabled" or "public". If "internal",
@@ -595,6 +623,14 @@ class Project < ActiveRecord::Base
end
end
+ def ci_pipelines
+ if builds_enabled?
+ super
+ else
+ super.external
+ end
+ end
+
# returns all ancestor-groups upto but excluding the given namespace
# when no namespace is given, all ancestors upto the top are returned
def ancestors_upto(top = nil, hierarchy_order: nil)
@@ -621,12 +657,25 @@ class Project < ActiveRecord::Base
end
def has_auto_devops_implicitly_enabled?
- auto_devops&.enabled.nil? &&
- (Gitlab::CurrentSettings.auto_devops_enabled? || Feature.enabled?(:force_autodevops_on_by_default, self))
+ auto_devops_config = first_auto_devops_config
+
+ auto_devops_config[:scope] != :project && auto_devops_config[:status]
end
def has_auto_devops_implicitly_disabled?
- auto_devops&.enabled.nil? && !(Gitlab::CurrentSettings.auto_devops_enabled? || Feature.enabled?(:force_autodevops_on_by_default, self))
+ auto_devops_config = first_auto_devops_config
+
+ auto_devops_config[:scope] != :project && !auto_devops_config[:status]
+ end
+
+ def first_auto_devops_config
+ return namespace.first_auto_devops_config if auto_devops&.enabled.nil?
+
+ { scope: :project, status: auto_devops&.enabled || Feature.enabled?(:force_autodevops_on_by_default, self) }
+ end
+
+ def daily_statistics_enabled?
+ Feature.enabled?(:project_daily_statistics, self, default_enabled: true)
end
def empty_repo?
@@ -1186,11 +1235,9 @@ class Project < ActiveRecord::Base
def repo_exists?
strong_memoize(:repo_exists) do
- begin
- repository.exists?
- rescue
- false
- end
+ repository.exists?
+ rescue
+ false
end
end
@@ -1206,7 +1253,7 @@ class Project < ActiveRecord::Base
"#{web_url}.git"
end
- # Is overriden in EE
+ # Is overridden in EE
def lfs_http_url_to_repo(_)
http_url_to_repo
end
@@ -1216,7 +1263,7 @@ class Project < ActiveRecord::Base
end
def fork_source
- return nil unless forked?
+ return unless forked?
forked_from_project || fork_network&.root_project
end
@@ -1364,6 +1411,7 @@ class Project < ActiveRecord::Base
repository.raw_repository.write_ref('HEAD', "refs/heads/#{branch}")
repository.copy_gitattributes(branch)
repository.after_change_head
+ ProjectCacheWorker.perform_async(self.id, [], [:commit_count])
reload_default_branch
else
errors.add(:base, "Could not change HEAD: branch '#{branch}' does not exist")
@@ -1665,7 +1713,7 @@ class Project < ActiveRecord::Base
end
def export_path
- return nil unless namespace.present? || hashed_storage?(:repository)
+ return unless namespace.present? || hashed_storage?(:repository)
import_export_shared.archive_path
end
@@ -1829,7 +1877,7 @@ class Project < ActiveRecord::Base
# Set repository as writable again
def set_repository_writable!
with_lock do
- update_column(repository_read_only, false)
+ update_column(:repository_read_only, false)
end
end
@@ -1916,6 +1964,14 @@ class Project < ActiveRecord::Base
persisted? && path_changed?
end
+ def human_merge_method
+ if merge_method == :ff
+ 'Fast-forward'
+ else
+ merge_method.to_s.humanize
+ end
+ end
+
def merge_method
if self.merge_requests_ff_only_enabled
:ff
@@ -1948,9 +2004,19 @@ class Project < ActiveRecord::Base
return unless storage_upgradable?
if git_transfer_in_progress?
- ProjectMigrateHashedStorageWorker.perform_in(Gitlab::ReferenceCounter::REFERENCE_EXPIRE_TIME, id)
+ HashedStorage::ProjectMigrateWorker.perform_in(Gitlab::ReferenceCounter::REFERENCE_EXPIRE_TIME, id)
else
- ProjectMigrateHashedStorageWorker.perform_async(id)
+ HashedStorage::ProjectMigrateWorker.perform_async(id)
+ end
+ end
+
+ def rollback_to_legacy_storage!
+ return if legacy_storage?
+
+ if git_transfer_in_progress?
+ HashedStorage::ProjectRollbackWorker.perform_in(Gitlab::ReferenceCounter::REFERENCE_EXPIRE_TIME, id)
+ else
+ HashedStorage::ProjectRollbackWorker.perform_async(id)
end
end
@@ -1964,12 +2030,8 @@ class Project < ActiveRecord::Base
@storage = nil if storage_version_changed?
end
- def gl_repository(is_wiki:)
- Gitlab::GlRepository.gl_repository(self, is_wiki)
- end
-
- def reference_counter(wiki: false)
- Gitlab::ReferenceCounter.new(gl_repository(is_wiki: wiki))
+ def reference_counter(type: Gitlab::GlRepository::PROJECT)
+ Gitlab::ReferenceCounter.new(type.identifier_for_subject(self))
end
def badges
@@ -2113,7 +2175,7 @@ class Project < ActiveRecord::Base
end
def wiki_reference_count
- reference_counter(wiki: true).value
+ reference_counter(type: Gitlab::GlRepository::WIKI).value
end
def check_repository_absence!
@@ -2153,17 +2215,6 @@ class Project < ActiveRecord::Base
"projects/#{id}/pushes_since_gc"
end
- # Similar to the normal callbacks that hook into the life cycle of an
- # Active Record object, you can also define callbacks that get triggered
- # when you add an object to an association collection. If any of these
- # callbacks throw an exception, the object will not be added to the
- # collection. Before you add a new board to the boards collection if you
- # already have 1, 2, or n it will fail, but it if you have 0 that is lower
- # than the number of permitted boards per project it won't fail.
- def validate_board_limit(board)
- raise BoardLimitExceeded, 'Number of permitted boards exceeded' if boards.size >= NUMBER_OF_PERMITTED_BOARDS
- end
-
def update_project_statistics
stats = statistics || build_statistics
stats.update(namespace_id: namespace_id)
diff --git a/app/models/project_daily_statistic.rb b/app/models/project_daily_statistic.rb
new file mode 100644
index 00000000000..ff115dd010f
--- /dev/null
+++ b/app/models/project_daily_statistic.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class ProjectDailyStatistic < ActiveRecord::Base
+ belongs_to :project
+
+ scope :of_project, -> (project) { where(project: project) }
+ scope :of_last_30_days, -> { where('date >= ?', 29.days.ago.utc.to_date) }
+ scope :sorted_by_date_desc, -> { order(project_id: :desc, date: :desc) }
+ scope :sum_fetch_count, -> { sum(:fetch_count) }
+end
diff --git a/app/models/project_feature.rb b/app/models/project_feature.rb
index f700090a493..e6787236c4e 100644
--- a/app/models/project_feature.rb
+++ b/app/models/project_feature.rb
@@ -76,7 +76,7 @@ class ProjectFeature < ActiveRecord::Base
# This feature might not be behind a feature flag at all, so default to true
return false unless ::Feature.enabled?(feature, user, default_enabled: true)
- get_permission(user, access_level(feature))
+ get_permission(user, feature)
end
def access_level(feature)
@@ -134,12 +134,12 @@ class ProjectFeature < ActiveRecord::Base
(FEATURES - %i(pages)).each {|f| validator.call("#{f}_access_level")}
end
- def get_permission(user, level)
- case level
+ def get_permission(user, feature)
+ case access_level(feature)
when DISABLED
false
when PRIVATE
- user && (project.team.member?(user) || user.full_private_access?)
+ team_access?(user, feature)
when ENABLED
true
when PUBLIC
@@ -148,4 +148,11 @@ class ProjectFeature < ActiveRecord::Base
true
end
end
+
+ def team_access?(user, feature)
+ return unless user
+ return true if user.full_private_access?
+
+ project.team.member?(user, ProjectFeature.required_minimum_access_level(feature))
+ end
end
diff --git a/app/models/project_services/campfire_service.rb b/app/models/project_services/campfire_service.rb
index 1d7877a1fb5..ad26e42a21b 100644
--- a/app/models/project_services/campfire_service.rb
+++ b/app/models/project_services/campfire_service.rb
@@ -57,7 +57,7 @@ class CampfireService < Service
# https://github.com/basecamp/campfire-api/blob/master/sections/messages.md#create-message
def speak(room_name, message, auth)
room = rooms(auth).find { |r| r["name"] == room_name }
- return nil unless room
+ return unless room
path = "/room/#{room["id"]}/speak.json"
body = {
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 83fd9a34438..fb76bc89c98 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -112,7 +112,7 @@ class IrkerService < Service
end
def consider_uri(uri)
- return nil if uri.scheme.nil?
+ return if uri.scheme.nil?
# Authorize both irc://domain.com/#chan and irc://domain.com/chan
if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil?
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 9066a0b7f1d..81302c516c2 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -205,17 +205,15 @@ class JiraService < IssueTrackerService
# if any transition fails it will log the error message and stop the transition sequence
def transition_issue(issue)
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
- log_error("Issue transition failed", error: error.message, client_url: client_url)
- return false
- end
+ issue.transitions.build.save!(transition: { id: transition_id })
+ rescue => error
+ log_error("Issue transition failed", error: error.message, client_url: client_url)
+ return false
end
end
def add_issue_solved_comment(issue, commit_id, commit_url)
- link_title = "GitLab: Solved by commit #{commit_id}."
+ link_title = "Solved by commit #{commit_id}."
comment = "Issue solved with [#{commit_id}|#{commit_url}]."
link_props = build_remote_link_props(url: commit_url, title: link_title, resolved: true)
send_message(issue, comment, link_props)
@@ -230,7 +228,7 @@ class JiraService < IssueTrackerService
project_name = data[:project][:name]
message = "[#{user_name}|#{user_url}] mentioned this issue in [a #{entity_name} of #{project_name}|#{entity_url}]:\n'#{entity_title.chomp}'"
- link_title = "GitLab: Mentioned on #{entity_name} - #{entity_title}"
+ link_title = "#{entity_name.capitalize} - #{entity_title}"
link_props = build_remote_link_props(url: entity_url, title: link_title)
unless comment_exists?(issue, message)
@@ -278,6 +276,7 @@ class JiraService < IssueTrackerService
{
GlobalID: 'GitLab',
+ relationship: 'mentioned on',
object: {
url: url,
title: title,
diff --git a/app/models/project_services/kubernetes_service.rb b/app/models/project_services/kubernetes_service.rb
index f69edd60003..f650dbd3726 100644
--- a/app/models/project_services/kubernetes_service.rb
+++ b/app/models/project_services/kubernetes_service.rb
@@ -113,7 +113,7 @@ class KubernetesService < DeploymentService
Gitlab::Ci::Variables::Collection.new.tap do |variables|
variables
.append(key: 'KUBE_URL', value: api_url)
- .append(key: 'KUBE_TOKEN', value: token, public: false)
+ .append(key: 'KUBE_TOKEN', value: token, public: false, masked: true)
.append(key: 'KUBE_NAMESPACE', value: actual_namespace)
.append(key: 'KUBECONFIG', value: kubeconfig, public: false, file: true)
@@ -131,8 +131,8 @@ class KubernetesService < DeploymentService
# short time later
def terminals(environment)
with_reactive_cache do |data|
- pods = filter_by_label(data[:pods], app: environment.slug)
- terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }
+ pods = filter_by_project_environment(data[:pods], project.full_path_slug, environment.slug)
+ terminals = pods.flat_map { |pod| terminals_for_pod(api_url, actual_namespace, pod) }.compact
terminals.each { |terminal| add_terminal_auth(terminal, terminal_auth) }
end
end
diff --git a/app/models/project_services/prometheus_service.rb b/app/models/project_services/prometheus_service.rb
index 60cb2d380d5..c68a9d923c8 100644
--- a/app/models/project_services/prometheus_service.rb
+++ b/app/models/project_services/prometheus_service.rb
@@ -71,7 +71,7 @@ class PrometheusService < MonitoringService
end
def prometheus_client
- RestClient::Resource.new(api_url, max_redirects: 0) if api_url && manual_configuration? && active?
+ RestClient::Resource.new(api_url, max_redirects: 0) if should_return_client?
end
def prometheus_available?
@@ -83,6 +83,10 @@ class PrometheusService < MonitoringService
private
+ def should_return_client?
+ api_url && manual_configuration? && active? && valid?
+ end
+
def synchronize_service_state
self.active = prometheus_available? || manual_configuration?
diff --git a/app/models/project_services/slack_slash_commands_service.rb b/app/models/project_services/slack_slash_commands_service.rb
index 6c82e088231..6a454070fe2 100644
--- a/app/models/project_services/slack_slash_commands_service.rb
+++ b/app/models/project_services/slack_slash_commands_service.rb
@@ -22,6 +22,10 @@ class SlackSlashCommandsService < SlashCommandsService
end
end
+ def chat_responder
+ ::Gitlab::Chat::Responder::Slack
+ end
+
private
def format(text)
diff --git a/app/models/project_services/youtrack_service.rb b/app/models/project_services/youtrack_service.rb
new file mode 100644
index 00000000000..957be685aea
--- /dev/null
+++ b/app/models/project_services/youtrack_service.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+class YoutrackService < IssueTrackerService
+ validates :project_url, :issues_url, presence: true, public_url: true, if: :activated?
+
+ prop_accessor :description, :project_url, :issues_url
+
+ # {PROJECT-KEY}-{NUMBER} Examples: YT-1, PRJ-1
+ def self.reference_pattern(only_long: false)
+ if only_long
+ /(?<issue>\b[A-Z][A-Za-z0-9_]*-\d+)/
+ else
+ /(?<issue>\b[A-Z][A-Za-z0-9_]*-\d+)|(#{Issue.reference_prefix}(?<issue>\d+))/
+ end
+ end
+
+ def title
+ 'YouTrack'
+ end
+
+ def description
+ if self.properties && self.properties['description'].present?
+ self.properties['description']
+ else
+ 'YouTrack issue tracker'
+ end
+ end
+
+ def self.to_param
+ 'youtrack'
+ end
+
+ def fields
+ [
+ { type: 'text', name: 'description', placeholder: description },
+ { type: 'text', name: 'project_url', placeholder: 'Project url', required: true },
+ { type: 'text', name: 'issues_url', placeholder: 'Issue url', required: true }
+ ]
+ end
+end
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index c43bd45a62f..268706a6aea 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -59,7 +59,7 @@ class ProjectWiki
# Returns the Gitlab::Git::Wiki object.
def wiki
@wiki ||= begin
- gl_repository = Gitlab::GlRepository.gl_repository(project, true)
+ gl_repository = Gitlab::GlRepository::WIKI.identifier_for_subject(project)
raw_repository = Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', gl_repository, full_path)
create_repo!(raw_repository) unless raw_repository.exists?
@@ -151,7 +151,7 @@ class ProjectWiki
end
def repository
- @repository ||= Repository.new(full_path, @project, disk_path: disk_path, is_wiki: true)
+ @repository ||= Repository.new(full_path, @project, disk_path: disk_path, repo_type: Gitlab::GlRepository::WIKI)
end
def default_branch
@@ -183,7 +183,7 @@ class ProjectWiki
end
def commit_details(action, message = nil, title = nil)
- commit_message = message || default_message(action, title)
+ commit_message = message.presence || default_message(action, title)
git_user = Gitlab::Git::User.from_gitlab(@user)
Gitlab::Git::Wiki::CommitDetails.new(@user.id,
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index d075440b147..597431be65a 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -18,13 +18,23 @@ class ProtectedBranch < ActiveRecord::Base
def self.protected?(project, ref_name)
return true if project.empty_repo? && default_branch_protected?
- refs = project.protected_branches.select(:name)
+ self.matching(ref_name, protected_refs: protected_refs(project)).present?
+ end
- self.matching(ref_name, protected_refs: refs).present?
+ def self.any_protected?(project, ref_names)
+ protected_refs(project).any? do |protected_ref|
+ ref_names.any? do |ref_name|
+ protected_ref.matches?(ref_name)
+ end
+ end
end
def self.default_branch_protected?
Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
end
+
+ def self.protected_refs(project)
+ project.protected_branches.select(:name)
+ end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index ed55a6e572b..574ce12b309 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -19,7 +19,7 @@ class Repository
include Gitlab::RepositoryCacheAdapter
- attr_accessor :full_path, :disk_path, :project, :is_wiki
+ attr_accessor :full_path, :disk_path, :project, :repo_type
delegate :ref_name_for_sha, to: :raw_repository
delegate :bundle_to_disk, to: :raw_repository
@@ -60,12 +60,12 @@ class Repository
xcode_config: :xcode_project?
}.freeze
- def initialize(full_path, project, disk_path: nil, is_wiki: false)
+ def initialize(full_path, project, disk_path: nil, repo_type: Gitlab::GlRepository::PROJECT)
@full_path = full_path
@disk_path = disk_path || full_path
@project = project
@commit_cache = {}
- @is_wiki = is_wiki
+ @repo_type = repo_type
end
def ==(other)
@@ -79,7 +79,7 @@ class Repository
end
def raw_repository
- return nil unless full_path
+ return unless full_path
@raw_repository ||= initialize_raw_repository
end
@@ -103,7 +103,7 @@ class Repository
end
def commit(ref = nil)
- return nil unless exists?
+ return unless exists?
return ref if ref.is_a?(::Commit)
find_commit(ref || root_ref)
@@ -265,16 +265,14 @@ class Repository
# to avoid unnecessary syncing.
def keep_around(*shas)
shas.each do |sha|
- begin
- next unless sha.present? && commit_by(oid: sha)
+ next unless sha.present? && commit_by(oid: sha)
- next if kept_around?(sha)
+ next if kept_around?(sha)
- # This will still fail if the file is corrupted (e.g. 0 bytes)
- raw_repository.write_ref(keep_around_ref_name(sha), sha)
- rescue Gitlab::Git::CommandError => ex
- Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}"
- end
+ # This will still fail if the file is corrupted (e.g. 0 bytes)
+ raw_repository.write_ref(keep_around_ref_name(sha), sha)
+ rescue Gitlab::Git::CommandError => ex
+ Rails.logger.error "Unable to create keep-around reference for repository #{disk_path}: #{ex}"
end
end
@@ -534,10 +532,9 @@ class Repository
end
def root_ref
- # When the repo does not exist, or there is no root ref, we raise this error so no data is cached.
- raw_repository&.root_ref or raise Gitlab::Git::Repository::NoRepository # rubocop:disable Style/AndOr
+ raw_repository&.root_ref
end
- cache_method :root_ref
+ cache_method_asymmetrically :root_ref
# Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/314
def exists?
@@ -854,6 +851,12 @@ class Repository
end
end
+ def merge_to_ref(user, source_sha, merge_request, target_ref, message)
+ branch = merge_request.target_branch
+
+ raw.merge_to_ref(user, source_sha, branch, target_ref, message)
+ end
+
def ff_merge(user, source, target_branch, merge_request: nil)
their_commit_id = commit(source)&.id
raise 'Invalid merge source' if their_commit_id.nil?
@@ -1109,7 +1112,7 @@ class Repository
def initialize_raw_repository
Gitlab::Git::Repository.new(project.repository_storage,
disk_path + '.git',
- Gitlab::GlRepository.gl_repository(project, is_wiki),
+ repo_type.identifier_for_subject(project),
project.full_path)
end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 3461e0bfe70..da523bfa426 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -266,6 +266,7 @@ class Service < ActiveRecord::Base
prometheus
pushover
redmine
+ youtrack
slack_slash_commands
slack
teamcity
diff --git a/app/models/ssh_host_key.rb b/app/models/ssh_host_key.rb
index fd23cc9ac87..b6fb39ee81f 100644
--- a/app/models/ssh_host_key.rb
+++ b/app/models/ssh_host_key.rb
@@ -27,7 +27,7 @@ class SshHostKey
def self.find_by(opts = {})
opts = HashWithIndifferentAccess.new(opts)
- return nil unless opts.key?(:id)
+ return unless opts.key?(:id)
project_id, url = opts[:id].split(':', 2)
project = Project.find_by(id: project_id)
diff --git a/app/models/suggestion.rb b/app/models/suggestion.rb
index 7eee4fbbe5f..22e2f11230d 100644
--- a/app/models/suggestion.rb
+++ b/app/models/suggestion.rb
@@ -1,11 +1,19 @@
# frozen_string_literal: true
class Suggestion < ApplicationRecord
+ include Suggestible
+
belongs_to :note, inverse_of: :suggestions
validates :note, presence: true
validates :commit_id, presence: true, if: :applied?
- delegate :original_position, :position, :noteable, to: :note
+ delegate :position, :noteable, to: :note
+
+ scope :active, -> { where(outdated: false) }
+
+ def diff_file
+ note.latest_diff_file
+ end
def project
noteable.source_project
@@ -19,42 +27,37 @@ class Suggestion < ApplicationRecord
position.file_path
end
- def diff_file
- repository = project.repository
- position.diff_file(repository)
- end
-
- # For now, suggestions only serve as a way to send patches that
- # will change a single line (being able to apply multiple in the same place),
- # which explains `from_line` and `to_line` being the same line.
- # We'll iterate on that in https://gitlab.com/gitlab-org/gitlab-ce/issues/53310
- # when allowing multi-line suggestions.
- def from_line
- position.new_line
- end
- alias_method :to_line, :from_line
-
- def from_original_line
- original_position.new_line
- end
- alias_method :to_original_line, :from_original_line
-
# `from_line_index` and `to_line_index` represents diff/blob line numbers in
# index-like way (N-1).
def from_line_index
from_line - 1
end
- alias_method :to_line_index, :from_line_index
- def appliable?
- return false unless note.supports_suggestion?
+ def to_line_index
+ to_line - 1
+ end
+ def appliable?(cached: true)
!applied? &&
noteable.opened? &&
+ !outdated?(cached: cached) &&
+ note.supports_suggestion? &&
different_content? &&
note.active?
end
+ # Overwrites outdated column
+ def outdated?(cached: true)
+ return super() if cached
+ return true unless diff_file
+
+ from_content != fetch_from_content
+ end
+
+ def target_line
+ position.new_line
+ end
+
private
def different_content?
diff --git a/app/models/todo.rb b/app/models/todo.rb
index d9b86d941b6..2b0dee875a3 100644
--- a/app/models/todo.rb
+++ b/app/models/todo.rb
@@ -31,7 +31,13 @@ class Todo < ActiveRecord::Base
belongs_to :note
belongs_to :project
belongs_to :group
- belongs_to :target, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
+ belongs_to :target, -> {
+ if self.klass.respond_to?(:with_api_entity_associations)
+ self.with_api_entity_associations
+ else
+ self
+ end
+ }, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
belongs_to :user
delegate :name, :email, to: :author, prefix: true, allow_nil: true
@@ -52,6 +58,7 @@ class Todo < ActiveRecord::Base
scope :for_type, -> (type) { where(target_type: type) }
scope :for_target, -> (id) { where(target_id: id) }
scope :for_commit, -> (id) { where(commit_id: id) }
+ scope :with_api_entity_associations, -> { preload(:target, :author, :note, group: :route, project: [:route, { namespace: :route }]) }
state_machine :state, initial: :pending do
event :done do
diff --git a/app/models/user.rb b/app/models/user.rb
index fd32d838e53..d2be26370ff 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -162,9 +162,9 @@ class User < ApplicationRecord
validates :name, presence: true
validates :email, confirmation: true
validates :notification_email, presence: true
- validates :notification_email, email: true, if: ->(user) { user.notification_email != user.email }
- validates :public_email, presence: true, uniqueness: true, email: true, allow_blank: true
- validates :commit_email, email: true, allow_nil: true, if: ->(user) { user.commit_email != user.email }
+ validates :notification_email, devise_email: true, if: ->(user) { user.notification_email != user.email }
+ validates :public_email, presence: true, uniqueness: true, devise_email: true, allow_blank: true
+ validates :commit_email, devise_email: true, allow_nil: true, if: ->(user) { user.commit_email != user.email }
validates :bio, length: { maximum: 255 }, allow_blank: true
validates :projects_limit,
presence: true,
@@ -388,7 +388,7 @@ class User < ApplicationRecord
find_by(id: user_id)
end
- def filter(filter_name)
+ def filter_items(filter_name)
case filter_name
when 'admins'
admins
@@ -470,7 +470,7 @@ class User < ApplicationRecord
end
def by_login(login)
- return nil unless login
+ return unless login
if login.include?('@'.freeze)
unscoped.iwhere(email: login).take
@@ -917,6 +917,10 @@ class User < ApplicationRecord
DeployKey.unscoped.in_projects(authorized_projects.pluck(:id)).distinct(:id)
end
+ def highest_role
+ members.maximum(:access_level) || Gitlab::Access::NO_ACCESS
+ end
+
def accessible_deploy_keys
@accessible_deploy_keys ||= begin
key_ids = project_deploy_keys.pluck(:id)
diff --git a/app/models/user_interacted_project.rb b/app/models/user_interacted_project.rb
index ae6778e49be..5fc59b274f5 100644
--- a/app/models/user_interacted_project.rb
+++ b/app/models/user_interacted_project.rb
@@ -26,16 +26,14 @@ class UserInteractedProject < ActiveRecord::Base
cached_exists?(attributes) do
transaction(requires_new: true) do
- begin
- where(attributes).select(1).first || create!(attributes)
- true # not caching the whole record here for now
- rescue ActiveRecord::RecordNotUnique
- # Note, above queries are not atomic and prone
- # to race conditions (similar like #find_or_create!).
- # In the case where we hit this, the record we want
- # already exists - shortcut and return.
- true
- end
+ where(attributes).select(1).first || create!(attributes)
+ true # not caching the whole record here for now
+ rescue ActiveRecord::RecordNotUnique
+ # Note, above queries are not atomic and prone
+ # to race conditions (similar like #find_or_create!).
+ # In the case where we hit this, the record we want
+ # already exists - shortcut and return.
+ true
end
end
end
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index b1d6d461928..64daba81dcf 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -132,7 +132,7 @@ class WikiPage
# The GitLab Commit instance for this page.
def version
- return nil unless persisted?
+ return unless persisted?
@version ||= @page.version
end
diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb
index 16c58730878..d412a591fdc 100644
--- a/app/policies/global_policy.rb
+++ b/app/policies/global_policy.rb
@@ -68,6 +68,10 @@ class GlobalPolicy < BasePolicy
enable :read_users_list
end
+ rule { ~anonymous }.policy do
+ enable :read_instance_metadata
+ end
+
rule { admin }.policy do
enable :read_custom_attribute
enable :update_custom_attribute
diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb
index c25766a5af8..db49d3bed9c 100644
--- a/app/policies/group_policy.rb
+++ b/app/policies/group_policy.rb
@@ -26,7 +26,7 @@ class GroupPolicy < BasePolicy
condition(:can_change_parent_share_with_group_lock) { can?(:change_share_with_group_lock, @subject.parent) }
condition(:has_projects) do
- GroupProjectsFinder.new(group: @subject, current_user: @user, options: { include_subgroups: true }).execute.any?
+ GroupProjectsFinder.new(group: @subject, current_user: @user, options: { include_subgroups: true, only_owned: true }).execute.any?
end
condition(:has_clusters, scope: :subject) { clusterable_has_clusters? }
@@ -53,8 +53,9 @@ class GroupPolicy < BasePolicy
rule { admin }.enable :read_group
rule { has_projects }.policy do
- enable :read_group
+ enable :read_list
enable :read_label
+ enable :read_group
end
rule { has_access }.enable :read_namespace
diff --git a/app/policies/identity_provider_policy.rb b/app/policies/identity_provider_policy.rb
new file mode 100644
index 00000000000..d34cdd5bdd4
--- /dev/null
+++ b/app/policies/identity_provider_policy.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class IdentityProviderPolicy < BasePolicy
+ desc "Provider is SAML or CAS3"
+ condition(:protected_provider, scope: :subject, score: 0) { %w(saml cas3).include?(@subject.to_s) }
+
+ rule { anonymous }.prevent_all
+
+ rule { default }.policy do
+ enable :unlink
+ enable :link
+ end
+
+ rule { protected_provider }.prevent(:unlink)
+end
diff --git a/app/policies/issuable_policy.rb b/app/policies/issuable_policy.rb
index ecb2797d1d9..537319addc2 100644
--- a/app/policies/issuable_policy.rb
+++ b/app/policies/issuable_policy.rb
@@ -17,6 +17,7 @@ class IssuablePolicy < BasePolicy
enable :reopen_issue
enable :read_merge_request
enable :update_merge_request
+ enable :reopen_merge_request
end
rule { locked & ~is_project_member }.policy do
diff --git a/app/policies/merge_request_policy.rb b/app/policies/merge_request_policy.rb
index a2950951d03..a3692857ff4 100644
--- a/app/policies/merge_request_policy.rb
+++ b/app/policies/merge_request_policy.rb
@@ -1,4 +1,7 @@
# frozen_string_literal: true
class MergeRequestPolicy < IssuablePolicy
+ rule { locked }.policy do
+ prevent :reopen_merge_request
+ end
end
diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb
index cadbc5ae009..dfbad4627eb 100644
--- a/app/policies/project_policy.rb
+++ b/app/policies/project_policy.rb
@@ -152,7 +152,6 @@ class ProjectPolicy < BasePolicy
enable :remove_fork_project
enable :destroy_merge_request
enable :destroy_issue
- enable :remove_pages
enable :set_issue_iid
enable :set_issue_created_at
@@ -188,6 +187,7 @@ class ProjectPolicy < BasePolicy
rule { can?(:reporter_access) }.policy do
enable :download_code
+ enable :read_statistics
enable :download_wiki_code
enable :fork_project
enable :create_project_snippet
@@ -232,6 +232,7 @@ class ProjectPolicy < BasePolicy
enable :admin_merge_request
enable :admin_milestone
enable :update_merge_request
+ enable :reopen_merge_request
enable :create_commit_status
enable :update_commit_status
enable :create_build
@@ -271,6 +272,7 @@ class ProjectPolicy < BasePolicy
enable :admin_pages
enable :read_pages
enable :update_pages
+ enable :remove_pages
enable :read_cluster
enable :add_cluster
enable :create_cluster
@@ -278,6 +280,8 @@ class ProjectPolicy < BasePolicy
enable :admin_cluster
enable :create_environment_terminal
enable :destroy_release
+ enable :destroy_artifacts
+ enable :daily_statistics
end
rule { (mirror_available & can?(:admin_project)) | admin }.enable :admin_remote_mirror
@@ -299,6 +303,8 @@ class ProjectPolicy < BasePolicy
rule { issues_disabled }.policy do
prevent(*create_read_update_admin_destroy(:issue))
+ prevent(*create_read_update_admin_destroy(:board))
+ prevent(*create_read_update_admin_destroy(:list))
end
rule { merge_requests_disabled | repository_disabled }.policy do
@@ -462,7 +468,7 @@ class ProjectPolicy < BasePolicy
when ProjectFeature::DISABLED
false
when ProjectFeature::PRIVATE
- guest? || admin?
+ admin? || team_access_level >= ProjectFeature.required_minimum_access_level(feature)
else
true
end
diff --git a/app/presenters/blobs/unfold_presenter.rb b/app/presenters/blobs/unfold_presenter.rb
new file mode 100644
index 00000000000..7b13db3bb74
--- /dev/null
+++ b/app/presenters/blobs/unfold_presenter.rb
@@ -0,0 +1,75 @@
+# frozen_string_literal: true
+
+require 'gt_one_coercion'
+
+module Blobs
+ class UnfoldPresenter < BlobPresenter
+ include Virtus.model
+ include Gitlab::Utils::StrongMemoize
+
+ attribute :full, Boolean, default: false
+ attribute :since, GtOneCoercion
+ attribute :to, GtOneCoercion
+ attribute :bottom, Boolean
+ attribute :unfold, Boolean, default: true
+ attribute :offset, Integer
+ attribute :indent, Integer, default: 0
+
+ def initialize(blob, params)
+ @subject = blob
+ @all_lines = highlight.lines
+ super(params)
+
+ if full?
+ self.attributes = { since: 1, to: @all_lines.size, bottom: false, unfold: false, offset: 0, indent: 0 }
+ end
+ end
+
+ # Converts a String array to Gitlab::Diff::Line array, with match line added
+ def diff_lines
+ diff_lines = lines.map do |line|
+ Gitlab::Diff::Line.new(line, nil, nil, nil, nil, rich_text: line)
+ end
+
+ add_match_line(diff_lines)
+
+ diff_lines
+ end
+
+ def lines
+ strong_memoize(:lines) do
+ lines = @all_lines
+ lines = lines[since - 1..to - 1] unless full?
+ lines.map(&:html_safe)
+ end
+ end
+
+ def match_line_text
+ return '' if bottom?
+
+ lines_length = lines.length - 1
+ line = [since, lines_length].join(',')
+ "@@ -#{line}+#{line} @@"
+ end
+
+ private
+
+ def add_match_line(diff_lines)
+ return unless unfold?
+
+ if bottom? && to < @all_lines.size
+ old_pos = to - offset
+ new_pos = to
+ elsif since != 1
+ old_pos = new_pos = since
+ end
+
+ # Match line is not needed when it reaches the top limit or bottom limit of the file.
+ return unless new_pos
+
+ match_line = Gitlab::Diff::Line.new(match_line_text, 'match', nil, old_pos, new_pos)
+
+ bottom? ? diff_lines.push(match_line) : diff_lines.unshift(match_line)
+ end
+ end
+end
diff --git a/app/presenters/ci/build_runner_presenter.rb b/app/presenters/ci/build_runner_presenter.rb
index 300f85e1e9d..29656b17183 100644
--- a/app/presenters/ci/build_runner_presenter.rb
+++ b/app/presenters/ci/build_runner_presenter.rb
@@ -2,6 +2,11 @@
module Ci
class BuildRunnerPresenter < SimpleDelegator
+ include Gitlab::Utils::StrongMemoize
+
+ RUNNER_REMOTE_TAG_PREFIX = 'refs/tags/'.freeze
+ RUNNER_REMOTE_BRANCH_PREFIX = 'refs/remotes/origin/'.freeze
+
def artifacts
return unless options[:artifacts]
@@ -11,6 +16,35 @@ module Ci
list.flatten.compact
end
+ def ref_type
+ if tag
+ 'tag'
+ else
+ 'branch'
+ end
+ end
+
+ def git_depth
+ strong_memoize(:git_depth) do
+ git_depth = variables&.find { |variable| variable[:key] == 'GIT_DEPTH' }&.dig(:value)
+ git_depth.to_i
+ end
+ end
+
+ def refspecs
+ specs = []
+
+ if git_depth > 0
+ specs << refspec_for_branch(ref) if branch? || merge_request_event?
+ specs << refspec_for_tag(ref) if tag?
+ else
+ specs << refspec_for_branch
+ specs << refspec_for_tag
+ end
+
+ specs
+ end
+
private
def create_archive(artifacts)
@@ -41,5 +75,13 @@ module Ci
}
end
end
+
+ def refspec_for_branch(ref = '*')
+ "+#{Gitlab::Git::BRANCH_REF_PREFIX}#{ref}:#{RUNNER_REMOTE_BRANCH_PREFIX}#{ref}"
+ end
+
+ def refspec_for_tag(ref = '*')
+ "+#{Gitlab::Git::TAG_REF_PREFIX}#{ref}:#{RUNNER_REMOTE_TAG_PREFIX}#{ref}"
+ end
end
end
diff --git a/app/presenters/ci/pipeline_presenter.rb b/app/presenters/ci/pipeline_presenter.rb
index 57daf04efc6..1c1347c5a57 100644
--- a/app/presenters/ci/pipeline_presenter.rb
+++ b/app/presenters/ci/pipeline_presenter.rb
@@ -3,6 +3,7 @@
module Ci
class PipelinePresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
+ include ActionView::Helpers::UrlHelper
# We use a class method here instead of a constant, allowing EE to redefine
# the returned `Hash` more easily.
@@ -32,5 +33,57 @@ module Ci
"Pipeline is redundant and is auto-canceled by Pipeline ##{auto_canceled_by_id}"
end
end
+
+ def ref_text
+ if pipeline.detached_merge_request_pipeline?
+ _("for %{link_to_merge_request} with %{link_to_merge_request_source_branch}").html_safe % { link_to_merge_request: link_to_merge_request, link_to_merge_request_source_branch: link_to_merge_request_source_branch }
+ elsif pipeline.merge_request_pipeline?
+ _("for %{link_to_merge_request} with %{link_to_merge_request_source_branch} into %{link_to_merge_request_target_branch}").html_safe % { link_to_merge_request: link_to_merge_request, link_to_merge_request_source_branch: link_to_merge_request_source_branch, link_to_merge_request_target_branch: link_to_merge_request_target_branch }
+ elsif pipeline.ref
+ if pipeline.ref_exists?
+ _("for %{link_to_pipeline_ref}").html_safe % { link_to_pipeline_ref: link_to_pipeline_ref }
+ else
+ _("for %{ref}") % { ref: content_tag(:span, pipeline.ref, class: 'ref-name') }
+ end
+ end
+ end
+
+ def link_to_pipeline_ref
+ link_to(pipeline.ref,
+ project_commits_path(pipeline.project, pipeline.ref),
+ class: "ref-name")
+ end
+
+ def link_to_merge_request
+ return unless merge_request_presenter
+
+ link_to(merge_request_presenter.to_reference,
+ project_merge_request_path(merge_request_presenter.project, merge_request_presenter),
+ class: 'mr-iid')
+ end
+
+ def link_to_merge_request_source_branch
+ return unless merge_request_presenter
+
+ link_to(merge_request_presenter.source_branch,
+ merge_request_presenter.source_branch_commits_path,
+ class: 'ref-name')
+ end
+
+ def link_to_merge_request_target_branch
+ return unless merge_request_presenter
+
+ link_to(merge_request_presenter.target_branch,
+ merge_request_presenter.target_branch_commits_path,
+ class: 'ref-name')
+ end
+
+ private
+
+ def merge_request_presenter
+ return unless pipeline.triggered_by_merge_request?
+
+ @merge_request_presenter ||= pipeline.merge_request.present(current_user: current_user)
+ end
end
end
diff --git a/app/presenters/clusterable_presenter.rb b/app/presenters/clusterable_presenter.rb
index d94d9118eee..34bdf156623 100644
--- a/app/presenters/clusterable_presenter.rb
+++ b/app/presenters/clusterable_presenter.rb
@@ -44,6 +44,10 @@ class ClusterablePresenter < Gitlab::View::Presenter::Delegated
raise NotImplementedError
end
+ def update_applications_cluster_path(cluster, application)
+ raise NotImplementedError
+ end
+
def cluster_path(cluster, params = {})
raise NotImplementedError
end
diff --git a/app/presenters/commit_status_presenter.rb b/app/presenters/commit_status_presenter.rb
index 0cd77da6303..28a25c8b7a3 100644
--- a/app/presenters/commit_status_presenter.rb
+++ b/app/presenters/commit_status_presenter.rb
@@ -11,7 +11,8 @@ class CommitStatusPresenter < Gitlab::View::Presenter::Delegated
runner_unsupported: 'Your runner is outdated, please upgrade your runner',
stale_schedule: 'Delayed job could not be executed by some reason, please try again',
job_execution_timeout: 'The script exceeded the maximum execution time set for the job',
- archived_failure: 'The job is archived and cannot be run'
+ archived_failure: 'The job is archived and cannot be run',
+ unmet_prerequisites: 'The job failed to complete prerequisite tasks'
}.freeze
private_constant :CALLOUT_FAILURE_MESSAGES
diff --git a/app/presenters/group_clusterable_presenter.rb b/app/presenters/group_clusterable_presenter.rb
index ef6bbc0d109..f5b0bb64487 100644
--- a/app/presenters/group_clusterable_presenter.rb
+++ b/app/presenters/group_clusterable_presenter.rb
@@ -14,6 +14,11 @@ class GroupClusterablePresenter < ClusterablePresenter
install_applications_group_cluster_path(clusterable, cluster, application)
end
+ override :update_applications_cluster_path
+ def update_applications_cluster_path(cluster, application)
+ update_applications_group_cluster_path(clusterable, cluster, application)
+ end
+
override :cluster_path
def cluster_path(cluster, params = {})
group_cluster_path(clusterable, cluster, params)
diff --git a/app/presenters/merge_request_presenter.rb b/app/presenters/merge_request_presenter.rb
index c59e73f824c..284b1ad9b55 100644
--- a/app/presenters/merge_request_presenter.rb
+++ b/app/presenters/merge_request_presenter.rb
@@ -98,6 +98,18 @@ class MergeRequestPresenter < Gitlab::View::Presenter::Delegated
end
end
+ def target_branch_path
+ if target_branch_exists?
+ project_branch_path(project, target_branch)
+ end
+ end
+
+ def source_branch_commits_path
+ if source_branch_exists?
+ project_commits_path(source_project, source_branch)
+ end
+ end
+
def source_branch_path
if source_branch_exists?
project_branch_path(source_project, source_branch)
diff --git a/app/presenters/project_clusterable_presenter.rb b/app/presenters/project_clusterable_presenter.rb
index 63e69b91b11..8661ee02b68 100644
--- a/app/presenters/project_clusterable_presenter.rb
+++ b/app/presenters/project_clusterable_presenter.rb
@@ -14,6 +14,11 @@ class ProjectClusterablePresenter < ClusterablePresenter
install_applications_project_cluster_path(clusterable, cluster, application)
end
+ override :update_applications_cluster_path
+ def update_applications_cluster_path(cluster, application)
+ update_applications_project_cluster_path(clusterable, cluster, application)
+ end
+
override :cluster_path
def cluster_path(cluster, params = {})
project_cluster_path(clusterable, cluster, params)
diff --git a/app/presenters/project_presenter.rb b/app/presenters/project_presenter.rb
index 4cac90c2567..161eebcfb3f 100644
--- a/app/presenters/project_presenter.rb
+++ b/app/presenters/project_presenter.rb
@@ -42,11 +42,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
def empty_repo_statistics_anchors
[
- license_anchor_data,
- commits_anchor_data,
- branches_anchor_data,
- tags_anchor_data,
- files_anchor_data
+ license_anchor_data
].compact.select { |item| item.is_link }
end
@@ -55,9 +51,7 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
new_file_anchor_data,
readme_anchor_data,
changelog_anchor_data,
- contribution_guide_anchor_data,
- autodevops_anchor_data,
- kubernetes_cluster_anchor_data
+ contribution_guide_anchor_data
].compact.reject { |item| item.is_link }
end
@@ -315,6 +309,10 @@ class ProjectPresenter < Gitlab::View::Presenter::Delegated
project.tag_list.take(MAX_TOPICS_TO_SHOW) # rubocop: disable CodeReuse/ActiveRecord
end
+ def topics_not_shown
+ project.tag_list - topics_to_show
+ end
+
def count_of_extra_topics_not_shown
if project.tag_list.count > MAX_TOPICS_TO_SHOW
project.tag_list.count - MAX_TOPICS_TO_SHOW
diff --git a/app/serializers/acts_as_taggable_on/tag_entity.rb b/app/serializers/acts_as_taggable_on/tag_entity.rb
new file mode 100644
index 00000000000..d4e4b69f8fa
--- /dev/null
+++ b/app/serializers/acts_as_taggable_on/tag_entity.rb
@@ -0,0 +1,6 @@
+# frozen_string_literal: true
+
+class ActsAsTaggableOn::TagEntity < Grape::Entity
+ expose :id
+ expose :name
+end
diff --git a/app/serializers/acts_as_taggable_on/tag_serializer.rb b/app/serializers/acts_as_taggable_on/tag_serializer.rb
new file mode 100644
index 00000000000..87f53606aa1
--- /dev/null
+++ b/app/serializers/acts_as_taggable_on/tag_serializer.rb
@@ -0,0 +1,5 @@
+# frozen_string_literal: true
+
+class ActsAsTaggableOn::TagSerializer < BaseSerializer
+ entity ActsAsTaggableOn::TagEntity
+end
diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb
index 02df1480828..a4a2c015c4e 100644
--- a/app/serializers/cluster_application_entity.rb
+++ b/app/serializers/cluster_application_entity.rb
@@ -6,6 +6,7 @@ class ClusterApplicationEntity < Grape::Entity
expose :status_reason
expose :version
expose :external_ip, if: -> (e, _) { e.respond_to?(:external_ip) }
+ expose :external_hostname, if: -> (e, _) { e.respond_to?(:external_hostname) }
expose :hostname, if: -> (e, _) { e.respond_to?(:hostname) }
expose :email, if: -> (e, _) { e.respond_to?(:email) }
expose :update_available?, as: :update_available, if: -> (e, _) { e.respond_to?(:update_available?) }
diff --git a/app/serializers/concerns/user_status_tooltip.rb b/app/serializers/concerns/user_status_tooltip.rb
index aa6e67e3351..633b117d392 100644
--- a/app/serializers/concerns/user_status_tooltip.rb
+++ b/app/serializers/concerns/user_status_tooltip.rb
@@ -11,7 +11,7 @@ module UserStatusTooltip
expose :user_status_if_loaded, as: :status_tooltip_html
def user_status_if_loaded
- return nil unless object.association(:status).loaded?
+ return unless object.association(:status).loaded?
user_status(object)
end
diff --git a/app/serializers/detailed_status_entity.rb b/app/serializers/detailed_status_entity.rb
index da994d78286..4f23ef0ed82 100644
--- a/app/serializers/detailed_status_entity.rb
+++ b/app/serializers/detailed_status_entity.rb
@@ -9,16 +9,14 @@ class DetailedStatusEntity < Grape::Entity
expose :details_path
expose :illustration do |status|
- begin
- illustration = {
- image: ActionController::Base.helpers.image_path(status.illustration[:image])
- }
- illustration = status.illustration.merge(illustration)
+ illustration = {
+ image: ActionController::Base.helpers.image_path(status.illustration[:image])
+ }
+ illustration = status.illustration.merge(illustration)
- illustration
- rescue NotImplementedError
- # ignored
- end
+ illustration
+ rescue NotImplementedError
+ # ignored
end
expose :favicon do |status|
diff --git a/app/serializers/diff_file_base_entity.rb b/app/serializers/diff_file_base_entity.rb
index ede9e04b722..d8630165e49 100644
--- a/app/serializers/diff_file_base_entity.rb
+++ b/app/serializers/diff_file_base_entity.rb
@@ -27,9 +27,13 @@ class DiffFileBaseEntity < Grape::Entity
next unless merge_request.source_project
- project_edit_blob_path(merge_request.source_project,
- tree_join(merge_request.source_branch, diff_file.new_path),
- options)
+ if Feature.enabled?(:web_ide_default)
+ ide_edit_path(merge_request.source_project, merge_request.source_branch, diff_file.new_path)
+ else
+ project_edit_blob_path(merge_request.source_project,
+ tree_join(merge_request.source_branch, diff_file.new_path),
+ options)
+ end
end
expose :old_path_html do |diff_file|
diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb
index 01ee7af37ed..d3d5883e46b 100644
--- a/app/serializers/diff_file_entity.rb
+++ b/app/serializers/diff_file_entity.rb
@@ -7,7 +7,7 @@ class DiffFileEntity < DiffFileBaseEntity
expose :added_lines
expose :removed_lines
- expose :load_collapsed_diff_url, if: -> (diff_file, options) { diff_file.viewer.collapsed? && options[:merge_request] } do |diff_file|
+ expose :load_collapsed_diff_url, if: -> (diff_file, options) { options[:merge_request] } do |diff_file|
merge_request = options[:merge_request]
project = merge_request.target_project
@@ -57,6 +57,10 @@ class DiffFileEntity < DiffFileBaseEntity
diff_file.diff_lines_for_serializer
end
+ expose :is_fully_expanded, if: -> (diff_file, _) { diff_file.text? } do |diff_file|
+ diff_file.fully_expanded?
+ end
+
# Used for parallel diffs
expose :parallel_diff_lines, using: DiffLineParallelEntity, if: -> (diff_file, _) { diff_file.text? }
end
diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb
index 4a7d13915dd..8258135da4e 100644
--- a/app/serializers/environment_entity.rb
+++ b/app/serializers/environment_entity.rb
@@ -8,10 +8,11 @@ class EnvironmentEntity < Grape::Entity
expose :state
expose :external_url
expose :environment_type
+ expose :name_without_type
expose :last_deployment, using: DeploymentEntity
expose :stop_action_available?, as: :has_stop_action
- expose :metrics_path, if: -> (environment, _) { environment.has_metrics? } do |environment|
+ expose :metrics_path, if: -> (*) { environment.has_metrics? } do |environment|
metrics_project_environment_path(environment.project, environment)
end
diff --git a/app/serializers/merge_request_for_pipeline_entity.rb b/app/serializers/merge_request_for_pipeline_entity.rb
new file mode 100644
index 00000000000..17a5c4ebbf9
--- /dev/null
+++ b/app/serializers/merge_request_for_pipeline_entity.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class MergeRequestForPipelineEntity < Grape::Entity
+ include RequestAwareEntity
+
+ expose :iid
+
+ expose :path do |merge_request|
+ project_merge_request_path(merge_request.project, merge_request)
+ end
+
+ expose :title
+ expose :source_branch
+ expose :source_branch_commits_path, as: :source_branch_path
+ expose :target_branch
+ expose :target_branch_commits_path, as: :target_branch_path
+end
diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb
index 29b1a6c244b..fba72410217 100644
--- a/app/serializers/pipeline_entity.rb
+++ b/app/serializers/pipeline_entity.rb
@@ -23,11 +23,13 @@ class PipelineEntity < Grape::Entity
expose :latest?, as: :latest
expose :stuck?, as: :stuck
expose :auto_devops_source?, as: :auto_devops
- expose :merge_request?, as: :merge_request
+ expose :merge_request_event?, as: :merge_request
expose :has_yaml_errors?, as: :yaml_errors
expose :can_retry?, as: :retryable
expose :can_cancel?, as: :cancelable
expose :failure_reason?, as: :failure_reason
+ expose :detached_merge_request_pipeline?, as: :detached_merge_request_pipeline
+ expose :merge_request_pipeline?, as: :merge_request_pipeline
end
expose :details do
@@ -36,6 +38,10 @@ class PipelineEntity < Grape::Entity
expose :finished_at
end
+ expose :merge_request, if: -> (*) { has_presentable_merge_request? }, with: MergeRequestForPipelineEntity do |pipeline|
+ pipeline.merge_request.present(current_user: request.current_user)
+ end
+
expose :ref do
expose :name do |pipeline|
pipeline.ref
@@ -49,7 +55,7 @@ class PipelineEntity < Grape::Entity
expose :tag?, as: :tag
expose :branch?, as: :branch
- expose :merge_request?, as: :merge_request
+ expose :merge_request_event?, as: :merge_request
end
expose :commit, using: CommitEntity
@@ -81,6 +87,11 @@ class PipelineEntity < Grape::Entity
pipeline.cancelable?
end
+ def has_presentable_merge_request?
+ pipeline.triggered_by_merge_request? &&
+ can?(request.current_user, :read_merge_request, pipeline.merge_request)
+ end
+
def detailed_status
pipeline.detailed_status(request.current_user)
end
diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb
index 7451433a841..dbbeca9431d 100644
--- a/app/serializers/pipeline_serializer.rb
+++ b/app/serializers/pipeline_serializer.rb
@@ -15,6 +15,7 @@ class PipelineSerializer < BaseSerializer
:manual_actions,
:scheduled_actions,
:artifacts,
+ :merge_request,
{
pending_builds: :project,
project: [:route, { namespace: :route }],
diff --git a/app/services/after_branch_delete_service.rb b/app/services/after_branch_delete_service.rb
index e7eb74d3e7d..ece9fbbef43 100644
--- a/app/services/after_branch_delete_service.rb
+++ b/app/services/after_branch_delete_service.rb
@@ -1,9 +1,6 @@
# frozen_string_literal: true
-##
-# Branch can be deleted either by DeleteBranchService
-# or by GitPushService.
-#
+# Branch can be deleted either by DeleteBranchService or by Git::BranchPushService.
class AfterBranchDeleteService < BaseService
attr_reader :branch_name
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index 2e4643ed668..9146eb96533 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -38,7 +38,7 @@ module ApplicationSettings
def performance_bar_allowed_group_id
performance_bar_enabled = !params.key?(:performance_bar_enabled) || params.delete(:performance_bar_enabled)
group_full_path = params.delete(:performance_bar_allowed_group_path)
- return nil unless Gitlab::Utils.to_boolean(performance_bar_enabled)
+ return unless Gitlab::Utils.to_boolean(performance_bar_enabled)
Group.find_by_full_path(group_full_path)&.id if group_full_path.present?
end
diff --git a/app/services/auth/container_registry_authentication_service.rb b/app/services/auth/container_registry_authentication_service.rb
index e95ba09c006..707caee482c 100644
--- a/app/services/auth/container_registry_authentication_service.rb
+++ b/app/services/auth/container_registry_authentication_service.rb
@@ -116,7 +116,7 @@ module Auth
build_can_pull?(requested_project) || user_can_pull?(requested_project) || deploy_token_can_pull?(requested_project)
when 'push'
build_can_push?(requested_project) || user_can_push?(requested_project)
- when '*'
+ when '*', 'delete'
user_can_admin?(requested_project)
else
false
diff --git a/app/services/boards/visits/latest_service.rb b/app/services/boards/visits/latest_service.rb
index 9e4c77a6317..d13e25b4f12 100644
--- a/app/services/boards/visits/latest_service.rb
+++ b/app/services/boards/visits/latest_service.rb
@@ -4,13 +4,15 @@ module Boards
module Visits
class LatestService < Boards::BaseService
def execute
- return nil unless current_user
+ return unless current_user
- if parent.is_a?(Group)
- BoardGroupRecentVisit.latest(current_user, parent)
- else
- BoardProjectRecentVisit.latest(current_user, parent)
- end
+ recent_visit_model.latest(current_user, parent, count: params[:count])
+ end
+
+ private
+
+ def recent_visit_model
+ parent.is_a?(Group) ? BoardGroupRecentVisit : BoardProjectRecentVisit
end
end
end
diff --git a/app/services/ci/create_pipeline_service.rb b/app/services/ci/create_pipeline_service.rb
index 354e53a367c..8973c5ffc9e 100644
--- a/app/services/ci/create_pipeline_service.rb
+++ b/app/services/ci/create_pipeline_service.rb
@@ -25,7 +25,9 @@ module Ci
origin_ref: params[:ref],
checkout_sha: params[:checkout_sha],
after_sha: params[:after],
- before_sha: params[:before],
+ before_sha: params[:before], # The base SHA of the source branch (i.e merge_request.diff_base_sha).
+ source_sha: params[:source_sha], # The HEAD SHA of the source branch (i.e merge_request.diff_head_sha).
+ target_sha: params[:target_sha], # The HEAD SHA of the target branch.
trigger_request: trigger_request,
schedule: schedule,
merge_request: merge_request,
@@ -36,6 +38,7 @@ module Ci
project: project,
current_user: current_user,
push_options: params[:push_options],
+ chat_data: params[:chat_data],
**extra_options(options))
sequence = Gitlab::Ci::Pipeline::Chain::Sequence
@@ -111,10 +114,10 @@ module Ci
def extra_options(options = {})
# In Ruby 2.4, even when options is empty, f(**options) doesn't work when f
# doesn't have any parameters. We reproduce the Ruby 2.5 behavior by
- # checking explicitely that no arguments are given.
+ # checking explicitly that no arguments are given.
raise ArgumentError if options.any?
- {} # overriden in EE
+ {} # overridden in EE
end
end
end
diff --git a/app/services/ci/destroy_pipeline_service.rb b/app/services/ci/destroy_pipeline_service.rb
index 5c4a34043c1..2292ec42b16 100644
--- a/app/services/ci/destroy_pipeline_service.rb
+++ b/app/services/ci/destroy_pipeline_service.rb
@@ -6,6 +6,8 @@ module Ci
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :destroy_pipeline, pipeline)
pipeline.destroy!
+
+ Gitlab::Cache::Ci::ProjectPipelineStatus.new(pipeline.project).delete_from_cache
end
end
end
diff --git a/app/services/ci/pipeline_trigger_service.rb b/app/services/ci/pipeline_trigger_service.rb
index 4ba3f5fb8ba..2dbb7c3917d 100644
--- a/app/services/ci/pipeline_trigger_service.rb
+++ b/app/services/ci/pipeline_trigger_service.rb
@@ -38,11 +38,11 @@ module Ci
end
def create_pipeline_from_job(job)
- # overriden in EE
+ # overridden in EE
end
def job_from_token
- # overriden in EE
+ # overridden in EE
end
def variables
diff --git a/app/services/ci/prepare_build_service.rb b/app/services/ci/prepare_build_service.rb
new file mode 100644
index 00000000000..32f11438b79
--- /dev/null
+++ b/app/services/ci/prepare_build_service.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Ci
+ class PrepareBuildService
+ attr_reader :build
+
+ def initialize(build)
+ @build = build
+ end
+
+ def execute
+ prerequisites.each(&:complete!)
+
+ unless build.enqueue
+ build.drop!(:unmet_prerequisites)
+ end
+ end
+
+ private
+
+ def prerequisites
+ build.prerequisites
+ end
+ end
+end
diff --git a/app/services/clusters/applications/base_helm_service.rb b/app/services/clusters/applications/base_helm_service.rb
index 8a71730d5ec..c38b2656260 100644
--- a/app/services/clusters/applications/base_helm_service.rb
+++ b/app/services/clusters/applications/base_helm_service.rb
@@ -46,6 +46,10 @@ module Clusters
@install_command ||= app.install_command
end
+ def update_command
+ @update_command ||= app.update_command
+ end
+
def upgrade_command(new_values = "")
app.upgrade_command(new_values)
end
diff --git a/app/services/clusters/applications/base_service.rb b/app/services/clusters/applications/base_service.rb
new file mode 100644
index 00000000000..cbd1cf03ae1
--- /dev/null
+++ b/app/services/clusters/applications/base_service.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Applications
+ class BaseService
+ InvalidApplicationError = Class.new(StandardError)
+
+ attr_reader :cluster, :current_user, :params
+
+ def initialize(cluster, user, params = {})
+ @cluster = cluster
+ @current_user = user
+ @params = params.dup
+ end
+
+ def execute(request)
+ instantiate_application.tap do |application|
+ if application.has_attribute?(:hostname)
+ application.hostname = params[:hostname]
+ end
+
+ if application.has_attribute?(:email)
+ application.email = params[:email]
+ end
+
+ if application.respond_to?(:oauth_application)
+ application.oauth_application = create_oauth_application(application, request)
+ end
+
+ worker = worker_class(application)
+
+ application.make_scheduled!
+
+ worker.perform_async(application.name, application.id)
+ end
+ end
+
+ protected
+
+ def worker_class(application)
+ raise NotImplementedError
+ end
+
+ def builders
+ raise NotImplementedError
+ end
+
+ def project_builders
+ raise NotImplementedError
+ end
+
+ def instantiate_application
+ builder.call(@cluster) || raise(InvalidApplicationError, "invalid application: #{application_name}")
+ end
+
+ def builder
+ builders[application_name] || raise(InvalidApplicationError, "invalid application: #{application_name}")
+ end
+
+ def application_name
+ params[:application]
+ end
+
+ def create_oauth_application(application, request)
+ oauth_application_params = {
+ name: params[:application],
+ redirect_uri: application.callback_url,
+ scopes: 'api read_user openid',
+ owner: current_user
+ }
+
+ ::Applications::CreateService.new(current_user, oauth_application_params).execute(request)
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/applications/check_ingress_ip_address_service.rb b/app/services/clusters/applications/check_ingress_ip_address_service.rb
index 0ec06e776a7..e254a0358a0 100644
--- a/app/services/clusters/applications/check_ingress_ip_address_service.rb
+++ b/app/services/clusters/applications/check_ingress_ip_address_service.rb
@@ -11,9 +11,13 @@ module Clusters
def execute
return if app.external_ip
+ return if app.external_hostname
return unless try_obtain_lease
- app.update!(external_ip: ingress_ip) if ingress_ip
+ app.external_ip = ingress_ip if ingress_ip
+ app.external_hostname = ingress_hostname if ingress_hostname
+
+ app.save! if app.changed?
end
private
@@ -25,12 +29,16 @@ module Clusters
end
def ingress_ip
- service.status.loadBalancer.ingress&.first&.ip
+ ingress_service&.ip
+ end
+
+ def ingress_hostname
+ ingress_service&.hostname
end
- def service
+ def ingress_service
strong_memoize(:ingress_service) do
- app.ingress_service
+ app.ingress_service.status.loadBalancer.ingress&.first
end
end
end
diff --git a/app/services/clusters/applications/create_service.rb b/app/services/clusters/applications/create_service.rb
index 92c2c1b9834..c6f729aaa8a 100644
--- a/app/services/clusters/applications/create_service.rb
+++ b/app/services/clusters/applications/create_service.rb
@@ -2,52 +2,19 @@
module Clusters
module Applications
- class CreateService
- InvalidApplicationError = Class.new(StandardError)
-
- attr_reader :cluster, :current_user, :params
-
- def initialize(cluster, user, params = {})
- @cluster = cluster
- @current_user = user
- @params = params.dup
- end
-
- def execute(request)
- create_application.tap do |application|
- if application.has_attribute?(:hostname)
- application.hostname = params[:hostname]
- end
-
- if application.has_attribute?(:email)
- application.email = params[:email]
- end
-
- if application.respond_to?(:oauth_application)
- application.oauth_application = create_oauth_application(application, request)
- end
-
- application.save!
-
- Clusters::Applications::ScheduleInstallationService.new(application).execute
- end
- end
-
+ class CreateService < Clusters::Applications::BaseService
private
- def create_application
- builder.call(@cluster)
- end
-
- def builder
- builders[application_name] || raise(InvalidApplicationError, "invalid application: #{application_name}")
+ def worker_class(application)
+ application.updateable? ? ClusterUpgradeAppWorker : ClusterInstallAppWorker
end
def builders
{
"helm" => -> (cluster) { cluster.application_helm || cluster.build_application_helm },
"ingress" => -> (cluster) { cluster.application_ingress || cluster.build_application_ingress },
- "cert_manager" => -> (cluster) { cluster.application_cert_manager || cluster.build_application_cert_manager }
+ "cert_manager" => -> (cluster) { cluster.application_cert_manager || cluster.build_application_cert_manager },
+ "runner" => -> (cluster) { cluster.application_runner || cluster.build_application_runner }
}.tap do |hash|
hash.merge!(project_builders) if cluster.project_type?
end
@@ -58,26 +25,10 @@ module Clusters
def project_builders
{
"prometheus" => -> (cluster) { cluster.application_prometheus || cluster.build_application_prometheus },
- "runner" => -> (cluster) { cluster.application_runner || cluster.build_application_runner },
"jupyter" => -> (cluster) { cluster.application_jupyter || cluster.build_application_jupyter },
"knative" => -> (cluster) { cluster.application_knative || cluster.build_application_knative }
}
end
-
- def application_name
- params[:application]
- end
-
- def create_oauth_application(application, request)
- oauth_application_params = {
- name: params[:application],
- redirect_uri: application.callback_url,
- scopes: 'api read_user openid',
- owner: current_user
- }
-
- ::Applications::CreateService.new(current_user, oauth_application_params).execute(request)
- end
end
end
end
diff --git a/app/services/clusters/applications/install_service.rb b/app/services/clusters/applications/install_service.rb
index 5a65dc4ef59..5bd3623a558 100644
--- a/app/services/clusters/applications/install_service.rb
+++ b/app/services/clusters/applications/install_service.rb
@@ -6,19 +6,17 @@ module Clusters
def execute
return unless app.scheduled?
- begin
- app.make_installing!
- helm_api.install(install_command)
+ app.make_installing!
+ helm_api.install(install_command)
- ClusterWaitForAppInstallationWorker.perform_in(
- ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id)
- rescue Kubeclient::HttpError => e
- log_error(e)
- app.make_errored!("Kubernetes error: #{e.error_code}")
- rescue StandardError => e
- log_error(e)
- app.make_errored!("Can't start installation process.")
- end
+ ClusterWaitForAppInstallationWorker.perform_in(
+ ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id)
+ rescue Kubeclient::HttpError => e
+ log_error(e)
+ app.make_errored!("Kubernetes error: #{e.error_code}")
+ rescue StandardError => e
+ log_error(e)
+ app.make_errored!("Can't start installation process.")
end
end
end
diff --git a/app/services/clusters/applications/patch_service.rb b/app/services/clusters/applications/patch_service.rb
new file mode 100644
index 00000000000..20c739af7a2
--- /dev/null
+++ b/app/services/clusters/applications/patch_service.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Applications
+ class PatchService < BaseHelmService
+ def execute
+ return unless app.scheduled?
+
+ app.make_updating!
+
+ helm_api.update(update_command)
+
+ ClusterWaitForAppInstallationWorker.perform_in(
+ ClusterWaitForAppInstallationWorker::INTERVAL, app.name, app.id)
+ rescue Kubeclient::HttpError => e
+ log_error(e)
+ app.make_update_errored!("Kubernetes error: #{e.error_code}")
+ rescue StandardError => e
+ log_error(e)
+ app.make_update_errored!("Can't start update process.")
+ end
+ end
+ end
+end
diff --git a/app/services/clusters/applications/schedule_installation_service.rb b/app/services/clusters/applications/schedule_installation_service.rb
deleted file mode 100644
index 15c93f1e79b..00000000000
--- a/app/services/clusters/applications/schedule_installation_service.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-# frozen_string_literal: true
-
-module Clusters
- module Applications
- class ScheduleInstallationService
- attr_reader :application
-
- def initialize(application)
- @application = application
- end
-
- def execute
- application.updateable? ? schedule_upgrade : schedule_install
- end
-
- private
-
- def schedule_upgrade
- application.make_scheduled!
-
- ClusterUpgradeAppWorker.perform_async(application.name, application.id)
- end
-
- def schedule_install
- application.make_scheduled!
-
- ClusterInstallAppWorker.perform_async(application.name, application.id)
- end
- end
- end
-end
diff --git a/app/services/clusters/applications/update_service.rb b/app/services/clusters/applications/update_service.rb
new file mode 100644
index 00000000000..a9d4e609992
--- /dev/null
+++ b/app/services/clusters/applications/update_service.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Clusters
+ module Applications
+ class UpdateService < Clusters::Applications::BaseService
+ private
+
+ def worker_class(application)
+ ClusterPatchAppWorker
+ end
+
+ def builders
+ {
+ "helm" => -> (cluster) { cluster.application_helm },
+ "ingress" => -> (cluster) { cluster.application_ingress },
+ "cert_manager" => -> (cluster) { cluster.application_cert_manager }
+ }.tap do |hash|
+ hash.merge!(project_builders) if cluster.project_type?
+ end
+ end
+
+ # These applications will need extra configuration to enable them to work
+ # with groups of projects
+ def project_builders
+ {
+ "prometheus" => -> (cluster) { cluster.application_prometheus },
+ "runner" => -> (cluster) { cluster.application_runner },
+ "jupyter" => -> (cluster) { cluster.application_jupyter },
+ "knative" => -> (cluster) { cluster.application_knative }
+ }
+ end
+ end
+ end
+end
diff --git a/app/services/commits/create_service.rb b/app/services/commits/create_service.rb
index 34593e12bd5..bb34a3d3352 100644
--- a/app/services/commits/create_service.rb
+++ b/app/services/commits/create_service.rb
@@ -11,6 +11,7 @@ module Commits
@start_project = params[:start_project] || @project
@start_branch = params[:start_branch]
@branch_name = params[:branch_name]
+ @force = params[:force] || false
end
def execute
@@ -42,10 +43,14 @@ module Commits
@start_branch != @branch_name || @start_project != @project
end
+ def force?
+ !!@force
+ end
+
def validate!
validate_permissions!
validate_on_branch!
- validate_branch_existance!
+ validate_branch_existence!
validate_new_branch_name! if different_branch?
end
@@ -64,14 +69,14 @@ module Commits
end
end
- def validate_branch_existance!
- if !project.empty_repo? && different_branch? && repository.branch_exists?(@branch_name)
+ def validate_branch_existence!
+ if !project.empty_repo? && different_branch? && repository.branch_exists?(@branch_name) && !force?
raise_error("A branch called '#{@branch_name}' already exists. Switch to that branch in order to make changes")
end
end
def validate_new_branch_name!
- result = ValidateNewBranchService.new(project, current_user).execute(@branch_name)
+ result = ValidateNewBranchService.new(project, current_user).execute(@branch_name, force: force?)
if result[:status] == :error
raise_error("Something went wrong when we tried to create '#{@branch_name}' for you: #{result[:message]}")
diff --git a/app/services/concerns/suggestible.rb b/app/services/concerns/suggestible.rb
new file mode 100644
index 00000000000..0b9822b1909
--- /dev/null
+++ b/app/services/concerns/suggestible.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Suggestible
+ extend ActiveSupport::Concern
+
+ # This translates into limiting suggestion changes to `suggestion:-100+100`.
+ MAX_LINES_CONTEXT = 100.freeze
+
+ def fetch_from_content
+ diff_file.new_blob_lines_between(from_line, to_line).join
+ end
+
+ def from_line
+ real_above = [lines_above, MAX_LINES_CONTEXT].min
+ [target_line - real_above, 1].max
+ end
+
+ def to_line
+ real_below = [lines_below, MAX_LINES_CONTEXT].min
+ target_line + real_below
+ end
+
+ def diff_file
+ raise NotImplementedError
+ end
+
+ def target_line
+ raise NotImplementedError
+ end
+end
diff --git a/app/services/concerns/users/participable_service.rb b/app/services/concerns/users/participable_service.rb
index 5b408bd96c7..6713b6617ae 100644
--- a/app/services/concerns/users/participable_service.rb
+++ b/app/services/concerns/users/participable_service.rb
@@ -11,7 +11,7 @@ module Users
def noteable_owner
return [] unless noteable && noteable.author.present?
- [as_hash(noteable.author)]
+ [user_as_hash(noteable.author)]
end
def participants_in_noteable
@@ -23,21 +23,24 @@ module Users
def sorted(users)
users.uniq.to_a.compact.sort_by(&:username).map do |user|
- as_hash(user)
+ user_as_hash(user)
end
end
def groups
current_user.authorized_groups.sort_by(&:path).map do |group|
- count = group.users.count
- { username: group.full_path, name: group.full_name, count: count, avatar_url: group.avatar_url }
+ group_as_hash(group)
end
end
private
- def as_hash(user)
- { username: user.username, name: user.name, avatar_url: user.avatar_url }
+ def user_as_hash(user)
+ { type: user.class.name, username: user.username, name: user.name, avatar_url: user.avatar_url }
+ end
+
+ def group_as_hash(group)
+ { type: group.class.name, username: group.full_path, name: group.full_name, avatar_url: group.avatar_url, count: group.users.count }
end
end
end
diff --git a/app/services/error_tracking/list_issues_service.rb b/app/services/error_tracking/list_issues_service.rb
index 4cc35cfa4a8..a6c6bec9598 100644
--- a/app/services/error_tracking/list_issues_service.rb
+++ b/app/services/error_tracking/list_issues_service.rb
@@ -6,15 +6,19 @@ module ErrorTracking
DEFAULT_LIMIT = 20
def execute
- return error('not enabled') unless enabled?
- return error('access denied') unless can_read?
+ return error('Error Tracking is not enabled') unless enabled?
+ return error('Access denied', :unauthorized) unless can_read?
result = project_error_tracking_setting
.list_sentry_issues(issue_status: issue_status, limit: limit)
# our results are not yet ready
unless result
- return error('not ready', :no_content)
+ return error('Not ready. Try again later', :no_content)
+ end
+
+ if result[:error].present?
+ return error(result[:error], :bad_request)
end
success(issues: result[:issues])
diff --git a/app/services/error_tracking/list_projects_service.rb b/app/services/error_tracking/list_projects_service.rb
index c6e8be0f2be..4e92353a13c 100644
--- a/app/services/error_tracking/list_projects_service.rb
+++ b/app/services/error_tracking/list_projects_service.rb
@@ -28,8 +28,8 @@ module ErrorTracking
(project.error_tracking_setting || project.build_error_tracking_setting).tap do |setting|
setting.api_url = ErrorTracking::ProjectErrorTrackingSetting.build_api_url_from(
api_host: params[:api_host],
- organization_slug: nil,
- project_slug: nil
+ organization_slug: 'org',
+ project_slug: 'proj'
)
setting.token = params[:token]
diff --git a/app/services/files/multi_service.rb b/app/services/files/multi_service.rb
index 927634c2159..c1bc26c330a 100644
--- a/app/services/files/multi_service.rb
+++ b/app/services/files/multi_service.rb
@@ -46,7 +46,8 @@ module Files
author_email: @author_email,
author_name: @author_name,
start_project: @start_project,
- start_branch_name: @start_branch
+ start_branch_name: @start_branch,
+ force: force?
)
rescue ArgumentError => e
raise_error(e)
diff --git a/app/services/git/branch_push_service.rb b/app/services/git/branch_push_service.rb
new file mode 100644
index 00000000000..b55aeb5f2b9
--- /dev/null
+++ b/app/services/git/branch_push_service.rb
@@ -0,0 +1,244 @@
+# frozen_string_literal: true
+
+module Git
+ class BranchPushService < 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
+
+ # This method will be called after each git update
+ # and only if the provided user and project are present in GitLab.
+ #
+ # All callbacks for post receive action should be placed here.
+ #
+ # Next, this method:
+ # 1. Creates the push event
+ # 2. Updates merge requests
+ # 3. Recognizes cross-references from commit messages
+ # 4. Executes the project's webhooks
+ # 5. Executes the project's services
+ # 6. Checks if the project's main language has changed
+ #
+ def execute
+ update_commits
+ execute_related_hooks
+ perform_housekeeping
+
+ update_remote_mirrors
+ update_caches
+
+ update_signatures
+ end
+
+ def update_commits
+ project.repository.after_create if project.empty_repo?
+ project.repository.after_push_commit(branch_name)
+
+ if push_remove_branch?
+ project.repository.after_remove_branch
+ @push_commits = []
+ elsif push_to_new_branch?
+ project.repository.after_create_branch
+
+ # Re-find the pushed commits.
+ if default_branch?
+ # Initial push to the default branch. Take the full history of that branch as "newly pushed".
+ process_default_branch
+ else
+ # 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])
+
+ # 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])
+
+ process_commit_messages
+
+ # Update the bare repositories info/attributes file using the contents of the default branches
+ # .gitattributes file
+ update_gitattributes if default_branch?
+ end
+ end
+
+ def update_gitattributes
+ project.repository.copy_gitattributes(params[:ref])
+ end
+
+ def update_caches
+ if default_branch?
+ if push_to_new_branch?
+ # If this is the initial push into the default branch, the file type caches
+ # will already be reset as a result of `Project#change_head`.
+ types = []
+ else
+ paths = Set.new
+
+ last_pushed_commits.each do |commit|
+ commit.raw_deltas.each do |diff|
+ paths << diff.new_path
+ end
+ end
+
+ 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])
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def update_signatures
+ commit_shas = last_pushed_commits.map(&:sha)
+
+ return if commit_shas.empty?
+
+ shas_with_cached_signatures = GpgSignature.where(commit_sha: commit_shas).pluck(:commit_sha)
+ commit_shas -= shas_with_cached_signatures
+
+ return if commit_shas.empty?
+
+ commit_shas = Gitlab::Git::Commit.shas_with_signatures(project.repository, commit_shas)
+
+ CreateGpgSignatureWorker.perform_async(commit_shas, project.id)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # Schedules processing of commit messages.
+ def process_commit_messages
+ default = default_branch?
+
+ last_pushed_commits.each do |commit|
+ if commit.matches_cross_reference_regex?
+ ProcessCommitWorker
+ .perform_async(project.id, current_user.id, commit.to_hash, default)
+ end
+ end
+ end
+
+ def update_remote_mirrors
+ return unless project.has_remote_mirror?
+
+ project.mark_stuck_remote_mirrors_as_failed!
+ project.update_remote_mirrors
+ end
+
+ def execute_related_hooks
+ # Update merge requests that may be affected by this push. A new branch
+ # 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])
+
+ EventCreateService.new.push(project, current_user, build_push_data)
+ Ci::CreatePipelineService.new(project, current_user, build_push_data).execute(:push, pipeline_options)
+
+ project.execute_hooks(build_push_data.dup, :push_hooks)
+ project.execute_services(build_push_data.dup, :push_hooks)
+
+ if push_remove_branch?
+ AfterBranchDeleteService
+ .new(project, current_user)
+ .execute(branch_name)
+ end
+ end
+
+ def perform_housekeeping
+ housekeeping = Projects::HousekeepingService.new(project)
+ housekeeping.increment!
+ housekeeping.execute if housekeeping.needed?
+ rescue Projects::HousekeepingService::LeaseTaken
+ end
+
+ def process_default_branch
+ offset = [push_commits_count_for_ref - PROCESS_COMMIT_LIMIT, 0].max
+ @push_commits = project.repository.commits(params[:newrev], offset: offset, limit: PROCESS_COMMIT_LIMIT)
+
+ project.after_create_default_branch
+ end
+
+ def build_push_data
+ @push_data ||= Gitlab::DataBuilder::Push.build(
+ project,
+ current_user,
+ params[:oldrev],
+ params[:newrev],
+ params[:ref],
+ @push_commits,
+ commits_count: commits_count,
+ push_options: params[:push_options] || []
+ )
+ end
+
+ def push_to_existing_branch?
+ # Return if this is not a push to a branch (e.g. new commits)
+ branch_ref? && !Gitlab::Git.blank_ref?(params[:oldrev])
+ end
+
+ def push_to_new_branch?
+ strong_memoize(:push_to_new_branch) do
+ branch_ref? && Gitlab::Git.blank_ref?(params[:oldrev])
+ end
+ end
+
+ def push_remove_branch?
+ strong_memoize(:push_remove_branch) do
+ branch_ref? && Gitlab::Git.blank_ref?(params[:newrev])
+ end
+ end
+
+ def default_branch?
+ branch_ref? &&
+ (branch_name == project.default_branch || project.default_branch.nil?)
+ end
+
+ def commit_user(commit)
+ commit.author || current_user
+ end
+
+ def branch_name
+ 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 commits_count
+ return push_commits_count_for_ref if default_branch? && push_to_new_branch?
+
+ Array(@push_commits).size
+ end
+
+ def push_commits_count_for_ref
+ strong_memoize(:push_commits_count_for_ref) 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
+
+ private
+
+ def pipeline_options
+ {} # to be overridden in EE
+ end
+ end
+end
diff --git a/app/services/git/tag_push_service.rb b/app/services/git/tag_push_service.rb
new file mode 100644
index 00000000000..318dfd4f886
--- /dev/null
+++ b/app/services/git/tag_push_service.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+module Git
+ class TagPushService < BaseService
+ attr_accessor :push_data
+
+ def execute
+ project.repository.after_create if project.empty_repo?
+ project.repository.before_push_tag
+
+ @push_data = build_push_data
+
+ EventCreateService.new.push(project, current_user, push_data)
+ Ci::CreatePipelineService.new(project, current_user, push_data).execute(:push, pipeline_options)
+
+ 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])
+
+ true
+ end
+
+ private
+
+ def build_push_data
+ commits = []
+ message = nil
+
+ unless Gitlab::Git.blank_ref?(params[:newrev])
+ tag_name = Gitlab::Git.ref_name(params[:ref])
+ tag = project.repository.find_tag(tag_name)
+
+ if tag && tag.target == params[:newrev]
+ commit = project.commit(tag.dereferenced_target)
+ commits = [commit].compact
+ message = tag.message
+ end
+ end
+
+ Gitlab::DataBuilder::Push.build(
+ project,
+ current_user,
+ params[:oldrev],
+ params[:newrev],
+ params[:ref],
+ commits,
+ message,
+ push_options: params[:push_options] || [])
+ end
+
+ def build_system_push_data
+ Gitlab::DataBuilder::Push.build(
+ project,
+ current_user,
+ params[:oldrev],
+ params[:newrev],
+ params[:ref],
+ [],
+ '')
+ end
+
+ def pipeline_options
+ {} # to be overridden in EE
+ end
+ end
+end
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
deleted file mode 100644
index 092fd64574d..00000000000
--- a/app/services/git_push_service.rb
+++ /dev/null
@@ -1,240 +0,0 @@
-# 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
-
- # This method will be called after each git update
- # and only if the provided user and project are present in GitLab.
- #
- # All callbacks for post receive action should be placed here.
- #
- # Next, this method:
- # 1. Creates the push event
- # 2. Updates merge requests
- # 3. Recognizes cross-references from commit messages
- # 4. Executes the project's webhooks
- # 5. Executes the project's services
- # 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)
-
- if push_remove_branch?
- project.repository.after_remove_branch
- @push_commits = []
- elsif push_to_new_branch?
- project.repository.after_create_branch
-
- # Re-find the pushed commits.
- if default_branch?
- # Initial push to the default branch. Take the full history of that branch as "newly pushed".
- process_default_branch
- else
- # 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])
-
- # 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])
-
- process_commit_messages
-
- # Update the bare repositories info/attributes file using the contents of the default branches
- # .gitattributes file
- update_gitattributes if default_branch?
- end
-
- execute_related_hooks
- perform_housekeeping
-
- update_remote_mirrors
- update_caches
-
- update_signatures
- end
-
- def update_gitattributes
- project.repository.copy_gitattributes(params[:ref])
- end
-
- def update_caches
- if default_branch?
- if push_to_new_branch?
- # If this is the initial push into the default branch, the file type caches
- # will already be reset as a result of `Project#change_head`.
- types = []
- else
- paths = Set.new
-
- last_pushed_commits.each do |commit|
- commit.raw_deltas.each do |diff|
- paths << diff.new_path
- end
- end
-
- 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])
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def update_signatures
- commit_shas = last_pushed_commits.map(&:sha)
-
- return if commit_shas.empty?
-
- shas_with_cached_signatures = GpgSignature.where(commit_sha: commit_shas).pluck(:commit_sha)
- commit_shas -= shas_with_cached_signatures
-
- return if commit_shas.empty?
-
- commit_shas = Gitlab::Git::Commit.shas_with_signatures(project.repository, commit_shas)
-
- CreateGpgSignatureWorker.perform_async(commit_shas, project.id)
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- # Schedules processing of commit messages.
- def process_commit_messages
- default = default_branch?
-
- last_pushed_commits.each do |commit|
- if commit.matches_cross_reference_regex?
- ProcessCommitWorker
- .perform_async(project.id, current_user.id, commit.to_hash, default)
- end
- end
- end
-
- protected
-
- def update_remote_mirrors
- return unless project.has_remote_mirror?
-
- project.mark_stuck_remote_mirrors_as_failed!
- project.update_remote_mirrors
- end
-
- def execute_related_hooks
- # Update merge requests that may be affected by this push. A new branch
- # 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])
-
- EventCreateService.new.push(project, current_user, build_push_data)
- Ci::CreatePipelineService.new(project, current_user, build_push_data).execute(:push, pipeline_options)
-
- project.execute_hooks(build_push_data.dup, :push_hooks)
- project.execute_services(build_push_data.dup, :push_hooks)
-
- if push_remove_branch?
- AfterBranchDeleteService
- .new(project, current_user)
- .execute(branch_name)
- end
- end
-
- def perform_housekeeping
- housekeeping = Projects::HousekeepingService.new(project)
- housekeeping.increment!
- housekeeping.execute if housekeeping.needed?
- rescue Projects::HousekeepingService::LeaseTaken
- end
-
- def process_default_branch
- offset = [push_commits_count_for_ref - PROCESS_COMMIT_LIMIT, 0].max
- @push_commits = project.repository.commits(params[:newrev], offset: offset, limit: PROCESS_COMMIT_LIMIT)
-
- project.after_create_default_branch
- end
-
- def build_push_data
- @push_data ||= Gitlab::DataBuilder::Push.build(
- project,
- current_user,
- params[:oldrev],
- params[:newrev],
- params[:ref],
- @push_commits,
- commits_count: commits_count,
- push_options: params[:push_options] || [])
- end
-
- def push_to_existing_branch?
- # Return if this is not a push to a branch (e.g. new commits)
- branch_ref? && !Gitlab::Git.blank_ref?(params[:oldrev])
- end
-
- def push_to_new_branch?
- strong_memoize(:push_to_new_branch) do
- branch_ref? && Gitlab::Git.blank_ref?(params[:oldrev])
- end
- end
-
- def push_remove_branch?
- strong_memoize(:push_remove_branch) do
- branch_ref? && Gitlab::Git.blank_ref?(params[:newrev])
- end
- end
-
- def default_branch?
- branch_ref? &&
- (branch_name == project.default_branch || project.default_branch.nil?)
- end
-
- def commit_user(commit)
- commit.author || current_user
- end
-
- def branch_name
- 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 commits_count
- return push_commits_count_for_ref if default_branch? && push_to_new_branch?
-
- Array(@push_commits).size
- end
-
- def push_commits_count_for_ref
- strong_memoize(:push_commits_count_for_ref) 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
-
- private
-
- def pipeline_options
- {} # to be overriden in EE
- end
-end
diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb
deleted file mode 100644
index 6fef5b3ed1d..00000000000
--- a/app/services/git_tag_push_service.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-# frozen_string_literal: true
-
-class GitTagPushService < BaseService
- attr_accessor :push_data
-
- def execute
- project.repository.after_create if project.empty_repo?
- project.repository.before_push_tag
-
- @push_data = build_push_data
-
- EventCreateService.new.push(project, current_user, push_data)
- Ci::CreatePipelineService.new(project, current_user, push_data).execute(:push, pipeline_options)
-
- 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])
-
- true
- end
-
- private
-
- def build_push_data
- commits = []
- message = nil
-
- unless Gitlab::Git.blank_ref?(params[:newrev])
- tag_name = Gitlab::Git.ref_name(params[:ref])
- tag = project.repository.find_tag(tag_name)
-
- if tag && tag.target == params[:newrev]
- commit = project.commit(tag.dereferenced_target)
- commits = [commit].compact
- message = tag.message
- end
- end
-
- Gitlab::DataBuilder::Push.build(
- project,
- current_user,
- params[:oldrev],
- params[:newrev],
- params[:ref],
- commits,
- message,
- push_options: params[:push_options] || [])
- end
-
- def build_system_push_data
- Gitlab::DataBuilder::Push.build(
- project,
- current_user,
- params[:oldrev],
- params[:newrev],
- params[:ref],
- [],
- '')
- end
-
- def pipeline_options
- {} # to be overriden in EE
- end
-end
diff --git a/app/services/groups/auto_devops_service.rb b/app/services/groups/auto_devops_service.rb
new file mode 100644
index 00000000000..1925e0cc0ea
--- /dev/null
+++ b/app/services/groups/auto_devops_service.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Groups
+ class AutoDevopsService < Groups::BaseService
+ def execute
+ raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_group, group)
+
+ group.update(auto_devops_enabled: auto_devops_enabled)
+ end
+
+ private
+
+ def auto_devops_enabled
+ params[:auto_devops_enabled]
+ end
+ end
+end
diff --git a/app/services/groups/create_service.rb b/app/services/groups/create_service.rb
index 55a3b9fa7b1..99ead467f74 100644
--- a/app/services/groups/create_service.rb
+++ b/app/services/groups/create_service.rb
@@ -33,7 +33,7 @@ module Groups
private
def after_build_hook(group, params)
- # overriden in EE
+ # overridden in EE
end
def create_chat_team?
diff --git a/app/services/groups/nested_create_service.rb b/app/services/groups/nested_create_service.rb
index f01f5656296..01bd685712b 100644
--- a/app/services/groups/nested_create_service.rb
+++ b/app/services/groups/nested_create_service.rb
@@ -12,7 +12,7 @@ module Groups
end
def execute
- return nil unless group_path
+ return unless group_path
if namespace = namespace_or_group(group_path)
return namespace
diff --git a/app/services/groups/transfer_service.rb b/app/services/groups/transfer_service.rb
index f64e327416a..94185605ab9 100644
--- a/app/services/groups/transfer_service.rb
+++ b/app/services/groups/transfer_service.rb
@@ -35,7 +35,10 @@ module Groups
def proceed_to_transfer
Group.transaction do
update_group_attributes
+ ensure_ownership
end
+
+ true
end
def ensure_allowed_transfer
@@ -95,6 +98,13 @@ module Groups
end
# rubocop: enable CodeReuse/ActiveRecord
+ def ensure_ownership
+ return if @new_parent_group
+ return unless @group.owners.empty?
+
+ @group.add_owner(current_user)
+ end
+
def raise_transfer_error(message)
raise TransferError, ERROR_MESSAGES[message]
end
diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb
index 9ff1da270e2..787445180f0 100644
--- a/app/services/groups/update_service.rb
+++ b/app/services/groups/update_service.rb
@@ -31,7 +31,7 @@ module Groups
private
def before_assignment_hook(group, params)
- # overriden in EE
+ # overridden in EE
end
def after_update
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index ef991eaf234..f35ad2a9d8b 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -89,7 +89,7 @@ class IssuableBaseService < BaseService
return unless labels
- params[:label_ids] = labels.split(",").map do |label_name|
+ params[:label_ids] = labels.map do |label_name|
label = Labels::FindOrCreateService.new(
current_user,
parent,
@@ -387,4 +387,10 @@ class IssuableBaseService < BaseService
def parent
project
end
+
+ # we need to check this because milestone from milestone_id param is displayed on "new" page
+ # where private project milestone could leak without this check
+ def ensure_milestone_available(issuable)
+ issuable.milestone_id = nil unless issuable.milestone_available?
+ end
end
diff --git a/app/services/issues/build_service.rb b/app/services/issues/build_service.rb
index 3fb2c2b3007..61615ac2058 100644
--- a/app/services/issues/build_service.rb
+++ b/app/services/issues/build_service.rb
@@ -6,7 +6,9 @@ module Issues
def execute
filter_resolve_discussion_params
- @issue = project.issues.new(issue_params)
+ @issue = project.issues.new(issue_params).tap do |issue|
+ ensure_milestone_available(issue)
+ end
end
def issue_params_with_info_from_discussions
diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb
index ac51fee0b3f..11ede5223e5 100644
--- a/app/services/merge_requests/base_service.rb
+++ b/app/services/merge_requests/base_service.rb
@@ -67,7 +67,7 @@ module MergeRequests
Ci::CreatePipelineService
.new(merge_request.source_project, user, ref: merge_request.source_branch)
- .execute(:merge_request,
+ .execute(:merge_request_event,
ignore_skip_ci: true,
save_on_errors: false,
merge_request: merge_request)
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 48419da98ad..109c964e577 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -19,6 +19,7 @@ module MergeRequests
merge_request.target_project = find_target_project
merge_request.target_branch = find_target_branch
merge_request.can_be_created = projects_and_branches_valid?
+ ensure_milestone_available(merge_request)
# compare branches only if branches are valid, otherwise
# compare_branches may raise an error
diff --git a/app/services/merge_requests/merge_base_service.rb b/app/services/merge_requests/merge_base_service.rb
new file mode 100644
index 00000000000..095bdca5472
--- /dev/null
+++ b/app/services/merge_requests/merge_base_service.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module MergeRequests
+ class MergeBaseService < MergeRequests::BaseService
+ include Gitlab::Utils::StrongMemoize
+
+ MergeError = Class.new(StandardError)
+
+ attr_reader :merge_request
+
+ # Overridden in EE.
+ def hooks_validation_pass?(_merge_request)
+ true
+ end
+
+ # Overridden in EE.
+ def hooks_validation_error(_merge_request)
+ # No-op
+ end
+
+ def source
+ if merge_request.squash
+ squash_sha!
+ else
+ merge_request.diff_head_sha
+ end
+ end
+
+ private
+
+ # Overridden in EE.
+ def error_check!
+ # No-op
+ end
+
+ def raise_error(message)
+ raise MergeError, message
+ end
+
+ def handle_merge_error(*args)
+ # No-op
+ end
+
+ def commit_message
+ params[:commit_message] ||
+ merge_request.default_merge_commit_message
+ end
+
+ def squash_sha!
+ strong_memoize(:squash_sha) do
+ params[:merge_request] = merge_request
+ squash_result = ::MergeRequests::SquashService.new(project, current_user, params).execute
+
+ case squash_result[:status]
+ when :success
+ squash_result[:squash_sha]
+ when :error
+ raise ::MergeRequests::MergeService::MergeError, squash_result[:message]
+ end
+ end
+ end
+ end
+end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 449997bcf07..d8a78001b79 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -7,13 +7,7 @@ module MergeRequests
# mark merge request as merged and execute all hooks and notifications
# Executed when you do merge via GitLab UI
#
- class MergeService < MergeRequests::BaseService
- include Gitlab::Utils::StrongMemoize
-
- MergeError = Class.new(StandardError)
-
- attr_reader :merge_request, :source
-
+ class MergeService < MergeRequests::MergeBaseService
delegate :merge_jid, :state, to: :@merge_request
def execute(merge_request)
@@ -24,7 +18,7 @@ module MergeRequests
@merge_request = merge_request
- error_check!
+ validate!
merge_request.in_locked_state do
if commit
@@ -38,22 +32,22 @@ module MergeRequests
handle_merge_error(log_message: e.message, save_message_on_model: true)
end
- def source
- if merge_request.squash
- squash_sha!
- else
- merge_request.diff_head_sha
- end
- end
+ private
- # Overridden in EE.
- def hooks_validation_pass?(_merge_request)
- true
+ def validate!
+ authorization_check!
+ error_check!
end
- private
+ def authorization_check!
+ unless @merge_request.can_be_merged_by?(current_user)
+ raise_error('You are not allowed to merge this merge request')
+ end
+ end
def error_check!
+ super
+
error =
if @merge_request.should_be_rebased?
'Only fast-forward merge is allowed for your project. Please update your source branch'
@@ -63,7 +57,7 @@ module MergeRequests
'No source for merge'
end
- raise MergeError, error if error
+ raise_error(error) if error
end
def commit
@@ -73,36 +67,20 @@ module MergeRequests
if commit_id
log_info("Git merge finished on JID #{merge_jid} commit #{commit_id}")
else
- raise MergeError, 'Conflicts detected during merge'
+ raise_error('Conflicts detected during merge')
end
merge_request.update!(merge_commit_sha: commit_id)
end
- def squash_sha!
- strong_memoize(:squash_sha) do
- params[:merge_request] = merge_request
- squash_result = ::MergeRequests::SquashService.new(project, current_user, params).execute
-
- case squash_result[:status]
- when :success
- squash_result[:squash_sha]
- when :error
- raise ::MergeRequests::MergeService::MergeError, squash_result[:message]
- end
- end
- end
-
def try_merge
- message = params[:commit_message] || merge_request.default_merge_commit_message
-
- repository.merge(current_user, source, merge_request, message)
+ repository.merge(current_user, source, merge_request, commit_message)
rescue Gitlab::Git::PreReceiveError => e
- handle_merge_error(log_message: e.message)
- raise MergeError, 'Something went wrong during merge pre-receive hook'
+ raise MergeError,
+ "Something went wrong during merge pre-receive hook. #{e.message}".strip
rescue => e
handle_merge_error(log_message: e.message)
- raise MergeError, 'Something went wrong during merge'
+ raise_error('Something went wrong during merge')
ensure
merge_request.update!(in_progress_merge_commit_sha: nil)
end
diff --git a/app/services/merge_requests/merge_to_ref_service.rb b/app/services/merge_requests/merge_to_ref_service.rb
new file mode 100644
index 00000000000..69cc441f1bb
--- /dev/null
+++ b/app/services/merge_requests/merge_to_ref_service.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+module MergeRequests
+ # Performs the merge between source SHA and the target branch. Instead
+ # of writing the result to the MR target branch, it targets the `target_ref`.
+ #
+ # Ideally this should leave the `target_ref` state with the same state the
+ # target branch would have if we used the regular `MergeService`, but without
+ # every side-effect that comes with it (MR updates, mails, source branch
+ # deletion, etc). This service should be kept idempotent (i.e. can
+ # be executed regardless of the `target_ref` current state).
+ #
+ class MergeToRefService < MergeRequests::MergeBaseService
+ def execute(merge_request)
+ @merge_request = merge_request
+
+ validate!
+
+ commit_id = commit
+
+ raise_error('Conflicts detected during merge') unless commit_id
+
+ commit = project.commit(commit_id)
+ target_id, source_id = commit.parent_ids
+
+ success(commit_id: commit.id,
+ target_id: target_id,
+ source_id: source_id)
+ rescue MergeError => error
+ error(error.message)
+ end
+
+ private
+
+ def validate!
+ authorization_check!
+ error_check!
+ end
+
+ def error_check!
+ super
+
+ error =
+ if Feature.disabled?(:merge_to_tmp_merge_ref_path, project)
+ 'Feature is not enabled'
+ elsif !hooks_validation_pass?(merge_request)
+ hooks_validation_error(merge_request)
+ elsif !@merge_request.mergeable_to_ref?
+ "Merge request is not mergeable to #{target_ref}"
+ elsif !source
+ 'No source for merge'
+ end
+
+ raise_error(error) if error
+ end
+
+ def authorization_check!
+ unless Ability.allowed?(current_user, :admin_merge_request, project)
+ raise_error("You are not allowed to merge to this ref")
+ end
+ end
+
+ def target_ref
+ merge_request.merge_ref_path
+ end
+
+ def commit
+ repository.merge_to_ref(current_user, source, merge_request, target_ref, commit_message)
+ rescue Gitlab::Git::PreReceiveError => error
+ raise MergeError, error.message
+ end
+ end
+end
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index f712b8863cd..f5dc5a0256d 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -20,6 +20,7 @@ module MergeRequests
close_upon_missing_source_branch_ref
post_merge_manually_merged
reload_merge_requests
+ outdate_suggestions
reset_merge_when_pipeline_succeeds
mark_pending_todos_done
cache_merge_requests_closing_issues
@@ -125,6 +126,14 @@ module MergeRequests
merge_request.source_branch == @push.branch_name
end
+ def outdate_suggestions
+ outdate_service = Suggestions::OutdateService.new
+
+ merge_requests_for_source_branch.each do |merge_request|
+ outdate_service.execute(merge_request)
+ end
+ end
+
def reset_merge_when_pipeline_succeeds
merge_requests_for_source_branch.each(&:reset_merge_when_pipeline_succeeds)
end
diff --git a/app/services/merge_requests/reopen_service.rb b/app/services/merge_requests/reopen_service.rb
index f6cbe769ef4..f87005bcb6c 100644
--- a/app/services/merge_requests/reopen_service.rb
+++ b/app/services/merge_requests/reopen_service.rb
@@ -3,7 +3,7 @@
module MergeRequests
class ReopenService < MergeRequests::BaseService
def execute(merge_request)
- return merge_request unless can?(current_user, :update_merge_request, merge_request)
+ return merge_request unless can?(current_user, :reopen_merge_request, merge_request)
if merge_request.reopen
create_event(merge_request)
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 5a6e7338b42..1b46f6d8a72 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -21,7 +21,7 @@ module Notes
if quick_actions_service.supported?(note)
options = { merge_request_diff_head_sha: merge_request_diff_head_sha }
- content, command_params = quick_actions_service.extract_commands(note, options)
+ content, update_params = quick_actions_service.execute(note, options)
only_commands = content.empty?
@@ -43,16 +43,17 @@ module Notes
Suggestions::CreateService.new(note).execute
end
- if command_params.present?
- quick_actions_service.execute(command_params, note)
+ if quick_actions_service.commands_executed_count.to_i > 0
+ if update_params.present?
+ quick_actions_service.apply_updates(update_params, note)
+ note.commands_changes = update_params
+ end
# We must add the error after we call #save because errors are reset
# when #save is called
if only_commands
note.errors.add(:commands_only, 'Commands applied')
end
-
- note.commands_changes = command_params
end
note
diff --git a/app/services/notes/quick_actions_service.rb b/app/services/notes/quick_actions_service.rb
index 985a03060bd..0852a708240 100644
--- a/app/services/notes/quick_actions_service.rb
+++ b/app/services/notes/quick_actions_service.rb
@@ -1,7 +1,18 @@
# frozen_string_literal: true
+# QuickActionsService class
+#
+# Executes quick actions commands extracted from note text
+#
+# Most commands returns parameters to be applied later
+# using QuickActionService#apply_updates
+#
module Notes
class QuickActionsService < BaseService
+ attr_reader :interpret_service
+
+ delegate :commands_executed_count, to: :interpret_service, allow_nil: true
+
UPDATE_SERVICES = {
'Issue' => Issues::UpdateService,
'MergeRequest' => MergeRequests::UpdateService,
@@ -25,18 +36,21 @@ module Notes
self.class.supported?(note)
end
- def extract_commands(note, options = {})
+ def execute(note, options = {})
return [note.note, {}] unless supported?(note)
- QuickActions::InterpretService.new(project, current_user, options)
- .execute(note.note, note.noteable)
+ @interpret_service = QuickActions::InterpretService.new(project, current_user, options)
+
+ @interpret_service.execute(note.note, note.noteable)
end
- def execute(command_params, note)
- return if command_params.empty?
+ # Applies updates extracted to note#noteable
+ # The update parameters are extracted on self#execute
+ def apply_updates(update_params, note)
+ return if update_params.empty?
return unless supported?(note)
- self.class.noteable_update_service(note).new(note.parent, current_user, command_params).execute(note.noteable)
+ self.class.noteable_update_service(note).new(note.parent, current_user, update_params).execute(note.noteable)
end
end
end
diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb
index dd297c9ba43..aba175eb79b 100644
--- a/app/services/projects/download_service.rb
+++ b/app/services/projects/download_service.rb
@@ -11,7 +11,7 @@ module Projects
end
def execute
- return nil unless valid_url?(@url)
+ return unless valid_url?(@url)
uploader = FileUploader.new(@project)
uploader.download!(@url)
diff --git a/app/services/projects/fetch_statistics_increment_service.rb b/app/services/projects/fetch_statistics_increment_service.rb
new file mode 100644
index 00000000000..8644e6bf313
--- /dev/null
+++ b/app/services/projects/fetch_statistics_increment_service.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Projects
+ class FetchStatisticsIncrementService
+ attr_reader :project
+
+ def initialize(project)
+ @project = project
+ end
+
+ def execute
+ increment_fetch_count_sql = <<~SQL
+ INSERT INTO #{table_name} (project_id, date, fetch_count)
+ VALUES (#{project.id}, '#{Date.today}', 1)
+ SQL
+
+ increment_fetch_count_sql += if Gitlab::Database.postgresql?
+ "ON CONFLICT (project_id, date) DO UPDATE SET fetch_count = #{table_name}.fetch_count + 1"
+ else
+ "ON DUPLICATE KEY UPDATE fetch_count = #{table_name}.fetch_count + 1"
+ end
+
+ ActiveRecord::Base.connection.execute(increment_fetch_count_sql)
+ end
+
+ private
+
+ def table_name
+ ProjectDailyStatistic.table_name
+ end
+ end
+end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 91091c4393d..fc234bafc57 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -38,8 +38,8 @@ module Projects
new_params = {
visibility_level: allowed_visibility_level,
description: @project.description,
- name: @project.name,
- path: @project.path,
+ name: target_name,
+ path: target_path,
shared_runners_enabled: @project.shared_runners_enabled,
namespace_id: target_namespace.id,
fork_network: fork_network,
@@ -94,6 +94,14 @@ module Projects
Projects::ForksCountService.new(@project).refresh_cache
end
+ def target_path
+ @target_path ||= @params[:path] || @project.path
+ end
+
+ def target_name
+ @target_name ||= @params[:name] || @project.name
+ end
+
def target_namespace
@target_namespace ||= @params[:namespace] || current_user.namespace
end
diff --git a/app/services/projects/group_links/create_service.rb b/app/services/projects/group_links/create_service.rb
index 1392775f805..e3d5bea0852 100644
--- a/app/services/projects/group_links/create_service.rb
+++ b/app/services/projects/group_links/create_service.rb
@@ -4,13 +4,19 @@ module Projects
module GroupLinks
class CreateService < BaseService
def execute(group)
- return false unless group
+ return error('Not Found', 404) unless group && can?(current_user, :read_namespace, group)
- project.project_group_links.create(
+ link = project.project_group_links.new(
group: group,
group_access: params[:link_group_access],
expires_at: params[:expires_at]
)
+
+ if link.save
+ success(link: link)
+ else
+ error(link.errors.full_messages.to_sentence, 409)
+ end
end
end
end
diff --git a/app/services/projects/hashed_storage/base_attachment_service.rb b/app/services/projects/hashed_storage/base_attachment_service.rb
new file mode 100644
index 00000000000..828ab616bab
--- /dev/null
+++ b/app/services/projects/hashed_storage/base_attachment_service.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Projects
+ module HashedStorage
+ AttachmentMigrationError = Class.new(StandardError)
+
+ AttachmentCannotMoveError = Class.new(StandardError)
+
+ class BaseAttachmentService < BaseService
+ # Returns the disk_path value before the execution
+ attr_reader :old_disk_path
+
+ # Returns the disk_path value after the execution
+ attr_reader :new_disk_path
+
+ # Returns the logger currently in use
+ attr_reader :logger
+
+ # Return whether this operation was skipped or not
+ #
+ # @return [Boolean] true if skipped of false otherwise
+ def skipped?
+ @skipped
+ end
+
+ protected
+
+ def move_folder!(old_path, new_path)
+ unless File.directory?(old_path)
+ logger.info("Skipped attachments move from '#{old_path}' to '#{new_path}', source path doesn't exist or is not a directory (PROJECT_ID=#{project.id})")
+ @skipped = true
+
+ return true
+ end
+
+ if File.exist?(new_path)
+ logger.error("Cannot move attachments from '#{old_path}' to '#{new_path}', target path already exist (PROJECT_ID=#{project.id})")
+ raise AttachmentCannotMoveError, "Target path '#{new_path}' already exists"
+ end
+
+ # Create base path folder on the new storage layout
+ FileUtils.mkdir_p(File.dirname(new_path))
+
+ FileUtils.mv(old_path, new_path)
+ logger.info("Project attachments moved from '#{old_path}' to '#{new_path}' (PROJECT_ID=#{project.id})")
+
+ true
+ end
+ end
+ end
+end
diff --git a/app/services/projects/hashed_storage/base_repository_service.rb b/app/services/projects/hashed_storage/base_repository_service.rb
index 761c81d776f..f97a28b8c3b 100644
--- a/app/services/projects/hashed_storage/base_repository_service.rb
+++ b/app/services/projects/hashed_storage/base_repository_service.rb
@@ -2,11 +2,8 @@
module Projects
module HashedStorage
- # Returned when there is an error with the Hashed Storage migration
- RepositoryMigrationError = Class.new(StandardError)
-
- # Returned when there is an error with the Hashed Storage rollback
- RepositoryRollbackError = Class.new(StandardError)
+ # Returned when repository can't be made read-only because there is already a git transfer in progress
+ RepositoryInUseError = Class.new(StandardError)
class BaseRepositoryService < BaseService
include Gitlab::ShellAdapter
@@ -38,7 +35,10 @@ module Projects
# project was not originally empty.
if !from_exists && !to_exists
logger.warn "Can't find a repository on either source or target paths for #{project.full_path} (ID=#{project.id}) ..."
- return false
+
+ # We return true so we still reflect the change in the database.
+ # Next time the repository is (re)created it will be under the new storage layout
+ return true
elsif !from_exists
# Repository have been moved already.
return true
@@ -52,6 +52,16 @@ module Projects
move_repository(new_disk_path, old_disk_path)
move_repository("#{new_disk_path}.wiki", old_wiki_disk_path)
end
+
+ def try_to_set_repository_read_only!
+ # Mitigate any push operation to start during migration
+ unless project.set_repository_read_only!
+ migration_error = "Target repository '#{old_disk_path}' cannot be made read-only as there is a git transfer in progress"
+ logger.error migration_error
+
+ raise RepositoryInUseError, migration_error
+ end
+ end
end
end
end
diff --git a/app/services/projects/hashed_storage/migrate_attachments_service.rb b/app/services/projects/hashed_storage/migrate_attachments_service.rb
index 03e0685d2cd..3d0b8f58612 100644
--- a/app/services/projects/hashed_storage/migrate_attachments_service.rb
+++ b/app/services/projects/hashed_storage/migrate_attachments_service.rb
@@ -2,62 +2,37 @@
module Projects
module HashedStorage
- AttachmentMigrationError = Class.new(StandardError)
-
- class MigrateAttachmentsService < BaseService
- attr_reader :logger, :old_disk_path, :new_disk_path
-
+ class MigrateAttachmentsService < BaseAttachmentService
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
@skipped = false
end
def execute
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
+ # 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)
- result = move_folder!(origin, target)
- project.save!
-
- if result && block_given?
- yield
- end
-
- result
- end
-
- def skipped?
- @skipped
- end
+ @new_disk_path = project.disk_path
- private
+ result = move_folder!(origin, target)
- 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})")
- @skipped = true
- return true
- end
+ if result
+ project.save!(validate: false)
- 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"
+ yield if block_given?
+ else
+ # Rollback changes
+ project.rollback!
end
- # Create hashed storage base path folder
- FileUtils.mkdir_p(File.dirname(new_path))
-
- FileUtils.mv(old_path, new_path)
- logger.info("Migrated project attachments from '#{old_path}' to '#{new_path}' (PROJECT_ID=#{project.id})")
-
- true
+ result
end
end
end
diff --git a/app/services/projects/hashed_storage/migrate_repository_service.rb b/app/services/projects/hashed_storage/migrate_repository_service.rb
index 9c672283c7e..e8393128d58 100644
--- a/app/services/projects/hashed_storage/migrate_repository_service.rb
+++ b/app/services/projects/hashed_storage/migrate_repository_service.rb
@@ -15,7 +15,7 @@ module Projects
result = move_repository(old_disk_path, new_disk_path)
if move_wiki
- result &&= move_repository("#{old_wiki_disk_path}", "#{new_disk_path}.wiki")
+ result &&= move_repository(old_wiki_disk_path, "#{new_disk_path}.wiki")
end
if result
@@ -27,7 +27,7 @@ module Projects
end
project.repository_read_only = false
- project.save!
+ project.save!(validate: false)
if result && block_given?
yield
@@ -35,18 +35,6 @@ module Projects
result
end
-
- private
-
- def try_to_set_repository_read_only!
- # Mitigate any push operation to start during migration
- unless project.set_repository_read_only!
- migration_error = "Target repository '#{old_disk_path}' cannot be made read-only as there is a git transfer in progress"
- logger.error migration_error
-
- raise RepositoryMigrationError, migration_error
- end
- end
end
end
end
diff --git a/app/services/projects/hashed_storage/rollback_attachments_service.rb b/app/services/projects/hashed_storage/rollback_attachments_service.rb
new file mode 100644
index 00000000000..5c6b92f965c
--- /dev/null
+++ b/app/services/projects/hashed_storage/rollback_attachments_service.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module Projects
+ module HashedStorage
+ class RollbackAttachmentsService < BaseAttachmentService
+ def initialize(project, logger: nil)
+ @project = project
+ @logger = logger || Rails.logger
+ @old_disk_path = project.disk_path
+ end
+
+ def execute
+ origin = FileUploader.absolute_base_dir(project)
+ project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
+ target = FileUploader.absolute_base_dir(project)
+
+ @new_disk_path = FileUploader.base_dir(project)
+
+ result = move_folder!(origin, target)
+
+ if result
+ project.save!(validate: false)
+
+ yield if block_given?
+ else
+ # Rollback changes
+ project.rollback!
+ end
+
+ result
+ end
+ end
+ end
+end
diff --git a/app/services/projects/hashed_storage/rollback_repository_service.rb b/app/services/projects/hashed_storage/rollback_repository_service.rb
new file mode 100644
index 00000000000..67733f4770b
--- /dev/null
+++ b/app/services/projects/hashed_storage/rollback_repository_service.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Projects
+ module HashedStorage
+ class RollbackRepositoryService < BaseRepositoryService
+ def execute
+ try_to_set_repository_read_only!
+
+ @old_storage_version = project.storage_version
+ project.storage_version = nil
+ project.ensure_storage_path_exists
+
+ @new_disk_path = project.disk_path
+
+ result = move_repository(old_disk_path, new_disk_path)
+
+ if move_wiki
+ result &&= move_repository(old_wiki_disk_path, "#{new_disk_path}.wiki")
+ end
+
+ if result
+ project.write_repository_config
+ project.track_project_repository
+ else
+ rollback_folder_move
+ project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
+ end
+
+ project.repository_read_only = false
+ project.save!(validate: false)
+
+ if result && block_given?
+ yield
+ end
+
+ result
+ end
+ end
+ end
+end
diff --git a/app/services/projects/hashed_storage/rollback_service.rb b/app/services/projects/hashed_storage/rollback_service.rb
new file mode 100644
index 00000000000..25767f5de5e
--- /dev/null
+++ b/app/services/projects/hashed_storage/rollback_service.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Projects
+ module HashedStorage
+ class RollbackService < BaseService
+ attr_reader :logger, :old_disk_path
+
+ def initialize(project, old_disk_path, logger: nil)
+ @project = project
+ @old_disk_path = old_disk_path
+ @logger = logger || Rails.logger
+ end
+
+ def execute
+ # Rollback attachments from Hashed Storage to Legacy
+ if project.hashed_storage?(:attachments)
+ return false unless rollback_attachments
+ end
+
+ # Rollback repository from Hashed Storage to Legacy
+ if project.hashed_storage?(:repository)
+ rollback_repository
+ end
+ end
+
+ private
+
+ def rollback_attachments
+ HashedStorage::RollbackAttachmentsService.new(project, logger: logger).execute
+ end
+
+ def rollback_repository
+ HashedStorage::RollbackRepositoryService.new(project, old_disk_path, logger: logger).execute
+ end
+ end
+ end
+end
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 7998976b00a..a9570176e81 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
@@ -42,17 +42,15 @@ module Projects
def parse_response_links(objects_response)
objects_response.each_with_object([]) do |entry, link_list|
- begin
- link = entry.dig('actions', DOWNLOAD_ACTION, 'href')
+ link = entry.dig('actions', DOWNLOAD_ACTION, 'href')
- raise DownloadLinkNotFound unless link
+ raise DownloadLinkNotFound unless link
- link_list << LfsDownloadObject.new(oid: entry['oid'],
- size: entry['size'],
- link: add_credentials(link))
- rescue DownloadLinkNotFound, Addressable::URI::InvalidURIError
- log_error("Link for Lfs Object with oid #{entry['oid']} not found or invalid.")
- end
+ link_list << LfsDownloadObject.new(oid: entry['oid'],
+ size: entry['size'],
+ link: add_credentials(link))
+ rescue DownloadLinkNotFound, Addressable::URI::InvalidURIError
+ log_error("Link for Lfs Object with oid #{entry['oid']} not found or invalid.")
end
end
diff --git a/app/services/projects/lfs_pointers/lfs_download_service.rb b/app/services/projects/lfs_pointers/lfs_download_service.rb
index 398f00a598d..a009f479d5d 100644
--- a/app/services/projects/lfs_pointers/lfs_download_service.rb
+++ b/app/services/projects/lfs_pointers/lfs_download_service.rb
@@ -75,17 +75,15 @@ module Projects
create_tmp_storage_dir
File.open(tmp_filename, 'wb') do |file|
- begin
- yield file
- rescue StandardError => e
- # If the lfs file is successfully downloaded it will be removed
- # when it is added to the project's lfs files.
- # Nevertheless if any excetion raises the file would remain
- # in the file system. Here we ensure to remove it
- File.unlink(file) if File.exist?(file)
-
- raise e
- end
+ yield file
+ rescue StandardError => e
+ # If the lfs file is successfully downloaded it will be removed
+ # when it is added to the project's lfs files.
+ # Nevertheless if any excetion raises the file would remain
+ # in the file system. Here we ensure to remove it
+ File.unlink(file) if File.exist?(file)
+
+ raise e
end
end
diff --git a/app/services/projects/operations/update_service.rb b/app/services/projects/operations/update_service.rb
index abd6d8de750..aedf79c86d7 100644
--- a/app/services/projects/operations/update_service.rb
+++ b/app/services/projects/operations/update_service.rb
@@ -12,7 +12,28 @@ module Projects
private
def project_update_params
- params.slice(:error_tracking_setting_attributes)
+ error_tracking_params
+ end
+
+ def error_tracking_params
+ settings = params[:error_tracking_setting_attributes]
+ return {} if settings.blank?
+
+ api_url = ErrorTracking::ProjectErrorTrackingSetting.build_api_url_from(
+ api_host: settings[:api_host],
+ project_slug: settings.dig(:project, :slug),
+ organization_slug: settings.dig(:project, :organization_slug)
+ )
+
+ {
+ error_tracking_setting_attributes: {
+ api_url: api_url,
+ token: settings[:token],
+ enabled: settings[:enabled],
+ project_name: settings.dig(:project, :name),
+ organization_name: settings.dig(:project, :organization_name)
+ }
+ }
end
end
end
diff --git a/app/services/prometheus/adapter_service.rb b/app/services/prometheus/adapter_service.rb
index a791845ba20..3be958e1613 100644
--- a/app/services/prometheus/adapter_service.rb
+++ b/app/services/prometheus/adapter_service.rb
@@ -30,7 +30,7 @@ module Prometheus
return unless deployment_platform.respond_to?(:cluster)
cluster = deployment_platform.cluster
- return unless cluster.application_prometheus&.ready?
+ return unless cluster.application_prometheus&.available?
cluster.application_prometheus
end
diff --git a/app/services/push_event_payload_service.rb b/app/services/push_event_payload_service.rb
index bb1259787af..fe366ac225b 100644
--- a/app/services/push_event_payload_service.rb
+++ b/app/services/push_event_payload_service.rb
@@ -46,7 +46,7 @@ class PushEventPayloadService
def commit_title
commit = @push_data.fetch(:commits).last
- return nil unless commit && commit[:message]
+ return unless commit && commit[:message]
raw_msg = commit[:message]
diff --git a/app/services/quick_actions/interpret_service.rb b/app/services/quick_actions/interpret_service.rb
index 5c58caee8cd..131efb8925e 100644
--- a/app/services/quick_actions/interpret_service.rb
+++ b/app/services/quick_actions/interpret_service.rb
@@ -7,6 +7,11 @@ module QuickActions
attr_reader :issuable
+ # Counts how many commands have been executed.
+ # Used to display relevant feedback on UI when a note
+ # with only commands has been processed.
+ attr_accessor :commands_executed_count
+
SHRUG = '¯\\_(ツ)_/¯'.freeze
TABLEFLIP = '(╯°□°)╯︵ â”»â”â”»'.freeze
diff --git a/app/services/releases/destroy_service.rb b/app/services/releases/destroy_service.rb
index 8c2bc3b4e6e..f9f6101abdd 100644
--- a/app/services/releases/destroy_service.rb
+++ b/app/services/releases/destroy_service.rb
@@ -5,7 +5,6 @@ module Releases
include Releases::Concerns
def execute
- return error('Tag does not exist', 404) unless existing_tag
return error('Release does not exist', 404) unless release
return error('Access Denied', 403) unless allowed?
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index d6af26d949d..f711839e389 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -23,7 +23,8 @@ module Search
def allowed_scopes
strong_memoize(:allowed_scopes) do
- %w[issues merge_requests milestones]
+ allowed_scopes = %w[issues merge_requests milestones]
+ allowed_scopes << 'users' if Feature.enabled?(:users_search, default_enabled: true)
end
end
diff --git a/app/services/search/group_service.rb b/app/services/search/group_service.rb
index 34803d005e3..6f3b5f00b86 100644
--- a/app/services/search/group_service.rb
+++ b/app/services/search/group_service.rb
@@ -11,6 +11,12 @@ module Search
@group = group
end
+ def execute
+ Gitlab::GroupSearchResults.new(
+ current_user, projects, group, params[:search], default_project_filter: default_project_filter
+ )
+ end
+
def projects
return Project.none unless group
return @projects if defined? @projects
diff --git a/app/services/search/project_service.rb b/app/services/search/project_service.rb
index f223c8be103..32d5cd7ddb2 100644
--- a/app/services/search/project_service.rb
+++ b/app/services/search/project_service.rb
@@ -16,7 +16,12 @@ module Search
end
def scope
- @scope ||= %w[notes issues merge_requests milestones wiki_blobs commits].delete(params[:scope]) { 'blobs' }
+ @scope ||= begin
+ allowed_scopes = %w[notes issues merge_requests milestones wiki_blobs commits]
+ allowed_scopes << 'users' if Feature.enabled?(:users_search, default_enabled: true)
+
+ allowed_scopes.delete(params[:scope]) { 'blobs' }
+ end
end
end
end
diff --git a/app/services/suggestions/apply_service.rb b/app/services/suggestions/apply_service.rb
index 1f720fc835f..8ba50e22b09 100644
--- a/app/services/suggestions/apply_service.rb
+++ b/app/services/suggestions/apply_service.rb
@@ -7,7 +7,7 @@ module Suggestions
end
def execute(suggestion)
- unless suggestion.appliable?
+ unless suggestion.appliable?(cached: false)
return error('Suggestion is not appliable')
end
@@ -15,7 +15,13 @@ module Suggestions
return error('The file has been changed')
end
- params = file_update_params(suggestion)
+ diff_file = suggestion.diff_file
+
+ unless diff_file
+ return error('The file was not found')
+ end
+
+ params = file_update_params(suggestion, diff_file)
result = ::Files::UpdateService.new(suggestion.project, @current_user, params).execute
if result[:status] == :success
@@ -38,8 +44,8 @@ module Suggestions
suggestion.position.head_sha == suggestion.noteable.source_branch_sha
end
- def file_update_params(suggestion)
- blob = suggestion.diff_file.new_blob
+ def file_update_params(suggestion, diff_file)
+ blob = diff_file.new_blob
file_path = suggestion.file_path
branch_name = suggestion.branch
file_content = new_file_content(suggestion, blob)
diff --git a/app/services/suggestions/create_service.rb b/app/services/suggestions/create_service.rb
index 77e958cbe0c..1d3338c1b45 100644
--- a/app/services/suggestions/create_service.rb
+++ b/app/services/suggestions/create_service.rb
@@ -9,48 +9,24 @@ module Suggestions
def execute
return unless @note.supports_suggestion?
- suggestions = Banzai::SuggestionsParser.parse(@note.note)
-
- # For single line suggestion we're only looking forward to
- # change the line receiving the comment. Though, in
- # https://gitlab.com/gitlab-org/gitlab-ce/issues/53310
- # we'll introduce a ```suggestion:L<x>-<y>, so this will
- # slightly change.
- comment_line = @note.position.new_line
+ suggestions = Gitlab::Diff::SuggestionsParser.parse(@note.note,
+ project: @note.project,
+ position: @note.position)
rows =
suggestions.map.with_index do |suggestion, index|
- from_content = changing_lines(comment_line, comment_line)
-
- # The parsed suggestion doesn't have information about the correct
- # ending characters (we may have a line break, or not), so we take
- # this information from the last line being changed (last
- # characters).
- endline_chars = line_break_chars(from_content.lines.last)
- to_content = "#{suggestion}#{endline_chars}"
+ creation_params =
+ suggestion.to_hash.slice(:from_content,
+ :to_content,
+ :lines_above,
+ :lines_below)
- {
- note_id: @note.id,
- from_content: from_content,
- to_content: to_content,
- relative_order: index
- }
+ creation_params.merge!(note_id: @note.id, relative_order: index)
end
rows.in_groups_of(100, false) do |rows|
Gitlab::Database.bulk_insert('suggestions', rows)
end
end
-
- private
-
- def changing_lines(from_line, to_line)
- @note.diff_file.new_blob_lines_between(from_line, to_line).join
- end
-
- def line_break_chars(line)
- match = /\r\n|\r|\n/.match(line)
- match[0] if match
- end
end
end
diff --git a/app/services/suggestions/outdate_service.rb b/app/services/suggestions/outdate_service.rb
new file mode 100644
index 00000000000..a33aac9f6b5
--- /dev/null
+++ b/app/services/suggestions/outdate_service.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Suggestions
+ class OutdateService
+ def execute(merge_request)
+ # rubocop: disable CodeReuse/ActiveRecord
+ suggestions = merge_request.suggestions.active.includes(:note)
+
+ suggestions.find_in_batches(batch_size: 100) do |group|
+ outdatable_suggestion_ids = group.select do |suggestion|
+ suggestion.outdated?(cached: false)
+ end.map(&:id)
+
+ Suggestion.where(id: outdatable_suggestion_ids).update_all(outdated: true)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+ end
+end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index ec6c306227b..ea8ac7e4656 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -360,7 +360,7 @@ module SystemNoteService
# author - User performing the change
# branch_type - 'source' or 'target'
# old_branch - old branch name
- # new_branch - new branch nmae
+ # new_branch - new branch name
#
# Example Note text:
#
diff --git a/app/services/upload_service.rb b/app/services/upload_service.rb
index 41ca95b3b6f..403944557a2 100644
--- a/app/services/upload_service.rb
+++ b/app/services/upload_service.rb
@@ -6,7 +6,7 @@ class UploadService
end
def execute
- return nil unless @file && @file.size <= max_attachment_size
+ return unless @file && @file.size <= max_attachment_size
uploader = @uploader_class.new(@model, nil, @uploader_context)
uploader.store!(@file)
diff --git a/app/services/validate_new_branch_service.rb b/app/services/validate_new_branch_service.rb
index c19e2ec2043..3f4a59e5cee 100644
--- a/app/services/validate_new_branch_service.rb
+++ b/app/services/validate_new_branch_service.rb
@@ -3,14 +3,14 @@
require_relative 'base_service'
class ValidateNewBranchService < BaseService
- def execute(branch_name)
+ def execute(branch_name, force: false)
valid_branch = Gitlab::GitRefValidator.validate(branch_name)
unless valid_branch
return error('Branch name is invalid')
end
- if project.repository.branch_exists?(branch_name)
+ if project.repository.branch_exists?(branch_name) && !force
return error('Branch already exists')
end
diff --git a/app/uploaders/file_mover.rb b/app/uploaders/file_mover.rb
index a7f8615e9ba..236b7ed2b3d 100644
--- a/app/uploaders/file_mover.rb
+++ b/app/uploaders/file_mover.rb
@@ -11,6 +11,8 @@ class FileMover
end
def execute
+ return unless valid?
+
move
if update_markdown
@@ -21,6 +23,12 @@ class FileMover
private
+ def valid?
+ Pathname.new(temp_file_path).realpath.to_path.start_with?(
+ (Pathname(temp_file_uploader.root) + temp_file_uploader.base_dir).to_path
+ )
+ end
+
def move
FileUtils.mkdir_p(File.dirname(file_path))
FileUtils.move(temp_file_path, file_path)
diff --git a/app/validators/devise_email_validator.rb b/app/validators/devise_email_validator.rb
new file mode 100644
index 00000000000..6ca921ca7fa
--- /dev/null
+++ b/app/validators/devise_email_validator.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+# DeviseEmailValidator
+#
+# Custom validator for email formats. It asserts that there are no
+# @ symbols or whitespaces in either the localpart or the domain, and that
+# there is a single @ symbol separating the localpart and the domain.
+#
+# The available options are:
+# - regexp: Email regular expression used to validate email formats as instance of Regexp class.
+# If provided value has different type then a new Rexexp class instance is created using the value.
+# Default: +Devise.email_regexp+
+#
+# Example:
+# class User < ActiveRecord::Base
+# validates :personal_email, devise_email: true
+#
+# validates :public_email, devise_email: { regexp: Devise.email_regexp }
+# end
+class DeviseEmailValidator < ActiveModel::EachValidator
+ DEFAULT_OPTIONS = {
+ regexp: Devise.email_regexp
+ }.freeze
+
+ def initialize(options)
+ options.reverse_merge!(DEFAULT_OPTIONS)
+
+ raise ArgumentError, "Expected 'regexp' argument of type class Regexp" unless options[:regexp].is_a?(Regexp)
+
+ super(options)
+ end
+
+ def validate_each(record, attribute, value)
+ record.errors.add(attribute, :invalid) unless value =~ options[:regexp]
+ end
+end
diff --git a/app/validators/email_validator.rb b/app/validators/email_validator.rb
deleted file mode 100644
index 9459edb7515..00000000000
--- a/app/validators/email_validator.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-# frozen_string_literal: true
-
-class EmailValidator < ActiveModel::EachValidator
- def validate_each(record, attribute, value)
- record.errors.add(attribute, :invalid) unless value =~ Devise.email_regexp
- end
-end
diff --git a/app/validators/sha_validator.rb b/app/validators/sha_validator.rb
new file mode 100644
index 00000000000..77e7cfa4f6b
--- /dev/null
+++ b/app/validators/sha_validator.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class ShaValidator < ActiveModel::EachValidator
+ def validate_each(record, attribute, value)
+ return if value.blank? || Commit.valid_hash?(value)
+
+ record.errors.add(attribute, 'is not a valid SHA')
+ end
+end
diff --git a/app/validators/url_validator.rb b/app/validators/url_validator.rb
index d3e32818dc7..3fd015c3cf5 100644
--- a/app/validators/url_validator.rb
+++ b/app/validators/url_validator.rb
@@ -93,6 +93,12 @@ class UrlValidator < ActiveModel::EachValidator
end
def allow_setting_local_requests?
+ # We cannot use Gitlab::CurrentSettings as ApplicationSetting itself
+ # uses UrlValidator to validate urls. This ends up in a cycle
+ # when Gitlab::CurrentSettings creates an ApplicationSetting which then
+ # calls this validator.
+ #
+ # See https://gitlab.com/gitlab-org/gitlab-ee/issues/9833
ApplicationSetting.current&.allow_local_requests_from_hooks_and_services?
end
end
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index 77e84abd76e..a5f34d0dab2 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -42,6 +42,7 @@
%br
Images with incorrect dimensions are not resized automatically, and may result in unexpected behavior.
+ = render partial: 'admin/appearances/system_header_footer_form', locals: { form: f }
%hr
.row
diff --git a/app/views/admin/appearances/_system_header_footer_form.html.haml b/app/views/admin/appearances/_system_header_footer_form.html.haml
new file mode 100644
index 00000000000..4301ebd05af
--- /dev/null
+++ b/app/views/admin/appearances/_system_header_footer_form.html.haml
@@ -0,0 +1,33 @@
+- form = local_assigns.fetch(:form)
+
+%hr
+.row
+ .col-lg-4.profile-settings-sidebar
+ %h4.prepend-top-0
+ = _('System header and footer')
+
+ .col-lg-8
+ .form-group
+ = form.label :header_message, _('Header message'), class: 'col-form-label label-bold'
+ = form.text_area :header_message, placeholder: _('State your message to activate'), class: "form-control js-autosize"
+ .form-group
+ = form.label :footer_message, _('Footer message'), class: 'col-form-label label-bold'
+ = form.text_area :footer_message, placeholder: _('State your message to activate'), class: "form-control js-autosize"
+ .form-group
+ .form-check
+ = form.check_box :email_header_and_footer_enabled, class: 'form-check-input'
+ = form.label :email_header_and_footer_enabled, class: 'label-bold' do
+ = _('Enable header and footer in emails')
+
+ .hint
+ = _('Add header and footer to emails. Please note that color settings will only be applied within the application interface')
+
+ .form-group.js-toggle-colors-container
+ %button.btn.btn-link.js-toggle-colors-link{ type: 'button' }
+ = _('Customize colors')
+ .form-group.js-toggle-colors-container.hide
+ = form.label :message_background_color, _('Background Color'), class: 'col-form-label label-bold'
+ = form.color_field :message_background_color, class: "form-control"
+ .form-group.js-toggle-colors-container.hide
+ = form.label :message_font_color, _('Font Color'), class: 'col-form-label label-bold'
+ = form.color_field :message_font_color, class: "form-control"
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 65a24854583..9ed4bc44aae 100644
--- a/app/views/admin/application_settings/_account_and_limit.html.haml
+++ b/app/views/admin/application_settings/_account_and_limit.html.haml
@@ -6,32 +6,35 @@
.form-check
= f.check_box :gravatar_enabled, class: 'form-check-input'
= f.label :gravatar_enabled, class: 'form-check-label' do
- Gravatar enabled
+ = _('Gravatar enabled')
.form-group
= 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-bold'
+ = f.label :max_attachment_size, _('Maximum attachment size (MB)'), class: 'label-bold'
= f.number_field :max_attachment_size, class: 'form-control'
+
+ = render_if_exists 'admin/application_settings/repository_size_limit_setting', form: f
+
.form-group
- = f.label :receive_max_input_size, 'Maximum push size (MB)', class: 'label-light'
+ = f.label :receive_max_input_size, _('Maximum push size (MB)'), class: 'label-light'
= f.number_field :receive_max_input_size, class: 'form-control qa-receive-max-input-size-field'
.form-group
- = f.label :session_expire_delay, 'Session duration (minutes)', class: 'label-light'
+ = f.label :session_expire_delay, _('Session duration (minutes)'), class: 'label-light'
= 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
+ %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-bold'
+ = 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
+ = _('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-bold'
+ = 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
- Newly registered users will by default be external
+ = _('Newly registered users will by default be external')
.prepend-top-10
= _('Internal users')
= f.text_field :user_default_internal_regex, placeholder: _('Regex pattern'), class: 'form-control prepend-top-5'
@@ -40,10 +43,12 @@
= link_to _('More information'), help_page_path('user/permissions', anchor: 'external-users-permissions'),
target: '_blank'
.form-group
- = f.label :user_show_add_ssh_key_message, 'Prompt users to upload SSH keys', class: 'label-bold'
+ = f.label :user_show_add_ssh_key_message, _('Prompt users to upload SSH keys'), class: 'label-bold'
.form-check
= f.check_box :user_show_add_ssh_key_message, class: 'form-check-input'
= f.label :user_show_add_ssh_key_message, class: 'form-check-label' do
- Inform users without uploaded SSH keys that they can't push over SSH until one is added
+ = _("Inform users without uploaded SSH keys that they can't push over SSH until one is added")
+
+ = render_if_exists 'admin/application_settings/availability_on_namespace_setting', form: f
- = f.submit 'Save changes', class: 'btn btn-success qa-save-changes-button'
+ = f.submit _('Save changes'), class: 'btn btn-success qa-save-changes-button'
diff --git a/app/views/admin/application_settings/_ci_cd.html.haml b/app/views/admin/application_settings/_ci_cd.html.haml
index c99d7e9b8e9..b8c481df0d2 100644
--- a/app/views/admin/application_settings/_ci_cd.html.haml
+++ b/app/views/admin/application_settings/_ci_cd.html.haml
@@ -8,7 +8,7 @@
.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
+ = s_('CICD|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'
@@ -21,34 +21,31 @@
.form-check
= f.check_box :shared_runners_enabled, class: 'form-check-input'
= f.label :shared_runners_enabled, class: 'form-check-label' do
- Enable shared runners for new projects
+ = s_("AdminSettings|Enable shared runners for new projects")
+
+ = render_if_exists 'admin/application_settings/shared_runners_minutes_setting', form: f
+
.form-group
= 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-text.text-muted= _("Markdown enabled")
.form-group
- = f.label :max_artifacts_size, 'Maximum artifacts size (MB)', class: 'label-bold'
+ = 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
+ = _("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-bold'
+ = 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.
- 0 for unlimited.
- The default unit is in seconds, but you can define an alternative. For example:
- <code>4 mins 2 sec</code>, <code>2h42min</code>.
+ = _("Set the default expiration time for each job's artifacts. 0 for unlimited. The default unit is in seconds, but you can define an alternative. For example: <code>4 mins 2 sec</code>, <code>2h42min</code>.").html_safe
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
.form-group
- = f.label :archive_builds_in_human_readable, 'Archive jobs', class: 'label-bold'
+ = f.label :archive_builds_in_human_readable, _('Archive jobs'), class: 'label-bold'
= f.text_field :archive_builds_in_human_readable, class: 'form-control', placeholder: 'never'
.form-text.text-muted
- Set the duration for which the jobs will be considered as old and expired.
- Once that time passes, the jobs will be archived and no longer able to be
- retried. Make it empty to never expire jobs. It has to be no less than 1 day,
- for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>.
+ = _("Set the duration for which the jobs will be considered as old and expired. Once that time passes, the jobs will be archived and no longer able to be retried. Make it empty to never expire jobs. It has to be no less than 1 day, for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>.").html_safe
.form-group
.form-check
= f.check_box :protected_ci_variables, class: 'form-check-input'
@@ -57,4 +54,4 @@
.form-text.text-muted
= s_('AdminSettings|When creating a new environment variable it will be protected by default.')
- = f.submit 'Save changes', class: "btn btn-success"
+ = f.submit _('Save changes'), class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_email.html.haml b/app/views/admin/application_settings/_email.html.haml
index 60a6be731ea..3f30c75fbb6 100644
--- a/app/views/admin/application_settings/_email.html.haml
+++ b/app/views/admin/application_settings/_email.html.haml
@@ -6,20 +6,16 @@
.form-check
= f.check_box :email_author_in_body, class: 'form-check-input'
= f.label :email_author_in_body, class: 'form-check-label' do
- Include author name in notification email body
+ = _('Include author name in notification email body')
.form-text.text-muted
- 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.
+ = _('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.')
.form-group
.form-check
= f.check_box :html_emails_enabled, class: 'form-check-input'
= f.label :html_emails_enabled, class: 'form-check-label' do
- Enable HTML emails
+ = _('Enable HTML emails')
.form-text.text-muted
- 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.
+ = _('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.')
.form-group
= f.label :commit_email_hostname, _('Custom hostname (for private commit emails)'), class: 'label-bold'
= f.text_field :commit_email_hostname, class: 'form-control'
@@ -27,4 +23,6 @@
- commit_email_hostname_docs_link = link_to _('Learn more'), help_page_path('user/admin_area/settings/email', anchor: 'custom-private-commit-email-hostname'), target: '_blank'
= _("This setting will update the hostname that is used to generate private commit emails. %{learn_more}").html_safe % { learn_more: commit_email_hostname_docs_link }
- = f.submit 'Save changes', class: "btn btn-success"
+ = render_if_exists 'admin/application_settings/email_additional_text_setting', form: f
+
+ = f.submit _('Save changes'), class: "btn btn-success"
diff --git a/app/views/admin/application_settings/_help_page.html.haml b/app/views/admin/application_settings/_help_page.html.haml
index 70c8c74cc5d..aa491c735d1 100644
--- a/app/views/admin/application_settings/_help_page.html.haml
+++ b/app/views/admin/application_settings/_help_page.html.haml
@@ -2,18 +2,20 @@
= form_errors(@application_setting)
%fieldset
+ = render_if_exists 'admin/application_settings/help_text_setting', form: f
+
.form-group
= 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-text.text-muted= _('Markdown enabled')
.form-group
.form-check
= f.check_box :help_page_hide_commercial_content, class: 'form-check-input'
= f.label :help_page_hide_commercial_content, class: 'form-check-label' do
- Hide marketing-related entries from help
+ = _('Hide marketing-related entries from help')
.form-group
- = f.label :help_page_support_url, 'Support page URL', class: 'label-bold'
+ = 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
+ %span.form-text.text-muted#support_help_block= _('Alternate support URL for help page')
- = f.submit 'Save changes', class: "btn btn-success"
+ = f.submit _('Save changes'), class: "btn btn-success"
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 615aa6317b0..f2f2cd1282a 100644
--- a/app/views/admin/application_settings/_repository_mirrors_form.html.haml
+++ b/app/views/admin/application_settings/_repository_mirrors_form.html.haml
@@ -3,13 +3,15 @@
%fieldset
.form-group
- = f.label :mirror_available, 'Enable mirror configuration', class: 'label-bold'
+ = 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
- Allow mirrors to be set up for projects
+ = _('Allow mirrors to be set up for projects')
%span.form-text.text-muted
- If disabled, only admins will be able to set up mirrors in projects.
+ = _('If disabled, only admins will be able to set up mirrors in projects.')
= link_to icon('question-circle'), help_page_path('workflow/repository_mirroring')
- = f.submit 'Save changes', class: "btn btn-success"
+ = render_if_exists 'admin/application_settings/mirror_settings', form: f
+
+ = 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 0725ffb7f6c..8122d81f578 100644
--- a/app/views/admin/application_settings/_visibility_and_access.html.haml
+++ b/app/views/admin/application_settings/_visibility_and_access.html.haml
@@ -5,6 +5,7 @@
.form-group
= 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'
+ = render_if_exists 'admin/application_settings/project_creation_level', form: f, application_setting: @application_setting
.form-group.visibility-level-setting
= 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)
@@ -22,32 +23,33 @@
.form-check
= level
%span.form-text.text-muted#restricted-visibility-help
- 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.
+ = _('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-bold'
= hidden_field_tag 'application_setting[import_sources][]'
- import_sources_checkboxes('import-sources-help', class: 'form-check-input').each do |source|
.form-check= source
%span.form-text.text-muted#import-sources-help
- Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
+ = _('Enabled sources for code import during project creation. OmniAuth must be configured for GitHub')
= link_to "(?)", help_page_path("integration/github")
, Bitbucket
= link_to "(?)", help_page_path("integration/bitbucket")
and GitLab.com
= link_to "(?)", help_page_path("integration/gitlab")
+ = render_if_exists 'admin/application_settings/ldap_access_setting', form: f
+
.form-group
.form-check
= f.check_box :project_export_enabled, class: 'form-check-input'
= f.label :project_export_enabled, class: 'form-check-label' do
- Project export enabled
+ = _('Project export enabled')
.form-group
- %label.label-bold 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.
+ = _('Allow only the selected protocols to be used for Git access.')
- ApplicationSetting::SUPPORTED_KEY_TYPES.each do |type|
- field_name = :"#{type}_key_restriction"
@@ -55,4 +57,4 @@
= 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"
+ = f.submit _('Save changes'), class: "btn btn-success"
diff --git a/app/views/admin/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index 6756299cf43..581f6ae0714 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -22,9 +22,10 @@
%h3.text-center
Users:
= approximate_count_with_delimiters(@counts, User)
- = render_if_exists 'admin/dashboard/users_statistics'
%hr
- = link_to 'New user', new_admin_user_path, class: "btn btn-success"
+ .btn-group.d-flex{ role: 'group' }
+ = link_to 'New user', new_admin_user_path, class: "btn btn-success"
+ = render_if_exists 'admin/dashboard/users_statistics'
.col-sm-4
.info-well.dark-well
.well-segment.well-centered
@@ -162,7 +163,7 @@
%span.float-right
#{Rails::VERSION::STRING}
%p
- = Gitlab::Database.adapter_name
+ = Gitlab::Database.human_adapter_name
%span.float-right
= Gitlab::Database.version
%p
diff --git a/app/views/admin/deploy_keys/edit.html.haml b/app/views/admin/deploy_keys/edit.html.haml
index 7c04ef03947..99d8af65068 100644
--- a/app/views/admin/deploy_keys/edit.html.haml
+++ b/app/views/admin/deploy_keys/edit.html.haml
@@ -1,10 +1,10 @@
-- page_title 'Edit Deploy Key'
-%h3.page-title Edit public deploy key
+- page_title _('Edit Deploy Key')
+%h3.page-title= _('Edit public deploy key')
%hr
%div
= form_for [:admin, @deploy_key], html: { class: 'deploy-key-form' } do |f|
= render partial: 'shared/deploy_keys/form', locals: { form: f, deploy_key: @deploy_key }
.form-actions
- = f.submit 'Save changes', class: 'btn-success btn'
- = link_to 'Cancel', admin_deploy_keys_path, class: 'btn btn-cancel'
+ = f.submit _('Save changes'), class: 'btn-success btn'
+ = link_to _('Cancel'), admin_deploy_keys_path, class: 'btn btn-cancel'
diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml
index 01013be06d6..9fffa97f969 100644
--- a/app/views/admin/deploy_keys/index.html.haml
+++ b/app/views/admin/deploy_keys/index.html.haml
@@ -1,19 +1,19 @@
-- page_title "Deploy Keys"
+- page_title _('Deploy Keys')
%h3.page-title.deploy-keys-title
- Public deploy keys (#{@deploy_keys.count})
+ = _('Public deploy keys (%{deploy_keys_count})') % { deploy_keys_count: @deploy_keys.count }
.float-right
- = link_to 'New deploy key', new_admin_deploy_key_path, class: 'btn btn-success btn-sm btn-inverted'
+ = link_to _('New deploy key'), new_admin_deploy_key_path, class: 'btn btn-success btn-sm btn-inverted'
- if @deploy_keys.any?
.table-holder.deploy-keys-list
%table.table
%thead
%tr
- %th.col-sm-2 Title
- %th.col-sm-4 Fingerprint
- %th.col-sm-2 Projects with write access
- %th.col-sm-2 Added at
+ %th.col-sm-2= _('Title')
+ %th.col-sm-4= _('Fingerprint')
+ %th.col-sm-2= _('Projects with write access')
+ %th.col-sm-2= _('Added at')
%th.col-sm-2
%tbody
- @deploy_keys.each do |deploy_key|
@@ -27,8 +27,8 @@
= link_to project.full_name, admin_project_path(project), class: 'label deploy-project-label'
%td
%span.cgray
- added #{time_ago_with_tooltip(deploy_key.created_at)}
+ = _('added %{created_at_timeago}').html_safe % { created_at_timeago: time_ago_with_tooltip(deploy_key.created_at) }
%td
.float-right
- = link_to 'Edit', edit_admin_deploy_key_path(deploy_key), class: 'btn btn-sm'
- = link_to 'Remove', admin_deploy_key_path(deploy_key), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove delete-key'
+ = link_to _('Edit'), edit_admin_deploy_key_path(deploy_key), class: 'btn btn-sm'
+ = link_to _('Remove'), admin_deploy_key_path(deploy_key), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn btn-sm btn-remove delete-key'
diff --git a/app/views/admin/groups/_group.html.haml b/app/views/admin/groups/_group.html.haml
index 0a688b90f3a..395c469255e 100644
--- a/app/views/admin/groups/_group.html.haml
+++ b/app/views/admin/groups/_group.html.haml
@@ -22,7 +22,7 @@
%span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
= visibility_level_icon(group.visibility_level, fw: false)
- .avatar-container.s40
+ .avatar-container.rect-avatar.s40
= group_icon(group, class: "avatar s40 d-none d-sm-block")
.title
= link_to [:admin, group], class: 'group-name' do
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 93da87538bc..00d255846f9 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -15,7 +15,7 @@
= _('Group info:')
%ul.content-list
%li
- .avatar-container.s60
+ .avatar-container.rect-avatar.s60
= group_icon(@group, class: "avatar s60")
%li
%span.light= _('Name:')
diff --git a/app/views/admin/projects/_projects.html.haml b/app/views/admin/projects/_projects.html.haml
index 50296a2afe7..5bc695aa7b5 100644
--- a/app/views/admin/projects/_projects.html.haml
+++ b/app/views/admin/projects/_projects.html.haml
@@ -19,7 +19,7 @@
.title
= link_to(admin_namespace_project_path(project.namespace, project)) do
.dash-project-avatar
- .avatar-container.s40
+ .avatar-container.rect-avatar.s40
= project_icon(project, alt: '', class: 'avatar project-avatar s40', width: 40, height: 40)
%span.project-full-name
%span.namespace-name
diff --git a/app/views/admin/runners/_runner.html.haml b/app/views/admin/runners/_runner.html.haml
index ec57eb1ed08..ecf2b1d60ba 100644
--- a/app/views/admin/runners/_runner.html.haml
+++ b/app/views/admin/runners/_runner.html.haml
@@ -44,12 +44,12 @@
.table-section.section-5
.table-mobile-header{ role: 'rowheader' }= _('Jobs')
.table-mobile-content
- = runner.builds.count(:all)
+ = limited_counter_with_delimiter(runner.builds)
.table-section.section-10.section-wrap
.table-mobile-header{ role: 'rowheader' }= _('Tags')
.table-mobile-content
- - runner.tag_list.sort.each do |tag|
+ - runner.tags.map(&:name).sort.each do |tag|
%span.badge.badge-primary
= tag
diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml
index 81380587fd2..2e23b748edb 100644
--- a/app/views/admin/runners/index.html.haml
+++ b/app/views/admin/runners/index.html.haml
@@ -92,6 +92,25 @@
= button_tag class: %w[btn btn-link] do
= runner_type.titleize
+ #js-dropdown-admin-runner-type.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ - Ci::Runner::AVAILABLE_TYPES.each do |runner_type|
+ %li.filter-dropdown-item{ data: { value: runner_type } }
+ = button_tag class: %w[btn btn-link] do
+ = runner_type.titleize
+
+ #js-dropdown-runner-tag.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'none' } }
+ %button.btn.btn-link
+ = _('No Tag')
+ %li.divider.droplab-item-ignore
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
+ %li.filter-dropdown-item
+ %button.btn.btn-link.js-data-value
+ %span.dropdown-light-content
+ {{name}}
+
= button_tag class: %w[clear-search hidden] do
= icon('times')
.filter-dropdown-container
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index a74e052707f..4ffa8d89504 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -117,6 +117,11 @@
%strong
= @user.sign_in_count
+ %li
+ %span.light= _("Highest role:")
+ %strong
+ = Gitlab::Access.human_access_with_none(@user.highest_role)
+
- if @user.ldap_user?
%li
%span.light LDAP uid:
diff --git a/app/views/ci/status/_icon.html.haml b/app/views/ci/status/_icon.html.haml
new file mode 100644
index 00000000000..f38bdb2e5ed
--- /dev/null
+++ b/app/views/ci/status/_icon.html.haml
@@ -0,0 +1,16 @@
+- status = local_assigns.fetch(:status)
+- size = local_assigns.fetch(:size, 16)
+- type = local_assigns.fetch(:type, 'pipeline')
+- tooltip_placement = local_assigns.fetch(:tooltip_placement, "left")
+- path = local_assigns.fetch(:path, status.has_details? ? status.details_path : nil)
+- css_classes = "ci-status-link ci-status-icon ci-status-icon-#{status.group} has-tooltip"
+- title = s_("PipelineStatusTooltip|Pipeline: %{ci_status}") % {ci_status: status.label}
+- if type == 'commit'
+ - title = s_("PipelineStatusTooltip|Commit: %{ci_status}") % {ci_status: status.label}
+
+- if path
+ = link_to path, class: css_classes, title: title, data: { placement: tooltip_placement } do
+ = sprite_icon(status.icon, size: size)
+- else
+ %span{ class: css_classes, title: title, data: { placement: tooltip_placement } }
+ = sprite_icon(status.icon, size: size)
diff --git a/app/views/clusters/clusters/_form.html.haml b/app/views/clusters/clusters/_form.html.haml
index 7acd9ce0562..455322b2089 100644
--- a/app/views/clusters/clusters/_form.html.haml
+++ b/app/views/clusters/clusters/_form.html.haml
@@ -28,19 +28,19 @@
.form-group
%h5= s_('ClusterIntegration|Base domain')
- = field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus'
+ = field.text_field :base_domain, class: 'col-md-6 form-control js-select-on-focus qa-base-domain'
.form-text.text-muted
- auto_devops_url = help_page_path('topics/autodevops/index')
- auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }
= s_('ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain.').html_safe % { auto_devops_start: auto_devops_start, auto_devops_end: '</a>'.html_safe }
- - if @cluster.application_ingress_external_ip.present?
+ %span{ :class => ["js-ingress-domain-help-text", ("hide" unless @cluster.application_ingress_external_ip.present?)] }
= s_('ClusterIntegration|Alternatively')
- %code #{@cluster.application_ingress_external_ip}.nip.io
+ %code{ :class => "js-ingress-domain-snippet" } #{@cluster.application_ingress_external_ip}.nip.io
= s_('ClusterIntegration| can be used instead of a custom domain.')
- - custom_domain_url = help_page_path('user/project/clusters/index', anchor: 'pointing-your-dns-at-the-cluster-ip')
+ - custom_domain_url = help_page_path('user/project/clusters/index', anchor: 'pointing-your-dns-at-the-external-endpoint')
- custom_domain_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: custom_domain_url }
= s_('ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}.').html_safe % { custom_domain_start: custom_domain_start, custom_domain_end: '</a>'.html_safe }
- if can?(current_user, :update_cluster, @cluster)
.form-group
- = field.submit _('Save changes'), class: 'btn btn-success'
+ = field.submit _('Save changes'), class: 'btn btn-success qa-save-domain'
diff --git a/app/views/clusters/clusters/_sidebar.html.haml b/app/views/clusters/clusters/_sidebar.html.haml
index 6e4415c21a9..60ccad5b943 100644
--- a/app/views/clusters/clusters/_sidebar.html.haml
+++ b/app/views/clusters/clusters/_sidebar.html.haml
@@ -4,3 +4,5 @@
= clusterable.sidebar_text
%p
= clusterable.learn_more_link
+
+= render_if_exists 'clusters/multiple_clusters_message'
diff --git a/app/views/clusters/clusters/gcp/_form.html.haml b/app/views/clusters/clusters/gcp/_form.html.haml
index 8ed4666e79a..00582e19662 100644
--- a/app/views/clusters/clusters/gcp/_form.html.haml
+++ b/app/views/clusters/clusters/gcp/_form.html.haml
@@ -17,9 +17,11 @@
.form-group
= 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-bold'
- = field.text_field :environment_scope, class: 'form-control', readonly: !has_multiple_clusters?, placeholder: s_('ClusterIntegration|Environment scope')
+ - if has_multiple_clusters?
+ .form-group
+ = field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
+ = field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope')
+ .form-text.text-muted= s_("ClusterIntegration|Choose which of your environments will use this cluster.")
= field.fields_for :provider_gcp, @gcp_cluster.provider_gcp do |provider_gcp_field|
.form-group
diff --git a/app/views/clusters/clusters/show.html.haml b/app/views/clusters/clusters/show.html.haml
index 1ef76ef801e..61188c6fa0b 100644
--- a/app/views/clusters/clusters/show.html.haml
+++ b/app/views/clusters/clusters/show.html.haml
@@ -15,14 +15,15 @@
install_runner_path: clusterable.install_applications_cluster_path(@cluster, :runner),
install_jupyter_path: clusterable.install_applications_cluster_path(@cluster, :jupyter),
install_knative_path: clusterable.install_applications_cluster_path(@cluster, :knative),
+ update_knative_path: clusterable.update_applications_cluster_path(@cluster, :knative),
toggle_status: @cluster.enabled? ? 'true': 'false',
- has_rbac: @cluster.platform_kubernetes_rbac? ? 'true': 'false',
+ has_rbac: has_rbac_enabled?(@cluster) ? 'true': 'false',
cluster_type: @cluster.cluster_type,
cluster_status: @cluster.status_name,
cluster_status_reason: @cluster.status_reason,
help_path: help_page_path('user/project/clusters/index.md', anchor: 'installing-applications'),
- ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-ip-address'),
- ingress_dns_help_path: help_page_path('topics/autodevops/quick_start_guide.md', anchor: 'point-dns-at-cluster-ip'),
+ ingress_help_path: help_page_path('user/project/clusters/index.md', anchor: 'getting-the-external-endpoint'),
+ ingress_dns_help_path: help_page_path('user/project/clusters/index.md', anchor: 'manually-determining-the-external-endpoint'),
manage_prometheus_path: manage_prometheus_path } }
.js-cluster-application-notice
@@ -33,6 +34,8 @@
= render 'banner'
= render 'form'
+ = render_if_exists 'health'
+
.cluster-applications-table#js-cluster-applications
%section.settings#js-cluster-details{ class: ('expanded' if expanded) }
diff --git a/app/views/clusters/clusters/user/_form.html.haml b/app/views/clusters/clusters/user/_form.html.haml
index 9793c77fc2b..136f98d0126 100644
--- a/app/views/clusters/clusters/user/_form.html.haml
+++ b/app/views/clusters/clusters/user/_form.html.haml
@@ -7,6 +7,7 @@
.form-group
= field.label :environment_scope, s_('ClusterIntegration|Environment scope'), class: 'label-bold'
= field.text_field :environment_scope, class: 'form-control', placeholder: s_('ClusterIntegration|Environment scope')
+ .form-text.text-muted= s_("ClusterIntegration|Choose which of your environments will use this cluster.")
= field.fields_for :platform_kubernetes, @user_cluster.platform_kubernetes do |platform_kubernetes_field|
.form-group
diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml
index 8d99f84755a..a05d0190efb 100644
--- a/app/views/dashboard/_snippets_head.html.haml
+++ b/app/views/dashboard/_snippets_head.html.haml
@@ -1,9 +1,9 @@
.page-title-holder
%h1.page-title= _('Snippets')
- - if current_user
+ - if current_user && current_user.snippets.any? || @snippets.any?
.page-title-controls
- = link_to "New snippet", new_snippet_path, class: "btn btn-success", title: "New snippet"
+ = link_to _("New snippet"), new_snippet_path, class: "btn btn-success", title: _("New snippet")
.top-area
%ul.nav-links.nav.nav-tabs
diff --git a/app/views/dashboard/activity.html.haml b/app/views/dashboard/activity.html.haml
index 4dbda5c754b..b1c192d7bad 100644
--- a/app/views/dashboard/activity.html.haml
+++ b/app/views/dashboard/activity.html.haml
@@ -5,7 +5,7 @@
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
- page_title "Activity"
- header_title "Activity", activity_dashboard_path
diff --git a/app/views/dashboard/groups/_groups.html.haml b/app/views/dashboard/groups/_groups.html.haml
index db856ef7d7b..2f9dbf87d95 100644
--- a/app/views/dashboard/groups/_groups.html.haml
+++ b/app/views/dashboard/groups/_groups.html.haml
@@ -1,4 +1,4 @@
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
- .loading-container.text-center
- = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
+ .loading-container.text-center.prepend-top-20
+ .spinner.spinner-md
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 19b06ba5cdd..d1d8d970b59 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -2,7 +2,7 @@
- page_title "Groups"
- header_title "Groups", dashboard_groups_path
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
= render 'dashboard/groups_head'
- if params[:filter].blank? && @groups.empty?
diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml
index afd46412fab..352ec986ccb 100644
--- a/app/views/dashboard/issues.html.haml
+++ b/app/views/dashboard/issues.html.haml
@@ -4,7 +4,7 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, safe_params.merge(rss_url_options).to_h, title: "#{current_user.name} issues")
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
.page-title-holder
%h1.page-title= _('Issues')
diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml
index 3e5f13b92e3..659cc254b10 100644
--- a/app/views/dashboard/merge_requests.html.haml
+++ b/app/views/dashboard/merge_requests.html.haml
@@ -2,7 +2,7 @@
- page_title _("Merge Requests")
- @breadcrumb_link = merge_requests_dashboard_path(assignee_username: current_user.username)
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
.page-title-holder
%h1.page-title= _('Merge Requests')
diff --git a/app/views/dashboard/projects/_starred_empty_state.html.haml b/app/views/dashboard/projects/_starred_empty_state.html.haml
new file mode 100644
index 00000000000..bea27f1a456
--- /dev/null
+++ b/app/views/dashboard/projects/_starred_empty_state.html.haml
@@ -0,0 +1,9 @@
+.row.empty-state
+ .col-12
+ .svg-content.svg-250
+ = image_tag 'illustrations/starred_empty.svg'
+ .text-content
+ %h4.text-center
+ = s_("StarredProjectsEmptyState|You don't have starred projects yet.")
+ %p.text-secondary
+ = s_("StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page.")
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 446b4715b2d..dc9468b3368 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -4,7 +4,7 @@
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, dashboard_projects_url(rss_url_options), title: "All activity")
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
- page_title "Projects"
- header_title "Projects", dashboard_projects_path
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index ad08409c8fe..a0d85446e5f 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -1,10 +1,10 @@
- @hide_top_links = true
- @no_container = true
-- breadcrumb_title "Projects"
-- page_title "Starred Projects"
-- header_title "Projects", dashboard_projects_path
+- breadcrumb_title _("Projects")
+- page_title _("Starred Projects")
+- header_title _("Projects"), dashboard_projects_path
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
%div{ class: container_class }
= render "projects/last_push"
@@ -13,5 +13,4 @@
- if params[:filter_projects] || any_projects?(@projects)
= render 'projects'
- else
- %h3.page-title You don't have starred projects yet
- %p.slead Visit project page and press on star icon and it will appear on this page.
+ = render 'starred_empty_state'
diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml
index 6eb067da95c..b649fe91c24 100644
--- a/app/views/dashboard/snippets/index.html.haml
+++ b/app/views/dashboard/snippets/index.html.haml
@@ -3,6 +3,14 @@
- header_title "Snippets", dashboard_snippets_path
= render 'dashboard/snippets_head'
-= render partial: 'snippets/snippets_scope_menu', locals: { include_private: true }
+- if current_user.snippets.exists?
+ = render partial: 'snippets/snippets_scope_menu', locals: { include_private: true }
-= render partial: 'snippets/snippets', locals: { link_project: true }
+.d-block.d-sm-none
+ &nbsp;
+ = link_to _("New snippet"), new_snippet_path, class: "btn btn-success btn-block", title: _("New snippet")
+
+- if current_user.snippets.exists?
+ = render partial: 'shared/snippets/list', locals: { link_project: true }
+- else
+ = render 'shared/empty_states/snippets', button_path: new_snippet_path
diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml
index 47729321961..c569bc682a6 100644
--- a/app/views/dashboard/todos/index.html.haml
+++ b/app/views/dashboard/todos/index.html.haml
@@ -2,7 +2,7 @@
- page_title "Todos"
- header_title "Todos", dashboard_todos_path
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
.page-title-holder
%h1.page-title= _('Todos')
diff --git a/app/views/discussions/_diff_with_notes.html.haml b/app/views/discussions/_diff_with_notes.html.haml
index 44c898e0fac..8a3c841de0b 100644
--- a/app/views/discussions/_diff_with_notes.html.haml
+++ b/app/views/discussions/_diff_with_notes.html.haml
@@ -11,8 +11,8 @@
= render "projects/diffs/file_header", diff_file: diff_file, url: discussion_path(discussion), show_toggle: false
- if diff_file.text?
- .diff-content.code.js-syntax-highlight
- %table
+ .diff-content
+ %table.code.js-syntax-highlight
- if expanded
- discussions = { discussion.original_line_code => [discussion] }
= render partial: "projects/diffs/line",
diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml
index 1f5c70a6c6e..5d85d9e431f 100644
--- a/app/views/doorkeeper/applications/index.html.haml
+++ b/app/views/doorkeeper/applications/index.html.haml
@@ -52,7 +52,7 @@
.oauth-authorized-applications.prepend-top-20.append-bottom-default
- if user_oauth_applications?
%h5
- = _("Authorized applications (%{size})") % { size: @authorized_tokens.size }
+ = _("Authorized applications (%{size})") % { size: @authorized_apps.size + @authorized_anonymous_tokens.size }
- if @authorized_tokens.any?
.table-responsive
diff --git a/app/views/explore/groups/_groups.html.haml b/app/views/explore/groups/_groups.html.haml
index ff57b39e947..a3249275d5e 100644
--- a/app/views/explore/groups/_groups.html.haml
+++ b/app/views/explore/groups/_groups.html.haml
@@ -1,4 +1,4 @@
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: explore_groups_path(format: :json), path: explore_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
- .loading-container.text-center
- = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
+ .loading-container.text-center.prepend-top-20
+ .spinner.spinner-md
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 869be4e8581..fd86d07fc86 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -2,7 +2,7 @@
- page_title _("Groups")
- header_title _("Groups"), dashboard_groups_path
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
- if current_user
= render 'dashboard/groups_head'
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index d18dec7bd8e..dd2bf6a5ef8 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -2,7 +2,7 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
- 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 d18dec7bd8e..dd2bf6a5ef8 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -2,7 +2,7 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
- 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 d18dec7bd8e..dd2bf6a5ef8 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -2,7 +2,7 @@
- page_title _("Projects")
- header_title _("Projects"), dashboard_projects_path
-= render_if_exists "shared/gold_trial_callout"
+= render_dashboard_gold_trial(current_user)
- if current_user
= render 'dashboard/projects_head'
diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml
index 94fc4ac21d2..d23c8301b10 100644
--- a/app/views/explore/snippets/index.html.haml
+++ b/app/views/explore/snippets/index.html.haml
@@ -7,4 +7,4 @@
- else
= render 'explore/head'
-= render partial: 'snippets/snippets', locals: { link_project: true }
+= render partial: 'shared/snippets/list', locals: { link_project: true }
diff --git a/app/views/groups/_archived_projects.html.haml b/app/views/groups/_archived_projects.html.haml
index ed79f5790f0..48e9f630050 100644
--- a/app/views/groups/_archived_projects.html.haml
+++ b/app/views/groups/_archived_projects.html.haml
@@ -4,5 +4,5 @@
%ul.content-list{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
.js-groups-list-holder
- .loading-container.text-center
- = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
+ .loading-container.text-center.prepend-top-20
+ .spinner.spinner-md
diff --git a/app/views/groups/_home_panel.html.haml b/app/views/groups/_home_panel.html.haml
index 3a8d95f44d1..4daf3683eaf 100644
--- a/app/views/groups/_home_panel.html.haml
+++ b/app/views/groups/_home_panel.html.haml
@@ -3,7 +3,7 @@
.group-home-panel
.row.mb-3
.home-panel-title-row.col-md-12.col-lg-6.d-flex
- .avatar-container.home-panel-avatar.append-right-default.float-none
+ .avatar-container.rect-avatar.s64.home-panel-avatar.append-right-default.float-none
= group_icon(@group, class: 'avatar avatar-tile s64', width: 64, height: 64)
.d-flex.flex-column.flex-wrap.align-items-baseline
.d-inline-flex.align-items-baseline
@@ -13,7 +13,7 @@
= visibility_level_icon(@group.visibility_level, fw: false, options: {class: 'icon'})
.home-panel-metadata.d-flex.align-items-center.text-secondary
%span
- = _("Group")
+ = _("Group ID: %{group_id}") % { group_id: @group.id }
- if current_user
%span.access-request-links.prepend-left-8
= render 'shared/members/access_request_links', source: @group
@@ -47,7 +47,7 @@
%strong= new_subgroup_label
%span= s_("GroupsTree|Create a subgroup in this group.")
- else
- = link_to new_project_label, new_project_path(namespace_id: @group.id), class: "btn btn-success"
+ = link_to new_project_label, new_project_path(namespace_id: @group.id), class: "btn btn-success prepend-top-default"
- if @group.description.present?
.group-home-desc.mt-1
diff --git a/app/views/groups/_shared_projects.html.haml b/app/views/groups/_shared_projects.html.haml
index 4eb8367f633..2769b69add3 100644
--- a/app/views/groups/_shared_projects.html.haml
+++ b/app/views/groups/_shared_projects.html.haml
@@ -4,5 +4,5 @@
%ul.content-list{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
.js-groups-list-holder
- .loading-container.text-center
- = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
+ .loading-container.text-center.prepend-top-20
+ .spinner.spinner-md
diff --git a/app/views/groups/_subgroups_and_projects.html.haml b/app/views/groups/_subgroups_and_projects.html.haml
index d53c8026df8..784f5ac233e 100644
--- a/app/views/groups/_subgroups_and_projects.html.haml
+++ b/app/views/groups/_subgroups_and_projects.html.haml
@@ -4,5 +4,5 @@
%ul.content-list{ data: { hide_projects: 'false', group_id: group.id, path: group_path(group) } }
.js-groups-list-holder
- .loading-container.text-center
- = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
+ .loading-container.text-center.prepend-top-20
+ .spinner.spinner-md
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 39d0f620283..2f635757902 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -25,6 +25,8 @@
.settings-content
= render 'groups/settings/permissions'
+= render_if_exists 'groups/insights', expanded: expanded
+
%section.settings.no-animate#js-badge-settings{ class: ('expanded' if expanded) }
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only{ role: 'button' }
diff --git a/app/views/groups/settings/_general.html.haml b/app/views/groups/settings/_general.html.haml
index 0424ece037d..9ed71d19d32 100644
--- a/app/views/groups/settings/_general.html.haml
+++ b/app/views/groups/settings/_general.html.haml
@@ -20,7 +20,7 @@
= render_if_exists 'shared/repository_size_limit_setting', form: f, type: :group
.form-group.prepend-top-default.append-bottom-20
- .avatar-container.s90
+ .avatar-container.rect-avatar.s90
= group_icon(@group, alt: '', class: 'avatar group-avatar s90')
= f.label :avatar, _('Group avatar'), class: 'label-bold d-block'
= render 'shared/choose_group_avatar_button', f: f
diff --git a/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml b/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml
new file mode 100644
index 00000000000..e7efc0237c8
--- /dev/null
+++ b/app/views/groups/settings/ci_cd/_auto_devops_form.html.haml
@@ -0,0 +1,15 @@
+= form_for group, url: update_auto_devops_group_settings_ci_cd_path(group), method: :patch do |f|
+ = form_errors(group)
+ %fieldset
+ .form-group
+ .card.auto-devops-card
+ .card-body
+ .form-check
+ = f.check_box :auto_devops_enabled, class: 'form-check-input', checked: group.auto_devops_enabled?
+ = f.label :auto_devops_enabled, class: 'form-check-label' do
+ %strong= s_('GroupSettings|Default to Auto DevOps pipeline for all projects within this group')
+ %span.badge.badge-info#auto-devops-badge= badge_for_auto_devops_scope(group)
+ .form-text.text-muted
+ = s_('GroupSettings|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'
+ = f.submit _('Save changes'), class: 'btn btn-success prepend-top-15'
diff --git a/app/views/groups/settings/ci_cd/show.html.haml b/app/views/groups/settings/ci_cd/show.html.haml
index d9332e36ef5..d0f5cd94002 100644
--- a/app/views/groups/settings/ci_cd/show.html.haml
+++ b/app/views/groups/settings/ci_cd/show.html.haml
@@ -19,3 +19,17 @@
= _('Register and see your runners for this group.')
.settings-content
= render 'groups/runners/index'
+
+%section.settings#auto-devops-settings.no-animate{ class: ('expanded' if expanded) }
+ .settings-header
+ %h4
+ = _('Auto DevOps')
+ %button.btn.btn-default.js-settings-toggle{ type: "button" }
+ = expanded ? _('Collapse') : _('Expand')
+ %p
+ - auto_devops_url = help_page_path('topics/autodevops/index')
+ - auto_devops_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: auto_devops_url }
+ = s_('GroupSettings|Auto DevOps will automatically build, test and deploy your application based on a predefined Continuous Integration and Delivery configuration. %{auto_devops_start}Learn more about Auto DevOps%{auto_devops_end}').html_safe % { auto_devops_start: auto_devops_start, auto_devops_end: '</a>'.html_safe }
+
+ .settings-content
+ = render 'groups/settings/ci_cd/auto_devops_form', group: @group
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 28ffb2dd63c..efb3815b257 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -356,6 +356,18 @@
%td.shortcut
%kbd l
%td Change Label
+ %tr
+ %td.shortcut
+ %kbd ]
+ \/
+ %kbd j
+ %td Move to next file
+ %tr
+ %td.shortcut
+ %kbd [
+ \/
+ %kbd k
+ %td Move to previous file
%tbody.hidden-shortcut{ style: 'display:none' }
%tr
%th
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index dfa5d820ce9..f40fdb0b86b 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -29,7 +29,7 @@
.row.prepend-top-default
.col-md-8
- .documentation-index.wiki
+ .documentation-index.md
= markdown(@help_index)
.col-md-4
.card
diff --git a/app/views/help/instance_configuration.html.haml b/app/views/help/instance_configuration.html.haml
index f09e3825a4b..99576d45f76 100644
--- a/app/views/help/instance_configuration.html.haml
+++ b/app/views/help/instance_configuration.html.haml
@@ -1,5 +1,5 @@
- page_title 'Instance Configuration'
-.wiki.documentation
+.documentation.md
%h1 Instance Configuration
%p
diff --git a/app/views/help/show.html.haml b/app/views/help/show.html.haml
index c07c148a12a..dce27dee9be 100644
--- a/app/views/help/show.html.haml
+++ b/app/views/help/show.html.haml
@@ -1,3 +1,3 @@
- page_title @path.split("/").reverse.map(&:humanize)
-.documentation.wiki.prepend-top-default
+.documentation.md.prepend-top-default
= markdown @markdown
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index 506f580b246..969df69aafb 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -513,7 +513,7 @@
%h2#markdown Markdown
%h4
- %code .md or .wiki and others
+ %code .md
Markdown rendering has a bit different css and presented in next UI elements:
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 0bb2363f65a..11e83ddfe64 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -38,6 +38,8 @@
= stylesheet_link_tag 'performance_bar' if performance_bar_enabled?
= stylesheet_link_tag 'csslab' if Feature.enabled?(:csslab)
+ = stylesheet_link_tag "highlight/themes/#{user_color_scheme}", media: "all"
+
= Gon::Base.render_data
- if content_for?(:library_javascripts)
diff --git a/app/views/layouts/_mailer.html.haml b/app/views/layouts/_mailer.html.haml
index 26fd34347ec..e13490ed410 100644
--- a/app/views/layouts/_mailer.html.haml
+++ b/app/views/layouts/_mailer.html.haml
@@ -52,6 +52,7 @@
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;background-color:#6b4fbb;height:4px;font-size:4px;line-height:4px;" }
%tr.header
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ = html_header_message
= header_logo
%tr
%td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;" }
@@ -72,3 +73,6 @@
= _("You're receiving this email because of your account on %{host}. %{manage_notifications_link} &middot; %{help_link}").html_safe % { host: Gitlab.config.gitlab.host, manage_notifications_link: manage_notifications_link, help_link: help_link }
= yield :additional_footer
+ %tr
+ %td{ style: "font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;padding:25px 0;font-size:13px;line-height:1.6;color:#5c5c5c;" }
+ = html_footer_message
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index 4373240001e..043cca6ad38 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -5,7 +5,9 @@
= render "layouts/init_auto_complete" if @gfm_form
= render "layouts/init_client_detection_flags"
= render 'peek/bar'
- = render partial: "layouts/header/default", locals: { project: @project, group: @group }
+ = header_message
+ = render partial: "layouts/header/default", locals: { project: @project, group: @group }
= render 'layouts/page', sidebar: sidebar, nav: nav
+ = footer_message
= yield :scripts_body
diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml
index 6003d973c88..2f3c13aaf6e 100644
--- a/app/views/layouts/devise.html.haml
+++ b/app/views/layouts/devise.html.haml
@@ -2,6 +2,7 @@
%html.devise-layout-html{ class: system_message_class }
= render "layouts/head"
%body.ui-indigo.login-page.application.navless.qa-login-page{ data: { page: body_data_page } }
+ = header_message
.page-wrap
= render "layouts/header/empty"
.login-page-broadcast
@@ -34,3 +35,4 @@
= link_to _("Explore"), explore_root_path
= link_to _("Help"), help_path
= link_to _("About GitLab"), "https://about.gitlab.com/"
+ = footer_message
diff --git a/app/views/layouts/devise_empty.html.haml b/app/views/layouts/devise_empty.html.haml
index 663e5b24368..6c9c8aa4431 100644
--- a/app/views/layouts/devise_empty.html.haml
+++ b/app/views/layouts/devise_empty.html.haml
@@ -2,6 +2,7 @@
%html{ lang: "en", class: system_message_class }
= render "layouts/head"
%body.ui-indigo.login-page.application.navless
+ = header_message
= render "layouts/header/empty"
= render "layouts/broadcast"
.container.navless-container
@@ -15,3 +16,4 @@
= link_to _("Explore"), explore_root_path
= link_to _("Help"), help_path
= link_to _("About GitLab"), "https://about.gitlab.com/"
+ = footer_message
diff --git a/app/views/layouts/empty_mailer.html.haml b/app/views/layouts/empty_mailer.html.haml
new file mode 100644
index 00000000000..a25dcefd445
--- /dev/null
+++ b/app/views/layouts/empty_mailer.html.haml
@@ -0,0 +1,5 @@
+= html_header_message
+
+= yield
+
+= html_footer_message
diff --git a/app/views/layouts/empty_mailer.text.erb b/app/views/layouts/empty_mailer.text.erb
new file mode 100644
index 00000000000..6ab0dbead07
--- /dev/null
+++ b/app/views/layouts/empty_mailer.text.erb
@@ -0,0 +1,5 @@
+<%= text_header_message %>
+
+<%= yield -%>
+
+<%= text_footer_message %>
diff --git a/app/views/layouts/header/_new_dropdown.haml b/app/views/layouts/header/_new_dropdown.haml
index 5a66b02c048..438340464bd 100644
--- a/app/views/layouts/header/_new_dropdown.haml
+++ b/app/views/layouts/header/_new_dropdown.haml
@@ -38,4 +38,4 @@
%li= 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
- %li= link_to _('New snippet'), new_snippet_path
+ %li= link_to _('New snippet'), new_snippet_path, class: 'qa-global-new-snippet-link'
diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb
index 8e11174f8d7..f8032f3262b 100644
--- a/app/views/layouts/mailer.text.erb
+++ b/app/views/layouts/mailer.text.erb
@@ -1,4 +1,8 @@
+<%= text_header_message %>
+
<%= yield -%>
-- <%# signature marker %>
<%= _("You're receiving this email because of your account on %{host}.") % { host: Gitlab.config.gitlab.host } %>
+
+<%= text_footer_message %>
diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml
index f659c89dd30..5a27237bf76 100644
--- a/app/views/layouts/nav/_dashboard.html.haml
+++ b/app/views/layouts/nav/_dashboard.html.haml
@@ -3,7 +3,7 @@
%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", data: { track_label: "projects_dropdown", track_event: "click_dropdown" } }) do
- %button{ type: 'button', data: { toggle: "dropdown" } }
+ %button.btn{ type: 'button', data: { toggle: "dropdown" } }
= _('Projects')
= sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu.frequent-items-dropdown-menu
@@ -11,7 +11,7 @@
- if dashboard_nav_link?(:groups)
= nav_link(controller: ['dashboard/groups', 'explore/groups'], html_options: { id: 'nav-groups-dropdown', class: "home dropdown header-groups qa-groups-dropdown", data: { track_label: "groups_dropdown", track_event: "click_dropdown" } }) do
- %button{ type: 'button', data: { toggle: "dropdown" } }
+ %button.btn{ type: 'button', data: { toggle: "dropdown" } }
= _('Groups')
= sprite_icon('angle-down', css_class: 'caret-down')
.dropdown-menu.frequent-items-dropdown-menu
@@ -29,7 +29,7 @@
- if dashboard_nav_link?(:snippets)
= nav_link(controller: 'dashboard/snippets', html_options: { class: ["d-none d-xl-block", ("d-lg-block" unless has_extra_nav_icons?)] }) do
- = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets', title: _('Snippets') do
+ = link_to dashboard_snippets_path, class: 'dashboard-shortcuts-snippets qa-snippets-link', title: _('Snippets') do
= _('Snippets')
- if any_dashboard_nav_link?([:groups, :milestones, :activity, :snippets])
diff --git a/app/views/layouts/nav/sidebar/_admin.html.haml b/app/views/layouts/nav/sidebar/_admin.html.haml
index 2fdd65f639b..bb2d206ba91 100644
--- a/app/views/layouts/nav/sidebar/_admin.html.haml
+++ b/app/views/layouts/nav/sidebar/_admin.html.haml
@@ -220,7 +220,7 @@
= _('Repository')
- if template_exists?('admin/application_settings/templates')
= nav_link(path: 'application_settings#templates') do
- = link_to templates_admin_application_settings_path, title: _('Templates') do
+ = link_to templates_admin_application_settings_path, title: _('Templates'), class: 'qa-admin-settings-template-item' do
%span
= _('Templates')
= nav_link(path: 'application_settings#ci_cd') do
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index 3fbaaafe89e..b950e53639a 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -6,7 +6,7 @@
.nav-sidebar-inner-scroll
.context-header
= link_to group_path(@group), title: @group.name do
- .avatar-container.s40.group-avatar
+ .avatar-container.rect-avatar.s40.group-avatar
= group_icon(@group, class: "avatar s40 avatar-tile")
.sidebar-context-title
= @group.name
@@ -20,13 +20,14 @@
= _('Overview')
%ul.sidebar-sub-level-items
- = nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
+ = nav_link(path: ['groups#show', 'groups#details', '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')
%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
+
+ = nav_link(path: ['groups#show', 'groups#details', 'groups#subgroups'], html_options: { class: 'home' }) do
+ = link_to details_group_path(@group), title: _('Group details') do
%span
= _('Details')
@@ -40,9 +41,11 @@
- if group_sidebar_link?(:contribution_analytics)
= nav_link(path: 'analytics#show') do
- = link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do
+ = link_to group_analytics_path(@group), title: _('Contribution Analytics'), data: { placement: 'right' } do
%span
- Contribution Analytics
+ = _('Contribution Analytics')
+
+ = render_if_exists 'layouts/nav/group_insights_link'
= render_if_exists "layouts/nav/ee/epic_link", group: @group
diff --git a/app/views/layouts/nav/sidebar/_project.html.haml b/app/views/layouts/nav/sidebar/_project.html.haml
index dd7833647b7..6b33189d1cf 100644
--- a/app/views/layouts/nav/sidebar/_project.html.haml
+++ b/app/views/layouts/nav/sidebar/_project.html.haml
@@ -3,7 +3,7 @@
- can_edit = can?(current_user, :admin_project, @project)
.context-header
= link_to project_path(@project), title: @project.name do
- .avatar-container.s40.project-avatar
+ .avatar-container.rect-avatar.s40.project-avatar
= project_icon(@project, alt: @project.name, class: 'avatar s40 avatar-tile', width: 40, height: 40)
.sidebar-context-title
= @project.name
@@ -41,6 +41,8 @@
= link_to project_cycle_analytics_path(@project), title: _('Cycle Analytics'), class: 'shortcuts-project-cycle-analytics' do
%span= _('Cycle Analytics')
+ = render_if_exists 'layouts/nav/project_insights_link'
+
- if project_nav_tab? :files
= nav_link(controller: sidebar_repository_paths) do
= link_to project_tree_path(@project), class: 'shortcuts-tree qa-project-menu-repo' do
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 1c3e05e07f4..8dff12c1b7f 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -7,6 +7,7 @@
= yield :head
%body
.content
+ = html_header_message
= yield
.footer{ style: "margin-top: 10px;" }
%p
@@ -30,3 +31,4 @@
adjust your notification settings.
= email_action @target_url
+ = html_footer_message
diff --git a/app/views/layouts/notify.text.erb b/app/views/layouts/notify.text.erb
index 9dc490efa9a..248916fba63 100644
--- a/app/views/layouts/notify.text.erb
+++ b/app/views/layouts/notify.text.erb
@@ -1,3 +1,5 @@
+<%= text_header_message %>
+
<%= yield -%>
-- <%# signature marker %>
@@ -10,3 +12,5 @@
<% end -%>
<%= "You're receiving this email because #{notification_reason_text(@reason)}." %>
+
+<%= text_footer_message -%>
diff --git a/app/views/notify/issue_moved_email.html.haml b/app/views/notify/issue_moved_email.html.haml
index 472c31e9a5e..b766cb1a523 100644
--- a/app/views/notify/issue_moved_email.html.haml
+++ b/app/views/notify/issue_moved_email.html.haml
@@ -1,6 +1,9 @@
%p
Issue was moved to another project.
-%p
- New issue:
- = link_to project_issue_url(@new_project, @new_issue) do
- = @new_issue.title
+- if @can_access_project
+ %p
+ New issue:
+ = link_to project_issue_url(@new_project, @new_issue) do
+ = @new_issue.title
+- else
+ You don't have access to the project.
diff --git a/app/views/notify/issue_moved_email.text.erb b/app/views/notify/issue_moved_email.text.erb
index 66ede43635b..985e689aa9d 100644
--- a/app/views/notify/issue_moved_email.text.erb
+++ b/app/views/notify/issue_moved_email.text.erb
@@ -1,4 +1,8 @@
Issue was moved to another project.
+<% if @can_access_project %>
New issue location:
<%= project_issue_url(@new_project, @new_issue) %>
+<% else %>
+You don't have access to the project.
+<% end %>
diff --git a/app/views/profiles/_email_settings.html.haml b/app/views/profiles/_email_settings.html.haml
new file mode 100644
index 00000000000..fb4da08e129
--- /dev/null
+++ b/app/views/profiles/_email_settings.html.haml
@@ -0,0 +1,16 @@
+- form = local_assigns.fetch(:form)
+- readonly = @user.read_only_attribute?(:email)
+- email_change_disabled = local_assigns.fetch(:email_change_disabled, nil)
+- read_only_help_text = readonly ? s_("Profiles|Your email address was automatically set based on your %{provider_label} account") % { provider_label: attribute_provider_label(:email) } : user_email_help_text(@user)
+- help_text = email_change_disabled ? s_("Your account uses dedicated credentials for the \"%{group_name}\" group and can only be updated through SSO.") % { group_name: @user.managing_group.name } : read_only_help_text
+
+= form.text_field :email, required: true, class: 'input-lg', value: (@user.email unless @user.temp_oauth_email?), help: help_text.html_safe, readonly: readonly || email_change_disabled
+= form.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email),
+ { help: s_("Profiles|This email will be displayed on your public profile"), include_blank: s_("Profiles|Do not show on profile") },
+ control_class: 'select2 input-lg', disabled: email_change_disabled
+- commit_email_link_url = help_page_path('user/profile/index', anchor: 'commit-email', target: '_blank')
+- commit_email_link_start = '<a href="%{url}">'.html_safe % { url: commit_email_link_url }
+- commit_email_docs_link = s_('Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more%{commit_email_link_end}').html_safe % { commit_email_link_start: commit_email_link_start, commit_email_link_end: '</a>'.html_safe }
+= form.select :commit_email, options_for_select(commit_email_select_options(@user), selected: selected_commit_email(@user)),
+ { help: commit_email_docs_link },
+ control_class: 'select2 input-lg', disabled: email_change_disabled
diff --git a/app/views/profiles/accounts/_providers.html.haml b/app/views/profiles/accounts/_providers.html.haml
new file mode 100644
index 00000000000..068f9cc70f7
--- /dev/null
+++ b/app/views/profiles/accounts/_providers.html.haml
@@ -0,0 +1,21 @@
+%label.label-bold
+ = s_('Profiles|Connected Accounts')
+ %p= s_('Profiles|Click on icon to activate signin with one of the following services')
+ - providers.each do |provider|
+ - unlink_allowed = unlink_provider_allowed?(provider)
+ - link_allowed = link_provider_allowed?(provider)
+ - if unlink_allowed || link_allowed
+ .provider-btn-group
+ .provider-btn-image
+ = provider_image_tag(provider)
+ - if auth_active?(provider)
+ - if unlink_allowed
+ = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
+ = s_('Profiles|Disconnect')
+ - else
+ %a.provider-btn
+ = s_('Profiles|Active')
+ - elsif link_allowed
+ = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active' do
+ = s_('Profiles|Connect')
+ = render_if_exists 'profiles/accounts/group_saml_unlink_buttons', group_saml_identities: group_saml_identities
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index ee2c5a13b8a..e6380817c8f 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -29,24 +29,7 @@
%p
= s_('Profiles|Activate signin with one of the following services')
.col-lg-8
- %label.label-bold
- = s_('Profiles|Connected Accounts')
- %p= s_('Profiles|Click on icon to activate signin with one of the following services')
- - button_based_providers.each do |provider|
- .provider-btn-group
- .provider-btn-image
- = provider_image_tag(provider)
- - if auth_active?(provider)
- - if unlink_allowed?(provider)
- = link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
- = s_('Profiles|Disconnect')
- - else
- %a.provider-btn
- = s_('Profiles|Active')
- - else
- = link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active' do
- = s_('Profiles|Connect')
- = render_if_exists 'profiles/accounts/group_saml_unlink_buttons', group_saml_identities: local_assigns[:group_saml_identities]
+ = render 'providers', providers: button_based_providers, group_saml_identities: local_assigns[:group_saml_identities]
%hr
- if current_user.can_change_username?
.row.prepend-top-default
diff --git a/app/views/profiles/active_sessions/_active_session.html.haml b/app/views/profiles/active_sessions/_active_session.html.haml
index 23ef31a0c85..2bf514d72a5 100644
--- a/app/views/profiles/active_sessions/_active_session.html.haml
+++ b/app/views/profiles/active_sessions/_active_session.html.haml
@@ -23,9 +23,3 @@
%strong Signed in
on
= l(active_session.created_at, format: :short)
-
- - unless is_current_session
- .float-right
- = link_to profile_active_session_path(active_session.session_id), data: { confirm: 'Are you sure? The device will be signed out of GitLab.' }, method: :delete, class: "btn btn-danger prepend-left-10" do
- %span.sr-only Revoke
- Revoke
diff --git a/app/views/profiles/notifications/_email_settings.html.haml b/app/views/profiles/notifications/_email_settings.html.haml
new file mode 100644
index 00000000000..34dcf8f5402
--- /dev/null
+++ b/app/views/profiles/notifications/_email_settings.html.haml
@@ -0,0 +1,6 @@
+- form = local_assigns.fetch(:form)
+.form-group
+ = form.label :notification_email, class: "label-bold"
+ = form.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2", disabled: local_assigns.fetch(:email_change_disabled, nil)
+ .help-block
+ = local_assigns.fetch(:help_text, nil)
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 712eb2a4573..e616e5546b3 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -22,9 +22,7 @@
Global notification settings
= 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-bold"
- = f.select :notification_email, @user.all_emails, { include_blank: false }, class: "select2"
+ = render_if_exists 'profiles/notifications/email_settings', form: f
= label_tag :global_notification_level, "Global notification level", class: "label-bold"
%br
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 1a9aca1f6bf..bfe1c3ddf33 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -73,6 +73,12 @@
= link_to _('Learn more'), help_page_path('user/profile/preferences', anchor: 'localization'), target: '_blank'
.col-lg-8
.form-group
+ = f.label :preferred_language, class: 'label-bold' do
+ = _('Language')
+ = f.select :preferred_language, language_choices, {}, class: 'select2'
+ .form-text.text-muted
+ = s_('Preferences|This feature is experimental and translations are not complete yet')
+ .form-group
= f.label :first_day_of_week, class: 'label-bold' do
= _('First day of the week')
= f.select :first_day_of_week, first_day_of_week_choices_with_default, {}, class: 'form-control'
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 753316b27e2..02c750a92c3 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -83,21 +83,7 @@
= f.text_field :name, label: 'Full name', required: true, title: s_("Profiles|Using emojis in names seems fun, but please try to set a status message instead"), wrapper: { class: 'col-md-9 qa-full-name' }, help: s_("Profiles|Enter your name, so people you know can recognize you")
= f.text_field :id, readonly: true, label: 'User ID', wrapper: { class: 'col-md-3' }
- - if @user.read_only_attribute?(:email)
- = f.text_field :email, required: true, class: 'input-lg', readonly: true, help: s_("Profiles|Your email address was automatically set based on your %{provider_label} account") % { provider_label: attribute_provider_label(:email) }
- - else
- = f.text_field :email, required: true, class: 'input-lg', value: (@user.email unless @user.temp_oauth_email?),
- help: user_email_help_text(@user)
- = f.select :public_email, options_for_select(@user.all_emails, selected: @user.public_email),
- { help: s_("Profiles|This email will be displayed on your public profile"), include_blank: s_("Profiles|Do not show on profile") },
- control_class: 'select2 input-lg'
- - commit_email_docs_link = link_to s_('Profiles|Learn more'), help_page_path('user/profile/index', anchor: 'commit-email', target: '_blank')
- = f.select :commit_email, options_for_select(commit_email_select_options(@user), selected: selected_commit_email(@user)),
- { help: s_("Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}").html_safe % { learn_more: commit_email_docs_link } },
- control_class: 'select2 input-lg'
- = f.select :preferred_language, Gitlab::I18n::AVAILABLE_LANGUAGES.map { |value, label| [label, value] },
- { help: s_("Profiles|This feature is experimental and translations are not complete yet") },
- control_class: 'select2 input-lg'
+ = render_if_exists 'profiles/email_settings', form: f
= f.text_field :skype, class: 'input-md', placeholder: s_("Profiles|username")
= f.text_field :linkedin, class: 'input-md', help: s_("Profiles|Your LinkedIn profile name from linkedin.com/in/profilename")
= f.text_field :twitter, class: 'input-md', placeholder: s_("Profiles|@username")
@@ -113,10 +99,10 @@
.checkbox-icon-inline-wrapper
- private_profile_label = capture do
= s_("Profiles|Don't display activity-related personal information on your profiles")
- = f.check_box :private_profile, label: private_profile_label
+ = f.check_box :private_profile, label: private_profile_label, inline: true, wrapper_class: 'mr-0'
= link_to icon('question-circle'), help_page_path('user/profile/index.md', anchor: 'private-profile')
%h5= s_("Profiles|Private contributions")
- = f.check_box :include_private_contributions, label: 'Include private contributions on my profile'
+ = f.check_box :include_private_contributions, label: 'Include private contributions on my profile', wrapper_class: 'mb-2', inline: true
.help-block
= s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information")
.prepend-top-default.append-bottom-default
diff --git a/app/views/projects/_flash_messages.html.haml b/app/views/projects/_flash_messages.html.haml
index 7a5fff96676..b72f0e39b23 100644
--- a/app/views/projects/_flash_messages.html.haml
+++ b/app/views/projects/_flash_messages.html.haml
@@ -5,4 +5,5 @@
- if current_user && can?(current_user, :download_code, project)
= render 'shared/no_ssh'
= render 'shared/no_password'
- = render 'shared/auto_devops_implicitly_enabled_banner', project: project
+ - unless project.empty_repo?
+ = render 'shared/auto_devops_implicitly_enabled_banner', project: project
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 0be41b5888c..4ac5a74c85c 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,9 +1,10 @@
- empty_repo = @project.empty_repo?
- show_auto_devops_callout = show_auto_devops_callout?(@project)
+- max_project_topic_length = 15
.project-home-panel{ class: ("empty-project" if empty_repo) }
.row.append-bottom-8
.home-panel-title-row.col-md-12.col-lg-6.d-flex
- .avatar-container.home-panel-avatar.append-right-default.float-none
+ .avatar-container.rect-avatar.s64.home-panel-avatar.append-right-default.float-none
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64)
.d-flex.flex-column.flex-wrap.align-items-baseline
.d-inline-flex.align-items-baseline
@@ -19,15 +20,21 @@
%span.access-request-links.prepend-left-8
= render 'shared/members/access_request_links', source: @project
- if @project.tag_list.present?
- %span.home-panel-topic-list.d-inline-flex.prepend-left-8.has-tooltip{ data: { container: 'body' }, title: @project.has_extra_topics? ? @project.tag_list.join(', ') : nil }
+ %span.home-panel-topic-list.d-inline-flex.prepend-left-8
= sprite_icon('tag', size: 16, css_class: 'icon append-right-4')
- @project.topics_to_show.each do |topic|
- %a{ class: 'badge badge-pill badge-secondary append-right-5 str-truncated-30', href: explore_projects_path(tag: topic) }
- = topic.titleize
+ - project_topics_classes = "badge badge-pill badge-secondary append-right-5"
+ - explore_project_topic_path = explore_projects_path(tag: topic)
+ - if topic.length > max_project_topic_length
+ %a{ class: "#{ project_topics_classes } str-truncated-30 has-tooltip", data: { container: "body" }, title: topic, href: explore_project_topic_path }
+ = topic.titleize
+ - else
+ %a{ class: project_topics_classes, href: explore_project_topic_path }
+ = topic.titleize
- if @project.has_extra_topics?
- .text-nowrap
+ .text-nowrap.has-tooltip{ data: { container: 'body' }, title: @project.has_extra_topics? ? @project.topics_not_shown.join(', ') : nil }
= _("+ %{count} more") % { count: @project.count_of_extra_topics_not_shown }
@@ -50,7 +57,10 @@
- if can?(current_user, :download_code, @project)
%nav.project-stats
.nav-links.quick-links
- = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
+ - if @project.empty_repo?
+ = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_anchors
+ - else
+ = render 'stat_anchor_list', anchors: @project.statistics_anchors(show_auto_devops_callout: show_auto_devops_callout)
.home-panel-home-desc.mt-1
- if @project.description.present?
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 03ba1104507..e1a9ec0217e 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -12,17 +12,17 @@
%ul.nav.nav-tabs.nav-links.clearfix
%li.md-header-tab.active
%button.js-md-write-button{ tabindex: -1 }
- Write
+ = _("Write")
%li.md-header-tab
%button.js-md-preview-button{ tabindex: -1 }
- Preview
+ = _("Preview")
%li.md-header-toolbar.active
= render 'projects/blob/markdown_buttons', show_fullscreen_button: true
.md-write-holder
= yield
- .md.md-preview-holder.js-md-preview.hide.md-preview{ data: { url: url } }
+ .md.md-preview-holder.js-md-preview.hide{ data: { url: url } }
.referenced-commands.hide
- if referenced_users
diff --git a/app/views/projects/_merge_request_merge_settings.html.haml b/app/views/projects/_merge_request_merge_settings.html.haml
index f178c94e008..6ac2e06afa5 100644
--- a/app/views/projects/_merge_request_merge_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_settings.html.haml
@@ -9,6 +9,7 @@
%span.descr
Pipelines need to be configured to enable this feature.
= link_to icon('question-circle'), help_page_path('user/project/merge_requests/merge_when_pipeline_succeeds', anchor: 'only-allow-merge-requests-to-be-merged-if-the-pipeline-succeeds'), target: '_blank'
+ = render_if_exists 'projects/merge_pipelines_settings', form: form
.form-check
= form.check_box :only_allow_merge_if_all_discussions_are_resolved, class: 'form-check-input'
= form.label :only_allow_merge_if_all_discussions_are_resolved, class: 'form-check-label' do
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index 276363df7da..5129f11875c 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -12,9 +12,9 @@
.form-group.project-path.col-sm-6
= f.label :namespace_id, class: 'label-bold' do
%span= s_("Project URL")
- .input-group
+ .input-group.flex-nowrap
- if current_user.can_select_namespace?
- .input-group-prepend.has-tooltip{ title: root_url }
+ .input-group-prepend.flex-shrink-0.has-tooltip{ title: root_url }
.input-group-text
= root_url
- namespace_id = namespace_id_from(params)
@@ -23,10 +23,10 @@
display_path: true,
extra_group: namespace_id),
{},
- { class: 'select2 js-select-namespace qa-project-namespace-select', tabindex: 1, data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_path", track_value: "" }})
+ { class: 'select2 js-select-namespace qa-project-namespace-select block-truncated', tabindex: 1, data: { track_label: "#{track_label}", track_event: "activate_form_input", track_property: "project_path", track_value: "" }})
- else
- .input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' }
+ .input-group-prepend.static-namespace.flex-shrink-0.has-tooltip{ title: user_url(current_user.username) + '/' }
.input-group-text.border-0
#{user_url(current_user.username)}/
= f.hidden_field :namespace_id, value: current_user.namespace_id
diff --git a/app/views/projects/_wiki.html.haml b/app/views/projects/_wiki.html.haml
index de4653dad2c..6103d86bf5a 100644
--- a/app/views/projects/_wiki.html.haml
+++ b/app/views/projects/_wiki.html.haml
@@ -1,8 +1,7 @@
- if @wiki_home.present?
%div{ class: container_class }
- .prepend-top-default.append-bottom-default
- .wiki
- = render_wiki_content(@wiki_home)
+ .md.md-file.prepend-top-default.append-bottom-default
+ = render_wiki_content(@wiki_home)
- else
- can_create_wiki = can?(current_user, :create_wiki, @project)
.landing{ class: [('row-content-block row p-0 align-items-center' if can_create_wiki), ('content-block' unless can_create_wiki)] }
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index 09295940529..6a7cb1499c5 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -4,7 +4,7 @@
= render "projects/jobs/header"
- add_to_breadcrumbs(s_('CICD|Jobs'), project_jobs_path(@project))
-- add_to_breadcrumbs("##{@build.id}", project_jobs_path(@project))
+- add_to_breadcrumbs("##{@build.id}", project_job_path(@project, @build))
.tree-holder
.nav-block
diff --git a/app/views/projects/blob/_header_content.html.haml b/app/views/projects/blob/_header_content.html.haml
index 4bef45932d0..88fa31a73b0 100644
--- a/app/views/projects/blob/_header_content.html.haml
+++ b/app/views/projects/blob/_header_content.html.haml
@@ -1,7 +1,7 @@
.file-header-content
= blob_icon blob.mode, blob.name
- %strong.file-title-name
+ %strong.file-title-name.qa-file-title-name
= blob.name
= copy_file_path_button(blob.path)
diff --git a/app/views/projects/blob/_markdown_buttons.html.haml b/app/views/projects/blob/_markdown_buttons.html.haml
index 1d6acd86108..28d1ff97825 100644
--- a/app/views/projects/blob/_markdown_buttons.html.haml
+++ b/app/views/projects/blob/_markdown_buttons.html.haml
@@ -1,13 +1,13 @@
.md-header-toolbar.active
- = markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: s_("MarkdownToolbar|Add bold text") })
- = markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: s_("MarkdownToolbar|Add italic text") })
- = markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: s_("MarkdownToolbar|Insert a quote") })
- = markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: s_("MarkdownToolbar|Insert code") })
- = markdown_toolbar_button({ icon: "link", data: { "md-tag" => "[{text}](url)", "md-select" => "url" }, title: s_("MarkdownToolbar|Add a link") })
- = markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a bullet list") })
- = markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a numbered list") })
- = markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a task list") })
- = markdown_toolbar_button({ icon: "table", data: { "md-tag" => "| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |", "md-prepend" => true }, title: s_("MarkdownToolbar|Add a table") })
+ = markdown_toolbar_button({ icon: "bold", data: { "md-tag" => "**" }, title: _("Add bold text") })
+ = markdown_toolbar_button({ icon: "italic", data: { "md-tag" => "*" }, title: _("Add italic text") })
+ = markdown_toolbar_button({ icon: "quote", data: { "md-tag" => "> ", "md-prepend" => true }, title: _("Insert a quote") })
+ = markdown_toolbar_button({ icon: "code", data: { "md-tag" => "`", "md-block" => "```" }, title: _("Insert code") })
+ = markdown_toolbar_button({ icon: "link", data: { "md-tag" => "[{text}](url)", "md-select" => "url" }, title: _("Add a link") })
+ = markdown_toolbar_button({ icon: "list-bulleted", data: { "md-tag" => "* ", "md-prepend" => true }, title: _("Add a bullet list") })
+ = markdown_toolbar_button({ icon: "list-numbered", data: { "md-tag" => "1. ", "md-prepend" => true }, title: _("Add a numbered list") })
+ = markdown_toolbar_button({ icon: "task-done", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: _("Add a task list") })
+ = markdown_toolbar_button({ icon: "table", data: { "md-tag" => "| header | header |\n| ------ | ------ |\n| cell | cell |\n| cell | cell |", "md-prepend" => true }, title: _("Add a table") })
- if show_fullscreen_button
- %button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: s_("MarkdownToolbar|Go full screen"), data: { container: "body" } }
+ %button.toolbar-btn.toolbar-fullscreen-btn.js-zen-enter.has-tooltip{ type: "button", tabindex: -1, "aria-label": "Go full screen", title: _("Go full screen"), data: { container: "body" } }
= sprite_icon("screen-full")
diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml
index ea7a71792a3..4f3db61f688 100644
--- a/app/views/projects/blob/diff.html.haml
+++ b/app/views/projects/blob/diff.html.haml
@@ -15,14 +15,14 @@
%a{ href: "#", data: { linenumber: line_number_old }, disabled: true }
%td.new_line.diff-line-num{ data: { linenumber: line_number_new } }
%a{ href: "#", data: { linenumber: line_number_new }, disabled: true }
- %td.line_content.noteable_line{ class: line_class }= line
+ %td.line_content{ class: line_class }= line
- when :parallel
%td.old_line.diff-line-num{ data: { linenumber: line_number_old } }
%a{ href: "##{line_number_old}", data: { linenumber: line_number_old }, disabled: true }
- %td.line_content.noteable_line.left-side{ class: line_class }= line
+ %td.line_content.left-side{ class: line_class }= line
%td.new_line.diff-line-num{ data: { linenumber: line_number_new } }
%a{ href: "##{line_number_new}", data: { linenumber: line_number_new }, disabled: true }
- %td.line_content.noteable_line.right-side{ class: line_class }= line
+ %td.line_content.right-side{ class: line_class }= line
- if @form.unfold? && @form.bottom? && @form.to < @blob.lines.size
%tr.line_holder{ id: @form.to, class: line_class }
diff --git a/app/views/projects/blob/preview.html.haml b/app/views/projects/blob/preview.html.haml
index 66687f087ff..3e893343165 100644
--- a/app/views/projects/blob/preview.html.haml
+++ b/app/views/projects/blob/preview.html.haml
@@ -1,21 +1,20 @@
-.diff-file.file-holder
- .diff-content
- - if markup?(@blob.name)
- .file-content.wiki.md{ class: ('use-csslab' if Feature.enabled?(:csslab)) }
- = markup(@blob.name, @content)
- - else
- .file-content.code.js-syntax-highlight
- - unless @diff_lines.empty?
- %table.text-file
- - @diff_lines.each do |line|
- %tr.line_holder{ class: "#{line.type}" }
- - if line.type == "match"
- %td.old_line.diff-line-num= "..."
- %td.new_line.diff-line-num= "..."
- %td.line_content.match= line.text
- - else
- %td.old_line.diff-line-num
- %td.new_line.diff-line-num
- %td.line_content{ class: "#{line.type}" }= diff_line_content(line.text)
- - else
- .nothing-here-block No changes.
+- if markup?(@blob.name)
+ .file-content.md.md-file{ class: ('use-csslab' if Feature.enabled?(:csslab)) }
+ = markup(@blob.name, @content)
+- else
+ .diff-file
+ .diff-content
+ - unless @diff_lines.empty?
+ %table.text-file.code.js-syntax-highlight
+ - @diff_lines.each do |line|
+ %tr.line_holder{ class: line.type }
+ - if line.type == "match"
+ %td.old_line.diff-line-num.match= "..."
+ %td.new_line.diff-line-num.match= "..."
+ %td.line_content.match= line.text
+ - else
+ %td.old_line.diff-line-num{ class: line.type }
+ %td.new_line.diff-line-num{ class: line.type }
+ %td.line_content{ class: line.type }= diff_line_content(line.text)
+ - else
+ .nothing-here-block No changes.
diff --git a/app/views/projects/blob/viewers/_dependency_manager.html.haml b/app/views/projects/blob/viewers/_dependency_manager.html.haml
index 87aa7c1dbf8..5970d41fdab 100644
--- a/app/views/projects/blob/viewers/_dependency_manager.html.haml
+++ b/app/views/projects/blob/viewers/_dependency_manager.html.haml
@@ -3,9 +3,4 @@
This project manages its dependencies using
%strong= viewer.manager_name
- - if viewer.package_name
- and defines a #{viewer.package_type} named
- %strong<
- = link_to_if viewer.package_url.present?, viewer.package_name, viewer.package_url, target: '_blank', rel: 'noopener noreferrer'
-
= link_to 'Learn more', viewer.manager_url, target: '_blank', rel: 'noopener noreferrer'
diff --git a/app/views/projects/blob/viewers/_markup.html.haml b/app/views/projects/blob/viewers/_markup.html.haml
index 1a77eb078be..abc74b66e90 100644
--- a/app/views/projects/blob/viewers/_markup.html.haml
+++ b/app/views/projects/blob/viewers/_markup.html.haml
@@ -1,4 +1,4 @@
- blob = viewer.blob
- context = blob.respond_to?(:rendered_markup) ? { rendered: blob.rendered_markup } : {}
-.file-content.wiki.md{ class: ('use-csslab' if Feature.enabled?(:csslab)) }
+.file-content.md.md-file{ class: ('use-csslab' if Feature.enabled?(:csslab)) }
= markup(blob.name, blob.data, context)
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index c64ad1c8147..91c51d5e091 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -53,9 +53,8 @@
= _('Merge request')
- if branch.name != @repository.root_ref
- = link_to project_compare_index_path(@project, from: @repository.root_ref, to: branch.name),
+ = link_to project_compare_path(@project, @repository.root_ref, branch.name),
class: "btn btn-default #{'prepend-left-10' unless merge_project}",
- method: :post,
title: s_('Branches|Compare') do
= s_('Branches|Compare')
diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml
index 159d9e44e17..09f05b30433 100644
--- a/app/views/projects/buttons/_clone.html.haml
+++ b/app/views/projects/buttons/_clone.html.haml
@@ -7,7 +7,7 @@
= sprite_icon("arrow-down", css_class: "icon")
%ul.p-3.dropdown-menu.dropdown-menu-right.dropdown-menu-large.dropdown-menu-selectable.clone-options-dropdown.qa-clone-options
- if ssh_enabled?
- %li.pb-2
+ %li
%label.label-bold
= _('Clone with SSH')
.input-group
@@ -16,7 +16,7 @@
= clipboard_button(target: '#ssh_project_clone', title: _("Copy URL to clipboard"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
- if http_enabled?
- %li
+ %li.pt-2
%label.label-bold
= _('Clone with %{http_label}') % { http_label: gitlab_config.protocol.upcase }
.input-group
@@ -24,5 +24,6 @@
.input-group-append
= clipboard_button(target: '#http_project_clone', title: _("Copy URL to clipboard"), class: "input-group-text btn-default btn-clipboard")
= render_if_exists 'projects/buttons/geo'
+ = render_if_exists 'projects/buttons/kerberos_clone_field'
= render_if_exists 'shared/geo_info_modal', project: project
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 0d3c6e7027c..ce55dd78747 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -20,12 +20,9 @@
= link_to commit.title, project_commit_path(project, commit.id, merge_request_iid: merge_request.iid), class: "commit-row-message item-title"
- else
= link_to_markdown_field(commit, :title, link, class: "commit-row-message item-title")
- %span.commit-row-message.d-block.d-sm-none
+ %span.commit-row-message.d-inline.d-sm-none
&middot;
= commit.short_id
- - if commit_status
- .d-block.d-sm-none
- = render_commit_status(commit, ref: ref)
- if commit.description?
%button.text-expander.js-toggle-button
= sprite_icon('ellipsis_h', size: 12)
@@ -40,7 +37,7 @@
%pre.commit-row-description.js-toggle-content.append-bottom-8
= preserve(markdown_field(commit, :description))
- .commit-actions.flex-row.d-none.d-sm-flex
+ .commit-actions.flex-row
- if request.xhr?
= render partial: 'projects/commit/signature', object: commit.signature
- else
@@ -51,7 +48,7 @@
.js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id, ref: ref) } }
- .commit-sha-group
+ .commit-sha-group.d-none.d-sm-flex
.label.label-monospace
= commit.short_id
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"), class: "btn btn-default", container: "body")
diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml
index bdf021fd87f..59f0afd59e6 100644
--- a/app/views/projects/cycle_analytics/show.html.haml
+++ b/app/views/projects/cycle_analytics/show.html.haml
@@ -10,7 +10,7 @@
.wrapper{ "v-show" => "!isLoading && !hasError" }
.card
.card-header
- {{ __('Pipeline Health') }}
+ {{ __('Recent Project Activity') }}
.content-block
.container-fluid
.row
diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml
index f4c91377ecb..e45c5be76de 100644
--- a/app/views/projects/deployments/_actions.haml
+++ b/app/views/projects/deployments/_actions.haml
@@ -11,4 +11,4 @@
- next unless can?(current_user, :update_build, action)
%li
= link_to [:play, @project.namespace.becomes(Namespace), @project, action], method: :post, rel: 'nofollow', class: 'btn' do
- %span= action.name.humanize
+ %span= action.name
diff --git a/app/views/projects/deployments/_confirm_rollback_modal.html.haml b/app/views/projects/deployments/_confirm_rollback_modal.html.haml
new file mode 100644
index 00000000000..ff40e404e5f
--- /dev/null
+++ b/app/views/projects/deployments/_confirm_rollback_modal.html.haml
@@ -0,0 +1,23 @@
+- commit_sha = link_to deployment.short_sha, project_commit_path(@project, deployment.sha), class: "commit-sha has-tooltip", title: h(deployment.commit_title)
+.modal.ws-normal.fade{ tabindex: -1, id: "confirm-rollback-modal-#{deployment.id}" }
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %h4.modal-title.d-flex.mw-100
+ - if deployment.last?
+ = s_("Environments|Re-deploy environment %{environment_name}?") % {environment_name: @environment.name}
+ - else
+ = s_("Environments|Rollback environment %{environment_name}?") % {environment_name: @environment.name}
+ .modal-body
+ - if deployment.last?
+ %p= s_('Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?').html_safe % {commit_id: commit_sha}
+ - else
+ %p
+ = s_('Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?').html_safe % {commit_id: commit_sha}
+ .modal-footer
+ = button_tag _('Cancel'), type: 'button', class: 'btn btn-cancel', data: { dismiss: 'modal' }
+ = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-danger' do
+ - if deployment.last?
+ = s_('Environments|Re-deploy')
+ - else
+ = s_('Environments|Rollback')
diff --git a/app/views/projects/deployments/_rollback.haml b/app/views/projects/deployments/_rollback.haml
index 1bd538a08ff..d6bf8d564de 100644
--- a/app/views/projects/deployments/_rollback.haml
+++ b/app/views/projects/deployments/_rollback.haml
@@ -1,7 +1,8 @@
- if can?(current_user, :create_deployment, deployment)
- 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
+ = button_tag class: 'btn btn-default btn-build has-tooltip', type: 'button', data: { toggle: 'modal', target: "#confirm-rollback-modal-#{deployment.id}" }, title: tooltip do
- if deployment.last?
= sprite_icon('repeat')
- else
= sprite_icon('redo')
+ = render 'projects/deployments/confirm_rollback_modal', deployment: deployment
diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml
index ffdca500abe..d35443cca1e 100644
--- a/app/views/projects/diffs/_line.html.haml
+++ b/app/views/projects/diffs/_line.html.haml
@@ -30,7 +30,7 @@
= link_text
- else
%a{ href: "##{line_code}", data: { linenumber: link_text } }
- %td.line_content.noteable_line{ class: type }<
+ %td.line_content{ class: type }<
- if email
%pre= line.rich_text
- else
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index 4b1d4b3ea17..311b0be19ab 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -1,7 +1,7 @@
/ Side-by-side diff view
-.text-file.diff-wrap-lines.code.js-syntax-highlight{ data: diff_view_data }
- %table
+.text-file{ data: diff_view_data }
+ %table.diff-wrap-lines.code.js-syntax-highlight
- diff_file.parallel_diff_lines.each do |line|
- left = line[:left]
- right = line[:right]
@@ -23,7 +23,7 @@
- discussion_left = discussions_left.try(:first)
- if discussion_left && discussion_left.resolvable?
%diff-note-avatars{ "discussion-id" => discussion_left.id }
- %td.line_content.parallel.noteable_line.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.rich_text)
+ %td.line_content.parallel.left-side{ id: left_line_code, class: left.type }= diff_line_content(left.rich_text)
- else
%td.old_line.diff-line-num.empty-cell
%td.line_content.parallel.left-side
@@ -44,7 +44,7 @@
- discussion_right = discussions_right.try(:first)
- if discussion_right && discussion_right.resolvable?
%diff-note-avatars{ "discussion-id" => discussion_right.id }
- %td.line_content.parallel.noteable_line.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.rich_text)
+ %td.line_content.parallel.right-side{ id: right_line_code, class: right.type }= diff_line_content(right.rich_text)
- else
%td.old_line.diff-line-num.empty-cell
%td.line_content.parallel.right-side
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index a58f736b5b4..a6a8ca489a9 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -1,17 +1,15 @@
-- breadcrumb_title "General Settings"
-- page_title "General"
+- breadcrumb_title _("General Settings")
+- page_title _("General")
- @content_class = "limit-container-width" unless fluid_layout
- expanded = Rails.env.test?
.project-edit-container
%section.settings.general-settings.no-animate#js-general-project-settings{ class: ('expanded' if expanded) }
.settings-header
- %h4
- General project
- %button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
- %p
- Update your project name, description, avatar, and other general settings.
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Naming, tags, avatar')
+ %button.btn.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
+ %p= _('Update your project name, tags, description and avatar.')
+
.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|
@@ -46,7 +44,7 @@
%h5.prepend-top-0= _("Project avatar")
.form-group
- if @project.avatar?
- .avatar-container.s160.append-bottom-15
+ .avatar-container.rect-avatar.s160.append-bottom-15
= project_icon(@project, alt: '', class: 'avatar project-avatar s160', width: 160, height: 160)
- if @project.avatar_in_git
%p.light
@@ -63,12 +61,10 @@
%section.settings.sharing-permissions.no-animate#js-shared-permissions{ class: ('expanded' if expanded) }
.settings-header
- %h4
- Permissions
- %button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
- %p
- Enable or disable certain project features and choose access levels.
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Visibility, project features, permissions')
+ %button.btn.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
+ %p= _('Choose visibility level, enable/disable project features (issues, repository, wiki, snippets) and set permissions.')
+
.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' }
@@ -81,12 +77,10 @@
%section.qa-merge-request-settings.settings.merge-requests-feature.no-animate#js-merge-request-settings{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)] }
.settings-header
- %h4
- Merge request
- %button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
- %p
- Customize your merge request restrictions.
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Merge requests')
+ %button.btn.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
+ %p= _('Choose your merge method, set up a default merge request description template.')
+
.settings-content
= render_if_exists 'shared/promotions/promote_mr_features'
@@ -97,11 +91,10 @@
= render_if_exists 'projects/merge_request_approvals_settings', expanded: expanded
- = render_if_exists 'projects/service_desk_settings'
%section.settings.no-animate{ class: ('expanded' if expanded) }
.settings-header
- %h4
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= s_('ProjectSettings|Badges')
%button.btn.js-settings-toggle{ type: 'button' }
= expanded ? 'Collapse' : 'Expand'
@@ -111,16 +104,15 @@
.settings-content
= render 'shared/badges/badge_settings'
+ = render_if_exists 'projects/service_desk_settings'
= render 'export', project: @project
%section.qa-advanced-settings.settings.advanced-settings.no-animate#js-project-advanced-settings{ class: ('expanded' if expanded) }
.settings-header
- %h4
- Advanced
- %button.btn.js-settings-toggle{ type: 'button' }
- = expanded ? 'Collapse' : 'Expand'
- %p
- Perform advanced options such as housekeeping, archiving, renaming, transferring, or removing your project.
+ %h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only= _('Advanced')
+ %button.btn.js-settings-toggle{ type: 'button' }= expanded ? _('Collapse') : _('Expand')
+ %p= _('Housekeeping, export, path, transfer, remove, archive.')
+
.settings-content
.sub-section
%h4 Housekeeping
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 081990ac9b7..9fa31c147eb 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -7,89 +7,64 @@
%div{ class: [container_class, ("limit-container-width" unless fluid_layout)] }
= render "home_panel"
- .project-empty-note-panel
- %h4.append-bottom-20
- = _('The repository for this project is empty')
+ %h4.prepend-top-0.append-bottom-8
+ = _('The repository for this project is empty')
- - if @project.can_current_user_push_code?
- %p
- - link_to_cli = link_to _('command line instructions'), '#repo-command-line-instructions'
- = _('If you already have files you can push them using the %{link_to_cli} below.').html_safe % { link_to_cli: link_to_cli }
- %p
- %em
- - link_to_protected_branches = link_to _('Learn more about protected branches'), help_page_path('user/project/protected_branches')
- = _('Note that the master branch is automatically protected. %{link_to_protected_branches}').html_safe % { link_to_protected_branches: link_to_protected_branches }
-
- %hr
- %p
- - link_to_auto_devops_settings = link_to(s_('AutoDevOps|enable Auto DevOps'), project_settings_ci_cd_path(@project, anchor: 'autodevops-settings'))
- - link_to_add_kubernetes_cluster = link_to(s_('AutoDevOps|add a Kubernetes cluster'), new_project_cluster_path(@project))
- = s_('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}.').html_safe % { link_to_auto_devops_settings: link_to_auto_devops_settings, link_to_add_kubernetes_cluster: link_to_add_kubernetes_cluster }
+ - if @project.can_current_user_push_code?
+ %p.append-bottom-0
+ = _('You can create files directly in GitLab using one of the following options.')
- %hr
- %p
- = _('Otherwise it is recommended you start with one of the options below.')
- .prepend-top-20
-
- %nav.project-buttons
- .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller.qa-quick-actions
- .fade-left= icon('angle-left')
- .fade-right= icon('angle-right')
- .nav-links.scrolling-tabs.quick-links
- = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
+ .project-buttons.qa-quick-actions
+ = render 'stat_anchor_list', anchors: @project.empty_repo_statistics_buttons
- if can?(current_user, :push_code, @project)
- %div
- .prepend-top-20
- .empty_wrapper
- %h3#repo-command-line-instructions.page-title-empty
- = _('Command line instructions')
- .git-empty.js-git-empty
- %fieldset
- %h5= _('Git global setup')
- %pre.bg-light
- :preserve
- git config --global user.name "#{h git_user_name}"
- git config --global user.email "#{h git_user_email}"
-
- %fieldset
- %h5= _('Create a new repository')
- %pre.bg-light
- :preserve
- git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
- cd #{h @project.path}
- touch README.md
- git add README.md
- git commit -m "add README"
- - if @project.can_current_user_push_to_default_branch?
- %span><
- git push -u origin master
+ .empty-wrapper.prepend-top-32
+ %h3#repo-command-line-instructions.page-title-empty
+ = _('Command line instructions')
+ %p
+ = _('You can also upload existing files from your computer using the instructions below.')
+ .git-empty.js-git-empty
+ %fieldset
+ %h5= _('Git global setup')
+ %pre.bg-light
+ :preserve
+ git config --global user.name "#{h git_user_name}"
+ git config --global user.email "#{h git_user_email}"
- %fieldset
- %h5= _('Existing folder')
- %pre.bg-light
- :preserve
- cd existing_folder
- git init
- git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
- git add .
- git commit -m "Initial commit"
- - if @project.can_current_user_push_to_default_branch?
- %span><
- git push -u origin master
+ %fieldset
+ %h5= _('Create a new repository')
+ %pre.bg-light
+ :preserve
+ git clone #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
+ cd #{h @project.path}
+ touch README.md
+ git add README.md
+ git commit -m "add README"
+ - if @project.can_current_user_push_to_default_branch?
+ %span><
+ git push -u origin master
- %fieldset
- %h5= _('Existing Git repository')
- %pre.bg-light
- :preserve
- cd existing_repo
- git remote rename origin old-origin
- git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
- - if @project.can_current_user_push_to_default_branch?
- %span><
- git push -u origin --all
- git push -u origin --tags
+ %fieldset
+ %h5= _('Push an existing folder')
+ %pre.bg-light
+ :preserve
+ cd existing_folder
+ git init
+ git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
+ git add .
+ git commit -m "Initial commit"
+ - if @project.can_current_user_push_to_default_branch?
+ %span><
+ git push -u origin master
- - if can? current_user, :remove_project, @project
- .prepend-top-20
- = link_to _('Remove project'), [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-inverted btn-remove float-right"
+ %fieldset
+ %h5= _('Push an existing Git repository')
+ %pre.bg-light
+ :preserve
+ cd existing_repo
+ git remote rename origin old-origin
+ git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'js-clone')}
+ - if @project.can_current_user_push_to_default_branch?
+ %span><
+ git push -u origin --all
+ git push -u origin --tags
diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml
index a69146513d8..3f0798a898d 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)
- = group_icon(namespace, class: "avatar s100 identicon")
+ = group_icon(namespace, class: "avatar rect-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)
- = group_icon(namespace, class: "avatar s100 identicon")
+ = group_icon(namespace, class: "avatar rect-avatar s100 identicon")
- else
.avatar-container.s100
= image_tag(avatar, class: "avatar s100")
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index 4917f4b8903..42b6aaa2634 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -5,7 +5,7 @@
= link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, format: 'json'), data: {original_text: "Reopen issue", alternative_text: "Comment & reopen issue"}, class: "btn btn-nr btn-reopen btn-comment js-note-target-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue'
= link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, format: 'json'), data: {original_text: "Close issue", alternative_text: "Comment & close issue"}, class: "btn btn-nr btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close issue'
-%section.js-vue-notes-event
+%section.issuable-discussion.js-vue-notes-event
#js-vue-notes{ data: { notes_data: notes_data(@issue).to_json,
noteable_data: serialize_issuable(@issue),
noteable_type: 'Issue',
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 31c72f2f759..ce7c7091c93 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -36,7 +36,7 @@
= issue.due_date.to_s(:medium)
- if issue.labels.any?
&nbsp;
- - issue.labels.each do |label|
+ - labels_sorted_by_title(issue.labels).each do |label|
= link_to_label(label, subject: issue.project, css_class: 'label-link')
.issuable-meta
diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml
index 310e339ac8d..6a66c2e57cc 100644
--- a/app/views/projects/issues/_merge_requests.html.haml
+++ b/app/views/projects/issues/_merge_requests.html.haml
@@ -27,7 +27,7 @@
= merge_request.to_reference
%span.mr-ci-status.flex-md-grow-1.justify-content-end.d-flex.ml-md-2
- if merge_request.can_read_pipeline?
- = render_pipeline_status(merge_request.head_pipeline, tooltip_placement: 'bottom')
+ = render 'ci/status/icon', status: merge_request.head_pipeline.detailed_status(current_user), tooltip_placement: 'bottom'
- elsif has_any_head_pipeline
= icon('blank fw')
diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml
index ffdd96870ef..6da4956a036 100644
--- a/app/views/projects/issues/_related_branches.html.haml
+++ b/app/views/projects/issues/_related_branches.html.haml
@@ -8,7 +8,7 @@
- pipeline = @project.pipeline_for(branch, target.sha) if target
- if can?(current_user, :read_pipeline, pipeline)
%span.related-branch-ci-status
- = render_pipeline_status(pipeline)
+ = render 'ci/status/icon', status: pipeline.detailed_status(current_user)
%span.related-branch-info
%strong
= link_to branch, project_compare_path(@project, from: @project.default_branch, to: branch), class: "ref-name"
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 653b7d4c6f3..00fbfd80ce5 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -14,9 +14,12 @@
.detail-page-header-body
.issuable-status-box.status-box.status-box-issue-closed{ class: issue_button_visibility(@issue, false) }
= sprite_icon('mobile-issue-close', size: 16, css_class: 'd-block d-sm-none')
- %span.d-none.d-sm-block
+ .d-none.d-sm-block
- if @issue.moved?
- = _("Closed (moved)")
+ - moved_link_start = "<a href=\"#{issue_path(@issue.moved_to)}\" class=\"text-white text-underline\">".html_safe
+ - moved_link_end = '</a>'.html_safe
+ = s_('IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})').html_safe % {moved_link_start: moved_link_start,
+ moved_link_end: moved_link_end}
- else
= _("Closed")
.issuable-status-box.status-box.status-box-open{ class: issue_button_visibility(@issue, true) }
@@ -69,7 +72,7 @@
%h2.title= markdown_field(@issue, :title)
- if @issue.description.present?
.description{ class: can?(current_user, :update_issue, @issue) ? 'js-task-list-container' : '' }
- .wiki= markdown_field(@issue, :description)
+ .md= markdown_field(@issue, :description)
%textarea.hidden.js-task-list-field= @issue.description
= edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago')
@@ -88,7 +91,6 @@
#js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(@issue), notes_filters: UserPreference.notes_filters.to_json } }
= render 'new_branch' unless @issue.confidential?
- %section.issuable-discussion
- = render 'projects/issues/discussion'
+ = render_if_exists 'projects/issues/discussion'
= render 'shared/issuable/sidebar', issuable_sidebar: @issuable_sidebar, assignees: @issue.assignees
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index ac29cd8f679..b8e0b66e277 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -34,7 +34,7 @@
= merge_request.target_branch
- if merge_request.labels.any?
&nbsp;
- - merge_request.labels.each do |label|
+ - labels_sorted_by_title(merge_request.labels).each do |label|
= link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link')
.issuable-meta
@@ -48,7 +48,7 @@
CLOSED
- if can?(current_user, :read_pipeline, merge_request.head_pipeline)
%li.issuable-pipeline-status.d-none.d-sm-inline-block
- = render_pipeline_status(merge_request.head_pipeline)
+ = render 'ci/status/icon', status: merge_request.head_pipeline.detailed_status(current_user)
- if merge_request.open? && merge_request.broken?
%li.issuable-pipeline-broken.d-none.d-sm-inline-block
= link_to merge_request_path(merge_request), class: "has-tooltip", title: _('Cannot be merged automatically') do
diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml
index bd6f1c05949..57fbd360d46 100644
--- a/app/views/projects/merge_requests/_merge_requests.html.haml
+++ b/app/views/projects/merge_requests/_merge_requests.html.haml
@@ -1,5 +1,5 @@
%ul.content-list.mr-list.issuable-list
- - if @merge_requests.exists?
+ - if @merge_requests.present?
= render @merge_requests
- else
= render 'shared/empty_states/merge_requests'
diff --git a/app/views/projects/merge_requests/_mr_box.html.haml b/app/views/projects/merge_requests/_mr_box.html.haml
index 1a9ab288683..7f2c9dcacfd 100644
--- a/app/views/projects/merge_requests/_mr_box.html.haml
+++ b/app/views/projects/merge_requests/_mr_box.html.haml
@@ -5,7 +5,7 @@
%div
- if @merge_request.description.present?
.description{ class: can?(current_user, :update_merge_request, @merge_request) ? 'js-task-list-container' : '' }
- .wiki
+ .md
= markdown_field(@merge_request, :description)
%textarea.hidden.js-task-list-field
= @merge_request.description
diff --git a/app/views/projects/merge_requests/_mr_title.html.haml b/app/views/projects/merge_requests/_mr_title.html.haml
index 3cd83feb842..92e34b3ceda 100644
--- a/app/views/projects/merge_requests/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/_mr_title.html.haml
@@ -1,8 +1,9 @@
- can_update_merge_request = can?(current_user, :update_merge_request, @merge_request)
+- can_reopen_merge_request = can?(current_user, :reopen_merge_request, @merge_request)
- if @merge_request.closed_without_fork?
.alert.alert-danger
- %p The source project of this merge request has been removed.
+ The source project of this merge request has been removed.
.detail-page-header
.detail-page-header-body
@@ -33,10 +34,11 @@
- if can_update_merge_request
%li{ class: [merge_request_button_visibility(@merge_request, true), 'js-close-item'] }
= link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request'
+ - if can_reopen_merge_request
%li{ class: merge_request_button_visibility(@merge_request, false) }
= link_to 'Reopen', merge_request_path(@merge_request, merge_request: { state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request'
- if can_update_merge_request
= link_to 'Edit', edit_project_merge_request_path(@project, @merge_request), class: "d-none d-sm-none d-md-block btn btn-grouped js-issuable-edit qa-edit-button"
- = render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request, can_reopen: can_update_merge_request
+ = render 'shared/issuable/close_reopen_button', issuable: @merge_request, can_update: can_update_merge_request, can_reopen: can_reopen_merge_request
diff --git a/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml b/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
index d828cb6cf9e..03226de120d 100644
--- a/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
+++ b/app/views/projects/merge_requests/conflicts/components/_inline_conflict_lines.html.haml
@@ -1,5 +1,5 @@
%inline-conflict-lines{ "inline-template" => "true", ":file" => "file" }
- %table
+ %table.diff-wrap-lines.code.js-syntax-highlight
%tr.line_holder.diff-inline{ "v-for" => "line in file.inlineLines" }
%td.diff-line-num.new_line{ ":class" => "lineCssClass(line)", "v-if" => "!line.isHeader" }
%a {{line.new_line}}
diff --git a/app/views/projects/merge_requests/conflicts/show.html.haml b/app/views/projects/merge_requests/conflicts/show.html.haml
index 09aeb81671a..f48390aa046 100644
--- a/app/views/projects/merge_requests/conflicts/show.html.haml
+++ b/app/views/projects/merge_requests/conflicts/show.html.haml
@@ -26,9 +26,9 @@
%strong {{file.filePath}}
= render partial: 'projects/merge_requests/conflicts/file_actions'
.diff-content.diff-wrap-lines
- .diff-wrap-lines.code.file-content.js-syntax-highlight{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
+ .file-content{ "v-show" => "!isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
= render partial: "projects/merge_requests/conflicts/components/inline_conflict_lines"
- .diff-wrap-lines.code.file-content.js-syntax-highlight{ "v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
+ .file-content{ "v-show" => "isParallel && file.resolveMode === 'interactive' && file.type === 'text'" }
%parallel-conflict-lines{ ":file" => "file" }
%div{ "v-show" => "file.resolveMode === 'edit' || file.type === 'text-editor'" }
= render partial: "projects/merge_requests/conflicts/components/diff_file_editor"
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 0542b349e44..78b416edd5c 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -54,9 +54,8 @@
%div
- if @milestone.description.present?
- .description
- .wiki
- = markdown_field(@milestone, :description)
+ .description.md
+ = markdown_field(@milestone, :description)
= render_if_exists 'shared/milestones/burndown', milestone: @milestone, project: @project
diff --git a/app/views/projects/mirrors/_authentication_method.html.haml b/app/views/projects/mirrors/_authentication_method.html.haml
index 293a2e3ebfe..ef6db07a1bb 100644
--- a/app/views/projects/mirrors/_authentication_method.html.haml
+++ b/app/views/projects/mirrors/_authentication_method.html.haml
@@ -9,6 +9,7 @@
= f.select :auth_method,
options_for_select(auth_options, mirror.auth_method),
{}, { class: "form-control js-mirror-auth-type qa-authentication-method" }
+ = f.hidden_field :auth_method, value: "password", class: "js-hidden-mirror-auth-type"
.form-group
.collapse.js-well-changing-auth
diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml
index 21b105e6f80..c031815200b 100644
--- a/app/views/projects/mirrors/_mirror_repos.html.haml
+++ b/app/views/projects/mirrors/_mirror_repos.html.haml
@@ -57,7 +57,7 @@
%td
- if mirror.last_error.present?
.badge.mirror-error-badge{ data: { toggle: 'tooltip', html: 'true' }, title: html_escape(mirror.last_error.try(:strip)) }= _('Error')
- %td.mirror-action-buttons
+ %td
.btn-group.mirror-actions-group.pull-right{ role: 'group' }
- if mirror.ssh_key_auth?
= clipboard_button(text: mirror.ssh_public_key, class: 'btn btn-default', title: _('Copy SSH public key'))
diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml
index ae8c801b705..138e2864bad 100644
--- a/app/views/projects/pages/_destroy.haml
+++ b/app/views/projects/pages/_destroy.haml
@@ -9,4 +9,4 @@
.form-actions
= link_to 'Remove pages', project_pages_path(@project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove"
- else
- .nothing-here-block Only the project owner can remove pages
+ .nothing-here-block Only project maintainers can remove pages
diff --git a/app/views/projects/pages/_https_only.html.haml b/app/views/projects/pages/_https_only.html.haml
index ce3ef29c32e..74478ee011c 100644
--- a/app/views/projects/pages/_https_only.html.haml
+++ b/app/views/projects/pages/_https_only.html.haml
@@ -3,7 +3,7 @@
.form-check
= f.check_box :pages_https_only, class: 'form-check-input', disabled: pages_https_only_disabled?
= f.label :pages_https_only, class: pages_https_only_label_class do
- %strong Force domains with SSL certificates to use HTTPS
+ %strong Force HTTPS (requires valid certificates)
- unless pages_https_only_disabled?
.prepend-top-10
diff --git a/app/views/projects/pipelines/_info.html.haml b/app/views/projects/pipelines/_info.html.haml
index 69a47faabed..5d307d6a70d 100644
--- a/app/views/projects/pipelines/_info.html.haml
+++ b/app/views/projects/pipelines/_info.html.haml
@@ -10,20 +10,14 @@
.icon-container
= icon('clock-o')
= pluralize @pipeline.total_size, "job"
- - if @pipeline.ref
- from
- - if @pipeline.ref_exists?
- = link_to @pipeline.ref, project_ref_path(@project, @pipeline.ref), class: "ref-name"
- - else
- %span.ref-name
- = @pipeline.ref
+ = @pipeline.ref_text
- 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
+ .well-segment.qa-pipeline-badges
.icon-container
= sprite_icon('flag')
- if @pipeline.latest?
@@ -48,9 +42,9 @@
content: "<a class='autodevops-link' href='#{popover_content_url}' target='_blank' rel='noopener noreferrer nofollow'>#{popover_content_text}</a>",
} }
Auto DevOps
- - if @pipeline.merge_request?
- %span.js-pipeline-url-mergerequest.badge.badge-info.has-tooltip{ title: "This pipeline is run in a merge request context" }
- merge request
+ - if @pipeline.detached_merge_request_pipeline?
+ %span.js-pipeline-url-mergerequest.badge.badge-info.has-tooltip{ title: "This pipeline is run on the source branch" }
+ detached
- if @pipeline.stuck?
%span.js-pipeline-url-stuck.badge.badge-warning
stuck
diff --git a/app/views/projects/pipelines/_with_tabs.html.haml b/app/views/projects/pipelines/_with_tabs.html.haml
index 66e202103a9..c04f076a3ab 100644
--- a/app/views/projects/pipelines/_with_tabs.html.haml
+++ b/app/views/projects/pipelines/_with_tabs.html.haml
@@ -2,15 +2,15 @@
%ul.pipelines-tabs.nav-links.no-top.no-bottom.mobile-separator.nav.nav-tabs
%li.js-pipeline-tab-link
= link_to project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-pipeline', action: 'pipelines', toggle: 'tab' }, class: 'pipeline-tab' do
- = _("Pipeline")
+ = _('Pipeline')
%li.js-builds-tab-link
= link_to builds_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-builds', action: 'builds', toggle: 'tab' }, class: 'builds-tab' do
- = _("Jobs")
+ = _('Jobs')
%span.badge.badge-pill.js-builds-counter= pipeline.total_size
- if @pipeline.failed_builds.present?
%li.js-failures-tab-link
= link_to failures_project_pipeline_path(@project, @pipeline), data: { target: '#js-tab-failures', action: 'failures', toggle: 'tab' }, class: 'failures-tab' do
- = _("Failed Jobs")
+ = _('Failed Jobs')
%span.badge.badge-pill.js-failures-counter= @pipeline.failed_builds.count
= render_if_exists "projects/pipelines/tabs_holder", pipeline: @pipeline, project: @project
@@ -24,41 +24,41 @@
%table.table.ci-table.pipeline
%thead
%tr
- %th Status
- %th Job ID
- %th Name
+ %th= _('Status')
+ %th= _('Job ID')
+ %th= _('Name')
%th
- %th Coverage
+ %th= _('Coverage')
%th
= render partial: "projects/stage/stage", collection: pipeline.legacy_stages, as: :stage
- elsif pipeline.project.builds_enabled? && !pipeline.ci_yaml_file
.bs-callout.bs-callout-warning
- \.gitlab-ci.yml not found in this commit
+ = _("%{gitlab_ci_yml} not found in this commit") % { gitlab_ci_yml: ".gitlab-ci.yml" }
- if @pipeline.failed_builds.present?
#js-tab-failures.build-failures.tab-pane.build-page
%table.table.responsive-table.ci-table.responsive-table-sm-rounded
%thead
%th.table-th-transparent
- %th.table-th-transparent= _("Name")
- %th.table-th-transparent= _("Stage")
- %th.table-th-transparent= _("Failure")
+ %th.table-th-transparent= _('Name')
+ %th.table-th-transparent= _('Stage')
+ %th.table-th-transparent= _('Failure')
%tbody
- @pipeline.failed_builds.each_with_index do |build, index|
- job = build.present(current_user: current_user)
%tr.build-state.responsive-table-border-start
- %td.responsive-table-cell.ci-status-icon-failed{ data: { column: "Status"} }
+ %td.responsive-table-cell.ci-status-icon-failed{ data: { column: _('Status')} }
.d-none.d-md-block.build-icon
= custom_icon("icon_status_#{build.status}")
.d-md-none.build-badge
= render "ci/status/badge", link: false, status: job.detailed_status(current_user)
- %td.responsive-table-cell.build-name{ data: { column: _("Name")} }
+ %td.responsive-table-cell.build-name{ data: { column: _('Name')} }
= link_to build.name, pipeline_job_url(pipeline, build)
- %td.responsive-table-cell.build-stage{ data: { column: _("Stage")} }
+ %td.responsive-table-cell.build-stage{ data: { column: _('Stage')} }
= build.stage.titleize
- %td.responsive-table-cell.build-failure{ data: { column: _("Failure")} }
+ %td.responsive-table-cell.build-failure{ data: { column: _('Failure')} }
= build.present.callout_failure_message
%td.responsive-table-cell.build-actions
- if can?(current_user, :update_build, job)
diff --git a/app/views/projects/pipelines/charts.html.haml b/app/views/projects/pipelines/charts.html.haml
index ec17eddba79..9da42fe99ac 100644
--- a/app/views/projects/pipelines/charts.html.haml
+++ b/app/views/projects/pipelines/charts.html.haml
@@ -1,5 +1,5 @@
- @no_container = true
-- page_title _("CI / CD Charts")
+- page_title _('CI / CD Charts')
%div{ class: container_class }
.sub-header-block
diff --git a/app/views/projects/pipelines/index.html.haml b/app/views/projects/pipelines/index.html.haml
index c0ee81fe28d..4e4638085fd 100644
--- a/app/views/projects/pipelines/index.html.haml
+++ b/app/views/projects/pipelines/index.html.haml
@@ -1,5 +1,7 @@
- @no_container = true
-- page_title "Pipelines"
+- page_title _('Pipelines')
+
+= render_if_exists "shared/shared_runners_minutes_limit_flash_message"
%div{ 'class' => container_class }
#pipelines-list-vue{ data: { endpoint: project_pipelines_path(@project, format: :json),
diff --git a/app/views/projects/pipelines/new.html.haml b/app/views/projects/pipelines/new.html.haml
index f1cdc0a70dd..41fe704601e 100644
--- a/app/views/projects/pipelines/new.html.haml
+++ b/app/views/projects/pipelines/new.html.haml
@@ -1,9 +1,9 @@
-- breadcrumb_title "Pipelines"
-- page_title s_("Pipeline|Run Pipeline")
+- breadcrumb_title _('Pipelines')
+- page_title s_('Pipeline|Run Pipeline')
- settings_link = link_to _('CI/CD settings'), project_settings_ci_cd_path(@project)
%h3.page-title
- = s_("Pipeline|Run Pipeline")
+ = s_('Pipeline|Run Pipeline')
%hr
= form_for @pipeline, as: :pipeline, url: project_pipelines_path(@project), html: { id: "new-pipeline-form", class: "js-new-pipeline-form js-requires-input" } do |f|
@@ -29,7 +29,7 @@
.form-actions
= f.submit s_('Pipeline|Create pipeline'), class: 'btn btn-success js-variables-save-button', tabindex: 3
- = link_to 'Cancel', project_pipelines_path(@project), class: 'btn btn-default float-right'
+ = link_to _('Cancel'), project_pipelines_path(@project), class: 'btn btn-default float-right'
-# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe
diff --git a/app/views/projects/pipelines/show.html.haml b/app/views/projects/pipelines/show.html.haml
index 193d437dad1..8a6d7b082e3 100644
--- a/app/views/projects/pipelines/show.html.haml
+++ b/app/views/projects/pipelines/show.html.haml
@@ -1,7 +1,7 @@
- @no_container = true
-- add_to_breadcrumbs "Pipelines", project_pipelines_path(@project)
+- add_to_breadcrumbs _('Pipelines'), project_pipelines_path(@project)
- breadcrumb_title "##{@pipeline.id}"
-- page_title "Pipeline"
+- page_title _('Pipeline')
.js-pipeline-container{ class: container_class, data: { controller_action: "#{controller.action_name}" } }
#js-pipeline-header-vue.pipeline-header-container
@@ -11,11 +11,13 @@
- if @pipeline.builds.empty? && @pipeline.yaml_errors.present?
.bs-callout.bs-callout-danger
- %h4 Found errors in your .gitlab-ci.yml:
+ %h4= _('Found errors in your %{gitlab_ci_yml}:') % { gitlab_ci_yml: '.gitlab-ci.yml' }
%ul
- @pipeline.yaml_errors.split(",").each do |error|
%li= error
- You can test your .gitlab-ci.yml in #{link_to "CI Lint", project_ci_lint_path(@project)}.
+ - lint_link_url = project_ci_lint_path(@project)
+ - lint_link_start = '<a href="%{url}">'.html_safe % { url: lint_link_url }
+ = s_('You can also test your %{gitlab_ci_yml} in %{lint_link_start}CI Lint%{lint_link_end}').html_safe % { gitlab_ci_yml: '.gitlab-ci.yml', lint_link_start: lint_link_start, lint_link_end: '</a>'.html_safe }
- else
= render "projects/pipelines/with_tabs", pipeline: @pipeline
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 8c4d1c32ebe..fac68a36e79 100644
--- a/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
+++ b/app/views/projects/settings/ci_cd/_autodevops_form.html.haml
@@ -8,15 +8,15 @@
.card.auto-devops-card
.card-body
.form-check
- = form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: @project.auto_devops_enabled?
+ = form.check_box :enabled, class: 'form-check-input js-toggle-extra-settings', checked: 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')
+ - if auto_devops_enabled
+ %span.badge.badge-info.js-instance-default-badge= badge_for_auto_devops_scope(@project)
.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'
- .card-footer.js-extra-settings{ class: @project.auto_devops_enabled? || 'hidden' }
+ .card-footer.js-extra-settings{ class: auto_devops_enabled || 'hidden' }
%p.settings-message.text-center
- kubernetes_cluster_link = help_page_path('user/project/clusters/index')
- kubernetes_cluster_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: kubernetes_cluster_link }
diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml
index 6966bf96724..548b7c06867 100644
--- a/app/views/projects/settings/ci_cd/show.html.haml
+++ b/app/views/projects/settings/ci_cd/show.html.haml
@@ -26,7 +26,7 @@
= s_('CICD|Auto DevOps will automatically build, test, and deploy your application based on a predefined Continuous Integration and Delivery configuration.')
= link_to s_('CICD|Learn more about Auto DevOps'), help_page_path('topics/autodevops/index.md')
.settings-content
- = render 'autodevops_form'
+ = render 'autodevops_form', auto_devops_enabled: @project.auto_devops_enabled?
= render_if_exists 'projects/settings/ci_cd/protected_environments', expanded: expanded
diff --git a/app/views/projects/settings/operations/_error_tracking.html.haml b/app/views/projects/settings/operations/_error_tracking.html.haml
index 4911e8d3770..451a79becc3 100644
--- a/app/views/projects/settings/operations/_error_tracking.html.haml
+++ b/app/views/projects/settings/operations/_error_tracking.html.haml
@@ -2,29 +2,17 @@
- setting = error_tracking_setting
-%section.settings.expanded.border-0.no-animate
+%section.settings.expanded.no-animate
.settings-header
%h4
= _('Error Tracking')
%p
= _('To link Sentry to GitLab, enter your Sentry URL and Auth Token.')
+ = link_to _('More information'), help_page_path('user/project/operations/error_tracking'), target: '_blank', rel: 'noopener noreferrer'
.settings-content
- = form_for @project, url: project_settings_operations_path(@project), method: :patch do |f|
- = form_errors(@project)
- .form-group
- = f.fields_for :error_tracking_setting_attributes, setting do |form|
- .form-check.form-group
- = form.check_box :enabled, class: 'form-check-input'
- = form.label :enabled, _('Active'), class: 'form-check-label'
- .form-group
- = form.label :api_url, _('Sentry API URL'), class: 'label-bold'
- = form.url_field :api_url, class: 'form-control', placeholder: _('http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/')
- %p.form-text.text-muted
- = _('Enter your Sentry API URL')
- .form-group
- = form.label :token, _('Auth Token'), class: 'label-bold'
- = form.text_field :token, class: 'form-control'
- %p.form-text.text-muted
- = _('Find and manage Auth Tokens in your Sentry account settings page.')
-
- = f.submit _('Save changes'), class: 'btn btn-success'
+ .js-error-tracking-form{ data: { list_projects_endpoint: list_projects_project_error_tracking_index_path(@project, format: :json),
+ operations_settings_endpoint: project_settings_operations_path(@project),
+ project: error_tracking_setting_project_json,
+ api_host: setting.api_host,
+ enabled: setting.enabled.to_json,
+ token: setting.token } }
diff --git a/app/views/projects/settings/operations/show.html.haml b/app/views/projects/settings/operations/show.html.haml
index b36fa9a5f51..6f777305a54 100644
--- a/app/views/projects/settings/operations/show.html.haml
+++ b/app/views/projects/settings/operations/show.html.haml
@@ -1,5 +1,7 @@
- @content_class = 'limit-container-width' unless fluid_layout
-- page_title _('Operations')
+- page_title _('Operations Settings')
+- breadcrumb_title _('Operations Settings')
+= render_if_exists 'projects/settings/operations/incidents'
= render 'projects/settings/operations/error_tracking', expanded: true
= render_if_exists 'projects/settings/operations/tracing'
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index a4974d89c1a..7682d01a5a1 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -1,12 +1,16 @@
- page_title _("Snippets")
-- if current_user
- .top-area
- - include_private = @project.team.member?(current_user) || current_user.admin?
- = render partial: 'snippets/snippets_scope_menu', locals: { subject: @project, include_private: include_private }
+- if @snippets.exists?
+ - if current_user
+ .top-area
+ - include_private = @project.team.member?(current_user) || current_user.admin?
+ = render partial: 'snippets/snippets_scope_menu', locals: { subject: @project, include_private: include_private }
- .nav-controls
- if can?(current_user, :create_project_snippet, @project)
- = link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-success", title: _("New snippet")
+ .nav-controls
+ - if can?(current_user, :create_project_snippet, @project)
+ = link_to _("New snippet"), new_project_snippet_path(@project), class: "btn btn-success", title: _("New snippet")
-= render 'snippets/snippets'
+ = render 'shared/snippets/list'
+- else
+ = render 'shared/empty_states/snippets', button_path: new_namespace_project_snippet_path(@project.namespace, @project)
diff --git a/app/views/projects/snippets/new.html.haml b/app/views/projects/snippets/new.html.haml
index 26b333d4ecf..d64e3a49a81 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")
+- page_title _("New Snippet")
%h3.page-title
- = _('New Snippet')
+ = _("New Snippet")
%hr
= render "shared/snippets/form", url: project_snippets_path(@project, @snippet)
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index cc203cfad86..8bfface3f5a 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -20,9 +20,8 @@
%p
= s_("TagsPage|Can't find HEAD commit for this tag")
- if release && release.description.present?
- .description.prepend-top-default
- .wiki
- = markdown_field(release, :description)
+ .description.md.prepend-top-default
+ = markdown_field(release, :description)
.row-fixed-content.controls.flex-row
= render 'projects/buttons/download', project: @project, ref: tag.name, pipeline: @tags_pipelines[tag.name]
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index feeaf799f51..0be62bc5612 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -23,7 +23,7 @@
= link_to edit_project_tag_release_path(@project, @tag.name), class: 'btn btn-edit controls-item has-tooltip', title: s_('TagsPage|Edit release notes') do
= icon("pencil")
= link_to project_tree_path(@project, @tag.name), class: 'btn controls-item has-tooltip', title: s_('TagsPage|Browse files') do
- = icon('files-o')
+ = sprite_icon('folder-open')
= link_to project_commits_path(@project, @tag.name), class: 'btn controls-item has-tooltip', title: s_('TagsPage|Browse commits') do
= icon('history')
.btn-container.controls-item
@@ -39,8 +39,7 @@
.append-bottom-default.prepend-top-default
- if @release.description.present?
- .description
- .wiki
- = markdown_field(@release, :description)
+ .description.md
+ = markdown_field(@release, :description)
- else
= s_('TagsPage|This tag has no release notes.')
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 8e1c054b50c..40d674f3fec 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -26,7 +26,7 @@
= (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe
.prepend-top-default.append-bottom-default
- .wiki.md{ class: ('use-csslab' if Feature.enabled?(:csslab)) }
+ .md.md-file{ class: ('use-csslab' if Feature.enabled?(:csslab)) }
= render_wiki_content(@page)
= render 'sidebar'
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index aaf9b973cda..df408e5fb60 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -1,3 +1,11 @@
+- users = capture_haml do
+ - if search_tabs?(:members)
+ %li{ class: active_when(@scope == 'users') }
+ = link_to search_filter_path(scope: 'users') do
+ Users
+ %span.badge.badge-pill
+ = limited_count(@search_results.limited_users_count)
+
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left')
.fade-right= icon('angle-right')
@@ -45,6 +53,7 @@
= _("Commits")
%span.badge.badge-pill
= @search_results.commits_count
+ = users
- elsif @show_snippets
%li{ class: active_when(@scope == 'snippet_blobs') }
@@ -78,3 +87,4 @@
= _("Milestones")
%span.badge.badge-pill
= limited_count(@search_results.limited_milestones_count)
+ = users
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index be7a2436d16..5b25a67bc87 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -20,7 +20,9 @@
.search-results
- if @scope == 'projects'
.term
- = render 'shared/projects/list', projects: @search_objects
+ = render 'shared/projects/list', projects: @search_objects, pipeline_status: false
+ - elsif %w[blobs wiki_blobs].include?(@scope)
+ = render partial: 'search/results/blob', collection: @search_objects, locals: { projects: blob_projects(@search_objects) }
- else
= render partial: "search/results/#{@scope.singularize}", collection: @search_objects
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index 2a602095845..bdad07f36d1 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -1,4 +1,4 @@
-- project = find_project_for_result_blob(blob)
+- project = find_project_for_result_blob(projects, blob)
- return unless project
- blob = parse_search_result(blob)
diff --git a/app/views/search/results/_snippet_blob.html.haml b/app/views/search/results/_snippet_blob.html.haml
index a60a4501557..f17dae0a94c 100644
--- a/app/views/search/results/_snippet_blob.html.haml
+++ b/app/views/search/results/_snippet_blob.html.haml
@@ -18,7 +18,7 @@
%i.fa.fa-file
%strong= snippet.file_name
- if markup?(snippet.file_name)
- .file-content.wiki
+ .file-content.md.md-file
- snippet_chunks.each do |chunk|
- unless chunk[:data].empty?
= markup(snippet.file_name, chunk[:data])
diff --git a/app/views/search/results/_user.html.haml b/app/views/search/results/_user.html.haml
new file mode 100644
index 00000000000..8060a1577e4
--- /dev/null
+++ b/app/views/search/results/_user.html.haml
@@ -0,0 +1,10 @@
+%ul.content-list
+ %li
+ .avatar-cell.d-none.d-sm-block
+ = user_avatar(user: user, user_name: user.name, css_class: 'd-none d-sm-inline avatar s40')
+ .user-info
+ = link_to user_path(user), class: 'd-none d-sm-inline' do
+ .item-title
+ = user.name
+ = user_status(user)
+ .cgray= user.to_reference
diff --git a/app/views/search/results/_wiki_blob.html.haml b/app/views/search/results/_wiki_blob.html.haml
index 389e4cc75b9..b351ecd4edf 100644
--- a/app/views/search/results/_wiki_blob.html.haml
+++ b/app/views/search/results/_wiki_blob.html.haml
@@ -1,4 +1,4 @@
-- project = find_project_for_result_blob(wiki_blob)
+- project = find_project_for_result_blob(projects, wiki_blob)
- wiki_blob = parse_search_result(wiki_blob)
- wiki_blob_link = project_wiki_path(project, wiki_blob.basename)
diff --git a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
index 422be28737c..755fd3a17d3 100644
--- a/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
+++ b/app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml
@@ -1,5 +1,5 @@
- if show_auto_devops_implicitly_enabled_banner?(project, current_user)
- .auto-devops-implicitly-enabled-banner.alert.alert-warning
+ .qa-auto-devops-banner.auto-devops-implicitly-enabled-banner.alert.alert-warning
- more_information_link = link_to _('More information'), help_page_path('topics/autodevops/index.md'), target: '_blank', class: 'alert-link'
- auto_devops_message = s_("AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}") % { more_information_link: more_information_link }
= auto_devops_message.html_safe
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index 5073e6ad48f..d7e57fc0d01 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -1,4 +1,4 @@
-.file-content.code.js-syntax-highlight
+.file-content.code.js-syntax-highlight.qa-file-content
.line-numbers
- if blob.data.present?
- link_icon = icon('link')
diff --git a/app/views/shared/_issuable_meta_data.html.haml b/app/views/shared/_issuable_meta_data.html.haml
index 6cc8c485666..31a5370a5f8 100644
--- a/app/views/shared/_issuable_meta_data.html.haml
+++ b/app/views/shared/_issuable_meta_data.html.haml
@@ -1,4 +1,4 @@
-- note_count = @issuable_meta_data[issuable.id].notes_count
+- note_count = @issuable_meta_data[issuable.id].user_notes_count
- issue_votes = @issuable_meta_data[issuable.id]
- upvotes, downvotes = issue_votes.upvotes, issue_votes.downvotes
- issuable_url = @collection_type == "Issue" ? issue_path(issuable, anchor: 'notes') : merge_request_path(issuable, anchor: 'notes')
diff --git a/app/views/shared/_mobile_clone_panel.html.haml b/app/views/shared/_mobile_clone_panel.html.haml
index 6e2527bd1a1..1e6b6f7c79b 100644
--- a/app/views/shared/_mobile_clone_panel.html.haml
+++ b/app/views/shared/_mobile_clone_panel.html.haml
@@ -13,3 +13,4 @@
- if http_enabled?
%li
= dropdown_item_with_description(http_copy_label, project.http_url_to_repo, href: project.http_url_to_repo, data: { clone_type: 'http' })
+ = render_if_exists 'shared/mobile_kerberos_clone'
diff --git a/app/views/shared/deploy_keys/_form.html.haml b/app/views/shared/deploy_keys/_form.html.haml
index 913c065e188..bc0dc7f9631 100644
--- a/app/views/shared/deploy_keys/_form.html.haml
+++ b/app/views/shared/deploy_keys/_form.html.haml
@@ -13,8 +13,9 @@
= form.label :key, class: 'col-form-label col-sm-2'
.col-sm-10
%p.light
- Paste a machine public key here. Read more about how to generate it
- = link_to 'here', help_page_path('ssh/README')
+ - link_start = "<a href='#{help_page_path('ssh/README')}' target='_blank' rel='noreferrer noopener'>".html_safe
+ - link_end = '</a>'
+ = _('Paste a machine public key here. Read more about how to generate it %{link_start}here%{link_end}').html_safe % { link_start: link_start, link_end: link_end.html_safe }
= form.text_area :key, class: 'form-control thin_area', rows: 5
- else
= form.label :fingerprint, class: 'col-form-label col-sm-2'
@@ -28,6 +29,6 @@
.col-sm-10
= deploy_keys_project_form.label :can_push do
= deploy_keys_project_form.check_box :can_push
- %strong Write access allowed
+ %strong= _('Write access allowed')
%p.light.append-bottom-0
- Allow this key to push to repository as well? (Default only allows pull access.)
+ = _('Allow this key to push to repository as well? (Default only allows pull access.)')
diff --git a/app/views/shared/empty_states/_snippets.html.haml b/app/views/shared/empty_states/_snippets.html.haml
new file mode 100644
index 00000000000..a1a16b9d067
--- /dev/null
+++ b/app/views/shared/empty_states/_snippets.html.haml
@@ -0,0 +1,20 @@
+- button_path = local_assigns.fetch(:button_path, false)
+
+.row.empty-state
+ .col-12
+ .svg-content
+ = image_tag 'illustrations/snippets_empty.svg'
+ .text-content
+ - if current_user
+ %h4
+ = s_('SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep.')
+ %p
+ = s_('SnippetsEmptyState|They can be either public or private.')
+ .text-center
+ = link_to s_('SnippetsEmptyState|New snippet'), button_path, class: 'btn btn-success', title: s_('SnippetsEmptyState|New snippet'), id: 'new_snippet_link'
+ - unless current_page?(dashboard_snippets_path)
+ = link_to s_('SnippetsEmptyState|Explore public snippets'), explore_snippets_path, class: 'btn btn-default', title: s_('SnippetsEmptyState|Explore public snippets')
+ - else
+ %h4.text-center= s_('SnippetsEmptyState|There are no snippets to show.')
+
+
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index a1b901aaffa..609b8dce21a 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -14,7 +14,7 @@
%span.visibility-icon.has-tooltip{ data: { container: 'body', placement: 'left' }, title: visibility_icon_description(group) }
= visibility_level_icon(group.visibility_level, fw: false)
- .avatar-container.s40
+ .avatar-container.rect-avatar.s40
= link_to group do
= group_icon(group, class: "avatar s40 d-none d-sm-block")
.title
diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml
index 588659c7e9c..2bcfcb6fa7c 100644
--- a/app/views/shared/issuable/_search_bar.html.haml
+++ b/app/views/shared/issuable/_search_bar.html.haml
@@ -71,6 +71,7 @@
= render 'shared/issuable/user_dropdown_item',
user: User.new(username: '{{username}}', name: '{{name}}'),
avatar: { lazy: true, url: '{{avatar_url}}' }
+ = render_if_exists 'shared/issuable/approver_dropdown'
#js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'None' } }
@@ -128,6 +129,19 @@
%li.filter-dropdown-item{ data: { value: 'no', capitalize: true } }
%button.btn.btn-link{ type: 'button' }
= _('No')
+ #js-dropdown-confidential.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul.filter-dropdown{ data: { dropdown: true } }
+ %li.filter-dropdown-item{ data: { value: 'yes', capitalize: true } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('Yes')
+ %li.filter-dropdown-item{ data: { value: 'no', capitalize: true } }
+ %button.btn.btn-link{ type: 'button' }
+ = _('No')
+ #js-dropdown-target-branch.filtered-search-input-dropdown-menu.dropdown-menu
+ %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
+ %li.filter-dropdown-item
+ %button.btn.btn-link.js-data-value.monospace
+ {{title}}
= render_if_exists 'shared/issuable/filter_weight', type: type
diff --git a/app/views/shared/issuable/_sort_dropdown.html.haml b/app/views/shared/issuable/_sort_dropdown.html.haml
index b6ea9185b10..967f31c8325 100644
--- a/app/views/shared/issuable/_sort_dropdown.html.haml
+++ b/app/views/shared/issuable/_sort_dropdown.html.haml
@@ -5,7 +5,7 @@
.dropdown.inline.prepend-left-10.issue-sort-dropdown
.btn-group{ role: 'group' }
.btn-group{ role: 'group' }
- %button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
+ %button.dropdown-menu-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= sort_title
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
diff --git a/app/views/shared/milestones/_milestone.html.haml b/app/views/shared/milestones/_milestone.html.haml
index 40b8374848e..e99aa3f1ee4 100644
--- a/app/views/shared/milestones/_milestone.html.haml
+++ b/app/views/shared/milestones/_milestone.html.haml
@@ -32,7 +32,7 @@
= 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
+ = link_to pluralize(milestone.merge_requests_visible_to_user(current_user).size, 'Merge Request'), merge_requests_path
.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
@@ -52,7 +52,7 @@
= 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"
+ = link_to 'Reopen Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
- if @group
- if can?(current_user, :admin_milestone, @group)
- if milestone.closed?
diff --git a/app/views/shared/milestones/_tabs.html.haml b/app/views/shared/milestones/_tabs.html.haml
index 55460acab8f..b877f66c71e 100644
--- a/app/views/shared/milestones/_tabs.html.haml
+++ b/app/views/shared/milestones/_tabs.html.haml
@@ -12,7 +12,7 @@
%li.nav-item
= link_to '#tab-merge-requests', class: 'nav-link', 'data-toggle' => 'tab', 'data-endpoint': milestone_merge_request_tab_path(milestone) do
Merge Requests
- %span.badge.badge-pill= milestone.merge_requests.size
+ %span.badge.badge-pill= milestone.merge_requests_visible_to_user(current_user).size
- else
%li.nav-item
= link_to '#tab-merge-requests', class: 'nav-link active', 'data-toggle' => 'tab', 'data-endpoint': milestone_merge_request_tab_path(milestone) do
@@ -21,11 +21,11 @@
%li.nav-item
= link_to '#tab-participants', class: 'nav-link', 'data-toggle' => 'tab', 'data-endpoint': milestone_participants_tab_path(milestone) do
Participants
- %span.badge.badge-pill= milestone.participants.count
+ %span.badge.badge-pill= milestone.issue_participants_visible_by_user(current_user).count
%li.nav-item
= link_to '#tab-labels', class: 'nav-link', 'data-toggle' => 'tab', 'data-endpoint': milestone_labels_tab_path(milestone) do
Labels
- %span.badge.badge-pill= milestone.labels.count
+ %span.badge.badge-pill= milestone.issue_labels_visible_by_user(current_user).count
- issues = milestone.sorted_issues(current_user)
- show_project_name = local_assigns.fetch(:show_project_name, false)
diff --git a/app/views/shared/milestones/_top.html.haml b/app/views/shared/milestones/_top.html.haml
index 55b1c14022f..edaeff782de 100644
--- a/app/views/shared/milestones/_top.html.haml
+++ b/app/views/shared/milestones/_top.html.haml
@@ -42,9 +42,8 @@
= markdown_field(milestone, :title)
- if milestone.group_milestone? && milestone.description.present?
%div
- .description
- .wiki
- = markdown_field(milestone, :description)
+ .description.md
+ = markdown_field(milestone, :description)
- if milestone.complete?(current_user) && milestone.active?
.alert.alert-success.prepend-top-default
diff --git a/app/views/shared/notes/_note.html.haml b/app/views/shared/notes/_note.html.haml
index 41d6ae79c81..6fec435cc87 100644
--- a/app/views/shared/notes/_note.html.haml
+++ b/app/views/shared/notes/_note.html.haml
@@ -36,14 +36,13 @@
= user_status(note.author)
%span.note-headline-light
= note.author.to_reference
- %span.note-headline-light
- %span.note-headline-meta
- - if note.system
- %span.system-note-message
- = markdown_field(note, :note)
- %span.system-note-separator
- &middot;
- %a.system-note-separator{ href: "##{dom_id(note)}" }= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
+ %span.note-headline-light.note-headline-meta
+ - if note.system
+ %span.system-note-message
+ = markdown_field(note, :note)
+ %span.system-note-separator
+ &middot;
+ %a.system-note-separator{ href: "##{dom_id(note)}" }= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
- unless note.system?
.note-actions
- if note.for_personal_snippet?
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index df17ae95e2a..90fb067e75d 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -10,17 +10,18 @@
- show_last_commit_as_description = false unless local_assigns[:show_last_commit_as_description] == true && can_show_last_commit_in_list?(project)
- css_class = '' unless local_assigns[:css_class]
- css_class += " no-description" if project.description.blank? && !show_last_commit_as_description
-- cache_key = project_list_cache_key(project)
+- cache_key = project_list_cache_key(project, pipeline_status: pipeline_status)
- updated_tooltip = time_ago_with_tooltip(project.last_activity_date)
- css_controls_class = compact_mode ? "" : "flex-lg-row justify-content-lg-between"
+- avatar_container_class = project.creator && use_creator_avatar ? '' : 'rect-avatar'
%li.project-row.d-flex{ class: css_class }
= cache(cache_key) do
- if avatar
- .avatar-container.s48.flex-grow-0.flex-shrink-0
+ .avatar-container.s48.flex-grow-0.flex-shrink-0{ class: avatar_container_class }
= link_to project_path(project), class: dom_class(project) do
- if project.creator && use_creator_avatar
- = image_tag avatar_icon_for_user(project.creator, 48), class: "avatar s65", alt:''
+ = image_tag avatar_icon_for_user(project.creator, 48), class: "avatar s48", alt:''
- else
= project_icon(project, alt: '', class: 'avatar project-avatar s48', width: 48, height: 48)
.project-details.d-sm-flex.flex-sm-fill.align-items-center
@@ -84,7 +85,8 @@
= sprite_icon('issues', size: 14, css_class: 'append-right-4')
= number_with_delimiter(project.open_issues_count)
- if pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project)
+ - pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref)
%span.icon-wrapper.pipeline-status
- = render_project_pipeline_status(project.pipeline_status, tooltip_placement: 'top')
+ = render 'ci/status/icon', status: project.commit.last_pipeline.detailed_status(current_user), type: 'commit', tooltip_placement: 'top', path: pipeline_path
.updated-note
%span Updated #{updated_tooltip}
diff --git a/app/views/shared/snippets/_form.html.haml b/app/views/shared/snippets/_form.html.haml
index 3007da0c189..6f2ddc5bdba 100644
--- a/app/views/shared/snippets/_form.html.haml
+++ b/app/views/shared/snippets/_form.html.haml
@@ -9,7 +9,7 @@
.form-group.row
= f.label :title, class: 'col-form-label col-sm-2'
.col-sm-10
- = f.text_field :title, class: 'form-control', required: true, autofocus: true
+ = f.text_field :title, class: 'form-control qa-snippet-title', required: true, autofocus: true
= render 'shared/form_elements/description', model: @snippet, project: @project, form: f
@@ -21,7 +21,7 @@
.col-sm-10
.file-holder.snippet
.js-file-title.file-title
- = f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name'
+ = f.text_field :file_name, placeholder: "Optionally name this file to add code highlighting, e.g. example.rb for Ruby.", class: 'form-control snippet-file-name qa-snippet-file-name'
.file-content.code
%pre#editor= @snippet.content
= f.hidden_field :content, class: 'snippet-file-content'
@@ -31,7 +31,7 @@
.form-actions
- if @snippet.new_record?
- = f.submit 'Create snippet', class: "btn-success btn"
+ = f.submit 'Create snippet', class: "btn-success btn qa-create-snippet-button"
- else
= f.submit 'Save changes', class: "btn-success btn"
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index a43296aa806..ebb634fe75f 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -1,6 +1,6 @@
.detail-page-header
.detail-page-header-body
- .snippet-box.has-tooltip.inline.append-right-5{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } }
+ .snippet-box.qa-snippet-box.has-tooltip.inline.append-right-5{ title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: "body" } }
%span.sr-only
= visibility_level_label(@snippet.visibility_level)
= visibility_level_icon(@snippet.visibility_level, fw: false)
@@ -17,12 +17,12 @@
= render "snippets/actions"
.snippet-header.limited-header-width
- %h2.snippet-title.prepend-top-0.append-bottom-0
+ %h2.snippet-title.prepend-top-0.append-bottom-0.qa-snippet-title
= markdown_field(@snippet, :title)
- if @snippet.description.present?
- .description
- .wiki
+ .description.qa-snippet-description
+ .md
= markdown_field(@snippet, :description)
%textarea.hidden.js-task-list-field
= @snippet.description
@@ -34,7 +34,7 @@
.embed-snippet
.input-group
.input-group-prepend
- %button.btn.btn-svg.embed-toggle.input-group-text{ 'data-toggle': 'dropdown', type: 'button' }
+ %button.btn.btn-svg.embed-toggle.input-group-text.qa-embed-type{ 'data-toggle': 'dropdown', type: 'button' }
%span.js-embed-action= _("Embed")
= sprite_icon('angle-down', size: 12, css_class: 'caret-down')
%ul.dropdown-menu.dropdown-menu-selectable.embed-toggle-list
diff --git a/app/views/shared/snippets/_list.html.haml b/app/views/shared/snippets/_list.html.haml
new file mode 100644
index 00000000000..5d2152eb411
--- /dev/null
+++ b/app/views/shared/snippets/_list.html.haml
@@ -0,0 +1,12 @@
+- remote = local_assigns.fetch(:remote, false)
+- link_project = local_assigns.fetch(:link_project, false)
+
+- if @snippets.exists?
+ .snippets-list-holder
+ %ul.content-list
+ = render partial: 'shared/snippets/snippet', collection: @snippets, locals: { link_project: link_project }
+
+ = paginate @snippets, theme: 'gitlab', remote: remote
+
+- else
+ .nothing-here-block= s_("SnippetsEmptyState|No snippets found")
diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml
index 4f418e2381f..f5fe75bed5a 100644
--- a/app/views/snippets/index.html.haml
+++ b/app/views/snippets/index.html.haml
@@ -10,4 +10,4 @@
= link_to user_path(@user) do
= _("%{user_name} profile page") % { user_name: @user.name }
-= render 'snippets'
+= render 'shared/snippets/list'
diff --git a/app/views/users/_groups.html.haml b/app/views/users/_groups.html.haml
index 55799e10a46..6d7a52c7688 100644
--- a/app/views/users/_groups.html.haml
+++ b/app/views/users/_groups.html.haml
@@ -1,5 +1,5 @@
.clearfix
- groups.each do |group|
= link_to group, class: 'profile-groups-avatars inline', title: group.name do
- .avatar-container.s40
+ .avatar-container.rect-avatar.s40
= group_icon(group, class: 'avatar group-avatar s40')
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 410411b1294..6ebd756d3da 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -23,6 +23,7 @@
- cronjob:prune_web_hook_logs
- gcp_cluster:cluster_install_app
+- gcp_cluster:cluster_patch_app
- gcp_cluster:cluster_upgrade_app
- gcp_cluster:cluster_provision
- gcp_cluster:cluster_wait_for_app_installation
@@ -47,6 +48,9 @@
- github_importer:github_import_stage_import_repository
- hashed_storage:hashed_storage_migrator
+- hashed_storage:hashed_storage_rollbacker
+- hashed_storage:hashed_storage_project_migrate
+- hashed_storage:hashed_storage_project_rollback
- mail_scheduler:mail_scheduler_issue_due
- mail_scheduler:mail_scheduler_notification_service
@@ -67,6 +71,7 @@
- pipeline_hooks:build_hooks
- pipeline_hooks:pipeline_hooks
- pipeline_processing:build_finished
+- pipeline_processing:ci_build_prepare
- pipeline_processing:build_queue
- pipeline_processing:build_success
- pipeline_processing:pipeline_process
@@ -101,6 +106,7 @@
- authorized_projects
- background_migration
+- chat_notification
- create_gpg_signature
- delete_merged_branches
- delete_user
@@ -125,7 +131,6 @@
- project_cache
- project_destroy
- project_export
-- project_migrate_hashed_storage
- project_service
- propagate_service_template
- reactive_caching
@@ -145,3 +150,4 @@
- repository_cleanup
- delete_stored_files
- import_issues_csv
+- project_daily_statistics
diff --git a/app/workers/build_finished_worker.rb b/app/workers/build_finished_worker.rb
index ae853ec9316..adc38226405 100644
--- a/app/workers/build_finished_worker.rb
+++ b/app/workers/build_finished_worker.rb
@@ -30,5 +30,6 @@ class BuildFinishedWorker
# We execute these async as these are independent operations.
BuildHooksWorker.perform_async(build.id)
ArchiveTraceWorker.perform_async(build.id)
+ ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
end
end
diff --git a/app/workers/chat_notification_worker.rb b/app/workers/chat_notification_worker.rb
new file mode 100644
index 00000000000..25a306e94d8
--- /dev/null
+++ b/app/workers/chat_notification_worker.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class ChatNotificationWorker
+ include ApplicationWorker
+
+ RESCHEDULE_INTERVAL = 2.seconds
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(build_id)
+ Ci::Build.find_by(id: build_id).try do |build|
+ send_response(build)
+ end
+ rescue Gitlab::Chat::Output::MissingBuildSectionError
+ # The creation of traces and sections appears to be eventually consistent.
+ # As a result it's possible for us to run the above code before the trace
+ # sections are present. To better handle such cases we'll just reschedule
+ # the job instead of producing an error.
+ self.class.perform_in(RESCHEDULE_INTERVAL, build_id)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def send_response(build)
+ Gitlab::Chat::Responder.responder_for(build).try do |responder|
+ if build.success?
+ output = Gitlab::Chat::Output.new(build)
+
+ responder.success(output.to_s)
+ else
+ responder.failure
+ end
+ end
+ end
+end
diff --git a/app/workers/ci/build_prepare_worker.rb b/app/workers/ci/build_prepare_worker.rb
new file mode 100644
index 00000000000..1a35a74ae53
--- /dev/null
+++ b/app/workers/ci/build_prepare_worker.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module Ci
+ class BuildPrepareWorker
+ include ApplicationWorker
+ include PipelineQueue
+
+ queue_namespace :pipeline_processing
+
+ def perform(build_id)
+ Ci::Build.find_by_id(build_id).try do |build|
+ Ci::PrepareBuildService.new(build).execute
+ end
+ end
+ end
+end
diff --git a/app/workers/cluster_configure_worker.rb b/app/workers/cluster_configure_worker.rb
index 63e6cc147be..b984dee5b21 100644
--- a/app/workers/cluster_configure_worker.rb
+++ b/app/workers/cluster_configure_worker.rb
@@ -5,6 +5,8 @@ class ClusterConfigureWorker
include ClusterQueue
def perform(cluster_id)
+ return if Feature.enabled?(:ci_preparing_state, default_enabled: true)
+
Clusters::Cluster.find_by_id(cluster_id).try do |cluster|
Clusters::RefreshService.create_or_update_namespaces_for_cluster(cluster)
end
diff --git a/app/workers/cluster_patch_app_worker.rb b/app/workers/cluster_patch_app_worker.rb
new file mode 100644
index 00000000000..0549e81ed05
--- /dev/null
+++ b/app/workers/cluster_patch_app_worker.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ClusterPatchAppWorker
+ include ApplicationWorker
+ include ClusterQueue
+ include ClusterApplications
+
+ def perform(app_name, app_id)
+ find_application(app_name, app_id) do |app|
+ Clusters::Applications::PatchService.new(app).execute
+ end
+ end
+end
diff --git a/app/workers/cluster_project_configure_worker.rb b/app/workers/cluster_project_configure_worker.rb
index 497e57c0d0b..d7bea69a01c 100644
--- a/app/workers/cluster_project_configure_worker.rb
+++ b/app/workers/cluster_project_configure_worker.rb
@@ -5,6 +5,8 @@ class ClusterProjectConfigureWorker
include ClusterQueue
def perform(project_id)
+ return if Feature.enabled?(:ci_preparing_state, default_enabled: true)
+
project = Project.find(project_id)
::Clusters::RefreshService.create_or_update_namespaces_for_project(project)
diff --git a/app/workers/concerns/waitable_worker.rb b/app/workers/concerns/waitable_worker.rb
index 27b94a82444..17946bbc5ca 100644
--- a/app/workers/concerns/waitable_worker.rb
+++ b/app/workers/concerns/waitable_worker.rb
@@ -25,11 +25,9 @@ module WaitableWorker
failed = []
args_list.each do |args|
- begin
- new.perform(*args)
- rescue
- failed << args
- end
+ new.perform(*args)
+ rescue
+ failed << args
end
bulk_perform_async(failed) if failed.present?
diff --git a/app/workers/create_gpg_signature_worker.rb b/app/workers/create_gpg_signature_worker.rb
index 49c7a403838..7fac7822cf7 100644
--- a/app/workers/create_gpg_signature_worker.rb
+++ b/app/workers/create_gpg_signature_worker.rb
@@ -5,8 +5,8 @@ class CreateGpgSignatureWorker
# rubocop: disable CodeReuse/ActiveRecord
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.
+ # Older versions of Git::BranchPushService 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?
@@ -20,11 +20,9 @@ class CreateGpgSignatureWorker
# This calculates and caches the signature in the database
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
+ Gitlab::Gpg::Commit.new(commit).signature
+ rescue => e
+ Rails.logger.error("Failed to create signature for commit #{commit.id}. Error: #{e.message}")
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index 17ad1d5ab88..ed3e354e4c2 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -52,24 +52,22 @@ class EmailsOnPushWorker
end
valid_recipients(recipients).each do |recipient|
- begin
- send_email(
- recipient,
- project_id,
- author_id: author_id,
- ref: ref,
- action: action,
- compare: compare,
- reverse_compare: reverse_compare,
- diff_refs: diff_refs,
- send_from_committer_email: send_from_committer_email,
- disable_diffs: disable_diffs
- )
-
- # These are input errors and won't be corrected even if Sidekiq retries
- rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e
- logger.info("Failed to send e-mail for project '#{project.full_name}' to #{recipient}: #{e}")
- end
+ send_email(
+ recipient,
+ project_id,
+ author_id: author_id,
+ ref: ref,
+ action: action,
+ compare: compare,
+ reverse_compare: reverse_compare,
+ diff_refs: diff_refs,
+ send_from_committer_email: send_from_committer_email,
+ disable_diffs: disable_diffs
+ )
+
+ # These are input errors and won't be corrected even if Sidekiq retries
+ rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e
+ logger.info("Failed to send e-mail for project '#{project.full_name}' to #{recipient}: #{e}")
end
ensure
@email = nil
diff --git a/app/workers/expire_pipeline_cache_worker.rb b/app/workers/expire_pipeline_cache_worker.rb
index 148384600b6..2c070d482a1 100644
--- a/app/workers/expire_pipeline_cache_worker.rb
+++ b/app/workers/expire_pipeline_cache_worker.rb
@@ -37,9 +37,9 @@ class ExpirePipelineCacheWorker
Gitlab::Routing.url_helpers.project_new_merge_request_path(project, format: :json)
end
- def each_pipelines_merge_request_path(project, pipeline)
+ def each_pipelines_merge_request_path(pipeline)
pipeline.all_merge_requests.each do |merge_request|
- path = Gitlab::Routing.url_helpers.pipelines_project_merge_request_path(project, merge_request, format: :json)
+ path = Gitlab::Routing.url_helpers.pipelines_project_merge_request_path(merge_request.target_project, merge_request, format: :json)
yield(path)
end
@@ -59,7 +59,7 @@ class ExpirePipelineCacheWorker
store.touch(project_pipeline_path(project, pipeline))
store.touch(commit_pipelines_path(project, pipeline.commit)) unless pipeline.commit.nil?
store.touch(new_merge_request_pipelines_path(project))
- each_pipelines_merge_request_path(project, pipeline) do |path|
+ each_pipelines_merge_request_path(pipeline) do |path|
store.touch(path)
end
end
diff --git a/app/workers/hashed_storage/base_worker.rb b/app/workers/hashed_storage/base_worker.rb
new file mode 100644
index 00000000000..816e0504db6
--- /dev/null
+++ b/app/workers/hashed_storage/base_worker.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module HashedStorage
+ class BaseWorker
+ include ExclusiveLeaseGuard
+
+ LEASE_TIMEOUT = 30.seconds.to_i
+ LEASE_KEY_SEGMENT = 'project_migrate_hashed_storage_worker'.freeze
+
+ protected
+
+ def lease_key
+ # we share the same lease key for both migration and rollback so they don't run simultaneously
+ "#{LEASE_KEY_SEGMENT}:#{project_id}"
+ end
+
+ def lease_timeout
+ LEASE_TIMEOUT
+ end
+ end
+end
diff --git a/app/workers/hashed_storage/project_migrate_worker.rb b/app/workers/hashed_storage/project_migrate_worker.rb
new file mode 100644
index 00000000000..f00a459a097
--- /dev/null
+++ b/app/workers/hashed_storage/project_migrate_worker.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module HashedStorage
+ class ProjectMigrateWorker < BaseWorker
+ include ApplicationWorker
+
+ queue_namespace :hashed_storage
+
+ attr_reader :project_id
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(project_id, old_disk_path = nil)
+ @project_id = project_id # we need to set this in order to create the lease_key
+
+ try_obtain_lease do
+ project = Project.without_deleted.find_by(id: project_id)
+ break unless project
+
+ old_disk_path ||= project.disk_path
+
+ ::Projects::HashedStorage::MigrationService.new(project, old_disk_path, logger: logger).execute
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/workers/hashed_storage/project_rollback_worker.rb b/app/workers/hashed_storage/project_rollback_worker.rb
new file mode 100644
index 00000000000..55e1d7ab23e
--- /dev/null
+++ b/app/workers/hashed_storage/project_rollback_worker.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module HashedStorage
+ class ProjectRollbackWorker < BaseWorker
+ include ApplicationWorker
+
+ queue_namespace :hashed_storage
+
+ attr_reader :project_id
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def perform(project_id, old_disk_path = nil)
+ @project_id = project_id # we need to set this in order to create the lease_key
+
+ try_obtain_lease do
+ project = Project.without_deleted.find_by(id: project_id)
+ break unless project
+
+ old_disk_path ||= project.disk_path
+
+ ::Projects::HashedStorage::RollbackService.new(project, old_disk_path, logger: logger).execute
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/app/workers/hashed_storage/rollbacker_worker.rb b/app/workers/hashed_storage/rollbacker_worker.rb
new file mode 100644
index 00000000000..a4da8443787
--- /dev/null
+++ b/app/workers/hashed_storage/rollbacker_worker.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+module HashedStorage
+ class RollbackerWorker
+ include ApplicationWorker
+
+ queue_namespace :hashed_storage
+
+ # @param [Integer] start initial ID of the batch
+ # @param [Integer] finish last ID of the batch
+ def perform(start, finish)
+ migrator = Gitlab::HashedStorage::Migrator.new
+ migrator.bulk_rollback(start: start, finish: finish)
+ end
+ end
+end
diff --git a/app/workers/object_storage/migrate_uploads_worker.rb b/app/workers/object_storage/migrate_uploads_worker.rb
index fe5d27b087d..206eb71b898 100644
--- a/app/workers/object_storage/migrate_uploads_worker.rb
+++ b/app/workers/object_storage/migrate_uploads_worker.rb
@@ -126,11 +126,9 @@ module ObjectStorage
def process_uploader(uploader)
MigrationResult.new(uploader.upload).tap do |result|
- begin
- uploader.migrate!(@to_store)
- rescue => e
- result.error = e
- end
+ uploader.migrate!(@to_store)
+ rescue => e
+ result.error = e
end
end
end
diff --git a/app/workers/pipeline_metrics_worker.rb b/app/workers/pipeline_metrics_worker.rb
index c2fbfd2b3a5..0ddad43b8d5 100644
--- a/app/workers/pipeline_metrics_worker.rb
+++ b/app/workers/pipeline_metrics_worker.rb
@@ -30,6 +30,6 @@ class PipelineMetricsWorker
# rubocop: enable CodeReuse/ActiveRecord
def merge_requests(pipeline)
- pipeline.merge_requests.map(&:id)
+ pipeline.merge_requests_as_head_pipeline.map(&:id)
end
end
diff --git a/app/workers/pipeline_schedule_worker.rb b/app/workers/pipeline_schedule_worker.rb
index ac4e9710f33..02a69ea3b54 100644
--- a/app/workers/pipeline_schedule_worker.rb
+++ b/app/workers/pipeline_schedule_worker.rb
@@ -8,16 +8,15 @@ class PipelineScheduleWorker
def perform
Ci::PipelineSchedule.active.where("next_run_at < ?", Time.now)
.preload(:owner, :project).find_each do |schedule|
- begin
- Ci::CreatePipelineService.new(schedule.project,
- schedule.owner,
- ref: schedule.ref)
- .execute!(:schedule, ignore_skip_ci: true, save_on_errors: true, schedule: schedule)
- rescue => e
- error(schedule, e)
- ensure
- schedule.schedule_next_run!
- end
+
+ Ci::CreatePipelineService.new(schedule.project,
+ schedule.owner,
+ ref: schedule.ref)
+ .execute!(:schedule, ignore_skip_ci: true, save_on_errors: true, schedule: schedule)
+ rescue => e
+ error(schedule, e)
+ ensure
+ schedule.schedule_next_run!
end
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index bbd4ab159e4..396f44396a3 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -4,7 +4,7 @@ class PostReceive
include ApplicationWorker
def perform(gl_repository, identifier, changes, push_options = [])
- project, is_wiki = Gitlab::GlRepository.parse(gl_repository)
+ project, repo_type = Gitlab::GlRepository.parse(gl_repository)
if project.nil?
log("Triggered hook for non-existing project with gl_repository \"#{gl_repository}\"")
@@ -17,7 +17,7 @@ class PostReceive
Sidekiq.logger.info "changes: #{changes.inspect}" if ENV['SIDEKIQ_LOG_ARGUMENTS']
post_received = Gitlab::GitPostReceive.new(project, identifier, changes, push_options)
- if is_wiki
+ if repo_type.wiki?
process_wiki_changes(post_received)
else
process_project_changes(post_received)
@@ -38,7 +38,7 @@ class PostReceive
post_received.changes_refs do |oldrev, newrev, ref|
if Gitlab::Git.tag_ref?(ref)
- GitTagPushService.new(
+ Git::TagPushService.new(
post_received.project,
@user,
oldrev: oldrev,
@@ -46,7 +46,7 @@ class PostReceive
ref: ref,
push_options: post_received.push_options).execute
elsif Gitlab::Git.branch_ref?(ref)
- GitPushService.new(
+ Git::BranchPushService.new(
post_received.project,
@user,
oldrev: oldrev,
diff --git a/app/workers/project_cache_worker.rb b/app/workers/project_cache_worker.rb
index d27b5e62574..b31099bc670 100644
--- a/app/workers/project_cache_worker.rb
+++ b/app/workers/project_cache_worker.rb
@@ -27,6 +27,7 @@ class ProjectCacheWorker
# rubocop: enable CodeReuse/ActiveRecord
def update_statistics(project, statistics = [])
+ return if Gitlab::Database.read_only?
return unless try_obtain_lease_for(project.id, :update_statistics)
Rails.logger.info("Updating statistics for project #{project.id}")
diff --git a/app/workers/project_daily_statistics_worker.rb b/app/workers/project_daily_statistics_worker.rb
new file mode 100644
index 00000000000..101f5c28459
--- /dev/null
+++ b/app/workers/project_daily_statistics_worker.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ProjectDailyStatisticsWorker
+ include ApplicationWorker
+
+ def perform(project_id)
+ project = Project.find_by_id(project_id)
+
+ return unless project&.repository&.exists?
+
+ Projects::FetchStatisticsIncrementService.new(project).execute
+ end
+end
diff --git a/app/workers/project_migrate_hashed_storage_worker.rb b/app/workers/project_migrate_hashed_storage_worker.rb
deleted file mode 100644
index 1c8f313e6e9..00000000000
--- a/app/workers/project_migrate_hashed_storage_worker.rb
+++ /dev/null
@@ -1,43 +0,0 @@
-# frozen_string_literal: true
-
-class ProjectMigrateHashedStorageWorker
- include ApplicationWorker
-
- LEASE_TIMEOUT = 30.seconds.to_i
- LEASE_KEY_SEGMENT = 'project_migrate_hashed_storage_worker'.freeze
-
- # rubocop: disable CodeReuse/ActiveRecord
- def perform(project_id, old_disk_path = nil)
- uuid = lease_for(project_id).try_obtain
-
- if uuid
- project = Project.find_by(id: project_id)
- return if project.nil? || project.pending_delete?
-
- old_disk_path ||= project.disk_path
-
- ::Projects::HashedStorage::MigrationService.new(project, old_disk_path, logger: logger).execute
- else
- return false
- end
-
- ensure
- cancel_lease_for(project_id, uuid) if uuid
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- def lease_for(project_id)
- Gitlab::ExclusiveLease.new(lease_key(project_id), timeout: LEASE_TIMEOUT)
- end
-
- private
-
- def lease_key(project_id)
- # we share the same lease key for both migration and rollback so they don't run simultaneously
- "#{LEASE_KEY_SEGMENT}:#{project_id}"
- end
-
- def cancel_lease_for(project_id, uuid)
- Gitlab::ExclusiveLease.cancel(lease_key(project_id), uuid)
- end
-end
diff --git a/app/workers/remove_expired_members_worker.rb b/app/workers/remove_expired_members_worker.rb
index 41913900571..3497a1f9280 100644
--- a/app/workers/remove_expired_members_worker.rb
+++ b/app/workers/remove_expired_members_worker.rb
@@ -6,11 +6,9 @@ class RemoveExpiredMembersWorker
def perform
Member.expired.find_each do |member|
- begin
- Members::DestroyService.new.execute(member, skip_authorization: true)
- rescue => ex
- logger.error("Expired Member ID=#{member.id} cannot be removed - #{ex}")
- end
+ Members::DestroyService.new.execute(member, skip_authorization: true)
+ rescue => ex
+ logger.error("Expired Member ID=#{member.id} cannot be removed - #{ex}")
end
end
end
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 00000000000..78d14095b0b
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,51 @@
+/* eslint-disable import/no-commonjs, filenames/match-regex */
+
+const BABEL_ENV = process.env.BABEL_ENV || process.env.NODE_ENV || null;
+
+const presets = [
+ [
+ '@babel/preset-env',
+ {
+ modules: false,
+ targets: {
+ ie: '11',
+ },
+ },
+ ],
+];
+
+// include stage 3 proposals
+const plugins = [
+ '@babel/plugin-syntax-dynamic-import',
+ '@babel/plugin-syntax-import-meta',
+ '@babel/plugin-proposal-class-properties',
+ '@babel/plugin-proposal-json-strings',
+ '@babel/plugin-proposal-private-methods',
+];
+
+// add code coverage tooling if necessary
+if (BABEL_ENV === 'coverage') {
+ plugins.push([
+ 'babel-plugin-istanbul',
+ {
+ exclude: ['spec/javascripts/**/*', 'app/assets/javascripts/locale/**/app.js'],
+ },
+ ]);
+}
+
+// add rewire support when running tests
+if (BABEL_ENV === 'karma' || BABEL_ENV === 'coverage') {
+ plugins.push('babel-plugin-rewire');
+}
+
+// Jest is running in node environment
+if (BABEL_ENV === 'jest') {
+ plugins.push('@babel/plugin-transform-modules-commonjs');
+ /*
+ without the following, babel-plugin-istanbul throws an error:
+ https://gitlab.com/gitlab-org/gitlab-ce/issues/58390
+ */
+ plugins.push('babel-plugin-dynamic-import-node');
+}
+
+module.exports = { presets, plugins };
diff --git a/bin/background_jobs b/bin/background_jobs
index f28e2f722dc..9d12422b81a 100755
--- a/bin/background_jobs
+++ b/bin/background_jobs
@@ -38,7 +38,14 @@ start_no_deamonize()
start_sidekiq()
{
- exec bundle exec sidekiq -C "${sidekiq_config}" -e $RAILS_ENV -P $sidekiq_pidfile "$@"
+ cmd="exec"
+ chpst=$(which chpst)
+
+ if [ -n "$chpst" ]; then
+ cmd="${cmd} ${chpst} -P"
+ fi
+
+ ${cmd} bundle exec sidekiq -C "${sidekiq_config}" -e $RAILS_ENV -P $sidekiq_pidfile "$@"
}
load_ok()
diff --git a/changelogs/unreleased/10029-env-item.yml b/changelogs/unreleased/10029-env-item.yml
new file mode 100644
index 00000000000..f4e742d3e17
--- /dev/null
+++ b/changelogs/unreleased/10029-env-item.yml
@@ -0,0 +1,5 @@
+---
+title: Removes EE differences for environment_item.vue
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/10081-env-table.yml b/changelogs/unreleased/10081-env-table.yml
new file mode 100644
index 00000000000..b27a1be8cca
--- /dev/null
+++ b/changelogs/unreleased/10081-env-table.yml
@@ -0,0 +1,5 @@
+---
+title: Removes EE differences for environments_table.vue
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/19745-forms-with-task-lists-can-be-overwritten-when-editing-simultaneously.yml b/changelogs/unreleased/19745-forms-with-task-lists-can-be-overwritten-when-editing-simultaneously.yml
deleted file mode 100644
index b1177e1717e..00000000000
--- a/changelogs/unreleased/19745-forms-with-task-lists-can-be-overwritten-when-editing-simultaneously.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Increase reliability and performance of toggling task items
-merge_request: 23938
-author:
-type: fixed
diff --git a/changelogs/unreleased/2105-add-setting-for-first-day-of-the-week.yml b/changelogs/unreleased/2105-add-setting-for-first-day-of-the-week.yml
deleted file mode 100644
index f4a52b1aacd..00000000000
--- a/changelogs/unreleased/2105-add-setting-for-first-day-of-the-week.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add setting for first day of the week
-merge_request: 22755
-author: Fabian Schneider @fabsrc
-type: added
diff --git a/changelogs/unreleased/24642-activity_service_optimization.yml b/changelogs/unreleased/24642-activity_service_optimization.yml
deleted file mode 100644
index bdfa769959e..00000000000
--- a/changelogs/unreleased/24642-activity_service_optimization.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Optimize Redis usage in User::ActivityService
-merge_request: 25005
-author:
-type: performance
diff --git a/changelogs/unreleased/24680-support-bamboo-api-polymorphism.yml b/changelogs/unreleased/24680-support-bamboo-api-polymorphism.yml
deleted file mode 100644
index 5117195cd0c..00000000000
--- a/changelogs/unreleased/24680-support-bamboo-api-polymorphism.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "Support bamboo api polymorphism"
-merge_request: 24680
-author: Alex Lossent
-type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/24875-label.yml b/changelogs/unreleased/24875-label.yml
deleted file mode 100644
index 1f9d2222edf..00000000000
--- a/changelogs/unreleased/24875-label.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Append prioritized label before pagination
-merge_request: 24815
-author:
-type: fixed
diff --git a/changelogs/unreleased/24971-align-emailvalidator-to-validate_email-gem-implementation.yml b/changelogs/unreleased/24971-align-emailvalidator-to-validate_email-gem-implementation.yml
new file mode 100644
index 00000000000..04dbc3a1d5a
--- /dev/null
+++ b/changelogs/unreleased/24971-align-emailvalidator-to-validate_email-gem-implementation.yml
@@ -0,0 +1,5 @@
+---
+title: Align EmailValidator to validate_email gem implementation
+merge_request: 24971
+author: Horatiu Eugen Vlad
+type: fixed
diff --git a/changelogs/unreleased/25043-empty-states.yml b/changelogs/unreleased/25043-empty-states.yml
deleted file mode 100644
index 529a8b3206f..00000000000
--- a/changelogs/unreleased/25043-empty-states.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make issuable empty states actionable
-merge_request: 24077
-author:
-type: changed
diff --git a/changelogs/unreleased/25569-changing-wording-to-delete-when-referring-to-removing-a-branch.yml b/changelogs/unreleased/25569-changing-wording-to-delete-when-referring-to-removing-a-branch.yml
deleted file mode 100644
index 02a667073ca..00000000000
--- a/changelogs/unreleased/25569-changing-wording-to-delete-when-referring-to-removing-a-branch.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use delete instead of remove when referring to `git branch -D`
-merge_request: !23966
-author:
-type: changed
diff --git a/changelogs/unreleased/25942-remove-fake-repository-path-response.yml b/changelogs/unreleased/25942-remove-fake-repository-path-response.yml
new file mode 100644
index 00000000000..e1da28ab03c
--- /dev/null
+++ b/changelogs/unreleased/25942-remove-fake-repository-path-response.yml
@@ -0,0 +1,5 @@
+---
+title: Remove fake repository_path response
+merge_request: 25942
+author: Fabio Papa
+type: other
diff --git a/changelogs/unreleased/26375-markdown-footnotes-not-working.yml b/changelogs/unreleased/26375-markdown-footnotes-not-working.yml
deleted file mode 100644
index 86adef84a2a..00000000000
--- a/changelogs/unreleased/26375-markdown-footnotes-not-working.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Footnotes now render properly in markdown
-merge_request: 24168
-author:
-type: fixed
diff --git a/changelogs/unreleased/28500-empty-states-for-profile-page.yml b/changelogs/unreleased/28500-empty-states-for-profile-page.yml
deleted file mode 100644
index 53f840521ae..00000000000
--- a/changelogs/unreleased/28500-empty-states-for-profile-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Refresh empty states for profile page tabs
-merge_request: 24549
-author:
-type: changed
diff --git a/changelogs/unreleased/30120-add-flat-square-badge-style.yml b/changelogs/unreleased/30120-add-flat-square-badge-style.yml
deleted file mode 100644
index a542a58d3fc..00000000000
--- a/changelogs/unreleased/30120-add-flat-square-badge-style.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add flat-square badge style
-merge_request: 24172
-author: Fabian Schneider @fabsrc
-type: added
diff --git a/changelogs/unreleased/36445-better-indication-that-an-issue-has-been-moved-or-marked-as-duplicated.yml b/changelogs/unreleased/36445-better-indication-that-an-issue-has-been-moved-or-marked-as-duplicated.yml
deleted file mode 100644
index 70b561ccbf6..00000000000
--- a/changelogs/unreleased/36445-better-indication-that-an-issue-has-been-moved-or-marked-as-duplicated.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Indicate on Issue Status if an Issue was Moved
-merge_request: 24470
-author:
-type: added
diff --git a/changelogs/unreleased/37990-task-list-bracket.yml b/changelogs/unreleased/37990-task-list-bracket.yml
deleted file mode 100644
index ffa77cf0af7..00000000000
--- a/changelogs/unreleased/37990-task-list-bracket.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix ambiguous brackets in task lists
-merge_request: 18514
-author: Jared Deckard <jared.deckard@gmail.com>
-type: fixed
diff --git a/changelogs/unreleased/39676-wiki-api-problems-on-update-parameters-and-500-error.yml b/changelogs/unreleased/39676-wiki-api-problems-on-update-parameters-and-500-error.yml
deleted file mode 100644
index 1af49fb6a2c..00000000000
--- a/changelogs/unreleased/39676-wiki-api-problems-on-update-parameters-and-500-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Require only one parameter when updating a wiki'
-merge_request: 25191
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/40997-gitlab-pages-deploy-jobs-have-a-null-status.yml b/changelogs/unreleased/40997-gitlab-pages-deploy-jobs-have-a-null-status.yml
deleted file mode 100644
index 01036253151..00000000000
--- a/changelogs/unreleased/40997-gitlab-pages-deploy-jobs-have-a-null-status.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix empty labels of CI builds for gitlab-pages on pipeline page
-merge_request: 24451
-author:
-type: fixed
diff --git a/changelogs/unreleased/42769-remove-expansion-hover-animation-from-status-icon-buttons.yml b/changelogs/unreleased/42769-remove-expansion-hover-animation-from-status-icon-buttons.yml
deleted file mode 100644
index 5a4ff8b3358..00000000000
--- a/changelogs/unreleased/42769-remove-expansion-hover-animation-from-status-icon-buttons.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove expansion hover animation from pipeline status icon buttons
-merge_request: 24268
-author: Nathan Friend
-type: changed
diff --git a/changelogs/unreleased/43297-authorized-application-count.yml b/changelogs/unreleased/43297-authorized-application-count.yml
new file mode 100644
index 00000000000..d22e155fb14
--- /dev/null
+++ b/changelogs/unreleased/43297-authorized-application-count.yml
@@ -0,0 +1,5 @@
+---
+title: Fix authorized application count
+merge_request: 25715
+author: moyuru
+type: fixed
diff --git a/changelogs/unreleased/43681-display-last-activity-and-created-at-datetimes-for-users-in-admin-users.yml b/changelogs/unreleased/43681-display-last-activity-and-created-at-datetimes-for-users-in-admin-users.yml
deleted file mode 100644
index 0fbf6314a27..00000000000
--- a/changelogs/unreleased/43681-display-last-activity-and-created-at-datetimes-for-users-in-admin-users.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display last activity and created at datetimes for users
-merge_request: 24181
-author:
-type: added
diff --git a/changelogs/unreleased/44332-add-openid-profile-scopes.yml b/changelogs/unreleased/44332-add-openid-profile-scopes.yml
deleted file mode 100644
index b554fab5139..00000000000
--- a/changelogs/unreleased/44332-add-openid-profile-scopes.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: GitLab now supports the profile and email scopes from OpenID Connect
-merge_request: 24335
-author: Goten Xiao
-type: added
diff --git a/changelogs/unreleased/44698-recaptcha.yml b/changelogs/unreleased/44698-recaptcha.yml
deleted file mode 100644
index e1760a6c635..00000000000
--- a/changelogs/unreleased/44698-recaptcha.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent unload when Recaptcha is open
-merge_request: 24625
-author:
-type: fixed
diff --git a/changelogs/unreleased/44740-api-to-verify-a-given-user-has-right-to-merge-a-given-mergerequest.yml b/changelogs/unreleased/44740-api-to-verify-a-given-user-has-right-to-merge-a-given-mergerequest.yml
deleted file mode 100644
index 1c739130fcc..00000000000
--- a/changelogs/unreleased/44740-api-to-verify-a-given-user-has-right-to-merge-a-given-mergerequest.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Expose if the current user can merge a MR'
-merge_request: 25207
-author: Robert Schilling
-type: added
diff --git a/changelogs/unreleased/45779-fix-default-visibility-level-for-projects.yml b/changelogs/unreleased/45779-fix-default-visibility-level-for-projects.yml
deleted file mode 100644
index b4cba5041d1..00000000000
--- a/changelogs/unreleased/45779-fix-default-visibility-level-for-projects.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix default visibility_level for new projects
-merge_request: 24120
-author: Fabian Schneider @fabsrc
-type: fixed
diff --git a/changelogs/unreleased/45791-number-of-repositories-usage-ping.yml b/changelogs/unreleased/45791-number-of-repositories-usage-ping.yml
deleted file mode 100644
index 8d1f5df56ea..00000000000
--- a/changelogs/unreleased/45791-number-of-repositories-usage-ping.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add repositories count to usage ping data
-merge_request: 24823
-author:
-type: added
diff --git a/changelogs/unreleased/46448-add-timestamps-for-each-stage-of-gitlab-rake-gitlab-backup-restore.yml b/changelogs/unreleased/46448-add-timestamps-for-each-stage-of-gitlab-rake-gitlab-backup-restore.yml
deleted file mode 100644
index 4ce6787570a..00000000000
--- a/changelogs/unreleased/46448-add-timestamps-for-each-stage-of-gitlab-rake-gitlab-backup-restore.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display timestamps to messages printed by gitlab:backup:restore rake tasks
-merge_request:
-author: Will Chandler
-type: changed
diff --git a/changelogs/unreleased/46787-create-project-label-window-is-cut-off-at-the-bottom.yml b/changelogs/unreleased/46787-create-project-label-window-is-cut-off-at-the-bottom.yml
new file mode 100644
index 00000000000..dca1d57d14e
--- /dev/null
+++ b/changelogs/unreleased/46787-create-project-label-window-is-cut-off-at-the-bottom.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed - Create project label window is cut off at the bottom
+merge_request: 26049
+author:
+type: fixed
diff --git a/changelogs/unreleased/47007-related-merge-requests-in-issue-design-restyle.yml b/changelogs/unreleased/47007-related-merge-requests-in-issue-design-restyle.yml
deleted file mode 100644
index 28e2a4cc377..00000000000
--- a/changelogs/unreleased/47007-related-merge-requests-in-issue-design-restyle.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redesigned related merge requests in issue page.
-merge_request: 24270
-author:
-type: changed
diff --git a/changelogs/unreleased/47150-update-sshkey.yml b/changelogs/unreleased/47150-update-sshkey.yml
deleted file mode 100644
index 342bdb1e2bc..00000000000
--- a/changelogs/unreleased/47150-update-sshkey.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix validation of certain ed25519 keys
-merge_request: 25115
-author: Merlijn B. W. Wajer
-type: fixed
diff --git a/changelogs/unreleased/47988-improve-milestone-queries-with-subqueries.yml b/changelogs/unreleased/47988-improve-milestone-queries-with-subqueries.yml
deleted file mode 100644
index d1a80ab43cf..00000000000
--- a/changelogs/unreleased/47988-improve-milestone-queries-with-subqueries.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve milestone queries using subqueries instead of separate queries for ids
-merge_request: 24325
-author:
-type: performance
diff --git a/changelogs/unreleased/48132-display-output-from-pre-receive-scripts.yml b/changelogs/unreleased/48132-display-output-from-pre-receive-scripts.yml
new file mode 100644
index 00000000000..e06a4d5ee75
--- /dev/null
+++ b/changelogs/unreleased/48132-display-output-from-pre-receive-scripts.yml
@@ -0,0 +1,5 @@
+---
+title: "Allow failed custom hook script errors to safely appear in GitLab UI by filtering error messages by the prefix GL-HOOK-ERR:"
+merge_request: 25625
+author:
+type: changed
diff --git a/changelogs/unreleased/48297-fix-code-selection.yml b/changelogs/unreleased/48297-fix-code-selection.yml
new file mode 100644
index 00000000000..14841b00969
--- /dev/null
+++ b/changelogs/unreleased/48297-fix-code-selection.yml
@@ -0,0 +1,6 @@
+---
+title: Resolve Code in other column of side-by-side diff is highlighted when selecting
+ code on one side
+merge_request: 26423
+author:
+type: fixed
diff --git a/changelogs/unreleased/49502-gpg-signature-api-endpoint.yml b/changelogs/unreleased/49502-gpg-signature-api-endpoint.yml
deleted file mode 100644
index 8393cb9d282..00000000000
--- a/changelogs/unreleased/49502-gpg-signature-api-endpoint.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add API endpoint to get a commit's GPG signature
-merge_request: 25032
-author:
-type: added
diff --git a/changelogs/unreleased/49856-upgrade-bootstrap-form-gem.yml b/changelogs/unreleased/49856-upgrade-bootstrap-form-gem.yml
new file mode 100644
index 00000000000..1dc8d5b4179
--- /dev/null
+++ b/changelogs/unreleased/49856-upgrade-bootstrap-form-gem.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade bootstrap_form Gem
+merge_request: 26568
+author:
+type: other
diff --git a/changelogs/unreleased/49863-ingress-ip-loading-state.yml b/changelogs/unreleased/49863-ingress-ip-loading-state.yml
new file mode 100644
index 00000000000..51bb27d3153
--- /dev/null
+++ b/changelogs/unreleased/49863-ingress-ip-loading-state.yml
@@ -0,0 +1,5 @@
+---
+title: Show loading spinner while Ingress/Knative IP is being assigned
+merge_request: 25912
+author:
+type: changed
diff --git a/changelogs/unreleased/49910-reopening-a-closed-milestone-from-the-closed-milestones-page-fails2.yml b/changelogs/unreleased/49910-reopening-a-closed-milestone-from-the-closed-milestones-page-fails2.yml
new file mode 100644
index 00000000000..68d38cd56c5
--- /dev/null
+++ b/changelogs/unreleased/49910-reopening-a-closed-milestone-from-the-closed-milestones-page-fails2.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug when reopening milestone from index page
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/50006-expose-textcolor-from-public-labels-api.yml b/changelogs/unreleased/50006-expose-textcolor-from-public-labels-api.yml
deleted file mode 100644
index 3c8b58f3001..00000000000
--- a/changelogs/unreleased/50006-expose-textcolor-from-public-labels-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Expose text_color for project and group labels'
-merge_request: 25172
-author: Robert Schilling
-type: added
diff --git a/changelogs/unreleased/50013-add-browser-platform-flags.yml b/changelogs/unreleased/50013-add-browser-platform-flags.yml
deleted file mode 100644
index 6176b8b64a7..00000000000
--- a/changelogs/unreleased/50013-add-browser-platform-flags.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add CSS & JS global flags to represent browser and platform
-merge_request: 24017
-author:
-type: other
diff --git a/changelogs/unreleased/50352-sort-save.yml b/changelogs/unreleased/50352-sort-save.yml
deleted file mode 100644
index cd046c8b785..00000000000
--- a/changelogs/unreleased/50352-sort-save.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Save issues/merge request sorting options to backend
-merge_request: 24198
-author:
-type: added
diff --git a/changelogs/unreleased/50433-make-emoji-picker-bigger.yml b/changelogs/unreleased/50433-make-emoji-picker-bigger.yml
deleted file mode 100644
index 8fcf41df09d..00000000000
--- a/changelogs/unreleased/50433-make-emoji-picker-bigger.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make emoji picker bigger
-merge_request: 25187
-author: Jacopo Beschi @jacopo-beschi
-type: changed
diff --git a/changelogs/unreleased/50521-block-emojis-and-symbol-characters-from-user-s-full-names-2.yml b/changelogs/unreleased/50521-block-emojis-and-symbol-characters-from-user-s-full-names-2.yml
deleted file mode 100644
index 04caf8262c6..00000000000
--- a/changelogs/unreleased/50521-block-emojis-and-symbol-characters-from-user-s-full-names-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Block emojis and symbol characters from users full names
-merge_request: 24523
-author:
-type: other
diff --git a/changelogs/unreleased/51754-admin-view-private-personal-snippets.yml b/changelogs/unreleased/51754-admin-view-private-personal-snippets.yml
deleted file mode 100644
index cf3d73fce0c..00000000000
--- a/changelogs/unreleased/51754-admin-view-private-personal-snippets.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow users with full private access to read private personal snippets.
-merge_request: 24560
-author:
-type: fixed
diff --git a/changelogs/unreleased/51759-filter-by-language.yml b/changelogs/unreleased/51759-filter-by-language.yml
deleted file mode 100644
index 6b5bedd6b2d..00000000000
--- a/changelogs/unreleased/51759-filter-by-language.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add `with_programming_language` filter for projects to API
-merge_request: 24377
-author: Dylan MacKenzie
-type: added
diff --git a/changelogs/unreleased/51913-api-getting-projects-for-users-with-dot-gets-404.yml b/changelogs/unreleased/51913-api-getting-projects-for-users-with-dot-gets-404.yml
deleted file mode 100644
index 9d72efdd52a..00000000000
--- a/changelogs/unreleased/51913-api-getting-projects-for-users-with-dot-gets-404.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Support username with dots'
-merge_request: 24395
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/51988-install-group-runner-on-group-cluster.yml b/changelogs/unreleased/51988-install-group-runner-on-group-cluster.yml
new file mode 100644
index 00000000000..86f08dd1798
--- /dev/null
+++ b/changelogs/unreleased/51988-install-group-runner-on-group-cluster.yml
@@ -0,0 +1,5 @@
+---
+title: Support installing Group runner on group-level cluster
+merge_request: 26260
+author:
+type: added
diff --git a/changelogs/unreleased/52275-fix-master-to-be-hyperlink.yml b/changelogs/unreleased/52275-fix-master-to-be-hyperlink.yml
deleted file mode 100644
index c1cde0ceff6..00000000000
--- a/changelogs/unreleased/52275-fix-master-to-be-hyperlink.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve In Merge Request diff screen, master is not a hyperlink
-merge_request: 23874
-author:
-type: fixed
diff --git a/changelogs/unreleased/52278-squash-checkbox-fix.yml b/changelogs/unreleased/52278-squash-checkbox-fix.yml
deleted file mode 100644
index c81748ae419..00000000000
--- a/changelogs/unreleased/52278-squash-checkbox-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve When merging an MR, the squash checkbox isnt always supported
-merge_request: 24296
-author:
-type: fixed
diff --git a/changelogs/unreleased/52347-lines-changed-statistics-is-not-easily-visible-in-mr-changes-view.yml b/changelogs/unreleased/52347-lines-changed-statistics-is-not-easily-visible-in-mr-changes-view.yml
deleted file mode 100644
index cf1c4378f18..00000000000
--- a/changelogs/unreleased/52347-lines-changed-statistics-is-not-easily-visible-in-mr-changes-view.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show MR statistics in diff comparisons
-merge_request: !24569
-author:
-type: changed
diff --git a/changelogs/unreleased/52363-modifies-environment-scope-field-on-cluster-page.yml b/changelogs/unreleased/52363-modifies-environment-scope-field-on-cluster-page.yml
deleted file mode 100644
index 07cb35e6529..00000000000
--- a/changelogs/unreleased/52363-modifies-environment-scope-field-on-cluster-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Modifies environment scope UI on cluster page
-merge_request: 24376
-author:
-type: other
diff --git a/changelogs/unreleased/52363-ui-changes-to-cluster-and-ado-pages.yml b/changelogs/unreleased/52363-ui-changes-to-cluster-and-ado-pages.yml
deleted file mode 100644
index eb4851971fb..00000000000
--- a/changelogs/unreleased/52363-ui-changes-to-cluster-and-ado-pages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Moves domain setting from Auto DevOps to Cluster's page
-merge_request: 24580
-author:
-type: added
diff --git a/changelogs/unreleased/52366-improved-group-lists-ui-spinners.yml b/changelogs/unreleased/52366-improved-group-lists-ui-spinners.yml
new file mode 100644
index 00000000000..ab09272eaf4
--- /dev/null
+++ b/changelogs/unreleased/52366-improved-group-lists-ui-spinners.yml
@@ -0,0 +1,5 @@
+---
+title: Update spinners in group list component
+merge_request: 26572
+author:
+type: changed
diff --git a/changelogs/unreleased/52424-goodbye-hipchat.yml b/changelogs/unreleased/52424-goodbye-hipchat.yml
deleted file mode 100644
index 26dc904af5f..00000000000
--- a/changelogs/unreleased/52424-goodbye-hipchat.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove HipChat integration from GitLab
-merge_request: 22223
-author:
-type: removed
diff --git a/changelogs/unreleased/52447-auto-devops-at-group-level.yml b/changelogs/unreleased/52447-auto-devops-at-group-level.yml
new file mode 100644
index 00000000000..0a21c6a2b7b
--- /dev/null
+++ b/changelogs/unreleased/52447-auto-devops-at-group-level.yml
@@ -0,0 +1,5 @@
+---
+title: Enable/disable Auto DevOps at the Group level
+merge_request: 25533
+author:
+type: added
diff --git a/changelogs/unreleased/52568-external-mr-diffs.yml b/changelogs/unreleased/52568-external-mr-diffs.yml
deleted file mode 100644
index b1c9d5cb809..00000000000
--- a/changelogs/unreleased/52568-external-mr-diffs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow merge request diffs to be placed into an object store
-merge_request: 24276
-author:
-type: added
diff --git a/changelogs/unreleased/52674-api-v4-projects-project_id-jobs-endpoint-hits-statement-timeout.yml b/changelogs/unreleased/52674-api-v4-projects-project_id-jobs-endpoint-hits-statement-timeout.yml
deleted file mode 100644
index f79078c1fd9..00000000000
--- a/changelogs/unreleased/52674-api-v4-projects-project_id-jobs-endpoint-hits-statement-timeout.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: "[API] Omit `X-Total` and `X-Total-Pages` headers when items count is more than 10,000"
-merge_request: 23931
-author:
-type: performance
diff --git a/changelogs/unreleased/52971-merge-request-file-browser-should-always-be-possible-show-hide.yml b/changelogs/unreleased/52971-merge-request-file-browser-should-always-be-possible-show-hide.yml
deleted file mode 100644
index b661c55957d..00000000000
--- a/changelogs/unreleased/52971-merge-request-file-browser-should-always-be-possible-show-hide.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make possible to toggle file tree while scrolling through diffs
-merge_request: !24103
-author:
-type: changed
diff --git a/changelogs/unreleased/53104-redesign-group-overview-ui-mvc.yml b/changelogs/unreleased/53104-redesign-group-overview-ui-mvc.yml
deleted file mode 100644
index cb810b7ac7f..00000000000
--- a/changelogs/unreleased/53104-redesign-group-overview-ui-mvc.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Refresh group overview to match project overview
-merge_request: 23866
-author:
-type: changed
diff --git a/changelogs/unreleased/53139-hide-tree-single-file.yml b/changelogs/unreleased/53139-hide-tree-single-file.yml
new file mode 100644
index 00000000000..17fe957e42e
--- /dev/null
+++ b/changelogs/unreleased/53139-hide-tree-single-file.yml
@@ -0,0 +1,5 @@
+---
+title: collapse file tree by default if the merge request changes only one file
+merge_request:
+author: Riccardo Padovani <riccardo@rpadovani.com>
+type: changed
diff --git a/changelogs/unreleased/53431-fix-upcoming-milestone-filter-for-groups.yml b/changelogs/unreleased/53431-fix-upcoming-milestone-filter-for-groups.yml
deleted file mode 100644
index 1e9c7f3913c..00000000000
--- a/changelogs/unreleased/53431-fix-upcoming-milestone-filter-for-groups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix upcoming milestones filter not including group milestones
-merge_request: 23098
-author: Heinrich Lee Yu
-type: fixed
diff --git a/changelogs/unreleased/53671-redirect-projects-id-to-project-page.yml b/changelogs/unreleased/53671-redirect-projects-id-to-project-page.yml
deleted file mode 100644
index 08c5ded05d5..00000000000
--- a/changelogs/unreleased/53671-redirect-projects-id-to-project-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redirect GET projects/:id to project page
-merge_request: 24467
-author:
-type: added
diff --git a/changelogs/unreleased/53676-ip-address-of-gitlab-runner-is-wrong-in-the-runners-description.yml b/changelogs/unreleased/53676-ip-address-of-gitlab-runner-is-wrong-in-the-runners-description.yml
deleted file mode 100644
index 12a6509e6f7..00000000000
--- a/changelogs/unreleased/53676-ip-address-of-gitlab-runner-is-wrong-in-the-runners-description.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Get remote IP address of runner
-merge_request: 24624
-author:
-type: changed
diff --git a/changelogs/unreleased/53714-inconsistent-text-color-for-labels.yml b/changelogs/unreleased/53714-inconsistent-text-color-for-labels.yml
deleted file mode 100644
index d804e2df2cd..00000000000
--- a/changelogs/unreleased/53714-inconsistent-text-color-for-labels.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix foreground color for labels to ensure consistency of label appearance
-merge_request: 23873
-author: Nathan Friend
-type: fixed
diff --git a/changelogs/unreleased/53856-changing-group-visibility-does-not-re-enable-save-button.yml b/changelogs/unreleased/53856-changing-group-visibility-does-not-re-enable-save-button.yml
deleted file mode 100644
index 1daa72fb9c4..00000000000
--- a/changelogs/unreleased/53856-changing-group-visibility-does-not-re-enable-save-button.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix suboptimal handling of checkbox and radio input events causing
- group general settings submit button to stay disabled after changing its visibility
-merge_request: 23022
-author:
-type: fixed
diff --git a/changelogs/unreleased/53950-commit-comments-displayed-on-a-merge-request.yml b/changelogs/unreleased/53950-commit-comments-displayed-on-a-merge-request.yml
deleted file mode 100644
index adaaed7f1aa..00000000000
--- a/changelogs/unreleased/53950-commit-comments-displayed-on-a-merge-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display "commented" only for commit discussions on merge requests
-merge_request: 24427
-author:
-type: changed
diff --git a/changelogs/unreleased/54167-rename-project-tags-to-project-topics.yml b/changelogs/unreleased/54167-rename-project-tags-to-project-topics.yml
deleted file mode 100644
index 6fc8aa1a195..00000000000
--- a/changelogs/unreleased/54167-rename-project-tags-to-project-topics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Rename project tags to project topics
-merge_request: 24219
-author:
-type: other
diff --git a/changelogs/unreleased/54213-standardize-token-value-capitalization-in-filter-bar.yml b/changelogs/unreleased/54213-standardize-token-value-capitalization-in-filter-bar.yml
deleted file mode 100644
index 37dea77b8d2..00000000000
--- a/changelogs/unreleased/54213-standardize-token-value-capitalization-in-filter-bar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Standardize filter value capitlization in filter bar in both issues and boards pages
-merge_request: 23846
-author: obahareth
-type: changed
diff --git a/changelogs/unreleased/54250-upstream-kubeclient-redirect-patch.yml b/changelogs/unreleased/54250-upstream-kubeclient-redirect-patch.yml
deleted file mode 100644
index d1bdbccb20a..00000000000
--- a/changelogs/unreleased/54250-upstream-kubeclient-redirect-patch.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade kubeclient to 4.2.2 and swap out monkey-patch to disallow redirects
-merge_request: 24284
-author:
-type: other
diff --git a/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml b/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml
deleted file mode 100644
index 4d543db567d..00000000000
--- a/changelogs/unreleased/54484-anchor-links-to-comments-or-system-notes-can-break-with-discussion-filters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensured links to a comment or system note anchor resolves to the right note if a user has a discussion filter.
-merge_request: 24228
-author:
-type: changed
diff --git a/changelogs/unreleased/54544-update-project-topics-styling-to-use-badges-design.yml b/changelogs/unreleased/54544-update-project-topics-styling-to-use-badges-design.yml
deleted file mode 100644
index de12c66e9ef..00000000000
--- a/changelogs/unreleased/54544-update-project-topics-styling-to-use-badges-design.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update project topics styling to use badges design
-merge_request: 24415
-author:
-type: changed
diff --git a/changelogs/unreleased/54725-fix-emoji-button-active-state.yml b/changelogs/unreleased/54725-fix-emoji-button-active-state.yml
deleted file mode 100644
index 4f0a436cc87..00000000000
--- a/changelogs/unreleased/54725-fix-emoji-button-active-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix hover and active state colors of award emoji button
-merge_request: 25295
-author:
-type: fixed
diff --git a/changelogs/unreleased/54796-api-sort-tie-breaker-for-pagination.yml b/changelogs/unreleased/54796-api-sort-tie-breaker-for-pagination.yml
deleted file mode 100644
index 92b27f63f82..00000000000
--- a/changelogs/unreleased/54796-api-sort-tie-breaker-for-pagination.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Sort tie breaker with id DESC'
-merge_request: 25311
-author: Nermin Vehabovic
-type: changed
diff --git a/changelogs/unreleased/54850-pages-domain-show-view-is-not-protected-by-access-control.yml b/changelogs/unreleased/54850-pages-domain-show-view-is-not-protected-by-access-control.yml
deleted file mode 100644
index 41761213d7b..00000000000
--- a/changelogs/unreleased/54850-pages-domain-show-view-is-not-protected-by-access-control.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Require maintainer access to show pages domain settings
-merge_request: 24926
-author:
-type: fixed
diff --git a/changelogs/unreleased/54905-milestone-search.yml b/changelogs/unreleased/54905-milestone-search.yml
deleted file mode 100644
index 88717242e7c..00000000000
--- a/changelogs/unreleased/54905-milestone-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds milestone search
-merge_request: 24265
-author: Jacopo Beschi @jacopo-beschi
-type: added
diff --git a/changelogs/unreleased/54916-extended-tooltip-for-merge-request-links.yml b/changelogs/unreleased/54916-extended-tooltip-for-merge-request-links.yml
new file mode 100644
index 00000000000..7fd0bcd1c00
--- /dev/null
+++ b/changelogs/unreleased/54916-extended-tooltip-for-merge-request-links.yml
@@ -0,0 +1,5 @@
+---
+title: Add extended merge request tooltip
+merge_request: !25221
+author:
+type: added
diff --git a/changelogs/unreleased/55098-ui-bug-adding-group-members-with-lower-permissions.yml b/changelogs/unreleased/55098-ui-bug-adding-group-members-with-lower-permissions.yml
deleted file mode 100644
index f22524ef4b2..00000000000
--- a/changelogs/unreleased/55098-ui-bug-adding-group-members-with-lower-permissions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve UI bug adding group members with lower permissions
-merge_request: 24820
-author:
-type: fixed
diff --git a/changelogs/unreleased/55109-jira-integration-api-doesn-t-respect-available-format.yml b/changelogs/unreleased/55109-jira-integration-api-doesn-t-respect-available-format.yml
deleted file mode 100644
index c58cdc19555..00000000000
--- a/changelogs/unreleased/55109-jira-integration-api-doesn-t-respect-available-format.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Support Jira transition ID as string'
-merge_request: 24400
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml b/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml
deleted file mode 100644
index b609fc2d60b..00000000000
--- a/changelogs/unreleased/55111-gitlab-api-does-not-manage-default_branch_protection-3-value.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Fix default_branch_protection admin setting'
-merge_request: 24398
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/55209-tool-tip-hides-menu-item.yml b/changelogs/unreleased/55209-tool-tip-hides-menu-item.yml
deleted file mode 100644
index 44ea4141632..00000000000
--- a/changelogs/unreleased/55209-tool-tip-hides-menu-item.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Close More Actions tooltip when menu opens
-merge_request: 24285
-author:
-type: fixed
diff --git a/changelogs/unreleased/55242-skeleton-loading-releases.yml b/changelogs/unreleased/55242-skeleton-loading-releases.yml
deleted file mode 100644
index 43cda64ce04..00000000000
--- a/changelogs/unreleased/55242-skeleton-loading-releases.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds skeleton loading to releases page
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/55312-svg.yml b/changelogs/unreleased/55312-svg.yml
deleted file mode 100644
index a6260aeaf2a..00000000000
--- a/changelogs/unreleased/55312-svg.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use export-import svgs from gitlab-svgs
-merge_request: 24954
-author:
-type: other
diff --git a/changelogs/unreleased/55376-related_merge_requests-api-call-returns-merge-requests-that-are-not-related-to-the-issue.yml b/changelogs/unreleased/55376-related_merge_requests-api-call-returns-merge-requests-that-are-not-related-to-the-issue.yml
deleted file mode 100644
index d2f24d6f499..00000000000
--- a/changelogs/unreleased/55376-related_merge_requests-api-call-returns-merge-requests-that-are-not-related-to-the-issue.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Ensure that related merge requests are referenced cross-project'
-merge_request: 25222
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/55447-validate-k8s-ca-cert.yml b/changelogs/unreleased/55447-validate-k8s-ca-cert.yml
deleted file mode 100644
index e0448d403da..00000000000
--- a/changelogs/unreleased/55447-validate-k8s-ca-cert.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Validate kubernetes cluster CA certificate
-merge_request: 24990
-author:
-type: changed
diff --git a/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml b/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml
deleted file mode 100644
index 724de733b7c..00000000000
--- a/changelogs/unreleased/55495-teamcity-use-revision-in-query.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Build number does not need to be tweaked anymore for the TeamCity integration to work properly.
-merge_request: 23898
-author:
-type: changed
diff --git a/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml b/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml
deleted file mode 100644
index 071036cd568..00000000000
--- a/changelogs/unreleased/55628-artifacts-from-a-job-defined-after-a-parallel-job-are-not-downloaded.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Handle regular job dependencies next to parallelized job dependencies.
-merge_request: 24273
-author:
-type: fixed
diff --git a/changelogs/unreleased/55820-adds-common-name-chart-value.yml b/changelogs/unreleased/55820-adds-common-name-chart-value.yml
deleted file mode 100644
index 1871abbfc6b..00000000000
--- a/changelogs/unreleased/55820-adds-common-name-chart-value.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensure Cert Manager works with Auto DevOps URLs greater than 64 bytes
-merge_request: 24683
-author:
-type: fixed
diff --git a/changelogs/unreleased/55884-adjust-emoji-and-cancel-buttons-height-in-user-status-modal-when-emoji-is-changed.yml b/changelogs/unreleased/55884-adjust-emoji-and-cancel-buttons-height-in-user-status-modal-when-emoji-is-changed.yml
deleted file mode 100644
index 2fbf334f5e9..00000000000
--- a/changelogs/unreleased/55884-adjust-emoji-and-cancel-buttons-height-in-user-status-modal-when-emoji-is-changed.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Emoji and cancel button are taller than input in set user status modal
-merge_request: 24173
-author: Dhiraj Bodicherla
-type: fixed
diff --git a/changelogs/unreleased/55893-artifacts-download.yml b/changelogs/unreleased/55893-artifacts-download.yml
deleted file mode 100644
index 30c118b7094..00000000000
--- a/changelogs/unreleased/55893-artifacts-download.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes not working dropdowns in pipelines page
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/55945-suggested-change-preview-highlight.yml b/changelogs/unreleased/55945-suggested-change-preview-highlight.yml
deleted file mode 100644
index 997290a5d50..00000000000
--- a/changelogs/unreleased/55945-suggested-change-preview-highlight.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix syntax highlighting for suggested changes preview
-merge_request: 24358
-author:
-type: fixed
diff --git a/changelogs/unreleased/55966-when-ref-is-ambiguous-createpipelineservice-raises-an-error.yml b/changelogs/unreleased/55966-when-ref-is-ambiguous-createpipelineservice-raises-an-error.yml
deleted file mode 100644
index 01a162944d3..00000000000
--- a/changelogs/unreleased/55966-when-ref-is-ambiguous-createpipelineservice-raises-an-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent checking protected_ref? for ambiguous refs.
-merge_request: 24437
-author:
-type: fixed
diff --git a/changelogs/unreleased/56010-user-profile-page-horizonal-whitespace-between-overview-columns-breaks-two-column-layout.yml b/changelogs/unreleased/56010-user-profile-page-horizonal-whitespace-between-overview-columns-breaks-two-column-layout.yml
deleted file mode 100644
index 407346bbf22..00000000000
--- a/changelogs/unreleased/56010-user-profile-page-horizonal-whitespace-between-overview-columns-breaks-two-column-layout.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove horizontal whitespace on user profile overview on small breakpoints
-merge_request: 24189
-author:
-type: other
diff --git a/changelogs/unreleased/56014-api-merge-request-squash-commit-messages.yml b/changelogs/unreleased/56014-api-merge-request-squash-commit-messages.yml
deleted file mode 100644
index e324baa94a3..00000000000
--- a/changelogs/unreleased/56014-api-merge-request-squash-commit-messages.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: API allows setting the squash commit message when squashing a merge request
-merge_request: 24784
-author:
-type: added
diff --git a/changelogs/unreleased/56015-remove-remote-timeout.yml b/changelogs/unreleased/56015-remove-remote-timeout.yml
new file mode 100644
index 00000000000..9b40ada5291
--- /dev/null
+++ b/changelogs/unreleased/56015-remove-remote-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Fix removing remote mirror failure which leaves unnecessary refs behind
+merge_request: 26213
+author:
+type: fixed
diff --git a/changelogs/unreleased/56019-archived-stuck.yml b/changelogs/unreleased/56019-archived-stuck.yml
deleted file mode 100644
index de3698a327b..00000000000
--- a/changelogs/unreleased/56019-archived-stuck.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes z-index and margins of archived alert in job page
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml b/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml
deleted file mode 100644
index ff9d4f2c175..00000000000
--- a/changelogs/unreleased/56036-fix-translation-of-in-in-job-details-sidebar.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-title: Remove multilingual translation from the word "in" in the job details sidebar.
-merge_request: 24192
-author: Nathan Friend
-type: changed
diff --git a/changelogs/unreleased/56089-merge-gitlab-keys.yml b/changelogs/unreleased/56089-merge-gitlab-keys.yml
new file mode 100644
index 00000000000..5e2cafd3254
--- /dev/null
+++ b/changelogs/unreleased/56089-merge-gitlab-keys.yml
@@ -0,0 +1,5 @@
+---
+title: Merge the gitlab-shell "gitlab-keys" functionality into GitLab CE
+merge_request: 25598
+author:
+type: other
diff --git a/changelogs/unreleased/56110-cluster-kubernetes-api-500-error-on-post-request.yml b/changelogs/unreleased/56110-cluster-kubernetes-api-500-error-on-post-request.yml
deleted file mode 100644
index 4da14114225..00000000000
--- a/changelogs/unreleased/56110-cluster-kubernetes-api-500-error-on-post-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improves restriction of multiple Kubernetes clusters through API
-merge_request: 24251
-author:
-type: fixed
diff --git a/changelogs/unreleased/56172-docs-fix-add-include-to-ci-param-list.yml b/changelogs/unreleased/56172-docs-fix-add-include-to-ci-param-list.yml
deleted file mode 100644
index 92592290ac4..00000000000
--- a/changelogs/unreleased/56172-docs-fix-add-include-to-ci-param-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update CI YAML param table with include
-merge_request: !24309
-author:
-type: fixed
diff --git a/changelogs/unreleased/56237-api-truncated-commit-title.yml b/changelogs/unreleased/56237-api-truncated-commit-title.yml
deleted file mode 100644
index 1a48d0fda1b..00000000000
--- a/changelogs/unreleased/56237-api-truncated-commit-title.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Expose full commit title'
-merge_request: 25189
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/56332-exclude-public-group-milestones-from-count.yml b/changelogs/unreleased/56332-exclude-public-group-milestones-from-count.yml
deleted file mode 100644
index 50ca9c94173..00000000000
--- a/changelogs/unreleased/56332-exclude-public-group-milestones-from-count.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix counts in milestones dashboard
-merge_request: 25230
-author:
-type: fixed
diff --git a/changelogs/unreleased/56334-runners-ipv6-address-overlaps-other-values.yml b/changelogs/unreleased/56334-runners-ipv6-address-overlaps-other-values.yml
deleted file mode 100644
index 8a6adef5dae..00000000000
--- a/changelogs/unreleased/56334-runners-ipv6-address-overlaps-other-values.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Runners IPv6 address overlaps other values
-merge_request: 24531
-author:
-type: fixed
diff --git a/changelogs/unreleased/56363-inconsitent-file-size-indication-across-different-ci-pages.yml b/changelogs/unreleased/56363-inconsitent-file-size-indication-across-different-ci-pages.yml
deleted file mode 100644
index 7c923422534..00000000000
--- a/changelogs/unreleased/56363-inconsitent-file-size-indication-across-different-ci-pages.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Show CI artifact file size with 3 significant digits on 'browse job artifacts'
- page
-merge_request: 24387
-author:
-type: fixed
diff --git a/changelogs/unreleased/56371-don-t-check-confidential-issues-for-spam.yml b/changelogs/unreleased/56371-don-t-check-confidential-issues-for-spam.yml
deleted file mode 100644
index fcfa29977d1..00000000000
--- a/changelogs/unreleased/56371-don-t-check-confidential-issues-for-spam.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Do not run spam checks on confidential issues
-merge_request: 24453
-author:
-type: fixed
diff --git a/changelogs/unreleased/56379-pipeline-stages-job-action-button-icon-is-not-aligned.yml b/changelogs/unreleased/56379-pipeline-stages-job-action-button-icon-is-not-aligned.yml
deleted file mode 100644
index ec8a1d9d6ea..00000000000
--- a/changelogs/unreleased/56379-pipeline-stages-job-action-button-icon-is-not-aligned.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Resolve Pipeline stages job action button icon is not aligned
-merge_request: 24577
-author:
-type: fixed
diff --git a/changelogs/unreleased/56389-remove-unwanted-suggestion-flash-margin.yml b/changelogs/unreleased/56389-remove-unwanted-suggestion-flash-margin.yml
deleted file mode 100644
index 3494feb9be1..00000000000
--- a/changelogs/unreleased/56389-remove-unwanted-suggestion-flash-margin.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove unwanted margin above suggested changes.
-merge_request: 24419
-author:
-type: fixed
diff --git a/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml b/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml
deleted file mode 100644
index 19ff408ddf4..00000000000
--- a/changelogs/unreleased/56398-fix-cluster-installation-loading-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix cluster installation processing spinner
-merge_request: 24814
-author:
-type: fixed
diff --git a/changelogs/unreleased/56417-update-helm-to-2-12-2.yml b/changelogs/unreleased/56417-update-helm-to-2-12-2.yml
deleted file mode 100644
index f01915c532f..00000000000
--- a/changelogs/unreleased/56417-update-helm-to-2-12-2.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Helm to 2.12.2 to address Helm client vulnerability
-merge_request: 24418
-author: Takuya Noguchi
-type: security
diff --git a/changelogs/unreleased/56485-implement-graphql-mergerequestsresolver.yml b/changelogs/unreleased/56485-implement-graphql-mergerequestsresolver.yml
deleted file mode 100644
index 5362ac65038..00000000000
--- a/changelogs/unreleased/56485-implement-graphql-mergerequestsresolver.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add field mergeRequests for project in GraphQL
-merge_request: 24805
-author:
-type: added
diff --git a/changelogs/unreleased/56507-sh-bump-katex-0.10.0.yml b/changelogs/unreleased/56507-sh-bump-katex-0.10.0.yml
deleted file mode 100644
index 671e204da21..00000000000
--- a/changelogs/unreleased/56507-sh-bump-katex-0.10.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade KaTeX to version 0.10.0
-merge_request: 24478
-author: Andrew Harmon
-type: fixed \ No newline at end of file
diff --git a/changelogs/unreleased/56543-project-lists-further-iteration-improvements.yml b/changelogs/unreleased/56543-project-lists-further-iteration-improvements.yml
deleted file mode 100644
index 388ff1d062a..00000000000
--- a/changelogs/unreleased/56543-project-lists-further-iteration-improvements.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Project list UI improvements
-merge_request: 24855
-author:
-type: other
diff --git a/changelogs/unreleased/56547-limit-sidekiq-logging-based-on-argument-size.yml b/changelogs/unreleased/56547-limit-sidekiq-logging-based-on-argument-size.yml
deleted file mode 100644
index 9ef274f3b49..00000000000
--- a/changelogs/unreleased/56547-limit-sidekiq-logging-based-on-argument-size.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Prevent Sidekiq arguments over 10 KB in size from being logged to JSON
-merge_request: 24493
-author:
-type: changed
diff --git a/changelogs/unreleased/56556-fix-markdown-table-border.yml b/changelogs/unreleased/56556-fix-markdown-table-border.yml
deleted file mode 100644
index 7724f49d4e9..00000000000
--- a/changelogs/unreleased/56556-fix-markdown-table-border.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix markdown table border.
-merge_request: 24601
-author:
-type: fixed
diff --git a/changelogs/unreleased/56622-admin-settings-cannot-read-property-addeventlistener-of-null.yml b/changelogs/unreleased/56622-admin-settings-cannot-read-property-addeventlistener-of-null.yml
deleted file mode 100644
index 52b2db0e999..00000000000
--- a/changelogs/unreleased/56622-admin-settings-cannot-read-property-addeventlistener-of-null.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Load initUserInternalRegexPlaceholder only when required
-merge_request: 24522
-author:
-type: fixed
diff --git a/changelogs/unreleased/56636-hashed-storage-afterrenameservice.yml b/changelogs/unreleased/56636-hashed-storage-afterrenameservice.yml
deleted file mode 100644
index 1f808850554..00000000000
--- a/changelogs/unreleased/56636-hashed-storage-afterrenameservice.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Hashed Storage: `AfterRenameService` was receiving the wrong `old_path` under some circunstances'
-merge_request: 24526
-author:
-type: fixed
diff --git a/changelogs/unreleased/56694-mark-group-level-labels-in-label-api-as-such.yml b/changelogs/unreleased/56694-mark-group-level-labels-in-label-api-as-such.yml
deleted file mode 100644
index ae2d9e18e0b..00000000000
--- a/changelogs/unreleased/56694-mark-group-level-labels-in-label-api-as-such.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Indicate if label is a project label'
-merge_request: 25219
-author: Robert Schilling
-type: added
diff --git a/changelogs/unreleased/56764-poor-ui-on-milestone-validation-error-page.yml b/changelogs/unreleased/56764-poor-ui-on-milestone-validation-error-page.yml
deleted file mode 100644
index 089ffd47321..00000000000
--- a/changelogs/unreleased/56764-poor-ui-on-milestone-validation-error-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix CSS grid on a new Project/Group Milestone
-merge_request: 24614
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml b/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml
deleted file mode 100644
index cc3a60479d3..00000000000
--- a/changelogs/unreleased/56787-realtime-validation-for-user-fullname-and-username.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add realtime validation for user fullname and username on validation
-merge_request: 25017
-author: Ehsan Abdulqader @EhsanZ
-type: added
diff --git a/changelogs/unreleased/56788-unicorn-metric-labels.yml b/changelogs/unreleased/56788-unicorn-metric-labels.yml
deleted file mode 100644
index 824c981780c..00000000000
--- a/changelogs/unreleased/56788-unicorn-metric-labels.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Clean up unicorn sampler metric labels
-merge_request: 24626
-author: bjk-gitlab
-type: fixed
diff --git a/changelogs/unreleased/56833-project-improve-empty-repository-state-ui-fe.yml b/changelogs/unreleased/56833-project-improve-empty-repository-state-ui-fe.yml
new file mode 100644
index 00000000000..19cf3d69db1
--- /dev/null
+++ b/changelogs/unreleased/56833-project-improve-empty-repository-state-ui-fe.yml
@@ -0,0 +1,5 @@
+---
+title: 'Project: Improve empty repository state UI'
+merge_request: 26024
+author:
+type: other
diff --git a/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml b/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml
deleted file mode 100644
index 5b9253793be..00000000000
--- a/changelogs/unreleased/56851-blank-values-in-reactive-cache.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow empty values such as [] to be stored in reactive cache
-merge_request: 25283
-author:
-type: fixed
diff --git a/changelogs/unreleased/56864-reopen-locked-mr.yml b/changelogs/unreleased/56864-reopen-locked-mr.yml
new file mode 100644
index 00000000000..d1d71531ac8
--- /dev/null
+++ b/changelogs/unreleased/56864-reopen-locked-mr.yml
@@ -0,0 +1,5 @@
+---
+title: Disallow reopening of a locked merge request
+merge_request: 24882
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/56937-edit-knative-domain-after-it-has-been-deployed.yml b/changelogs/unreleased/56937-edit-knative-domain-after-it-has-been-deployed.yml
deleted file mode 100644
index 11d93b34700..00000000000
--- a/changelogs/unreleased/56937-edit-knative-domain-after-it-has-been-deployed.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes functions finder for upgraded Knative app
-merge_request: 25067
-author:
-type: fixed
diff --git a/changelogs/unreleased/56938-diff-file-headers-on-compare-not-quite-right.yml b/changelogs/unreleased/56938-diff-file-headers-on-compare-not-quite-right.yml
deleted file mode 100644
index f619a009a63..00000000000
--- a/changelogs/unreleased/56938-diff-file-headers-on-compare-not-quite-right.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Correct spacing for comparison page
-merge_request: !24783
-author:
-type: fixed
diff --git a/changelogs/unreleased/56954-improve-knative-after-installing-tiller.yml b/changelogs/unreleased/56954-improve-knative-after-installing-tiller.yml
new file mode 100644
index 00000000000..b9fb27c3218
--- /dev/null
+++ b/changelogs/unreleased/56954-improve-knative-after-installing-tiller.yml
@@ -0,0 +1,5 @@
+---
+title: Improve the Knative installation on Clusters
+merge_request: 26339
+author:
+type: added
diff --git a/changelogs/unreleased/57063-implement-new-arguments-iid-for-issuesresolver-in-graphql.yml b/changelogs/unreleased/57063-implement-new-arguments-iid-for-issuesresolver-in-graphql.yml
deleted file mode 100644
index b05ab07e14c..00000000000
--- a/changelogs/unreleased/57063-implement-new-arguments-iid-for-issuesresolver-in-graphql.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add argument iids for issues in GraphQL
-merge_request: 24802
-author:
-type: added
diff --git a/changelogs/unreleased/57101-api-docs-for-hangouts-chat-service-incorrect.yml b/changelogs/unreleased/57101-api-docs-for-hangouts-chat-service-incorrect.yml
deleted file mode 100644
index 2e0ae9c3732..00000000000
--- a/changelogs/unreleased/57101-api-docs-for-hangouts-chat-service-incorrect.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Fix docs and parameters for hangouts-chat service'
-merge_request: 25180
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml b/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml
new file mode 100644
index 00000000000..2141c75ec72
--- /dev/null
+++ b/changelogs/unreleased/57115-just-in-time-k8s-resource-creation.yml
@@ -0,0 +1,5 @@
+---
+title: Create Kubernetes resources for projects when their deployment jobs run.
+merge_request: 25586
+author:
+type: changed
diff --git a/changelogs/unreleased/57160-merge-request-tabs-header-is-missing-bottom-border.yml b/changelogs/unreleased/57160-merge-request-tabs-header-is-missing-bottom-border.yml
deleted file mode 100644
index 3146d07db3d..00000000000
--- a/changelogs/unreleased/57160-merge-request-tabs-header-is-missing-bottom-border.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Return bottom border on MR Tabs
-merge_request: !25198
-author:
-type: fixed
diff --git a/changelogs/unreleased/57223-wiki-finder.yml b/changelogs/unreleased/57223-wiki-finder.yml
deleted file mode 100644
index 5ddf197568d..00000000000
--- a/changelogs/unreleased/57223-wiki-finder.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove BATCH_SIZE from WikiFileFinder
-merge_request: 24933
-author:
-type: other
diff --git a/changelogs/unreleased/57227-absolute-uri-missing-hierarchical-segment.yml b/changelogs/unreleased/57227-absolute-uri-missing-hierarchical-segment.yml
deleted file mode 100644
index 3a663ce2132..00000000000
--- a/changelogs/unreleased/57227-absolute-uri-missing-hierarchical-segment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix potential Addressable::URI::InvalidURIError
-merge_request: 24908
-author:
-type: fixed
diff --git a/changelogs/unreleased/57353-git-push-fails-on-large-lfs-files-where-the-push-take-a-long-time.yml b/changelogs/unreleased/57353-git-push-fails-on-large-lfs-files-where-the-push-take-a-long-time.yml
deleted file mode 100644
index 46f82afda62..00000000000
--- a/changelogs/unreleased/57353-git-push-fails-on-large-lfs-files-where-the-push-take-a-long-time.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Provide expires_in in LFS authentication payload
-merge_request: 25082
-author:
-type: fixed
diff --git a/changelogs/unreleased/57357-automate-base-domain-help-text.yml b/changelogs/unreleased/57357-automate-base-domain-help-text.yml
new file mode 100644
index 00000000000..fa1831b66ea
--- /dev/null
+++ b/changelogs/unreleased/57357-automate-base-domain-help-text.yml
@@ -0,0 +1,5 @@
+---
+title: Automate base domain help text on Clusters page
+merge_request: 26124
+author:
+type: changed
diff --git a/changelogs/unreleased/57409-loading-button-transition.yml b/changelogs/unreleased/57409-loading-button-transition.yml
new file mode 100644
index 00000000000..3cf169d79de
--- /dev/null
+++ b/changelogs/unreleased/57409-loading-button-transition.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent fade out transition on loading-button component.
+merge_request: 26428
+author:
+type: fixed
diff --git a/changelogs/unreleased/57410-api-create-release-link-with-ftp-address-return-400-bad-request.yml b/changelogs/unreleased/57410-api-create-release-link-with-ftp-address-return-400-bad-request.yml
deleted file mode 100644
index 6be6a2115b9..00000000000
--- a/changelogs/unreleased/57410-api-create-release-link-with-ftp-address-return-400-bad-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for FTP assets for releases
-merge_request: 25071
-author: Robert Schilling
-type: added
diff --git a/changelogs/unreleased/57540-filename-trailing-space.yml b/changelogs/unreleased/57540-filename-trailing-space.yml
new file mode 100644
index 00000000000..db85fb350db
--- /dev/null
+++ b/changelogs/unreleased/57540-filename-trailing-space.yml
@@ -0,0 +1,5 @@
+---
+title: Implemented whitespace-trimming for file names in Web IDE
+merge_request: 26270
+author:
+type: fixed
diff --git a/changelogs/unreleased/57544-web-ide-new-directory-dialog-shows-file-templates.yml b/changelogs/unreleased/57544-web-ide-new-directory-dialog-shows-file-templates.yml
deleted file mode 100644
index 9d9158ca4af..00000000000
--- a/changelogs/unreleased/57544-web-ide-new-directory-dialog-shows-file-templates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Do not show file templates when creating a new directory in WebIDE
-merge_request: !25119
-author:
-type: fixed
diff --git a/changelogs/unreleased/57564-contributing-button-border.yml b/changelogs/unreleased/57564-contributing-button-border.yml
deleted file mode 100644
index e5875ef1c0f..00000000000
--- a/changelogs/unreleased/57564-contributing-button-border.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix the border style of CONTRIBUTING button when it exists
-merge_request: 25124
-author: Takuya Noguchi
-type: fixed
diff --git a/changelogs/unreleased/57579-gitlab-project-import-fails-sidekiq-undefined-method-import_jid.yml b/changelogs/unreleased/57579-gitlab-project-import-fails-sidekiq-undefined-method-import_jid.yml
deleted file mode 100644
index f7d6a6c4863..00000000000
--- a/changelogs/unreleased/57579-gitlab-project-import-fails-sidekiq-undefined-method-import_jid.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix import_jid error on project import
-merge_request: 25239
-author:
-type: fixed
diff --git a/changelogs/unreleased/57589-update-workhorse.yml b/changelogs/unreleased/57589-update-workhorse.yml
deleted file mode 100644
index 525913bba4c..00000000000
--- a/changelogs/unreleased/57589-update-workhorse.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Workhorse to v8.3.1
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/57648-make-emoji-picker-full-width-on-mobile.yml b/changelogs/unreleased/57648-make-emoji-picker-full-width-on-mobile.yml
new file mode 100644
index 00000000000..d92fd2a762e
--- /dev/null
+++ b/changelogs/unreleased/57648-make-emoji-picker-full-width-on-mobile.yml
@@ -0,0 +1,5 @@
+---
+title: Makes emoji picker full width on mobile.
+merge_request: 25883
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/57650-remove-tld-validation-from-cluster.yml b/changelogs/unreleased/57650-remove-tld-validation-from-cluster.yml
deleted file mode 100644
index 683b007a8a1..00000000000
--- a/changelogs/unreleased/57650-remove-tld-validation-from-cluster.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes incorrect TLD validation errors for Kubernetes cluster domain
-merge_request: 25262
-author:
-type: fixed
diff --git a/changelogs/unreleased/57655-fix-markdown-tables-border.yml b/changelogs/unreleased/57655-fix-markdown-tables-border.yml
new file mode 100644
index 00000000000..6a8ba8c4353
--- /dev/null
+++ b/changelogs/unreleased/57655-fix-markdown-tables-border.yml
@@ -0,0 +1,5 @@
+---
+title: Fix markdown table header and table content borders
+merge_request: 25666
+author:
+type: fixed
diff --git a/changelogs/unreleased/57671-fix_merge_request_base_pipeline.yml b/changelogs/unreleased/57671-fix_merge_request_base_pipeline.yml
deleted file mode 100644
index d89819eee60..00000000000
--- a/changelogs/unreleased/57671-fix_merge_request_base_pipeline.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Ensure the base pipeline of a Merge Request belongs to its target branch
-merge_request: 25226
-author:
-type: fixed
diff --git a/changelogs/unreleased/57768-remove-vertical-line.yml b/changelogs/unreleased/57768-remove-vertical-line.yml
deleted file mode 100644
index b73b0fa229e..00000000000
--- a/changelogs/unreleased/57768-remove-vertical-line.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove vertical connecting line placeholder from diff discussion notes
-merge_request: 25292
-author:
-type: fixed
diff --git a/changelogs/unreleased/57894-buttons-on-group-page-are-misaligned.yml b/changelogs/unreleased/57894-buttons-on-group-page-are-misaligned.yml
new file mode 100644
index 00000000000..ca0f529df6c
--- /dev/null
+++ b/changelogs/unreleased/57894-buttons-on-group-page-are-misaligned.yml
@@ -0,0 +1,5 @@
+---
+title: Fix misalignment of group overview page buttons
+merge_request: 26292
+author:
+type: fixed
diff --git a/changelogs/unreleased/57984-store-branch-name.yml b/changelogs/unreleased/57984-store-branch-name.yml
new file mode 100644
index 00000000000..26dfdb7a5d6
--- /dev/null
+++ b/changelogs/unreleased/57984-store-branch-name.yml
@@ -0,0 +1,5 @@
+---
+title: Resolves Branch name is lost if I change commit mode in Web IDE
+merge_request: 26180
+author:
+type: fixed
diff --git a/changelogs/unreleased/58208-explicitly-set-masterauth.yml b/changelogs/unreleased/58208-explicitly-set-masterauth.yml
new file mode 100644
index 00000000000..e3512d11113
--- /dev/null
+++ b/changelogs/unreleased/58208-explicitly-set-masterauth.yml
@@ -0,0 +1,6 @@
+---
+title: Explicitly set master_auth setting to enable basic auth and client certificate
+ for new GKE clusters
+merge_request: 26018
+author:
+type: other
diff --git a/changelogs/unreleased/58410-change-pixel-size-of-instance-header-footer-message-to-16px.yml b/changelogs/unreleased/58410-change-pixel-size-of-instance-header-footer-message-to-16px.yml
new file mode 100644
index 00000000000..3e494847e75
--- /dev/null
+++ b/changelogs/unreleased/58410-change-pixel-size-of-instance-header-footer-message-to-16px.yml
@@ -0,0 +1,5 @@
+---
+title: Reduce height of instance system header and footer
+merge_request: 25752
+author:
+type: changed
diff --git a/changelogs/unreleased/58482-update-airminc-clair-local-scan-to-2-0-6.yml b/changelogs/unreleased/58482-update-airminc-clair-local-scan-to-2-0-6.yml
new file mode 100644
index 00000000000..be9c38aba1e
--- /dev/null
+++ b/changelogs/unreleased/58482-update-airminc-clair-local-scan-to-2-0-6.yml
@@ -0,0 +1,5 @@
+---
+title: Update clair-local-scan to 2.0.6
+merge_request: 25743
+author: Takuya Noguchi
+type: added
diff --git a/changelogs/unreleased/58739-hashed-storage-prevent-a-migration-and-rollback-running-at-the-same-time.yml b/changelogs/unreleased/58739-hashed-storage-prevent-a-migration-and-rollback-running-at-the-same-time.yml
new file mode 100644
index 00000000000..765a991bb6a
--- /dev/null
+++ b/changelogs/unreleased/58739-hashed-storage-prevent-a-migration-and-rollback-running-at-the-same-time.yml
@@ -0,0 +1,5 @@
+---
+title: 'Hashed Storage: Prevent a migration and rollback running at the same time'
+merge_request: 25976
+author:
+type: changed
diff --git a/changelogs/unreleased/58781-silent-progress-in-auto-devops.yml b/changelogs/unreleased/58781-silent-progress-in-auto-devops.yml
new file mode 100644
index 00000000000..e45db8eafc3
--- /dev/null
+++ b/changelogs/unreleased/58781-silent-progress-in-auto-devops.yml
@@ -0,0 +1,5 @@
+---
+title: Use curl silent/show-error options on Auto DevOps
+merge_request: 25954
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/58789-some-system-notes-on-issuable-are-folded-on-mobile.yml b/changelogs/unreleased/58789-some-system-notes-on-issuable-are-folded-on-mobile.yml
new file mode 100644
index 00000000000..ebfb7aeaa1f
--- /dev/null
+++ b/changelogs/unreleased/58789-some-system-notes-on-issuable-are-folded-on-mobile.yml
@@ -0,0 +1,5 @@
+---
+title: Keep inline as much as possible in system notes on issuable
+merge_request: 25968
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/58797-broken-ui-on-a-closed-merge-request-from-a-deleted-source-project.yml b/changelogs/unreleased/58797-broken-ui-on-a-closed-merge-request-from-a-deleted-source-project.yml
new file mode 100644
index 00000000000..e30f48ed1a8
--- /dev/null
+++ b/changelogs/unreleased/58797-broken-ui-on-a-closed-merge-request-from-a-deleted-source-project.yml
@@ -0,0 +1,5 @@
+---
+title: Fix UI for closed MR when source project is removed
+merge_request: 25967
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/58805-allow-incomplete-commit-data-to-be-fetched-from-collection.yml b/changelogs/unreleased/58805-allow-incomplete-commit-data-to-be-fetched-from-collection.yml
new file mode 100644
index 00000000000..4377ebfdbdf
--- /dev/null
+++ b/changelogs/unreleased/58805-allow-incomplete-commit-data-to-be-fetched-from-collection.yml
@@ -0,0 +1,5 @@
+---
+title: Fix merge commits being used as default squash commit messages
+merge_request: 26445
+author:
+type: fixed
diff --git a/changelogs/unreleased/58883-fix-fetching-comments.yml b/changelogs/unreleased/58883-fix-fetching-comments.yml
new file mode 100644
index 00000000000..14c0f1687f2
--- /dev/null
+++ b/changelogs/unreleased/58883-fix-fetching-comments.yml
@@ -0,0 +1,5 @@
+---
+title: Fix error shown when loading links to specific comments
+merge_request: 26092
+author:
+type: fixed
diff --git a/changelogs/unreleased/58889-spinners-are-active-prematurely-in-bitbucket-cloud-import.yml b/changelogs/unreleased/58889-spinners-are-active-prematurely-in-bitbucket-cloud-import.yml
new file mode 100644
index 00000000000..ec357d9a832
--- /dev/null
+++ b/changelogs/unreleased/58889-spinners-are-active-prematurely-in-bitbucket-cloud-import.yml
@@ -0,0 +1,5 @@
+---
+title: Fix continuous bitbucket import loading spinner
+merge_request: 26175
+author:
+type: fixed
diff --git a/changelogs/unreleased/58933-broken-ui-on-commits-on-mobile.yml b/changelogs/unreleased/58933-broken-ui-on-commits-on-mobile.yml
new file mode 100644
index 00000000000..ca9f9dd21c9
--- /dev/null
+++ b/changelogs/unreleased/58933-broken-ui-on-commits-on-mobile.yml
@@ -0,0 +1,5 @@
+---
+title: Fix UI layout on Commits on mobile
+merge_request: 26133
+author: Takuya Noguchi
+type: fixed
diff --git a/changelogs/unreleased/59057-buttons-on-top-from-a-user-profile-page-on-mobile.yml b/changelogs/unreleased/59057-buttons-on-top-from-a-user-profile-page-on-mobile.yml
new file mode 100644
index 00000000000..febbbce2139
--- /dev/null
+++ b/changelogs/unreleased/59057-buttons-on-top-from-a-user-profile-page-on-mobile.yml
@@ -0,0 +1,5 @@
+---
+title: Improve mobile UI on User Profile page
+merge_request: 26240
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/59117-inconsistent-hover-behavior-on-navbar-items.yml b/changelogs/unreleased/59117-inconsistent-hover-behavior-on-navbar-items.yml
new file mode 100644
index 00000000000..eb9dcef4a89
--- /dev/null
+++ b/changelogs/unreleased/59117-inconsistent-hover-behavior-on-navbar-items.yml
@@ -0,0 +1,5 @@
+---
+title: Fix hover animation consistency in top navbar items
+merge_request: 26345
+author:
+type: fixed
diff --git a/changelogs/unreleased/59189-long-names-in-project-path-namespace-dropdown-breaks-past-container.yml b/changelogs/unreleased/59189-long-names-in-project-path-namespace-dropdown-breaks-past-container.yml
new file mode 100644
index 00000000000..bed7fcf2651
--- /dev/null
+++ b/changelogs/unreleased/59189-long-names-in-project-path-namespace-dropdown-breaks-past-container.yml
@@ -0,0 +1,5 @@
+---
+title: Prevent namespace dropdown in new project form from exceeding container
+merge_request: 26343
+author:
+type: fixed
diff --git a/changelogs/unreleased/59296-add-filter-by-title-milestones-api.yml b/changelogs/unreleased/59296-add-filter-by-title-milestones-api.yml
new file mode 100644
index 00000000000..440b24a548c
--- /dev/null
+++ b/changelogs/unreleased/59296-add-filter-by-title-milestones-api.yml
@@ -0,0 +1,5 @@
+---
+title: Add select by title to milestones API
+merge_request: 26573
+author:
+type: added
diff --git a/changelogs/unreleased/59352-fix-mr-discussion-expansion.yml b/changelogs/unreleased/59352-fix-mr-discussion-expansion.yml
new file mode 100644
index 00000000000..ab9ad53835c
--- /dev/null
+++ b/changelogs/unreleased/59352-fix-mr-discussion-expansion.yml
@@ -0,0 +1,5 @@
+---
+title: Expand resolved discussion when linking to a comment in the discussion
+merge_request: 26483
+author:
+type: fixed
diff --git a/changelogs/unreleased/59502-fix-breadcrumb-artifacts.yml b/changelogs/unreleased/59502-fix-breadcrumb-artifacts.yml
new file mode 100644
index 00000000000..da65c3bc870
--- /dev/null
+++ b/changelogs/unreleased/59502-fix-breadcrumb-artifacts.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes job link in artifacts page breadcrumb
+merge_request: 26592
+author:
+type: fixed
diff --git a/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml b/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml
deleted file mode 100644
index cd7b56a1e05..00000000000
--- a/changelogs/unreleased/8688-recursive-pipelines-ce-backport.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Creates mixin to reduce code duplication between CE and EE in graph component
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/MaxWinterstein-master-patch-23232.yml b/changelogs/unreleased/MaxWinterstein-master-patch-23232.yml
new file mode 100644
index 00000000000..8fb9f1057fe
--- /dev/null
+++ b/changelogs/unreleased/MaxWinterstein-master-patch-23232.yml
@@ -0,0 +1,5 @@
+---
+title: Unify behaviour of 'Copy commit SHA to clipboard' to use full commit SHA.
+merge_request: 25829
+author: Max Winterstein
+type: changed
diff --git a/changelogs/unreleased/Projects--dropdown-is-misaligned-on-issue-boards-page.yml b/changelogs/unreleased/Projects--dropdown-is-misaligned-on-issue-boards-page.yml
deleted file mode 100644
index 49511294c48..00000000000
--- a/changelogs/unreleased/Projects--dropdown-is-misaligned-on-issue-boards-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Proper align Projects dropdown on issue boards page
-merge_request: 24277
-author: Johann Hubert Sonntagbauer
-type: fixed
diff --git a/changelogs/unreleased/ab-54270-github-iid.yml b/changelogs/unreleased/ab-54270-github-iid.yml
deleted file mode 100644
index 1776b0aeb86..00000000000
--- a/changelogs/unreleased/ab-54270-github-iid.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve efficiency of GitHub importer by reducing amount of locks needed.
-merge_request: 24102
-author:
-type: performance
diff --git a/changelogs/unreleased/ac-pages-subgroups.yml b/changelogs/unreleased/ac-pages-subgroups.yml
deleted file mode 100644
index ef5a0c1872e..00000000000
--- a/changelogs/unreleased/ac-pages-subgroups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Pages for subgroups
-merge_request: 23505
-author:
-type: added
diff --git a/changelogs/unreleased/actioncontroller-parameters-deprecations.yml b/changelogs/unreleased/actioncontroller-parameters-deprecations.yml
deleted file mode 100644
index ddd15c37542..00000000000
--- a/changelogs/unreleased/actioncontroller-parameters-deprecations.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix several ActionController::Parameters deprecations
-merge_request: 24332
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/add-badge-count-to-projects-and-groups.yml b/changelogs/unreleased/add-badge-count-to-projects-and-groups.yml
deleted file mode 100644
index e200bbaa806..00000000000
--- a/changelogs/unreleased/add-badge-count-to-projects-and-groups.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add badge count to projects
-merge_request: 18425
-author: George Tsiolis
-type: added
diff --git a/changelogs/unreleased/add-related-merge-request-count-to-api-response.yml b/changelogs/unreleased/add-related-merge-request-count-to-api-response.yml
deleted file mode 100644
index 7438053a84f..00000000000
--- a/changelogs/unreleased/add-related-merge-request-count-to-api-response.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add related merge request count to api response
-merge_request: 24974
-author:
-type: added
diff --git a/changelogs/unreleased/add-title-attribute-to-file-row.yml b/changelogs/unreleased/add-title-attribute-to-file-row.yml
deleted file mode 100644
index c68d3d544e7..00000000000
--- a/changelogs/unreleased/add-title-attribute-to-file-row.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: add title attribute to display file name
-merge_request: 25154
-author: Satoshi Nakamatsu @satoshicano
-type: added
diff --git a/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml b/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml
deleted file mode 100644
index 7d767e220f7..00000000000
--- a/changelogs/unreleased/add-uniqueness-validation-to-url-column-in-releases-link-model.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add uniqueness validation to url column in Releases::Link model
-merge_request: 24223
-author:
-type: other
diff --git a/changelogs/unreleased/adrianmoisey-GITLAB_PAGES_PREDEFINED_VARIABLES.yml b/changelogs/unreleased/adrianmoisey-GITLAB_PAGES_PREDEFINED_VARIABLES.yml
deleted file mode 100644
index a664c44e1d7..00000000000
--- a/changelogs/unreleased/adrianmoisey-GITLAB_PAGES_PREDEFINED_VARIABLES.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add GitLab Pages predefined CI variables 'CI_PAGES_DOMAIN' and 'CI_PAGES_URL'
-merge_request: 24504
-author: Adrian Moisey
-type: added
diff --git a/changelogs/unreleased/adriel-remove-feature-flag.yml b/changelogs/unreleased/adriel-remove-feature-flag.yml
deleted file mode 100644
index d442e120d60..00000000000
--- a/changelogs/unreleased/adriel-remove-feature-flag.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update metrics dashboard graph design
-merge_request: 24653
-author:
-type: changed
diff --git a/changelogs/unreleased/allow-filtering-labels-by-a-single-character.yml b/changelogs/unreleased/allow-filtering-labels-by-a-single-character.yml
new file mode 100644
index 00000000000..31165bbadb7
--- /dev/null
+++ b/changelogs/unreleased/allow-filtering-labels-by-a-single-character.yml
@@ -0,0 +1,5 @@
+---
+title: Allow filtering labels list by one or two characters
+merge_request: 26012
+author:
+type: changed
diff --git a/changelogs/unreleased/an-dtracing-test-for-invalid-tracers.yml b/changelogs/unreleased/an-dtracing-test-for-invalid-tracers.yml
deleted file mode 100644
index 5365260cbae..00000000000
--- a/changelogs/unreleased/an-dtracing-test-for-invalid-tracers.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid overwriting default jaeger values with nil
-merge_request: 24482
-author:
-type: fixed
diff --git a/changelogs/unreleased/an-gilab-process-name.yml b/changelogs/unreleased/an-gilab-process-name.yml
deleted file mode 100644
index 72d811ee21f..00000000000
--- a/changelogs/unreleased/an-gilab-process-name.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extract process_name from GitLab::Sentry
-merge_request: 24422
-author:
-type: other
diff --git a/changelogs/unreleased/an-opentracing-active-record-tracing.yml b/changelogs/unreleased/an-opentracing-active-record-tracing.yml
deleted file mode 100644
index 59b480675ec..00000000000
--- a/changelogs/unreleased/an-opentracing-active-record-tracing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds tracing support for ActiveRecord notifications
-merge_request: 24604
-author:
-type: other
diff --git a/changelogs/unreleased/an-opentracing-factory.yml b/changelogs/unreleased/an-opentracing-factory.yml
deleted file mode 100644
index c04736f3e63..00000000000
--- a/changelogs/unreleased/an-opentracing-factory.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Conditionally initialize the global opentracing tracer
-merge_request: 24186
-author:
-type: other
diff --git a/changelogs/unreleased/an-opentracing-propagation.yml b/changelogs/unreleased/an-opentracing-propagation.yml
deleted file mode 100644
index d9aa7cd0048..00000000000
--- a/changelogs/unreleased/an-opentracing-propagation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adds inter-service OpenTracing propagation
-merge_request: 24239
-author:
-type: other
diff --git a/changelogs/unreleased/an-opentracing-render-tracing.yml b/changelogs/unreleased/an-opentracing-render-tracing.yml
deleted file mode 100644
index 6ff7f1f3cf2..00000000000
--- a/changelogs/unreleased/an-opentracing-render-tracing.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add OpenTracing instrumentation for Action View Render events
-merge_request: 24728
-author:
-type: other
diff --git a/changelogs/unreleased/an-peek-jaeger.yml b/changelogs/unreleased/an-peek-jaeger.yml
deleted file mode 100644
index 8659ee4f9e0..00000000000
--- a/changelogs/unreleased/an-peek-jaeger.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Provide a performance bar link to the Jaeger UI
-merge_request: 24902
-author:
-type: other
diff --git a/changelogs/unreleased/api-group-labels.yml b/changelogs/unreleased/api-group-labels.yml
deleted file mode 100644
index 0df6f15a9b6..00000000000
--- a/changelogs/unreleased/api-group-labels.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Add support for group labels'
-merge_request: 21368
-author: Robert Schilling
-type: added
diff --git a/changelogs/unreleased/api-nested-group-permission.yml b/changelogs/unreleased/api-nested-group-permission.yml
deleted file mode 100644
index 3ec0df6893f..00000000000
--- a/changelogs/unreleased/api-nested-group-permission.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Return the maximum group access level in the projects API
-merge_request: 24403
-author:
-type: changed
diff --git a/changelogs/unreleased/api-tags-search.yml b/changelogs/unreleased/api-tags-search.yml
deleted file mode 100644
index 1501acd5a9e..00000000000
--- a/changelogs/unreleased/api-tags-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Support searching for tags'
-merge_request: 24385
-author: Robert Schilling
-type: added
diff --git a/changelogs/unreleased/api-wiki-dot-slug.yml b/changelogs/unreleased/api-wiki-dot-slug.yml
deleted file mode 100644
index 82c76fa7450..00000000000
--- a/changelogs/unreleased/api-wiki-dot-slug.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'API: Support dots in wiki slugs'
-merge_request: 24383
-author: Robert Schilling
-type: fixed
diff --git a/changelogs/unreleased/auto-devops-custom-domains.yml b/changelogs/unreleased/auto-devops-custom-domains.yml
deleted file mode 100644
index 37e8ee26a4d..00000000000
--- a/changelogs/unreleased/auto-devops-custom-domains.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added support for custom hosts/domains to Auto DevOps
-merge_request: 24248
-author: walkafwalka
-type: added
diff --git a/changelogs/unreleased/auto-devops-kubectl-1-11-6.yml b/changelogs/unreleased/auto-devops-kubectl-1-11-6.yml
deleted file mode 100644
index 1a8cdead4ac..00000000000
--- a/changelogs/unreleased/auto-devops-kubectl-1-11-6.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump kubectl in Auto DevOps to 1.11.6
-merge_request: 24176
-author:
-type: other
diff --git a/changelogs/unreleased/avoid_es_loading_project_ci_status.yml b/changelogs/unreleased/avoid_es_loading_project_ci_status.yml
new file mode 100644
index 00000000000..514909c730d
--- /dev/null
+++ b/changelogs/unreleased/avoid_es_loading_project_ci_status.yml
@@ -0,0 +1,5 @@
+---
+title: Avoid loading pipeline status in project search
+merge_request: 26342
+author:
+type: performance
diff --git a/changelogs/unreleased/backup_aws_sse-c.yml b/changelogs/unreleased/backup_aws_sse-c.yml
deleted file mode 100644
index 78b57d7efc3..00000000000
--- a/changelogs/unreleased/backup_aws_sse-c.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-title: Add support for customer provided encryption keys for Amazon S3 remote backups
-merge_request: 23797
-author: Pepijn Van Eeckhoudt
-type: added
-
diff --git a/changelogs/unreleased/backup_restore_fix_issue_46891.yml b/changelogs/unreleased/backup_restore_fix_issue_46891.yml
deleted file mode 100644
index b8fe3b1b861..00000000000
--- a/changelogs/unreleased/backup_restore_fix_issue_46891.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Modify file restore to rectify tar issue
-merge_request: 24000
-author:
-type: fixed
diff --git a/changelogs/unreleased/bump-ingress-chart-112.yml b/changelogs/unreleased/bump-ingress-chart-112.yml
deleted file mode 100644
index 8a46fedb4b0..00000000000
--- a/changelogs/unreleased/bump-ingress-chart-112.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump nginx-ingress chart to 1.1.2
-merge_request: 24203
-author:
-type: other
diff --git a/changelogs/unreleased/bvl-fix-race-condition-creating-signature.yml b/changelogs/unreleased/bvl-fix-race-condition-creating-signature.yml
deleted file mode 100644
index 307b4f526bb..00000000000
--- a/changelogs/unreleased/bvl-fix-race-condition-creating-signature.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Avoid race conditions when creating GpgSignature
-merge_request: 24939
-author:
-type: fixed
diff --git a/changelogs/unreleased/ce-56153-error-tracking-counts.yml b/changelogs/unreleased/ce-56153-error-tracking-counts.yml
new file mode 100644
index 00000000000..fc3d8c01d7f
--- /dev/null
+++ b/changelogs/unreleased/ce-56153-error-tracking-counts.yml
@@ -0,0 +1,5 @@
+---
+title: Add usage counts for error tracking feature
+merge_request: 25472
+author:
+type: added
diff --git a/changelogs/unreleased/changelogs-readme.yml b/changelogs/unreleased/changelogs-readme.yml
deleted file mode 100644
index 9f391699575..00000000000
--- a/changelogs/unreleased/changelogs-readme.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: add readme to changelogs directory
-merge_request: 25209
-author: "@glensc"
-type: added
diff --git a/changelogs/unreleased/chore-update-js-regex.yml b/changelogs/unreleased/chore-update-js-regex.yml
deleted file mode 100644
index d45d0b47457..00000000000
--- a/changelogs/unreleased/chore-update-js-regex.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade js-regex gem to version 3.1
-merge_request: 24433
-author: rroger
-type: changed
diff --git a/changelogs/unreleased/cleanup-leagcy-artifact-migration.yml b/changelogs/unreleased/cleanup-leagcy-artifact-migration.yml
deleted file mode 100644
index 6e8dac97249..00000000000
--- a/changelogs/unreleased/cleanup-leagcy-artifact-migration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cleanup legacy artifact background migration
-merge_request: 24144
-author:
-type: other
diff --git a/changelogs/unreleased/cluster_application_version_updated.yml b/changelogs/unreleased/cluster_application_version_updated.yml
deleted file mode 100644
index 34fe55dcc5e..00000000000
--- a/changelogs/unreleased/cluster_application_version_updated.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update cluster application version on updated and installed status
-merge_request: 24810
-author:
-type: other
diff --git a/changelogs/unreleased/cluster_status_for_ugprading.yml b/changelogs/unreleased/cluster_status_for_ugprading.yml
deleted file mode 100644
index ca1f8b3a786..00000000000
--- a/changelogs/unreleased/cluster_status_for_ugprading.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Expose version for each application in cluster_status JSON endpoint
-merge_request: 24791
-author:
-type: other
diff --git a/changelogs/unreleased/container-repository-cleanup-api.yml b/changelogs/unreleased/container-repository-cleanup-api.yml
deleted file mode 100644
index c2b23a9add0..00000000000
--- a/changelogs/unreleased/container-repository-cleanup-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Container Registry API with cleanup function
-merge_request: 24303
-author:
-type: added
diff --git a/changelogs/unreleased/custom-helm-chart-repo.yml b/changelogs/unreleased/custom-helm-chart-repo.yml
deleted file mode 100644
index 592d2f60ca2..00000000000
--- a/changelogs/unreleased/custom-helm-chart-repo.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added feature to specify a custom Auto DevOps chart repository
-merge_request: 24162
-author: walkafwalka
-type: added
diff --git a/changelogs/unreleased/delete-release-when-delete-tag.yml b/changelogs/unreleased/delete-release-when-delete-tag.yml
new file mode 100644
index 00000000000..58acd449bf1
--- /dev/null
+++ b/changelogs/unreleased/delete-release-when-delete-tag.yml
@@ -0,0 +1,5 @@
+---
+title: Releases will now be automatically deleted when deleting corresponding tag
+merge_request: 26530
+author:
+type: fixed
diff --git a/changelogs/unreleased/deploy-keys-ext.yml b/changelogs/unreleased/deploy-keys-ext.yml
new file mode 100644
index 00000000000..e1d2fe08425
--- /dev/null
+++ b/changelogs/unreleased/deploy-keys-ext.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize admin deploy keys strings
+merge_request:
+author:
+type: other
diff --git a/changelogs/unreleased/deprecated-force-reload.yml b/changelogs/unreleased/deprecated-force-reload.yml
deleted file mode 100644
index 2a0e97089e0..00000000000
--- a/changelogs/unreleased/deprecated-force-reload.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: 'Fix deprecation: Passing an argument to force an association to reload is
- now deprecated'
-merge_request: 24136
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/deprecated-migration-inheritance.yml b/changelogs/unreleased/deprecated-migration-inheritance.yml
deleted file mode 100644
index 814c511195b..00000000000
--- a/changelogs/unreleased/deprecated-migration-inheritance.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Directly inheriting from ActiveRecord::Migration is deprecated
-merge_request: 25066
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/diff-file-finder.yml b/changelogs/unreleased/diff-file-finder.yml
deleted file mode 100644
index 3160e9fc91b..00000000000
--- a/changelogs/unreleased/diff-file-finder.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added fuzzy file finder to merge requests
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/diff-tree-collapse-directories.yml b/changelogs/unreleased/diff-tree-collapse-directories.yml
deleted file mode 100644
index 6eae48f2352..00000000000
--- a/changelogs/unreleased/diff-tree-collapse-directories.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Collapse directory structure in merge request file tree
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/diff-tree-resizable.yml b/changelogs/unreleased/diff-tree-resizable.yml
deleted file mode 100644
index 7411640aea5..00000000000
--- a/changelogs/unreleased/diff-tree-resizable.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make file tree in merge requests resizable
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/dm-copy-suggestion-as-gfm.yml b/changelogs/unreleased/dm-copy-suggestion-as-gfm.yml
deleted file mode 100644
index 96115e6ade1..00000000000
--- a/changelogs/unreleased/dm-copy-suggestion-as-gfm.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow suggestions to be copied and pasted as GFM
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/dm-trim-discussion-truncated-line-first-chars.yml b/changelogs/unreleased/dm-trim-discussion-truncated-line-first-chars.yml
deleted file mode 100644
index 1e1fa8295c3..00000000000
--- a/changelogs/unreleased/dm-trim-discussion-truncated-line-first-chars.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix bug that caused Suggestion Markdown toolbar button to insert snippet with leading +/-/<space>
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/do-not-force-2fa.yml b/changelogs/unreleased/do-not-force-2fa.yml
new file mode 100644
index 00000000000..f9be40e8f37
--- /dev/null
+++ b/changelogs/unreleased/do-not-force-2fa.yml
@@ -0,0 +1,6 @@
+---
+title: Add link on two-factor authorization settings page to leave group that enforces
+ two-factor authorization
+merge_request: 25731
+author:
+type: changed
diff --git a/changelogs/unreleased/docs-push-mirror-GitLab-GitHub.yml b/changelogs/unreleased/docs-push-mirror-GitLab-GitHub.yml
deleted file mode 100644
index 4539a9b7985..00000000000
--- a/changelogs/unreleased/docs-push-mirror-GitLab-GitHub.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Updated docs for fields in pushing mirror from GitLab to GitHub
-merge_request: 24566
-author: Joseph Yu
-type: other
diff --git a/changelogs/unreleased/expire-job-artifacts-worker.yml b/changelogs/unreleased/expire-job-artifacts-worker.yml
deleted file mode 100644
index cda6e9ff497..00000000000
--- a/changelogs/unreleased/expire-job-artifacts-worker.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Efficiently remove expired artifacts in `ExpireBuildArtifactsWorker`
-merge_request: 24450
-author:
-type: performance
diff --git a/changelogs/unreleased/expose-group-id-on-home-panel.yml b/changelogs/unreleased/expose-group-id-on-home-panel.yml
new file mode 100644
index 00000000000..1efe15a6e1a
--- /dev/null
+++ b/changelogs/unreleased/expose-group-id-on-home-panel.yml
@@ -0,0 +1,5 @@
+---
+title: Expose group id on home panel
+merge_request: 25897
+author: Peter Marko
+type: added
diff --git a/changelogs/unreleased/fast-destroy-uploads.yml b/changelogs/unreleased/fast-destroy-uploads.yml
deleted file mode 100644
index ee3363a6ae9..00000000000
--- a/changelogs/unreleased/fast-destroy-uploads.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: File uploads are deleted asynchronously when deleting a project or group.
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml b/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml
deleted file mode 100644
index ad92135d401..00000000000
--- a/changelogs/unreleased/feature-gb-enable-ci-persisted-stages-by-default.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Enable persisted pipeline stages by default
-merge_request: 25347
-author:
-type: performance
diff --git a/changelogs/unreleased/feature-users-search-results.yml b/changelogs/unreleased/feature-users-search-results.yml
new file mode 100644
index 00000000000..151d08bce12
--- /dev/null
+++ b/changelogs/unreleased/feature-users-search-results.yml
@@ -0,0 +1,5 @@
+---
+title: Add users search results to global search
+merge_request: 21197
+author: Alexis Reigel
+type: added
diff --git a/changelogs/unreleased/features-document-graphicsmagick-source-installation.yml b/changelogs/unreleased/features-document-graphicsmagick-source-installation.yml
deleted file mode 100644
index b224cace4bf..00000000000
--- a/changelogs/unreleased/features-document-graphicsmagick-source-installation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Document graphicsmagick installation for source installation
-merge_request: 24404
-author: Alexis Reigel
-type: added
diff --git a/changelogs/unreleased/filter-merge-requests-by-target-branch.yml b/changelogs/unreleased/filter-merge-requests-by-target-branch.yml
new file mode 100644
index 00000000000..d0aba631c96
--- /dev/null
+++ b/changelogs/unreleased/filter-merge-requests-by-target-branch.yml
@@ -0,0 +1,5 @@
+---
+title: Add target branch filter to merge requests search bar
+merge_request: 24380
+author: Hiroyuki Sato
+type: added
diff --git a/changelogs/unreleased/filter-note-parameters.yml b/changelogs/unreleased/filter-note-parameters.yml
deleted file mode 100644
index fca2a394820..00000000000
--- a/changelogs/unreleased/filter-note-parameters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Include note in the Rails filter_parameters configuration
-merge_request: 25238
-author:
-type: other
diff --git a/changelogs/unreleased/fix-39759-new-project-icon-vertical-align.yml b/changelogs/unreleased/fix-39759-new-project-icon-vertical-align.yml
deleted file mode 100644
index 3d87807dbc1..00000000000
--- a/changelogs/unreleased/fix-39759-new-project-icon-vertical-align.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjust vertical alignment for project visibility icons
-merge_request: 24511
-author: Martin Hobert
-type: fixed
diff --git a/changelogs/unreleased/fix-403-page-is-rendered-but-404-is-the-response.yml b/changelogs/unreleased/fix-403-page-is-rendered-but-404-is-the-response.yml
deleted file mode 100644
index eda69b32094..00000000000
--- a/changelogs/unreleased/fix-403-page-is-rendered-but-404-is-the-response.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Show the correct error page when access is denied
-merge_request: 23932
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-49388.yml b/changelogs/unreleased/fix-49388.yml
deleted file mode 100644
index f8b5e3e1943..00000000000
--- a/changelogs/unreleased/fix-49388.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update metrics environment dropdown to show complete option set
-merge_request: 24441
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-55956-oversized-dropdown-button-custom-notifications.yml b/changelogs/unreleased/fix-55956-oversized-dropdown-button-custom-notifications.yml
deleted file mode 100644
index e33699a2112..00000000000
--- a/changelogs/unreleased/fix-55956-oversized-dropdown-button-custom-notifications.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed oversized custom project notification selector dropdown
-merge_request: 24557
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-56558-move-primary-button.yml b/changelogs/unreleased/fix-56558-move-primary-button.yml
deleted file mode 100644
index 4dcc896b327..00000000000
--- a/changelogs/unreleased/fix-56558-move-primary-button.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Moved primary button for labels to follow the design patterns used on rest of the site
-merge_request:
-author: Martin Hobert
-type: fixed
diff --git a/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml b/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml
deleted file mode 100644
index bb0b193a846..00000000000
--- a/changelogs/unreleased/fix-auto-devops-domain-title-on-admin-settings.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixes Auto DevOps title on CI/CD admin settings
-merge_request: 24249
-author:
-type: other
diff --git a/changelogs/unreleased/fix-hidden-statistics.yml b/changelogs/unreleased/fix-hidden-statistics.yml
new file mode 100644
index 00000000000..4d99bd00136
--- /dev/null
+++ b/changelogs/unreleased/fix-hidden-statistics.yml
@@ -0,0 +1,5 @@
+---
+title: Show statistics also when repository is disabled
+merge_request: 26509
+author: Peter Marko
+type: fixed
diff --git a/changelogs/unreleased/fix-ide-web-worker-relative-url.yml b/changelogs/unreleased/fix-ide-web-worker-relative-url.yml
new file mode 100644
index 00000000000..2accad68c4e
--- /dev/null
+++ b/changelogs/unreleased/fix-ide-web-worker-relative-url.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed Web IDE web workers not working with relative URLs
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-missing-border.yml b/changelogs/unreleased/fix-missing-border.yml
new file mode 100644
index 00000000000..21728223cb8
--- /dev/null
+++ b/changelogs/unreleased/fix-missing-border.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes missing border color in discussion card component
+merge_request: 26242
+author: Farhad Yasir
+type: fixed
diff --git a/changelogs/unreleased/fix-new-merge-request-diff-headers-sticky-position.yml b/changelogs/unreleased/fix-new-merge-request-diff-headers-sticky-position.yml
new file mode 100644
index 00000000000..dadbd5c940f
--- /dev/null
+++ b/changelogs/unreleased/fix-new-merge-request-diff-headers-sticky-position.yml
@@ -0,0 +1,5 @@
+---
+title: Fixed sticky headers in merge request creation diffs
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/fix-pipeline-entity.yml b/changelogs/unreleased/fix-pipeline-entity.yml
new file mode 100644
index 00000000000..b429139402c
--- /dev/null
+++ b/changelogs/unreleased/fix-pipeline-entity.yml
@@ -0,0 +1,5 @@
+---
+title: Add merge request pipeline flag to pipeline entity
+merge_request: 25846
+author:
+type: added
diff --git a/changelogs/unreleased/fix-repo-settings-file-upload-error.yml b/changelogs/unreleased/fix-repo-settings-file-upload-error.yml
deleted file mode 100644
index b219fdfaa1e..00000000000
--- a/changelogs/unreleased/fix-repo-settings-file-upload-error.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix bug causing repository mirror settings UI to break
-merge_request: 23712
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-review-app-env-url.yml b/changelogs/unreleased/fix-review-app-env-url.yml
new file mode 100644
index 00000000000..963cd0c2992
--- /dev/null
+++ b/changelogs/unreleased/fix-review-app-env-url.yml
@@ -0,0 +1,5 @@
+---
+title: Fixes long review app subdomains
+merge_request: 25990
+author: walkafwalka
+type: fixed
diff --git a/changelogs/unreleased/fix_-56347.yml b/changelogs/unreleased/fix_-56347.yml
deleted file mode 100644
index 1d03ed8864c..00000000000
--- a/changelogs/unreleased/fix_-56347.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix overlapping empty-header logo
-merge_request: 24868
-author: Jonas L.
-type: fixed
diff --git a/changelogs/unreleased/fix_jira_integration_VCS1019.yml b/changelogs/unreleased/fix_jira_integration_VCS1019.yml
deleted file mode 100644
index 3582ec1fe0f..00000000000
--- a/changelogs/unreleased/fix_jira_integration_VCS1019.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Jira Service password validation on project integration services.
-merge_request: 24896
-author: Daniel Juarez
-type: fixed
diff --git a/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml b/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml
deleted file mode 100644
index f64b29644b0..00000000000
--- a/changelogs/unreleased/fj-55882-fix-files-api-content-disposition.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix files/blob api endpoints content disposition
-merge_request: 24267
-author:
-type: fixed
diff --git a/changelogs/unreleased/force-redeploy-on-updated-secrets.yml b/changelogs/unreleased/force-redeploy-on-updated-secrets.yml
deleted file mode 100644
index 3b727c99dd5..00000000000
--- a/changelogs/unreleased/force-redeploy-on-updated-secrets.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Redeploy Auto DevOps deployment on variable updates
-merge_request: 24498
-author: walkafwalka
-type: added
diff --git a/changelogs/unreleased/gitaly-update-1-13-0.yml b/changelogs/unreleased/gitaly-update-1-13-0.yml
deleted file mode 100644
index 73de25a532d..00000000000
--- a/changelogs/unreleased/gitaly-update-1-13-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade Gitaly to 1.13.0
-merge_request: 24429
-author:
-type: other
diff --git a/changelogs/unreleased/gitaly-update-1.18.0.yml b/changelogs/unreleased/gitaly-update-1.18.0.yml
deleted file mode 100644
index 392527f5e5d..00000000000
--- a/changelogs/unreleased/gitaly-update-1.18.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade gitaly to 1.18.0
-merge_request: 24981
-author:
-type: other
diff --git a/changelogs/unreleased/gitaly-version-v1.29.0.yml b/changelogs/unreleased/gitaly-version-v1.29.0.yml
new file mode 100644
index 00000000000..b6ce14c33a2
--- /dev/null
+++ b/changelogs/unreleased/gitaly-version-v1.29.0.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade to Gitaly v1.29.0
+merge_request: 26406
+author:
+type: changed
diff --git a/changelogs/unreleased/gitlab-workhorse-update-8.1.0.yml b/changelogs/unreleased/gitlab-workhorse-update-8.1.0.yml
deleted file mode 100644
index 1e0160c4d40..00000000000
--- a/changelogs/unreleased/gitlab-workhorse-update-8.1.0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade gitlab-workhorse to 8.1.0
-merge_request: 24571
-author:
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-clusters.yml b/changelogs/unreleased/gt-externalize-app-views-clusters.yml
deleted file mode 100644
index 6d2284ead37..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-clusters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/clusters`
-merge_request: 24666
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-email_rejection_mailer.yml b/changelogs/unreleased/gt-externalize-app-views-email_rejection_mailer.yml
deleted file mode 100644
index 8f6fbdceb54..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-email_rejection_mailer.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/email_rejection_mailer`
-merge_request: 24869
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-instance_statistics.yml b/changelogs/unreleased/gt-externalize-app-views-instance_statistics.yml
deleted file mode 100644
index a3bf54a1339..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-instance_statistics.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/instance_statistics`
-merge_request: 24809
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-ci.yml b/changelogs/unreleased/gt-externalize-app-views-projects-ci.yml
deleted file mode 100644
index ecc878ab892..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-ci.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/ci`
-merge_request: 24617
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml b/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml
deleted file mode 100644
index 29dbf2367b7..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-commit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/commit`
-merge_request: 24668
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-milestones.yml b/changelogs/unreleased/gt-externalize-app-views-projects-milestones.yml
deleted file mode 100644
index 56aaac812bb..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-milestones.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/milestones`
-merge_request: 24726
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-pages_domains.yml b/changelogs/unreleased/gt-externalize-app-views-projects-pages_domains.yml
deleted file mode 100644
index f60776a2ed8..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-pages_domains.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/pages_domains`
-merge_request: 24723
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-pipelines.yml b/changelogs/unreleased/gt-externalize-app-views-projects-pipelines.yml
new file mode 100644
index 00000000000..094cd3ab751
--- /dev/null
+++ b/changelogs/unreleased/gt-externalize-app-views-projects-pipelines.yml
@@ -0,0 +1,5 @@
+---
+title: Externalize strings from `/app/views/projects/pipelines`
+merge_request: 26035
+author: George Tsiolis
+type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml b/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml
deleted file mode 100644
index 1acea10fcaa..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-projects-project_members.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/projects/project_members`
-merge_request: 23227
-author: Tao Wang
-type: other
diff --git a/changelogs/unreleased/gt-externalize-app-views-sent_notifications.yml b/changelogs/unreleased/gt-externalize-app-views-sent_notifications.yml
deleted file mode 100644
index e77b5376fa8..00000000000
--- a/changelogs/unreleased/gt-externalize-app-views-sent_notifications.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Externalize strings from `/app/views/sent_notifications`
-merge_request: 24576
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-remove-unused-button-class.yml b/changelogs/unreleased/gt-remove-unused-button-class.yml
deleted file mode 100644
index f7889e1d6f6..00000000000
--- a/changelogs/unreleased/gt-remove-unused-button-class.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove unused button classes `btn-create` and `comment-btn`
-merge_request: 23232
-author: George Tsiolis
-type: performance
diff --git a/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml b/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml
deleted file mode 100644
index b612bb3ee39..00000000000
--- a/changelogs/unreleased/gt-rename-gray-theme-color-variables.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove all `$theme-gray-{weight}` variables in favor of `$gray-{weight}`
-merge_request: 24333
-author: George Tsiolis
-type: other
diff --git a/changelogs/unreleased/gt-update-new-password-breadcrumb.yml b/changelogs/unreleased/gt-update-new-password-breadcrumb.yml
deleted file mode 100644
index 43ea2f0d44b..00000000000
--- a/changelogs/unreleased/gt-update-new-password-breadcrumb.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update new password breadcrumb
-merge_request: 25037
-author: George Tsiolis
-type: fixed
diff --git a/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml b/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml
deleted file mode 100644
index fa06a78adae..00000000000
--- a/changelogs/unreleased/gt-update-string-struture-for-group-runners.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update string structure for available group runners
-merge_request: 24187
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/hnk-master-patch-61932.yml b/changelogs/unreleased/hnk-master-patch-61932.yml
deleted file mode 100644
index 8cc9d0057a9..00000000000
--- a/changelogs/unreleased/hnk-master-patch-61932.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update runner admin page to make description field larger
-merge_request: 23593
-author: Sascha Reynolds
-type: fixed
diff --git a/changelogs/unreleased/homepage-proj-descr-cutoff.yml b/changelogs/unreleased/homepage-proj-descr-cutoff.yml
deleted file mode 100644
index 837c01f6722..00000000000
--- a/changelogs/unreleased/homepage-proj-descr-cutoff.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Increase line height of project summaries
-merge_request:
-author: gfyoung
-type: fixed
diff --git a/changelogs/unreleased/import-go-to-project-cta.yml b/changelogs/unreleased/import-go-to-project-cta.yml
deleted file mode 100644
index ae719f08790..00000000000
--- a/changelogs/unreleased/import-go-to-project-cta.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve GitHub and Gitea project import table UI
-merge_request: 24606
-author:
-type: other
diff --git a/changelogs/unreleased/improve-performance-for-diverging-commit-counts.yml b/changelogs/unreleased/improve-performance-for-diverging-commit-counts.yml
deleted file mode 100644
index 76ff15cba5b..00000000000
--- a/changelogs/unreleased/improve-performance-for-diverging-commit-counts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Improve performance for diverging commit counts
-merge_request: 24287
-author:
-type: performance
diff --git a/changelogs/unreleased/introduce-environment-search-endpoint.yml b/changelogs/unreleased/introduce-environment-search-endpoint.yml
deleted file mode 100644
index 01851ba7d27..00000000000
--- a/changelogs/unreleased/introduce-environment-search-endpoint.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Introduce Internal API for searching environment names
-merge_request: 24923
-author:
-type: added
diff --git a/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml b/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml
deleted file mode 100644
index 8025cd472bd..00000000000
--- a/changelogs/unreleased/iss-32584-preserve-line-number-fragment-after-redirect.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fix lost line number when navigating to a specific line in a protected file
- before authenticating.
-merge_request: 19165
-author: Scott Escue
-type: fixed
diff --git a/changelogs/unreleased/issue_55744.yml b/changelogs/unreleased/issue_55744.yml
deleted file mode 100644
index 6a643732b18..00000000000
--- a/changelogs/unreleased/issue_55744.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix template labels not being created on new projects
-merge_request: 24803
-author:
-type: fixed
diff --git a/changelogs/unreleased/issue_58547.yml b/changelogs/unreleased/issue_58547.yml
new file mode 100644
index 00000000000..553c752e72d
--- /dev/null
+++ b/changelogs/unreleased/issue_58547.yml
@@ -0,0 +1,5 @@
+---
+title: Add API access check to Graphql
+merge_request: 26570
+author:
+type: other
diff --git a/changelogs/unreleased/jej-avoid-csrf-check-on-saml-failure.yml b/changelogs/unreleased/jej-avoid-csrf-check-on-saml-failure.yml
deleted file mode 100644
index 18cced2906a..00000000000
--- a/changelogs/unreleased/jej-avoid-csrf-check-on-saml-failure.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Display SAML failure messages instead of expecting CSRF token
-merge_request: 24509
-author:
-type: fixed
diff --git a/changelogs/unreleased/jej-feature-gates-can-be-set-by-group-path.yml b/changelogs/unreleased/jej-feature-gates-can-be-set-by-group-path.yml
deleted file mode 100644
index ba882112f70..00000000000
--- a/changelogs/unreleased/jej-feature-gates-can-be-set-by-group-path.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow setting feature flags per GitLab group through the API
-merge_request: 25022
-author:
-type: added
diff --git a/changelogs/unreleased/jlenny-AddPagesTemplates.yml b/changelogs/unreleased/jlenny-AddPagesTemplates.yml
deleted file mode 100644
index 0985e4e18ed..00000000000
--- a/changelogs/unreleased/jlenny-AddPagesTemplates.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add templates for most popular Pages templates
-merge_request: 24906
-author:
-type: added
diff --git a/changelogs/unreleased/jlenny-NewAndroidTemplate.yml b/changelogs/unreleased/jlenny-NewAndroidTemplate.yml
deleted file mode 100644
index ae8c58da859..00000000000
--- a/changelogs/unreleased/jlenny-NewAndroidTemplate.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add template for Android with Fastlane
-merge_request: 24722
-author:
-type: changed
diff --git a/changelogs/unreleased/jprovazn-remove-redcarpet.yml b/changelogs/unreleased/jprovazn-remove-redcarpet.yml
deleted file mode 100644
index 4e12de2d19b..00000000000
--- a/changelogs/unreleased/jprovazn-remove-redcarpet.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Removed deprecated Redcarpet markdown engine.
-merge_request:
-author:
-type: removed
diff --git a/changelogs/unreleased/k8s_new_deployment_labels.yml b/changelogs/unreleased/k8s_new_deployment_labels.yml
new file mode 100644
index 00000000000..e9ef3ee0082
--- /dev/null
+++ b/changelogs/unreleased/k8s_new_deployment_labels.yml
@@ -0,0 +1,5 @@
+---
+title: Update deploy boards to additionally select on "app.gitlab.com" annotations
+merge_request: 25623
+author:
+type: changed
diff --git a/changelogs/unreleased/kinolaev-master-patch-87865.yml b/changelogs/unreleased/kinolaev-master-patch-87865.yml
deleted file mode 100644
index b4dbc2c0e1f..00000000000
--- a/changelogs/unreleased/kinolaev-master-patch-87865.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix rollout status for statefulsets and daemonsets
-merge_request: 24972
-author: Sergej Nikolaev <kinolaev@gmail.com>
-type: fixed
diff --git a/changelogs/unreleased/knative-list.yml b/changelogs/unreleased/knative-list.yml
deleted file mode 100644
index 754d8e172cf..00000000000
--- a/changelogs/unreleased/knative-list.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Modified Knative list view to provide more details
-merge_request: 24072
-author: Chris Baumbauer
-type: changed
diff --git a/changelogs/unreleased/knative-show-page.yml b/changelogs/unreleased/knative-show-page.yml
deleted file mode 100644
index a48b754940f..00000000000
--- a/changelogs/unreleased/knative-show-page.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add Knative detailed view
-merge_request: 23863
-author: Chris Baumbauer
-type: added
diff --git a/changelogs/unreleased/local-markdown-version-bkp3.yml b/changelogs/unreleased/local-markdown-version-bkp3.yml
deleted file mode 100644
index ce5bff6ae6b..00000000000
--- a/changelogs/unreleased/local-markdown-version-bkp3.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow admins to invalidate markdown texts by setting local markdown version.
-merge_request:
-author:
-type: added
diff --git a/changelogs/unreleased/mg-fix-bad-cluster-update-entrypoint.yml b/changelogs/unreleased/mg-fix-bad-cluster-update-entrypoint.yml
deleted file mode 100644
index 932850cc825..00000000000
--- a/changelogs/unreleased/mg-fix-bad-cluster-update-entrypoint.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix cluster page non-interactive on form validation error
-merge_request: 24583
-author:
-type: fixed
diff --git a/changelogs/unreleased/monospace-registry-tags.yml b/changelogs/unreleased/monospace-registry-tags.yml
deleted file mode 100644
index b5992707d8c..00000000000
--- a/changelogs/unreleased/monospace-registry-tags.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use monospace font for registry table tag id and tag name
-merge_request: 24205
-author:
-type: other
diff --git a/changelogs/unreleased/move-job-cancel-btn.yml b/changelogs/unreleased/move-job-cancel-btn.yml
deleted file mode 100644
index 41f8e1be5f8..00000000000
--- a/changelogs/unreleased/move-job-cancel-btn.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move cancel & new issue button on job page
-merge_request: 24074
-author:
-type: changed
diff --git a/changelogs/unreleased/move-permission-check-manual-actions-on-deployments.yml b/changelogs/unreleased/move-permission-check-manual-actions-on-deployments.yml
deleted file mode 100644
index 9e979b48ad1..00000000000
--- a/changelogs/unreleased/move-permission-check-manual-actions-on-deployments.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Move permission check of manual actions of deployments
-merge_request: 24660
-author:
-type: other
diff --git a/changelogs/unreleased/mr-file-tree-blob-truncate-improvements.yml b/changelogs/unreleased/mr-file-tree-blob-truncate-improvements.yml
deleted file mode 100644
index b01962591c6..00000000000
--- a/changelogs/unreleased/mr-file-tree-blob-truncate-improvements.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add folder header to files in merge request tree list
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/mr-rebase-failing-tests.yml b/changelogs/unreleased/mr-rebase-failing-tests.yml
deleted file mode 100644
index 07ae05766b1..00000000000
--- a/changelogs/unreleased/mr-rebase-failing-tests.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed rebase button not showing in merge request widget
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/nfriend-update-job-detail-view-sidebar.yml b/changelogs/unreleased/nfriend-update-job-detail-view-sidebar.yml
new file mode 100644
index 00000000000..5364d29710a
--- /dev/null
+++ b/changelogs/unreleased/nfriend-update-job-detail-view-sidebar.yml
@@ -0,0 +1,5 @@
+---
+title: Update job detail sidebar to accommodate post-merge pipeline information
+merge_request: 25777
+author:
+type: added
diff --git a/changelogs/unreleased/nfriend-update-merge-request-widget-pipeline-block.yml b/changelogs/unreleased/nfriend-update-merge-request-widget-pipeline-block.yml
new file mode 100644
index 00000000000..bd4120eb06f
--- /dev/null
+++ b/changelogs/unreleased/nfriend-update-merge-request-widget-pipeline-block.yml
@@ -0,0 +1,6 @@
+---
+title: Update pipeline block on merge request page to accommodate post-merge pipeline
+ information
+merge_request: 25745
+author:
+type: added
diff --git a/changelogs/unreleased/nfriend-update-pipeline-detail-view.yml b/changelogs/unreleased/nfriend-update-pipeline-detail-view.yml
new file mode 100644
index 00000000000..a24325c4eb6
--- /dev/null
+++ b/changelogs/unreleased/nfriend-update-pipeline-detail-view.yml
@@ -0,0 +1,5 @@
+---
+title: Update pipeline detail view to accommodate post-merge pipelines
+merge_request: 25775
+author:
+type: added
diff --git a/changelogs/unreleased/nfriend-update-pipeline-list-view.yml b/changelogs/unreleased/nfriend-update-pipeline-list-view.yml
new file mode 100644
index 00000000000..34e43162b5c
--- /dev/null
+++ b/changelogs/unreleased/nfriend-update-pipeline-list-view.yml
@@ -0,0 +1,5 @@
+---
+title: Update pipeline list view to accommodate post-merge pipeline information
+merge_request: 25690
+author:
+type: added
diff --git a/changelogs/unreleased/not-run-pipeline-on-empty-merge-request.yml b/changelogs/unreleased/not-run-pipeline-on-empty-merge-request.yml
deleted file mode 100644
index 732e4baf4e9..00000000000
--- a/changelogs/unreleased/not-run-pipeline-on-empty-merge-request.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Don't create new merge request pipeline without commits
-merge_request: 24503
-author: Hiroyuki Sato
-type: added
diff --git a/changelogs/unreleased/notebook-multiple-outputs.yml b/changelogs/unreleased/notebook-multiple-outputs.yml
deleted file mode 100644
index 38cc52c0634..00000000000
--- a/changelogs/unreleased/notebook-multiple-outputs.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Support multiple outputs in jupyter notebooks
-merge_request:
-author:
-type: changed
diff --git a/changelogs/unreleased/notes-awards-double-tooltip-fix.yml b/changelogs/unreleased/notes-awards-double-tooltip-fix.yml
deleted file mode 100644
index 23338a60c2a..00000000000
--- a/changelogs/unreleased/notes-awards-double-tooltip-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed double tooltips on note awards buttons
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/only-counted-active-milestones-as-started.yml b/changelogs/unreleased/only-counted-active-milestones-as-started.yml
new file mode 100644
index 00000000000..1a9c4b9023b
--- /dev/null
+++ b/changelogs/unreleased/only-counted-active-milestones-as-started.yml
@@ -0,0 +1,5 @@
+---
+title: Only consider active milestones when using the special Started milestone filter
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-enforces-project-removal-with-past-failed-attempts.yml b/changelogs/unreleased/osw-enforces-project-removal-with-past-failed-attempts.yml
deleted file mode 100644
index 6a2a67e7aa8..00000000000
--- a/changelogs/unreleased/osw-enforces-project-removal-with-past-failed-attempts.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Cleanup stale +deleted repo paths on project removal (adjusts project removal bug)
-merge_request: 24269
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-fix-bottom-expansion-diff-comment.yml b/changelogs/unreleased/osw-fix-bottom-expansion-diff-comment.yml
deleted file mode 100644
index b2ac53312ae..00000000000
--- a/changelogs/unreleased/osw-fix-bottom-expansion-diff-comment.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjusts duplicated line when commenting on unfolded diff lines (in the bottom)
-merge_request: 24201
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-multi-line-suggestions-creation-strategy.yml b/changelogs/unreleased/osw-multi-line-suggestions-creation-strategy.yml
new file mode 100644
index 00000000000..01bd7ede270
--- /dev/null
+++ b/changelogs/unreleased/osw-multi-line-suggestions-creation-strategy.yml
@@ -0,0 +1,5 @@
+---
+title: Implements the creation strategy for multi-line suggestions
+merge_request: 26057
+author:
+type: changed
diff --git a/changelogs/unreleased/osw-multi-line-suggestions-parsing.yml b/changelogs/unreleased/osw-multi-line-suggestions-parsing.yml
new file mode 100644
index 00000000000..985b01e9254
--- /dev/null
+++ b/changelogs/unreleased/osw-multi-line-suggestions-parsing.yml
@@ -0,0 +1,5 @@
+---
+title: Prepare multi-line suggestions for rendering in Markdown
+merge_request: 26107
+author:
+type: other
diff --git a/changelogs/unreleased/patch-38.yml b/changelogs/unreleased/patch-38.yml
deleted file mode 100644
index 9179fc6846e..00000000000
--- a/changelogs/unreleased/patch-38.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: fix display comment avatars issue in IE 11
-merge_request: 24777
-author: Gokhan Apaydin
-type: fixed
diff --git a/changelogs/unreleased/pl-serialize-ac-parameters.yml b/changelogs/unreleased/pl-serialize-ac-parameters.yml
deleted file mode 100644
index aad222b5506..00000000000
--- a/changelogs/unreleased/pl-serialize-ac-parameters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Make `ActionController::Parameters` serializable for sidekiq jobs
-merge_request: 24864
-author:
-type: fixed
diff --git a/changelogs/unreleased/pravi-gitlab-ce-update-recaptcha.yml b/changelogs/unreleased/pravi-gitlab-ce-update-recaptcha.yml
new file mode 100644
index 00000000000..95379fb2ec1
--- /dev/null
+++ b/changelogs/unreleased/pravi-gitlab-ce-update-recaptcha.yml
@@ -0,0 +1,5 @@
+---
+title: Apply recaptcha API change in 4.0
+merge_request: 25921
+author: Praveen Arimbrathodiyil
+type: other
diff --git a/changelogs/unreleased/profile-project-empty-state.yml b/changelogs/unreleased/profile-project-empty-state.yml
deleted file mode 100644
index 484306d5b98..00000000000
--- a/changelogs/unreleased/profile-project-empty-state.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added empty project illustration and updated text to user profile overview
-merge_request: 23973
-author: Fernando Arias
-type: changed
diff --git a/changelogs/unreleased/raise-on-unfiltered-params.yml b/changelogs/unreleased/raise-on-unfiltered-params.yml
deleted file mode 100644
index 531e9ba807e..00000000000
--- a/changelogs/unreleased/raise-on-unfiltered-params.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Actually set raise_on_unfiltered_parameters to true
-merge_request: 24443
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/ravlen-fix-spaces-unicode.yml b/changelogs/unreleased/ravlen-fix-spaces-unicode.yml
deleted file mode 100644
index fbcbdc53cfe..00000000000
--- a/changelogs/unreleased/ravlen-fix-spaces-unicode.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Correct non-standard unicode spaces to regular unicode
-merge_request: 24795
-author: Marcel Amirault
-type: other
diff --git a/changelogs/unreleased/rd-update-last_activity_on-on-logins-and-browsing-activity-54947.yml b/changelogs/unreleased/rd-update-last_activity_on-on-logins-and-browsing-activity-54947.yml
deleted file mode 100644
index abce9dcc0c6..00000000000
--- a/changelogs/unreleased/rd-update-last_activity_on-on-logins-and-browsing-activity-54947.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update last_activity_on for Users on some main GET endpoints
-merge_request: 24642
-author:
-type: changed
diff --git a/changelogs/unreleased/recreate-all-diffs-on-import.yml b/changelogs/unreleased/recreate-all-diffs-on-import.yml
new file mode 100644
index 00000000000..fd9124372f3
--- /dev/null
+++ b/changelogs/unreleased/recreate-all-diffs-on-import.yml
@@ -0,0 +1,5 @@
+---
+title: Force to recreate all MR diffs on import
+merge_request: 26480
+author:
+type: fixed
diff --git a/changelogs/unreleased/refactor-56366-extract-resolve-discussion-button.yml b/changelogs/unreleased/refactor-56366-extract-resolve-discussion-button.yml
deleted file mode 100644
index 98859e8aa07..00000000000
--- a/changelogs/unreleased/refactor-56366-extract-resolve-discussion-button.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Refactored NoteableDiscussion by extracting ResolveDiscussionButton
-merge_request: 24505
-author: Martin Hobert
-type: other
diff --git a/changelogs/unreleased/refactor-56369-extract-jump-to-next-discussion-button.yml b/changelogs/unreleased/refactor-56369-extract-jump-to-next-discussion-button.yml
deleted file mode 100644
index 9a0d16c2d70..00000000000
--- a/changelogs/unreleased/refactor-56369-extract-jump-to-next-discussion-button.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extracted JumpToNextDiscussionButton to its own component
-author: Martin Hobert
-merge_request: 24506
-type: other
diff --git a/changelogs/unreleased/refactor-56370-extract-reply-placeholder-component.yml b/changelogs/unreleased/refactor-56370-extract-reply-placeholder-component.yml
deleted file mode 100644
index a216d294b30..00000000000
--- a/changelogs/unreleased/refactor-56370-extract-reply-placeholder-component.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Extracted ReplyPlaceholder to its own component
-merge_request: 24507
-author: Martin Hobert
-type: other
diff --git a/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml b/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml
deleted file mode 100644
index 06546bc5a8e..00000000000
--- a/changelogs/unreleased/remove-cancel-all-button-in-job-list-view.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove Cancel all jobs button in general jobs list view
-merge_request:
-author: Jordi Llull
-type: removed
diff --git a/changelogs/unreleased/remove-diff-coloring.yml b/changelogs/unreleased/remove-diff-coloring.yml
deleted file mode 100644
index 1ee1b525c35..00000000000
--- a/changelogs/unreleased/remove-diff-coloring.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'remove red/green colors from diff view of no-color syntax theme'
-merge_request: 24582
-author: khm
-type: changed
diff --git a/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml b/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml
deleted file mode 100644
index ce8e1829b48..00000000000
--- a/changelogs/unreleased/remove-gap-between-mr-tabs-and-file-header.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove extra space between MR tab bar and sticky file headers
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/remove-second-primary-button-on-wiki-edit.yml b/changelogs/unreleased/remove-second-primary-button-on-wiki-edit.yml
deleted file mode 100644
index 045fbbb48b7..00000000000
--- a/changelogs/unreleased/remove-second-primary-button-on-wiki-edit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Remove second primary button on wiki edit
-merge_request: 19959
-author: George Tsiolis
-type: changed
diff --git a/changelogs/unreleased/rs-admin-user-case-insensitive.yml b/changelogs/unreleased/rs-admin-user-case-insensitive.yml
deleted file mode 100644
index 40398c46a1e..00000000000
--- a/changelogs/unreleased/rs-admin-user-case-insensitive.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Admin section finds users case-insensitively
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/search-title.yml b/changelogs/unreleased/search-title.yml
deleted file mode 100644
index ff0933ed0b2..00000000000
--- a/changelogs/unreleased/search-title.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add 'in' filter that modifies scope of 'search' filter to issues and merge requests API
-merge_request: 24350
-author: Hiroyuki Sato
-type: added
diff --git a/changelogs/unreleased/security-22076-sanitize-url-in-names.yml b/changelogs/unreleased/security-22076-sanitize-url-in-names.yml
deleted file mode 100644
index 4e0ad4dd4c4..00000000000
--- a/changelogs/unreleased/security-22076-sanitize-url-in-names.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Sanitize user full name to clean up any URL to prevent mail clients from auto-linking
- URLs
-merge_request: 2793
-author:
-type: security
diff --git a/changelogs/unreleased/security-2770-verify-bundle-import-files.yml b/changelogs/unreleased/security-2770-verify-bundle-import-files.yml
deleted file mode 100644
index dea40dd1ef1..00000000000
--- a/changelogs/unreleased/security-2770-verify-bundle-import-files.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Validate bundle files before unpacking them
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-55320-stored-xss-in-user-status.yml b/changelogs/unreleased/security-55320-stored-xss-in-user-status.yml
deleted file mode 100644
index 8ea9ae0ccdf..00000000000
--- a/changelogs/unreleased/security-55320-stored-xss-in-user-status.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use sanitized user status message for user popover
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/security-stored-xss-via-katex.yml b/changelogs/unreleased/security-stored-xss-via-katex.yml
deleted file mode 100644
index a71ae1123f2..00000000000
--- a/changelogs/unreleased/security-stored-xss-via-katex.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed XSS content in KaTex links
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/sh-cache-root-ref-asymetrically.yml b/changelogs/unreleased/sh-cache-root-ref-asymetrically.yml
new file mode 100644
index 00000000000..106d070cc05
--- /dev/null
+++ b/changelogs/unreleased/sh-cache-root-ref-asymetrically.yml
@@ -0,0 +1,5 @@
+---
+title: Cache Repository#root_ref within a request
+merge_request: 25903
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-clear-pipeline-status-cache-upon-destroy.yml b/changelogs/unreleased/sh-clear-pipeline-status-cache-upon-destroy.yml
new file mode 100644
index 00000000000..55779f0f9d3
--- /dev/null
+++ b/changelogs/unreleased/sh-clear-pipeline-status-cache-upon-destroy.yml
@@ -0,0 +1,5 @@
+---
+title: Clear pipeline status cache after destruction of pipeline
+merge_request: 26575
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-disable-nil-user-id-identity-validation.yml b/changelogs/unreleased/sh-disable-nil-user-id-identity-validation.yml
deleted file mode 100644
index 5af3bdce51b..00000000000
--- a/changelogs/unreleased/sh-disable-nil-user-id-identity-validation.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix failed LDAP logins when nil user_id present
-merge_request: 24749
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-encode-content-disposition.yml b/changelogs/unreleased/sh-encode-content-disposition.yml
deleted file mode 100644
index b40ee6a85a8..00000000000
--- a/changelogs/unreleased/sh-encode-content-disposition.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Encode Content-Disposition filenames
-merge_request: 24919
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml b/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml
deleted file mode 100644
index d1d4412eb50..00000000000
--- a/changelogs/unreleased/sh-fix-backfill-project-repo-migration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix duplicate project disk path in BackfillLegacyProjectRepositories
-merge_request: 24213
-author:
-type: changed
diff --git a/changelogs/unreleased/sh-fix-bitbucket-server-error-handling.yml b/changelogs/unreleased/sh-fix-bitbucket-server-error-handling.yml
deleted file mode 100644
index 87405fa0a78..00000000000
--- a/changelogs/unreleased/sh-fix-bitbucket-server-error-handling.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Bitbucket Server importer error handling
-merge_request: 24343
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-board-user-assigns.yml b/changelogs/unreleased/sh-fix-board-user-assigns.yml
deleted file mode 100644
index 89c228107f0..00000000000
--- a/changelogs/unreleased/sh-fix-board-user-assigns.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 403 errors when adding an assignee list in project boards
-merge_request: 25263
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-import-redirect-vulnerability.yml b/changelogs/unreleased/sh-fix-import-redirect-vulnerability.yml
deleted file mode 100644
index addf327b69d..00000000000
--- a/changelogs/unreleased/sh-fix-import-redirect-vulnerability.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Alias GitHub and BitBucket OAuth2 callback URLs
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/sh-fix-issue-59065.yml b/changelogs/unreleased/sh-fix-issue-59065.yml
new file mode 100644
index 00000000000..41cd5ce0960
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-issue-59065.yml
@@ -0,0 +1,5 @@
+---
+title: Fix Error 500 when user commits Wiki page with no commit message
+merge_request: 26247
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-fix-pages-zip-constant.yml b/changelogs/unreleased/sh-fix-pages-zip-constant.yml
deleted file mode 100644
index fcd8aa45825..00000000000
--- a/changelogs/unreleased/sh-fix-pages-zip-constant.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix uninitialized constant with GitLab Pages
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-snippet-uploads-path-lookup.yml b/changelogs/unreleased/sh-fix-snippet-uploads-path-lookup.yml
deleted file mode 100644
index 414c8663049..00000000000
--- a/changelogs/unreleased/sh-fix-snippet-uploads-path-lookup.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 404s with snippet uploads in object storage
-merge_request: 24550
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-fix-upload-snippets-with-relative-url-root.yml b/changelogs/unreleased/sh-fix-upload-snippets-with-relative-url-root.yml
deleted file mode 100644
index 8bc1e4b4f8a..00000000000
--- a/changelogs/unreleased/sh-fix-upload-snippets-with-relative-url-root.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix 404s for snippet uploads when relative URL root used
-merge_request: 24588
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-import-source-branch-github-forks.yml b/changelogs/unreleased/sh-import-source-branch-github-forks.yml
deleted file mode 100644
index b5ea60202c0..00000000000
--- a/changelogs/unreleased/sh-import-source-branch-github-forks.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Create the source branch for a GitHub import
-merge_request: 25064
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-include-project-path-for-internal-api.yml b/changelogs/unreleased/sh-include-project-path-for-internal-api.yml
deleted file mode 100644
index 1973049e9e3..00000000000
--- a/changelogs/unreleased/sh-include-project-path-for-internal-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Include gl_project_path in API /internal/allowed response
-merge_request: 25314
-author:
-type: other
diff --git a/changelogs/unreleased/sh-issue-53419-fix.yml b/changelogs/unreleased/sh-issue-53419-fix.yml
deleted file mode 100644
index ab8b65857e2..00000000000
--- a/changelogs/unreleased/sh-issue-53419-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix Bitbucket Server import not allowing personal projects
-merge_request: 23601
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-log-rails-queue-duration.yml b/changelogs/unreleased/sh-log-rails-queue-duration.yml
deleted file mode 100644
index 89390aef108..00000000000
--- a/changelogs/unreleased/sh-log-rails-queue-duration.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Log queue duration in production_json.log
-merge_request: 25075
-author:
-type: other
diff --git a/changelogs/unreleased/sh-optimize-projects-api.yml b/changelogs/unreleased/sh-optimize-projects-api.yml
new file mode 100644
index 00000000000..2f2459be77f
--- /dev/null
+++ b/changelogs/unreleased/sh-optimize-projects-api.yml
@@ -0,0 +1,5 @@
+---
+title: Optimize /api/v4/projects endpoint for visibility level
+merge_request: 26481
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-preload-associations-for-group-api.yml b/changelogs/unreleased/sh-preload-associations-for-group-api.yml
deleted file mode 100644
index 24e424b7efb..00000000000
--- a/changelogs/unreleased/sh-preload-associations-for-group-api.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Eliminate N+1 queries in /api/groups/:id
-merge_request: 24513
-author:
-type: performance
diff --git a/changelogs/unreleased/sh-reject-info-refs-head-requests.yml b/changelogs/unreleased/sh-reject-info-refs-head-requests.yml
new file mode 100644
index 00000000000..0dca18e2fd8
--- /dev/null
+++ b/changelogs/unreleased/sh-reject-info-refs-head-requests.yml
@@ -0,0 +1,5 @@
+---
+title: Reject HEAD requests to info/refs endpoint
+merge_request: 26334
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-skip-sti-tables-reltuples.yml b/changelogs/unreleased/sh-skip-sti-tables-reltuples.yml
new file mode 100644
index 00000000000..5bf0ccf3e9d
--- /dev/null
+++ b/changelogs/unreleased/sh-skip-sti-tables-reltuples.yml
@@ -0,0 +1,5 @@
+---
+title: Fix counting of groups in admin dashboard
+merge_request: 26009
+author:
+type: fixed
diff --git a/changelogs/unreleased/shared_with_group_path.yml b/changelogs/unreleased/shared_with_group_path.yml
deleted file mode 100644
index 73ba9a9f30a..00000000000
--- a/changelogs/unreleased/shared_with_group_path.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add group full path to project's shared_with_groups
-merge_request: 24052
-author: Mathieu Parent
-type: added
diff --git a/changelogs/unreleased/support-chunking-in-client.yml b/changelogs/unreleased/support-chunking-in-client.yml
deleted file mode 100644
index e50648ea4b2..00000000000
--- a/changelogs/unreleased/support-chunking-in-client.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix code search when text is larger than max gRPC message size
-merge_request: 24111
-author:
-type: changed
diff --git a/changelogs/unreleased/support-only-changes-on-mr-pipelines.yml b/changelogs/unreleased/support-only-changes-on-mr-pipelines.yml
deleted file mode 100644
index fbab898b799..00000000000
--- a/changelogs/unreleased/support-only-changes-on-mr-pipelines.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'Support `only: changes:` on MR pipelines'
-merge_request: 24490
-author: Hiroyuki Sato
-type: added
diff --git a/changelogs/unreleased/syntax-highlighting-again.yml b/changelogs/unreleased/syntax-highlighting-again.yml
deleted file mode 100644
index 14223fc0927..00000000000
--- a/changelogs/unreleased/syntax-highlighting-again.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix suggested changes syntax highlighting
-merge_request: 25116
-author:
-type: fixed
diff --git a/changelogs/unreleased/test-permissions.yml b/changelogs/unreleased/test-permissions.yml
deleted file mode 100644
index cfb69fdcb1e..00000000000
--- a/changelogs/unreleased/test-permissions.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disallows unauthorized users from accessing the pipelines section.
-merge_request:
-author:
-type: security
diff --git a/changelogs/unreleased/tooltips-to-top.yml b/changelogs/unreleased/tooltips-to-top.yml
deleted file mode 100644
index 51bf127089e..00000000000
--- a/changelogs/unreleased/tooltips-to-top.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Change spawning of tooltips to be top by default
-merge_request: 21223
-author:
-type: changed
diff --git a/changelogs/unreleased/tpresa-add-highest-role-to-user.yml b/changelogs/unreleased/tpresa-add-highest-role-to-user.yml
new file mode 100644
index 00000000000..9714d8dcc99
--- /dev/null
+++ b/changelogs/unreleased/tpresa-add-highest-role-to-user.yml
@@ -0,0 +1,5 @@
+---
+title: Adding highest role property to admin's user details page
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml b/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml
deleted file mode 100644
index 1af1fe09f33..00000000000
--- a/changelogs/unreleased/twang2218-gitlab-ce-i18n-extract-app-views-search.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: 'i18n: externalize strings from ''app/views/search'''
-merge_request: 24297
-author: Tao Wang
-type: other
diff --git a/changelogs/unreleased/update-gitaly.yml b/changelogs/unreleased/update-gitaly.yml
deleted file mode 100644
index 4ba42a689a7..00000000000
--- a/changelogs/unreleased/update-gitaly.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Gitaly to v1.17.0
-merge_request: 24873
-author:
-type: other
diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-1-45.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-1-45.yml
deleted file mode 100644
index 7d92929221f..00000000000
--- a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-1-45.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update GitLab Runner Helm Chart to 0.1.45
-merge_request: 24564
-author:
-type: other
diff --git a/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-3-0.yml b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-3-0.yml
new file mode 100644
index 00000000000..2e1adb1e1e9
--- /dev/null
+++ b/changelogs/unreleased/update-gitlab-runner-helm-chart-to-0-3-0.yml
@@ -0,0 +1,5 @@
+---
+title: Update GitLab Runner Helm Chart to 0.3.0/11.9.0
+merge_request: 26467
+author:
+type: other
diff --git a/changelogs/unreleased/update-gitlab-styles.yml b/changelogs/unreleased/update-gitlab-styles.yml
deleted file mode 100644
index 379f0ad4486..00000000000
--- a/changelogs/unreleased/update-gitlab-styles.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update gitlab-styles to 2.5.1
-merge_request: 24336
-author: Jasper Maes
-type: other
diff --git a/changelogs/unreleased/update-pages-config-only-when-changed.yml b/changelogs/unreleased/update-pages-config-only-when-changed.yml
deleted file mode 100644
index 8d9e02df678..00000000000
--- a/changelogs/unreleased/update-pages-config-only-when-changed.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Do not reload daemon if configuration file of pages does not change
-merge_request:
-author:
-type: performance
diff --git a/changelogs/unreleased/update-pages-extensionless-urls.yml b/changelogs/unreleased/update-pages-extensionless-urls.yml
deleted file mode 100644
index 13b3e1df500..00000000000
--- a/changelogs/unreleased/update-pages-extensionless-urls.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Add support for extensionless pages URLs
-merge_request: 24876
-author:
-type: added
diff --git a/changelogs/unreleased/update-rack-oauth2.yml b/changelogs/unreleased/update-rack-oauth2.yml
new file mode 100644
index 00000000000..dc2e7017695
--- /dev/null
+++ b/changelogs/unreleased/update-rack-oauth2.yml
@@ -0,0 +1,5 @@
+---
+title: Update rack-oauth2 1.2.1 -> 1.9.3
+merge_request: 17868
+author:
+type: other
diff --git a/changelogs/unreleased/update-sidekiq-cron.yml b/changelogs/unreleased/update-sidekiq-cron.yml
deleted file mode 100644
index edce32e3753..00000000000
--- a/changelogs/unreleased/update-sidekiq-cron.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Update sidekiq-cron to 1.0.4 and use fugit to replace rufus-scheduler to parse
- cron syntax
-merge_request: 24235
-author:
-type: other
diff --git a/changelogs/unreleased/update-smooshpack.yml b/changelogs/unreleased/update-smooshpack.yml
deleted file mode 100644
index a9222088d53..00000000000
--- a/changelogs/unreleased/update-smooshpack.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgraded Codesandbox smooshpack package
-merge_request:
-author:
-type: other
diff --git a/changelogs/unreleased/update-spriteicon-from-icon-on-profile.yml b/changelogs/unreleased/update-spriteicon-from-icon-on-profile.yml
deleted file mode 100644
index 32259bfacd4..00000000000
--- a/changelogs/unreleased/update-spriteicon-from-icon-on-profile.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update to GitLab SVG icon from Font Awesome in profile for location and work
-merge_request: 24671
-author: Yoginth
-type: changed
diff --git a/changelogs/unreleased/update-ui-admin-appearance.yml b/changelogs/unreleased/update-ui-admin-appearance.yml
deleted file mode 100644
index 7bc35029d77..00000000000
--- a/changelogs/unreleased/update-ui-admin-appearance.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update UI for admin appearance settings
-merge_request: 24685
-author:
-type: other
diff --git a/changelogs/unreleased/update-workhorse-8-2-0.yml b/changelogs/unreleased/update-workhorse-8-2-0.yml
deleted file mode 100644
index 7d593917a25..00000000000
--- a/changelogs/unreleased/update-workhorse-8-2-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Workhorse to v8.2.0
-merge_request: 24909
-author:
-type: fixed
diff --git a/changelogs/unreleased/use-deployment-relation-to-fetch-environment-ce.yml b/changelogs/unreleased/use-deployment-relation-to-fetch-environment-ce.yml
deleted file mode 100644
index 1ec276b4abc..00000000000
--- a/changelogs/unreleased/use-deployment-relation-to-fetch-environment-ce.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use deployment relation to get an environment name
-merge_request: 24890
-author:
-type: performance
diff --git a/changelogs/unreleased/use-only-all-pipelines.yml b/changelogs/unreleased/use-only-all-pipelines.yml
new file mode 100644
index 00000000000..68364d2a923
--- /dev/null
+++ b/changelogs/unreleased/use-only-all-pipelines.yml
@@ -0,0 +1,5 @@
+---
+title: Refactor all_pipelines in Merge request
+merge_request: 25676
+author:
+type: other
diff --git a/changelogs/unreleased/use_upgrade_install_for_helm_apps.yml b/changelogs/unreleased/use_upgrade_install_for_helm_apps.yml
deleted file mode 100644
index b41c3cfa1ab..00000000000
--- a/changelogs/unreleased/use_upgrade_install_for_helm_apps.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added ability to upgrade cluster applications
-merge_request: 24789
-author:
-type: added
diff --git a/changelogs/unreleased/web-ide-commit-header-icon-alignment-fix.yml b/changelogs/unreleased/web-ide-commit-header-icon-alignment-fix.yml
deleted file mode 100644
index 7a6bda1580d..00000000000
--- a/changelogs/unreleased/web-ide-commit-header-icon-alignment-fix.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fixed alignment of changed icon in Web IDE
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/winh-add-list-dropdown-height.yml b/changelogs/unreleased/winh-add-list-dropdown-height.yml
deleted file mode 100644
index 6bcedc15cc9..00000000000
--- a/changelogs/unreleased/winh-add-list-dropdown-height.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Adjust height of "Add list" dropdown in issue boards
-merge_request: 24227
-author:
-type: fixed
diff --git a/changelogs/unreleased/winh-toggle-comment-draft.yml b/changelogs/unreleased/winh-toggle-comment-draft.yml
new file mode 100644
index 00000000000..6b4aad55a05
--- /dev/null
+++ b/changelogs/unreleased/winh-toggle-comment-draft.yml
@@ -0,0 +1,5 @@
+---
+title: Display draft when toggling replies
+merge_request: 25563
+author:
+type: fixed
diff --git a/changelogs/unreleased/workhorse-8-3-0.yml b/changelogs/unreleased/workhorse-8-3-0.yml
deleted file mode 100644
index 6ae01d64ae5..00000000000
--- a/changelogs/unreleased/workhorse-8-3-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Update Workhorse to v8.3.0
-merge_request: 24959
-author:
-type: other
diff --git a/changelogs/unreleased/yoginth-avatar-on-settings-sidebar.yml b/changelogs/unreleased/yoginth-avatar-on-settings-sidebar.yml
deleted file mode 100644
index 0ec76f9ce02..00000000000
--- a/changelogs/unreleased/yoginth-avatar-on-settings-sidebar.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Added Avatar in the settings sidebar
-merge_request: 24515
-author: Yoginth
-type: changed
diff --git a/changelogs/unreleased/zj-feature-gate-set-project-path.yml b/changelogs/unreleased/zj-feature-gate-set-project-path.yml
deleted file mode 100644
index b426a2f3fe7..00000000000
--- a/changelogs/unreleased/zj-feature-gate-set-project-path.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Allow setting of feature gates per project
-merge_request: 24184
-author:
-type: added
diff --git a/config/application.rb b/config/application.rb
index 49e7f5836e4..6bdf61edfb1 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -94,6 +94,7 @@ module Gitlab
# - Webhook URLs (:hook)
# - Sentry DSN (:sentry_dsn)
# - File content from Web Editor (:content)
+ # - Jira shared secret (:sharedSecret)
#
# NOTE: It is **IMPORTANT** to also update gitlab-workhorse's filter when adding parameters here to not
# introduce another security vulnerability: https://gitlab.com/gitlab-org/gitlab-workhorse/issues/182
@@ -108,6 +109,7 @@ module Gitlab
trace
variables
content
+ sharedSecret
)
# Enable escaping HTML in JSON.
@@ -147,6 +149,8 @@ module Gitlab
config.assets.precompile << "errors.css"
config.assets.precompile << "csslab.css"
+ config.assets.precompile << "highlight/themes/*.css"
+
# Import gitlab-svgs directly from vendored directory
config.assets.paths << "#{config.root}/node_modules/@gitlab/svgs/dist"
config.assets.precompile << "icons.svg"
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index be23166cb7b..eba7d2b9fb7 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -227,7 +227,7 @@ production: &base
# endpoint: 'http://127.0.0.1:9000' # default: nil
# path_style: true # Use 'host/bucket_name/object' instead of 'bucket_name.host/object'
- ## Packages (maven repository so far)
+ ## Packages (maven repository, npm registry, etc...)
packages:
enabled: false
@@ -379,19 +379,54 @@ production: &base
# "start_tls" or "simple_tls". Defaults to true.
verify_certificates: true
- # Specifies the path to a file containing a PEM-format CA certificate,
- # e.g. if you need to use an internal CA.
- #
- # Example: '/etc/ca.pem'
- #
- ca_file: ''
-
- # Specifies the SSL version for OpenSSL to use, if the OpenSSL default
- # is not appropriate.
- #
- # Example: 'TLSv1_1'
- #
- ssl_version: ''
+ # OpenSSL::SSL::SSLContext options.
+ tls_options:
+ # Specifies the path to a file containing a PEM-format CA certificate,
+ # e.g. if you need to use an internal CA.
+ #
+ # Example: '/etc/ca.pem'
+ #
+ ca_file: ''
+
+ # Specifies the SSL version for OpenSSL to use, if the OpenSSL default
+ # is not appropriate.
+ #
+ # Example: 'TLSv1_1'
+ #
+ ssl_version: ''
+
+ # Specific SSL ciphers to use in communication with LDAP servers.
+ #
+ # Example: 'ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2'
+ ciphers: ''
+
+ # Client certificate
+ #
+ # Example:
+ # cert: |
+ # -----BEGIN CERTIFICATE-----
+ # MIIDbDCCAlSgAwIBAgIGAWkJxLmKMA0GCSqGSIb3DQEBCwUAMHcxFDASBgNVBAoTC0dvb2dsZSBJ
+ # bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQDEwtMREFQIENsaWVudDEPMA0GA1UE
+ # CxMGR1N1aXRlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTAeFw0xOTAyMjAwNzE4
+ # rntnF4d+0dd7zP3jrWkbdtoqjLDT/5D7NYRmVCD5vizV98FJ5//PIHbD1gL3a9b2MPAc6k7NV8tl
+ # ...
+ # 4SbuJPAiJxC1LQ0t39dR6oMCAMab3hXQqhL56LrR6cRBp6Mtlphv7alu9xb/x51y2x+g2zWtsf80
+ # Jrv/vKMsIh/sAyuogb7hqMtp55ecnKxceg==
+ # -----END CERTIFICATE -----
+ cert: ''
+
+ # Client private key
+ # key: |
+ # -----BEGIN PRIVATE KEY-----
+ # MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3DmJtLRmJGY4xU1QtI3yjvxO6
+ # bNuyE4z1NF6Xn7VSbcAaQtavWQ6GZi5uukMo+W5DHVtEkgDwh92ySZMuJdJogFbNvJvHAayheCdN
+ # 7mCQ2UUT9jGXIbmksUn9QMeJVXTZjgJWJzPXToeUdinx9G7+lpVa62UATEd1gaI3oyL72WmpDy/C
+ # rntnF4d+0dd7zP3jrWkbdtoqjLDT/5D7NYRmVCD5vizV98FJ5//PIHbD1gL3a9b2MPAc6k7NV8tl
+ # ...
+ # +9IhSYX+XIg7BZOVDeYqlPfxRvQh8vy3qjt/KUihmEPioAjLaGiihs1Fk5ctLk9A2hIUyP+sEQv9
+ # l6RG+a/mW+0rCWn8JAd464Ps9hE=
+ # -----END PRIVATE KEY-----
+ key: ''
# Set a timeout, in seconds, for LDAP queries. This helps avoid blocking
# a request if the LDAP server becomes unresponsive.
@@ -653,8 +688,8 @@ production: &base
# # Turns on AWS Server-Side Encryption with Amazon S3-Managed Keys for backups, this is optional
# # encryption: 'AES256'
# # Turns on AWS Server-Side Encryption with Amazon Customer-Provided Encryption Keys for backups, this is optional
- # # This should be set to the 256-bit, base64-encoded encryption key for Amazon S3 to use to encrypt or decrypt your data.
- # # 'encryption' must also be set in order for this to have any effect.
+ # # This should be set to the 256-bit, base64-encoded encryption key for Amazon S3 to use to encrypt or decrypt your data.
+ # # 'encryption' must also be set in order for this to have any effect.
# # encryption_key: '<base64 key>'
# # Specifies Amazon S3 storage class to use for backups, this is optional
# # storage_class: 'STANDARD'
@@ -662,7 +697,7 @@ production: &base
## GitLab Shell settings
gitlab_shell:
path: /home/git/gitlab-shell/
- hooks_path: /home/git/gitlab-shell/hooks/
+ authorized_keys_file: /home/git/.ssh/authorized_keys
# File that contains the secret key for verifying access for gitlab-shell.
# Default is '.gitlab_shell_secret' relative to Rails.root (i.e. root of the GitLab app).
@@ -820,7 +855,7 @@ test:
path: tmp/tests/backups
gitlab_shell:
path: tmp/tests/gitlab-shell/
- hooks_path: tmp/tests/gitlab-shell/hooks/
+ authorized_keys_file: tmp/tests/authorized_keys
issues_tracker:
redmine:
title: "Redmine"
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index dfcf1e648b4..99bdf5a95c2 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -40,6 +40,24 @@ if Settings.ldap['enabled'] || Rails.env.test?
# Since GitLab 10.0, verify_certificates defaults to true for security.
server['verify_certificates'] = true if server['verify_certificates'].nil?
+ # Expose ability to set `tls_options` directly. Deprecate `ca_file` and
+ # `ssl_version` in favor of `tls_options` hash option.
+ server['tls_options'] ||= {}
+
+ if server['ssl_version'] || server['ca_file']
+ Rails.logger.warn 'DEPRECATED: LDAP options `ssl_version` and `ca_file` should be nested within `tls_options`'
+ end
+
+ if server['ssl_version']
+ server['tls_options']['ssl_version'] ||= server['ssl_version']
+ server.delete('ssl_version')
+ end
+
+ if server['ca_file']
+ server['tls_options']['ca_file'] ||= server['ca_file']
+ server.delete('ca_file')
+ end
+
Settings.ldap['servers'][key] = server
end
end
@@ -337,7 +355,8 @@ Settings['sidekiq']['log_format'] ||= 'default'
#
Settings['gitlab_shell'] ||= Settingslogic.new({})
Settings.gitlab_shell['path'] = Settings.absolute(Settings.gitlab_shell['path'] || Settings.gitlab['user_home'] + '/gitlab-shell/')
-Settings.gitlab_shell['hooks_path'] = Settings.absolute(Settings.gitlab_shell['hooks_path'] || Settings.gitlab['user_home'] + '/gitlab-shell/hooks/')
+Settings.gitlab_shell['hooks_path'] = :deprecated_use_gitlab_shell_path_instead
+Settings.gitlab_shell['authorized_keys_file'] ||= nil
Settings.gitlab_shell['secret_file'] ||= Rails.root.join('.gitlab_shell_secret')
Settings.gitlab_shell['receive_pack'] = true if Settings.gitlab_shell['receive_pack'].nil?
Settings.gitlab_shell['upload_pack'] = true if Settings.gitlab_shell['upload_pack'].nil?
diff --git a/config/initializers/console_message.rb b/config/initializers/console_message.rb
index f7c26732e6d..05eb395028d 100644
--- a/config/initializers/console_message.rb
+++ b/config/initializers/console_message.rb
@@ -5,6 +5,6 @@ if defined?(Rails::Console)
puts "-------------------------------------------------------------------------------------"
puts " GitLab:".ljust(justify) + "#{Gitlab::VERSION} (#{Gitlab.revision})"
puts " GitLab Shell:".ljust(justify) + "#{Gitlab::VersionInfo.parse(Gitlab::Shell.new.version)}"
- puts " #{Gitlab::Database.adapter_name}:".ljust(justify) + Gitlab::Database.version
+ puts " #{Gitlab::Database.human_adapter_name}:".ljust(justify) + Gitlab::Database.version
puts "-------------------------------------------------------------------------------------"
end
diff --git a/config/initializers/fog_core_patch.rb b/config/initializers/fog_core_patch.rb
new file mode 100644
index 00000000000..d3d02216d45
--- /dev/null
+++ b/config/initializers/fog_core_patch.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+#
+# fog-core v2 changed the namespace format:
+#
+# Old: Fog::<service>::<provider> (e.g. Fog::Storage::AWS).
+# New: Fog::<provider>::<service> (e.g. Fog::AWS::Storage)
+#
+# To preserve backwards compatibility, fog-core v2.1.0 tries to load the
+# old schema first, but falls back to the older version if that
+# fails. This creates misleading warnings with fog-aws. See
+# https://github.com/fog/fog-aws/issues/504#issuecomment-468067991 for
+# more details.
+#
+# fog-core v2.1.2 reverses the load order
+# (https://github.com/fog/fog-core/pull/229), which works for fog-aws
+# but causes a stream of deprecation warnings for fog-google.
+# fog-google locked the dependency on fog-core v2.1.0 as a result
+# (https://github.com/fog/fog-google/issues/421) until the new namespace
+# is supported.
+#
+# Since we currently have some Fog gems that have not been updated, this
+# monkey patch makes a smarter decision about which namespace to try
+# first. This squelches a significant number of warning messages.
+#
+# Since this patch is mostly cosmetic, it can be removed safely at any
+# time, but it's probably best to wait until the following issues are
+# closed:
+#
+# fog-google: https://github.com/fog/fog-google/issues/421
+# fog-rackspace: https://github.com/fog/fog-rackspace/issues/29
+# fog-aliyun: https://github.com/fog/fog-aliyun/issues/23
+module Fog
+ module ServicesMixin
+ # Gems that have not yet updated with the new fog-core namespace
+ LEGACY_FOG_PROVIDERS = %w(google rackspace aliyun).freeze
+
+ def service_provider_constant(service_name, provider_name)
+ args = service_provider_search_args(service_name, provider_name)
+ Fog.const_get(args.first).const_get(*const_get_args(args.second))
+ rescue NameError # Try to find the constant from in an alternate location
+ Fog.const_get(args.second).const_get(*const_get_args(args.first))
+ end
+
+ def service_provider_search_args(service_name, provider_name)
+ if LEGACY_FOG_PROVIDERS.include?(provider_name.downcase)
+ [service_name, provider_name]
+ else
+ [provider_name, service_name]
+ end
+ end
+ end
+end
diff --git a/config/initializers/graphql.rb b/config/initializers/graphql.rb
new file mode 100644
index 00000000000..e653556231d
--- /dev/null
+++ b/config/initializers/graphql.rb
@@ -0,0 +1,4 @@
+# frozen_string_literal: true
+
+GraphQL::Field.accepts_definitions(authorize: GraphQL::Define.assign_metadata_key(:authorize))
+GraphQL::Schema::Field.accepts_definition(:authorize)
diff --git a/config/initializers/rspec_profiling.rb b/config/initializers/rspec_profiling.rb
index 2de310753a9..715e17057e0 100644
--- a/config/initializers/rspec_profiling.rb
+++ b/config/initializers/rspec_profiling.rb
@@ -1,7 +1,28 @@
+# frozen_string_literal: true
+
+return unless Rails.env.test?
+
module RspecProfilingExt
- module PSQL
- def establish_connection
- ::RspecProfiling::Collectors::PSQL::Result.establish_connection(ENV['RSPEC_PROFILING_POSTGRES_URL'])
+ module Collectors
+ class CSVWithTimestamps < ::RspecProfiling::Collectors::CSV
+ TIMESTAMP_FIELDS = %w(created_at updated_at).freeze
+ HEADERS = (::RspecProfiling::Collectors::CSV::HEADERS + TIMESTAMP_FIELDS).freeze
+
+ def insert(attributes)
+ output << HEADERS.map do |field|
+ if TIMESTAMP_FIELDS.include?(field)
+ Time.now
+ else
+ attributes.fetch(field.to_sym)
+ end
+ end
+ end
+
+ private
+
+ def output
+ @output ||= ::CSV.open(path, "w").tap { |csv| csv << HEADERS }
+ end
end
end
@@ -10,9 +31,13 @@ module RspecProfilingExt
if ENV['CI_COMMIT_REF_NAME']
"#{defined?(Gitlab::License) ? 'ee' : 'ce'}:#{ENV['CI_COMMIT_REF_NAME']}"
else
- super
+ super&.chomp
end
end
+
+ def sha
+ super&.chomp
+ end
end
module Run
@@ -30,16 +55,11 @@ module RspecProfilingExt
end
end
-if Rails.env.test?
- RspecProfiling.configure do |config|
- if ENV['RSPEC_PROFILING_POSTGRES_URL'].present?
- RspecProfiling::Collectors::PSQL.prepend(RspecProfilingExt::PSQL)
- config.collector = RspecProfiling::Collectors::PSQL
- end
-
- if ENV.key?('CI')
- RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git)
- RspecProfiling::Run.prepend(RspecProfilingExt::Run)
- end
+RspecProfiling.configure do |config|
+ if ENV.key?('CI') || ENV.key?('RSPEC_PROFILING')
+ RspecProfiling::VCS::Git.prepend(RspecProfilingExt::Git)
+ RspecProfiling::Run.prepend(RspecProfilingExt::Run)
+ config.collector = RspecProfilingExt::Collectors::CSVWithTimestamps
+ config.csv_path = -> { "rspec_profiling/#{Time.now.to_i}-#{SecureRandom.hex(8)}-rspec-data.csv" }
end
end
diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb
index abc91c3ae51..680cfa6f0ed 100644
--- a/config/initializers/sentry.rb
+++ b/config/initializers/sentry.rb
@@ -20,6 +20,21 @@ def configure_sentry
# Sanitize authentication headers
config.sanitize_http_headers = %w[Authorization Private-Token]
config.tags = { program: Gitlab.process_name }
+ # Debugging for https://gitlab.com/gitlab-org/gitlab-ce/issues/57727
+ config.before_send = lambda do |event, hint|
+ if ActiveModel::MissingAttributeError === hint[:exception]
+ columns_hash = ActiveRecord::Base
+ .connection
+ .schema_cache
+ .instance_variable_get(:@columns_hash)
+ .map { |k, v| [k, v.map(&:first)] }
+ .to_h
+
+ event.extra.merge!(columns_hash)
+ end
+
+ event
+ end
end
end
end
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index be4183f39be..2e4aa9c1053 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -11,6 +11,10 @@ queues_config_hash[:namespace] = Gitlab::Redis::Queues::SIDEKIQ_NAMESPACE
# Default is to retry 25 times with exponential backoff. That's too much.
Sidekiq.default_worker_options = { retry: 3 }
+if Rails.env.development?
+ Sidekiq.default_worker_options[:backtrace] = true
+end
+
enable_json_logs = Gitlab.config.sidekiq.log_format == 'json'
Sidekiq.configure_server do |config|
@@ -18,7 +22,7 @@ Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Gitlab::SidekiqMiddleware::ArgumentsLogger if ENV['SIDEKIQ_LOG_ARGUMENTS'] && !enable_json_logs
- chain.add Gitlab::SidekiqMiddleware::Shutdown
+ chain.add Gitlab::SidekiqMiddleware::MemoryKiller if ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS']
chain.add Gitlab::SidekiqMiddleware::RequestStoreMiddleware unless ENV['SIDEKIQ_REQUEST_STORE'] == '0'
chain.add Gitlab::SidekiqMiddleware::BatchLoader
chain.add Gitlab::SidekiqMiddleware::CorrelationLogger
@@ -73,6 +77,9 @@ Sidekiq.configure_server do |config|
# Avoid autoload issue such as 'Mail::Parsers::AddressStruct'
# https://github.com/mikel/mail/issues/912#issuecomment-214850355
Mail.eager_autoload!
+
+ # Ensure the whole process group is terminated if possible
+ Gitlab::SidekiqSignals.install!(Sidekiq::CLI::SIGNAL_HANDLERS)
end
Sidekiq.configure_client do |config|
diff --git a/config/initializers/trusted_proxies.rb b/config/initializers/trusted_proxies.rb
index 7af465d8443..13896408806 100644
--- a/config/initializers/trusted_proxies.rb
+++ b/config/initializers/trusted_proxies.rb
@@ -14,10 +14,8 @@ module Rack
end
gitlab_trusted_proxies = Array(Gitlab.config.gitlab.trusted_proxies).map do |proxy|
- begin
- IPAddr.new(proxy)
- rescue IPAddr::InvalidAddressError
- end
+ IPAddr.new(proxy)
+rescue IPAddr::InvalidAddressError
end.compact
Rails.application.config.action_dispatch.trusted_proxies = (
diff --git a/config/karma.config.js b/config/karma.config.js
index e1d7c30b1c2..7e1e89f3c10 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -6,6 +6,7 @@ const argumentsParser = require('commander');
const webpackConfig = require('./webpack.config.js');
const ROOT_PATH = path.resolve(__dirname, '..');
+const SPECS_PATH = /^(?:\.[\\\/])?(ee[\\\/])?spec[\\\/]javascripts[\\\/]/;
function fatalError(message) {
console.error(chalk.red(`\nError: ${message}\n`));
@@ -26,7 +27,7 @@ webpackConfig.devtool = 'cheap-inline-source-map';
webpackConfig.plugins.push(
new webpack.DefinePlugin({
'process.env.BABEL_ENV': JSON.stringify(process.env.BABEL_ENV || process.env.NODE_ENV || null),
- })
+ }),
);
const specFilters = argumentsParser
@@ -37,13 +38,23 @@ const specFilters = argumentsParser
memo.push(filter, filter.replace(/\/?$/, '/**/*.js'));
return memo;
},
- []
+ [],
)
.parse(process.argv).filterSpec;
-if (specFilters.length) {
- const specsPath = /^(?:\.[\\\/])?spec[\\\/]javascripts[\\\/]/;
+const createContext = (specFiles, regex, suffix) => {
+ const newContext = specFiles.reduce((context, file) => {
+ const relativePath = file.replace(SPECS_PATH, '');
+ context[file] = `./${relativePath}`;
+ return context;
+ }, {});
+
+ webpackConfig.plugins.push(
+ new webpack.ContextReplacementPlugin(regex, path.join(ROOT_PATH, suffix), newContext),
+ );
+};
+if (specFilters.length) {
// resolve filters
let filteredSpecFiles = specFilters.map(filter =>
glob
@@ -51,7 +62,7 @@ if (specFilters.length) {
root: ROOT_PATH,
matchBase: true,
})
- .filter(path => path.endsWith('spec.js'))
+ .filter(path => path.endsWith('spec.js')),
);
// flatten
@@ -64,23 +75,15 @@ if (specFilters.length) {
fatalError('Your filter did not match any test files.');
}
- if (!filteredSpecFiles.every(file => specsPath.test(file))) {
+ if (!filteredSpecFiles.every(file => SPECS_PATH.test(file))) {
fatalError('Test files must be located within /spec/javascripts.');
}
- const newContext = filteredSpecFiles.reduce((context, file) => {
- const relativePath = file.replace(specsPath, '');
- context[file] = `./${relativePath}`;
- return context;
- }, {});
+ const CE_FILES = filteredSpecFiles.filter(file => !file.startsWith('ee'));
+ createContext(CE_FILES, /[^e]{2}[\\\/]spec[\\\/]javascripts$/, 'spec/javascripts');
- webpackConfig.plugins.push(
- new webpack.ContextReplacementPlugin(
- /spec[\\\/]javascripts$/,
- path.join(ROOT_PATH, 'spec/javascripts'),
- newContext
- )
- );
+ const EE_FILES = filteredSpecFiles.filter(file => file.startsWith('ee'));
+ createContext(EE_FILES, /ee[\\\/]spec[\\\/]javascripts$/, 'ee/spec/javascripts');
}
// Karma configuration
@@ -107,22 +110,45 @@ module.exports = function(config) {
frameworks: ['jasmine'],
files: [
{ pattern: 'spec/javascripts/test_bundle.js', watched: false },
- { pattern: 'spec/javascripts/fixtures/**/*@(.json|.html|.html.raw|.png)', included: false },
+ { pattern: 'spec/javascripts/fixtures/**/*@(.json|.html|.png)', included: false },
],
preprocessors: {
'spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
+ 'ee/spec/javascripts/**/*.js': ['webpack', 'sourcemap'],
},
- reporters: ['progress'],
+ reporters: ['mocha'],
webpack: webpackConfig,
webpackMiddleware: { stats: 'errors-only' },
+ plugins: [
+ 'karma-chrome-launcher',
+ 'karma-coverage-istanbul-reporter',
+ 'karma-jasmine',
+ 'karma-junit-reporter',
+ 'karma-mocha-reporter',
+ 'karma-sourcemap-loader',
+ 'karma-webpack',
+ ],
};
if (process.env.CI) {
- karmaConfig.reporters = ['mocha', 'junit'];
+ karmaConfig.reporters.push('junit');
karmaConfig.junitReporter = {
outputFile: 'junit_karma.xml',
useBrowserName: false,
};
+ } else {
+ // ignore 404s in local environment because we are not fixing them and they bloat the log
+ function ignore404() {
+ return (request, response /* next */) => {
+ response.writeHead(404);
+ return response.end('NOT FOUND');
+ };
+ }
+
+ karmaConfig.middleware = ['ignore-404'];
+ karmaConfig.plugins.push({
+ 'middleware:ignore-404': ['factory', ignore404],
+ });
}
if (process.env.BABEL_ENV === 'coverage' || process.env.NODE_ENV === 'coverage') {
diff --git a/config/locales/en.yml b/config/locales/en.yml
index e8dbc033a7c..eb3b7771968 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -10,6 +10,10 @@ en:
target: Target issue
group:
path: Group URL
+ project/error_tracking_setting:
+ token: "Auth Token"
+ project: "Project"
+ api_url: "Sentry API URL"
errors:
messages:
label_already_exists_at_group_level: "already exists at group level for %{group}. Please choose another one."
diff --git a/config/routes.rb b/config/routes.rb
index 484e05114be..bbf00208545 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -43,6 +43,7 @@ Rails.application.routes.draw do
get '/autocomplete/users/:id' => 'autocomplete#user'
get '/autocomplete/projects' => 'autocomplete#projects'
get '/autocomplete/award_emojis' => 'autocomplete#award_emojis'
+ get '/autocomplete/merge_request_target_branches' => 'autocomplete#merge_request_target_branches'
# Search
get 'search' => 'search#show'
@@ -101,6 +102,7 @@ Rails.application.routes.draw do
member do
scope :applications do
post '/:application', to: 'clusters/applications#create', as: :install_applications
+ patch '/:application', to: 'clusters/applications#update', as: :update_applications
end
get :cluster_status, format: :json
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index af333bdc748..a01003b6039 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -120,6 +120,10 @@ namespace :admin do
get :resume
get :pause
end
+
+ collection do
+ get :tag_list, format: :json
+ end
end
resources :jobs, only: :index do
diff --git a/config/routes/git_http.rb b/config/routes/git_http.rb
index ec5c68f81df..a959d40881b 100644
--- a/config/routes/git_http.rb
+++ b/config/routes/git_http.rb
@@ -40,7 +40,7 @@ scope(path: '*namespace_id/:project_id',
# /info/refs?service=git-receive-pack, but nothing else.
#
git_http_handshake = lambda do |request|
- ::Constraints::ProjectUrlConstrainer.new.matches?(request) &&
+ ::Constraints::ProjectUrlConstrainer.new.matches?(request, existence_check: false) &&
(request.query_string.blank? ||
request.query_string.match(/\Aservice=git-(upload|receive)-pack\z/))
end
diff --git a/config/routes/group.rb b/config/routes/group.rb
index a0aeebe4b91..b300fcb757f 100644
--- a/config/routes/group.rb
+++ b/config/routes/group.rb
@@ -14,6 +14,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :issues, as: :issues_group
get :merge_requests, as: :merge_requests_group
get :projects, as: :projects_group
+ get :details, as: :details_group
get :activity, as: :activity_group
put :transfer, as: :transfer_group
# TODO: Remove as part of refactor in https://gitlab.com/gitlab-org/gitlab-ce/issues/49693
@@ -31,6 +32,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
namespace :settings do
resource :ci_cd, only: [:show], controller: 'ci_cd' do
put :reset_registration_token
+ patch :update_auto_devops
end
end
@@ -67,7 +69,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
end
end
- resources :boards, only: [:index, :show]
+ resources :boards, only: [:index, :show], constraints: { id: /\d+/ }
resources :runners, only: [:index, :edit, :update, :destroy, :show] do
member do
diff --git a/config/routes/project.rb b/config/routes/project.rb
index b4ebc7df4fe..d60a5cc9ae8 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -394,8 +394,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
get 'noteable/:target_type/:target_id/notes' => 'notes#index', as: 'noteable_notes'
- # On CE only index and show are needed
- resources :boards, only: [:index, :show]
+ resources :boards, only: [:index, :show], constraints: { id: /\d+/ }
resources :todos, only: [:create]
diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml
index 1e094c03171..cef123b86ae 100644
--- a/config/sidekiq_queues.yml
+++ b/config/sidekiq_queues.yml
@@ -68,6 +68,7 @@
- [background_migration, 1]
- [gcp_cluster, 1]
- [project_migrate_hashed_storage, 1]
+ - [project_rollback_hashed_storage, 1]
- [hashed_storage, 1]
- [pages_domain_verification, 1]
- [object_storage_upload, 1]
@@ -85,4 +86,6 @@
- [repository_cleanup, 1]
- [delete_stored_files, 1]
- [remote_mirror_notification, 2]
+ - [project_daily_statistics, 1]
- [import_issues_csv, 2]
+ - [chat_notification, 2]
diff --git a/config/webpack.config.js b/config/webpack.config.js
index cf9e77d2424..20b3f4c0264 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -1,3 +1,4 @@
+const fs = require('fs');
const path = require('path');
const glob = require('glob');
const webpack = require('webpack');
@@ -11,6 +12,10 @@ const ROOT_PATH = path.resolve(__dirname, '..');
const CACHE_PATH = process.env.WEBPACK_CACHE_PATH || path.join(ROOT_PATH, 'tmp/cache');
const IS_PRODUCTION = process.env.NODE_ENV === 'production';
const IS_DEV_SERVER = process.argv.join(' ').indexOf('webpack-dev-server') !== -1;
+const IS_EE =
+ process.env.EE !== undefined
+ ? JSON.parse(process.env.EE)
+ : fs.existsSync(path.join(ROOT_PATH, 'ee'));
const DEV_SERVER_HOST = process.env.DEV_SERVER_HOST || 'localhost';
const DEV_SERVER_PORT = parseInt(process.env.DEV_SERVER_PORT, 10) || 3808;
const DEV_SERVER_LIVERELOAD = IS_DEV_SERVER && process.env.DEV_SERVER_LIVERELOAD !== 'false';
@@ -44,6 +49,14 @@ function generateEntries() {
pageEntries.forEach(path => generateAutoEntries(path));
+ if (IS_EE) {
+ const eePageEntries = glob.sync('pages/**/index.js', {
+ cwd: path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
+ });
+ eePageEntries.forEach(path => generateAutoEntries(path, 'ee'));
+ watchAutoEntries.push(path.join(ROOT_PATH, 'ee/app/assets/javascripts/pages/'));
+ }
+
const autoEntryKeys = Object.keys(autoEntriesMap);
autoEntriesCount = autoEntryKeys.length;
@@ -68,6 +81,31 @@ function generateEntries() {
return Object.assign(manualEntries, autoEntries);
}
+const alias = {
+ '~': path.join(ROOT_PATH, 'app/assets/javascripts'),
+ emojis: path.join(ROOT_PATH, 'fixtures/emojis'),
+ empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'),
+ icons: path.join(ROOT_PATH, 'app/views/shared/icons'),
+ images: path.join(ROOT_PATH, 'app/assets/images'),
+ vendor: path.join(ROOT_PATH, 'vendor/assets/javascripts'),
+ vue$: 'vue/dist/vue.esm.js',
+ spec: path.join(ROOT_PATH, 'spec/javascripts'),
+
+ // the following resolves files which are different between CE and EE
+ ee_else_ce: path.join(ROOT_PATH, 'app/assets/javascripts'),
+};
+
+if (IS_EE) {
+ Object.assign(alias, {
+ ee: path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
+ ee_empty_states: path.join(ROOT_PATH, 'ee/app/views/shared/empty_states'),
+ ee_icons: path.join(ROOT_PATH, 'ee/app/views/shared/icons'),
+ ee_images: path.join(ROOT_PATH, 'ee/app/assets/images'),
+ ee_spec: path.join(ROOT_PATH, 'ee/spec/javascripts'),
+ ee_else_ce: path.join(ROOT_PATH, 'ee/app/assets/javascripts'),
+ });
+}
+
module.exports = {
mode: IS_PRODUCTION ? 'production' : 'development',
@@ -85,19 +123,7 @@ module.exports = {
resolve: {
extensions: ['.js', '.gql', '.graphql'],
- alias: {
- '~': path.join(ROOT_PATH, 'app/assets/javascripts'),
- emojis: path.join(ROOT_PATH, 'fixtures/emojis'),
- empty_states: path.join(ROOT_PATH, 'app/views/shared/empty_states'),
- icons: path.join(ROOT_PATH, 'app/views/shared/icons'),
- images: path.join(ROOT_PATH, 'app/assets/images'),
- vendor: path.join(ROOT_PATH, 'vendor/assets/javascripts'),
- vue$: 'vue/dist/vue.esm.js',
- spec: path.join(ROOT_PATH, 'spec/javascripts'),
-
- // the following resolves files which are different between CE and EE
- ee_else_ce: path.join(ROOT_PATH, 'app/assets/javascripts'),
- },
+ alias,
},
module: {
@@ -245,6 +271,17 @@ module.exports = {
jQuery: 'jquery',
}),
+ new webpack.NormalModuleReplacementPlugin(/^ee_component\/(.*)\.vue/, function(resource) {
+ if (Object.keys(module.exports.resolve.alias).indexOf('ee') >= 0) {
+ resource.request = resource.request.replace(/^ee_component/, 'ee');
+ } else {
+ resource.request = path.join(
+ ROOT_PATH,
+ 'app/assets/javascripts/vue_shared/components/empty_component.js',
+ );
+ }
+ }),
+
// compression can require a lot of compute time and is disabled in CI
IS_PRODUCTION && !NO_COMPRESSION && new CompressionPlugin(),
@@ -256,7 +293,7 @@ module.exports = {
const missingDeps = Array.from(compilation.missingDependencies);
const nodeModulesPath = path.join(ROOT_PATH, 'node_modules');
const hasMissingNodeModules = missingDeps.some(
- file => file.indexOf(nodeModulesPath) !== -1
+ file => file.indexOf(nodeModulesPath) !== -1,
);
// watch for changes to missing node_modules
@@ -267,7 +304,7 @@ module.exports = {
// report our auto-generated bundle count
console.log(
- `${autoEntriesCount} entries from '/pages' automatically added to webpack output.`
+ `${autoEntriesCount} entries from '/pages' automatically added to webpack output.`,
);
callback();
@@ -287,6 +324,10 @@ module.exports = {
reportFilename: path.join(ROOT_PATH, 'webpack-report/index.html'),
statsFilename: path.join(ROOT_PATH, 'webpack-report/stats.json'),
}),
+
+ new webpack.DefinePlugin({
+ 'process.env.EE': JSON.stringify(IS_EE),
+ }),
].filter(Boolean),
devServer: {
diff --git a/danger/commit_messages/Dangerfile b/danger/commit_messages/Dangerfile
index c20c8b77e6a..9be1ce2ff86 100644
--- a/danger/commit_messages/Dangerfile
+++ b/danger/commit_messages/Dangerfile
@@ -64,11 +64,12 @@ def too_many_changed_lines?(commit)
lines_changed_in_commit(commit) >= 30
end
-def lint_commits(commits)
- failures = false
- emoji_checker = EmojiChecker.new
+def emoji_checker
+ @emoji_checker ||= EmojiChecker.new
+end
- unicode_emoji_regex = %r((
+def unicode_emoji_regex
+ @unicode_emoji_regex ||= %r((
[\u{1F300}-\u{1F5FF}] |
[\u{1F1E6}-\u{1F1FF}] |
[\u{2700}-\u{27BF}] |
@@ -77,120 +78,132 @@ def lint_commits(commits)
[\u{1F680}-\u{1F6FF}] |
[\u{2600}-\u{26FF}]
))x
+end
+
+def lint_commit(commit)
+ # For now we'll ignore merge commits, as getting rid of those is a problem
+ # separate from enforcing good commit messages.
+ return false if commit.message.start_with?('Merge branch')
+
+ # We ignore revert commits as they are well structured by Git already
+ return false if commit.message.start_with?('Revert "')
+
+ failures = false
+ subject, separator, details = commit.message.split("\n", 3)
+
+ if subject.split.length < 3
+ fail_commit(
+ commit,
+ 'The commit subject must contain at least three words'
+ )
+
+ failures = true
+ end
+
+ if subject.length > 72
+ fail_commit(
+ commit,
+ 'The commit subject may not be longer than 72 characters'
+ )
+
+ failures = true
+ elsif subject.length > 50
+ warn_commit(
+ commit,
+ "This commit's subject line is acceptable, but please try to [reduce it to 50 characters](#{URL_LIMIT_SUBJECT})."
+ )
+ end
+
+ unless subject_starts_with_capital?(subject)
+ fail_commit(commit, 'The commit subject must start with a capital letter')
+ failures = true
+ end
+
+ if subject.end_with?('.')
+ fail_commit(commit, 'The commit subject must not end with a period')
+ failures = true
+ end
+
+ if separator && !separator.empty?
+ fail_commit(
+ commit,
+ 'The commit subject and body must be separated by a blank line'
+ )
+
+ failures = true
+ end
+
+ details&.each_line do |line|
+ line = line.strip
- commits.each do |commit|
- # For now we'll ignore merge commits, as getting rid of those is a problem
- # separate from enforcing good commit messages.
- next if commit.message.start_with?('Merge branch')
-
- subject, separator, details = commit.message.split("\n", 3)
-
- if subject.split.length < 3
- fail_commit(
- commit,
- 'The commit subject must contain at least three words'
- )
-
- failures = true
- end
-
- if subject.length > 72
- fail_commit(
- commit,
- 'The commit subject may not be longer than 72 characters'
- )
-
- failures = true
- elsif subject.length > 50
- warn_commit(
- commit,
- "This commit's subject line is acceptable, but please try to [reduce it to 50 characters](#{URL_LIMIT_SUBJECT})."
- )
- end
-
- unless subject_starts_with_capital?(subject)
- fail_commit(commit, 'The commit subject must start with a capital letter')
- failures = true
- end
-
- if subject.end_with?('.')
- fail_commit(commit, 'The commit subject must not end with a period')
- failures = true
- end
-
- if separator && !separator.empty?
- fail_commit(
- commit,
- 'The commit subject and body must be separated by a blank line'
- )
-
- failures = true
- end
-
- details&.each_line do |line|
- line = line.strip
-
- next if line.length <= 72
-
- url_size = line.scan(%r((https?://\S+))).sum { |(url)| url.length }
-
- # If the line includes a URL, we'll allow it to exceed 72 characters, but
- # only if the line _without_ the URL does not exceed this limit.
- next if line.length - url_size <= 72
-
- fail_commit(
- commit,
- 'The commit body should not contain more than 72 characters per line'
- )
-
- failures = true
- end
-
- if !details && too_many_changed_lines?(commit)
- fail_commit(
- commit,
- 'Commits that change 30 or more lines across at least three files ' \
- 'must describe these changes in the commit body'
- )
-
- failures = true
- end
-
- if emoji_checker.includes_emoji?(commit.message)
- fail_commit(
- commit,
- 'Avoid the use of Markdown Emoji such as `:+1:`. ' \
- 'These add no value to the commit message, ' \
- 'and are displayed as plain text outside of GitLab'
- )
-
- failures = true
- end
-
- if commit.message.match?(unicode_emoji_regex)
- fail_commit(
- commit,
- 'Avoid the use of Unicode Emoji. ' \
- 'These add no value to the commit message, ' \
- 'and may not be displayed properly everywhere'
- )
-
- failures = true
- end
-
- if commit.message.match?(%r(([\w\-\/]+)?(#|!|&|%)\d+\b))
- fail_commit(
- commit,
- 'Use full URLs instead of short references ' \
- '(`gitlab-org/gitlab-ce#123` or `!123`), as short references are ' \
- 'displayed as plain text outside of GitLab'
- )
-
- failures = true
- end
+ next if line.length <= 72
+
+ url_size = line.scan(%r((https?://\S+))).sum { |(url)| url.length }
+
+ # If the line includes a URL, we'll allow it to exceed 72 characters, but
+ # only if the line _without_ the URL does not exceed this limit.
+ next if line.length - url_size <= 72
+
+ fail_commit(
+ commit,
+ 'The commit body should not contain more than 72 characters per line'
+ )
+
+ failures = true
+ end
+
+ if !details && too_many_changed_lines?(commit)
+ fail_commit(
+ commit,
+ 'Commits that change 30 or more lines across at least three files ' \
+ 'must describe these changes in the commit body'
+ )
+
+ failures = true
+ end
+
+ if emoji_checker.includes_emoji?(commit.message)
+ fail_commit(
+ commit,
+ 'Avoid the use of Markdown Emoji such as `:+1:`. ' \
+ 'These add no value to the commit message, ' \
+ 'and are displayed as plain text outside of GitLab'
+ )
+
+ failures = true
+ end
+
+ if commit.message.match?(unicode_emoji_regex)
+ fail_commit(
+ commit,
+ 'Avoid the use of Unicode Emoji. ' \
+ 'These add no value to the commit message, ' \
+ 'and may not be displayed properly everywhere'
+ )
+
+ failures = true
+ end
+
+ if commit.message.match?(%r(([\w\-\/]+)?(#|!|&|%)\d+\b))
+ fail_commit(
+ commit,
+ 'Use full URLs instead of short references ' \
+ '(`gitlab-org/gitlab-ce#123` or `!123`), as short references are ' \
+ 'displayed as plain text outside of GitLab'
+ )
+
+ failures = true
+ end
+
+ failures
+end
+
+def lint_commits(commits)
+ failed = commits.select do |commit|
+ lint_commit(commit)
end
- if failures
+ if failed.any?
markdown(<<~MARKDOWN)
## Commit message standards
diff --git a/danger/documentation/Dangerfile b/danger/documentation/Dangerfile
index 2b09536f42a..96c0d9b7478 100644
--- a/danger/documentation/Dangerfile
+++ b/danger/documentation/Dangerfile
@@ -3,34 +3,20 @@
docs_paths_to_review = helper.changes_by_category[:docs]
unless docs_paths_to_review.empty?
- message 'This merge request adds or changes files that require a ' \
- 'review from the Docs team.'
+ message 'This merge request adds or changes files that require a review ' \
+ 'from the Technical Writing team.'
markdown(<<~MARKDOWN)
-## Docs review
+## Documentation review
-The following files require a review from the Documentation team:
+The following files require a review from a technical writer:
* #{docs_paths_to_review.map { |path| "`#{path}`" }.join("\n* ")}
-When your content is ready for review, assign the MR to a technical writer
-according to the [DevOps stages](https://about.gitlab.com/handbook/product/categories/#devops-stages)
-in the table below. If necessary, mention them in a comment explaining what needs
-to be reviewed.
+The review does not need to block merging this merge request. See the:
-| Tech writer | Stage(s) |
-| ------------ | ------------------------------------------------------------ |
-| `@marcia` | ~Create ~Release + ~"development guidelines" |
-| `@axil` | ~Distribution ~Gitaly ~Gitter ~Monitor ~Package ~Secure |
-| `@eread` | ~Manage ~Configure ~Geo ~Verify |
-| `@mikelewis` | ~Plan |
-
-You are welcome to mention them sooner if you have questions about writing or
-updating the documentation. GitLabbers are also welcome to use the
-[#docs](https://gitlab.slack.com/archives/C16HYA2P5) channel on Slack.
-
-If you are not sure which category the change falls within, or the change is not
-part of one of these categories, mention one of the usernames above.
+- [DevOps stages](https://about.gitlab.com/handbook/product/categories/#devops-stages) for the appropriate technical writer for this review.
+- [Documentation workflows](https://docs.gitlab.com/ee/development/documentation/workflow.html) for information on when to assign a merge request for review.
MARKDOWN
unless gitlab.mr_labels.include?('Documentation')
diff --git a/danger/roulette/Dangerfile b/danger/roulette/Dangerfile
index 6cf54d0f854..808bc96a0a0 100644
--- a/danger/roulette/Dangerfile
+++ b/danger/roulette/Dangerfile
@@ -58,7 +58,9 @@ changes = helper.changes_by_category
changes.delete(:none)
categories = changes.keys - [:unknown]
-unless changes.empty?
+# Single codebase MRs are reviewed using a slightly different process, so we
+# disable the review roulette for such MRs.
+if changes.any? && !gitlab.mr_labels.include?('single codebase')
team =
begin
helper.project_team
diff --git a/danger/single_codebase/Dangerfile b/danger/single_codebase/Dangerfile
new file mode 100644
index 00000000000..a5938cd6783
--- /dev/null
+++ b/danger/single_codebase/Dangerfile
@@ -0,0 +1,56 @@
+def mention_single_codebase_approvers
+ frontend_maintainers = %w(@filipa @iamphill)
+ backend_maintainers = %w(@rspeicher @rymai @yorickpeterse @godfat)
+
+ rows = []
+ users = []
+
+ if gitlab.mr_labels.include?('frontend')
+ frontend_maintainer = frontend_maintainers.sample
+
+ rows << "| ~frontend | `#{frontend_maintainer}`"
+ users << frontend_maintainer
+ end
+
+ if gitlab.mr_labels.include?('backend')
+ backend_maintainer = backend_maintainers.sample
+
+ rows << "| ~backend | `#{backend_maintainer}`"
+ users << backend_maintainer
+ end
+
+ if rows.empty?
+ backup_maintainer = backend_maintainers.sample
+
+ rows << "| ~frontend / ~backend | `#{backup_maintainer}`"
+ users << backup_maintainer
+ end
+
+ markdown(<<~MARKDOWN.strip)
+ ## Single codebase changes
+
+ This merge request contains changes related to the work of moving towards a
+ [single codebase](https://gitlab.com/groups/gitlab-org/-/epics/802) for
+ Community Edition and Enterprise Edition. These changes will need to be
+ reviewed and approved by the following engineers:
+
+ | Category | Reviewer
+ |----------|---------
+ #{rows.join("\n")}
+
+ To make sure this happens, please follow these steps:
+
+ 1. Add all of the mentioned users to the list of merge request approvals.
+ 2. Assign the merge request to the first person in the above list.
+
+ If you are a reviewer, please follow these steps:
+
+ 1. Review the merge request. If it is good to go, approve it.
+ 2. Once approved, assign to the next person in the above list. If you are
+ the last person in the list, merge the merge request.
+ MARKDOWN
+end
+
+if gitlab.mr_labels.include?('single codebase')
+ mention_single_codebase_approvers
+end
diff --git a/db/fixtures/development/03_settings.rb b/db/fixtures/development/02_settings.rb
index 3a4a5d436bf..3a4a5d436bf 100644
--- a/db/fixtures/development/03_settings.rb
+++ b/db/fixtures/development/02_settings.rb
diff --git a/db/fixtures/development/03_project.rb b/db/fixtures/development/03_project.rb
new file mode 100644
index 00000000000..46018cf68aa
--- /dev/null
+++ b/db/fixtures/development/03_project.rb
@@ -0,0 +1,137 @@
+require './spec/support/sidekiq'
+
+# rubocop:disable Rails/Output
+
+Sidekiq::Testing.inline! do
+ Gitlab::Seeder.quiet do
+ Gitlab::Seeder.without_gitaly_timeout do
+ project_urls = %w[
+ https://gitlab.com/gitlab-org/gitlab-test.git
+ https://gitlab.com/gitlab-org/gitlab-shell.git
+ https://gitlab.com/gnuwget/wget2.git
+ https://gitlab.com/Commit451/LabCoat.git
+ https://github.com/jashkenas/underscore.git
+ https://github.com/flightjs/flight.git
+ https://github.com/twitter/typeahead.js.git
+ https://github.com/h5bp/html5-boilerplate.git
+ https://github.com/google/material-design-lite.git
+ https://github.com/jlevy/the-art-of-command-line.git
+ https://github.com/FreeCodeCamp/freecodecamp.git
+ https://github.com/google/deepdream.git
+ https://github.com/jtleek/datasharing.git
+ https://github.com/WebAssembly/design.git
+ https://github.com/airbnb/javascript.git
+ https://github.com/tessalt/echo-chamber-js.git
+ https://github.com/atom/atom.git
+ https://github.com/mattermost/mattermost-server.git
+ https://github.com/purifycss/purifycss.git
+ https://github.com/facebook/nuclide.git
+ https://github.com/wbkd/awesome-d3.git
+ https://github.com/kilimchoi/engineering-blogs.git
+ https://github.com/gilbarbara/logos.git
+ https://github.com/reduxjs/redux.git
+ https://github.com/awslabs/s2n.git
+ https://github.com/arkency/reactjs_koans.git
+ https://github.com/twbs/bootstrap.git
+ https://github.com/chjj/ttystudio.git
+ https://github.com/MostlyAdequate/mostly-adequate-guide.git
+ https://github.com/octocat/Spoon-Knife.git
+ https://github.com/opencontainers/runc.git
+ https://github.com/googlesamples/android-topeka.git
+ ]
+
+ large_project_urls = %w[
+ https://github.com/torvalds/linux.git
+ https://gitlab.gnome.org/GNOME/gimp.git
+ https://gitlab.gnome.org/GNOME/gnome-mud.git
+ https://gitlab.com/fdroid/fdroidclient.git
+ https://gitlab.com/inkscape/inkscape.git
+ https://github.com/gnachman/iTerm2.git
+ ]
+
+ def create_project(url, force_latest_storage: false)
+ group_path, project_path = url.split('/')[-2..-1]
+
+ group = Group.find_by(path: group_path)
+
+ unless group
+ group = Group.new(
+ name: group_path.titleize,
+ path: group_path
+ )
+ group.description = FFaker::Lorem.sentence
+ group.save!
+
+ group.add_owner(User.first)
+ end
+
+ project_path.gsub!(".git", "")
+
+ params = {
+ import_url: url,
+ namespace_id: group.id,
+ name: project_path.titleize,
+ description: FFaker::Lorem.sentence,
+ visibility_level: Gitlab::VisibilityLevel.values.sample,
+ skip_disk_validation: true
+ }
+
+ if force_latest_storage
+ params[:storage_version] = Project::LATEST_STORAGE_VERSION
+ end
+
+ project = nil
+
+ Sidekiq::Worker.skipping_transaction_check do
+ project = Projects::CreateService.new(User.first, params).execute
+
+ # Seed-Fu runs this entire fixture in a transaction, so the `after_commit`
+ # hook won't run until after the fixture is loaded. That is too late
+ # since the Sidekiq::Testing block has already exited. Force clearing
+ # the `after_commit` queue to ensure the job is run now.
+ project.send(:_run_after_commit_queue)
+ project.import_state.send(:_run_after_commit_queue)
+ end
+
+ if project.valid? && project.valid_repo?
+ print '.'
+ else
+ puts project.errors.full_messages
+ print 'F'
+ end
+ end
+
+ # You can specify how many projects you need during seed execution
+ size = ENV['SIZE'].present? ? ENV['SIZE'].to_i : 8
+
+ project_urls.first(size).each_with_index do |url, i|
+ create_project(url, force_latest_storage: i.even?)
+ end
+
+ if ENV['LARGE_PROJECTS'].present?
+ large_project_urls.each(&method(:create_project))
+
+ if ENV['FORK'].present?
+ puts "\nGenerating forks"
+
+ project_name = ENV['FORK'] == 'true' ? 'torvalds/linux' : ENV['FORK']
+
+ project = Project.find_by_full_path(project_name)
+
+ User.offset(1).first(5).each do |user|
+ new_project = Projects::ForkService.new(project, user).execute
+
+ if new_project.valid? && (new_project.valid_repo? || new_project.import_state.scheduled?)
+ print '.'
+ else
+ new_project.errors.full_messages.each do |error|
+ puts "#{new_project.full_path}: #{error}"
+ end
+ print 'F'
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/db/fixtures/development/04_labels.rb b/db/fixtures/development/04_labels.rb
new file mode 100644
index 00000000000..b9ae4098d76
--- /dev/null
+++ b/db/fixtures/development/04_labels.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+require 'digest/md5'
+
+class Gitlab::Seeder::GroupLabels
+ def initialize(group, label_per_group: 10)
+ @group = group
+ @label_per_group = label_per_group
+ end
+
+ def seed!
+ @label_per_group.times do
+ label_title = FFaker::Product.brand
+ Labels::CreateService
+ .new(title: label_title, color: "##{Digest::MD5.hexdigest(label_title)[0..5]}")
+ .execute(group: @group)
+ print '.'
+ end
+ end
+end
+
+class Gitlab::Seeder::ProjectLabels
+ def initialize(project, label_per_project: 5)
+ @project = project
+ @label_per_project = label_per_project
+ end
+
+ def seed!
+ @label_per_project.times do
+ label_title = FFaker::Vehicle.model
+ Labels::CreateService
+ .new(title: label_title, color: "##{Digest::MD5.hexdigest(label_title)[0..5]}")
+ .execute(project: @project)
+ print '.'
+ end
+ end
+end
+
+Gitlab::Seeder.quiet do
+ puts "\nGenerating group labels"
+ Group.all.find_each do |group|
+ Gitlab::Seeder::GroupLabels.new(group).seed!
+ end
+
+ puts "\nGenerating project labels"
+ Project.all.find_each do |project|
+ Gitlab::Seeder::ProjectLabels.new(project).seed!
+ end
+end
diff --git a/db/fixtures/development/04_project.rb b/db/fixtures/development/04_project.rb
deleted file mode 100644
index 9a5f7cf8175..00000000000
--- a/db/fixtures/development/04_project.rb
+++ /dev/null
@@ -1,137 +0,0 @@
-require './spec/support/sidekiq'
-
-# rubocop:disable Rails/Output
-
-Sidekiq::Testing.inline! do
- Gitlab::Seeder.quiet do
- Gitlab::Seeder.without_gitaly_timeout do
- project_urls = %w[
- https://gitlab.com/gitlab-org/gitlab-test.git
- https://gitlab.com/gitlab-org/gitlab-shell.git
- https://gitlab.com/gnuwget/wget2.git
- https://gitlab.com/Commit451/LabCoat.git
- https://github.com/jashkenas/underscore.git
- https://github.com/flightjs/flight.git
- https://github.com/twitter/typeahead.js.git
- https://github.com/h5bp/html5-boilerplate.git
- https://github.com/google/material-design-lite.git
- https://github.com/jlevy/the-art-of-command-line.git
- https://github.com/FreeCodeCamp/freecodecamp.git
- https://github.com/google/deepdream.git
- https://github.com/jtleek/datasharing.git
- https://github.com/WebAssembly/design.git
- https://github.com/airbnb/javascript.git
- https://github.com/tessalt/echo-chamber-js.git
- https://github.com/atom/atom.git
- https://github.com/mattermost/mattermost-server.git
- https://github.com/purifycss/purifycss.git
- https://github.com/facebook/nuclide.git
- https://github.com/wbkd/awesome-d3.git
- https://github.com/kilimchoi/engineering-blogs.git
- https://github.com/gilbarbara/logos.git
- https://github.com/reduxjs/redux.git
- https://github.com/awslabs/s2n.git
- https://github.com/arkency/reactjs_koans.git
- https://github.com/twbs/bootstrap.git
- https://github.com/chjj/ttystudio.git
- https://github.com/MostlyAdequate/mostly-adequate-guide.git
- https://github.com/octocat/Spoon-Knife.git
- https://github.com/opencontainers/runc.git
- https://github.com/googlesamples/android-topeka.git
- ]
-
- large_project_urls = %w[
- https://github.com/torvalds/linux.git
- https://gitlab.gnome.org/GNOME/gimp.git
- https://gitlab.gnome.org/GNOME/gnome-mud.git
- https://gitlab.com/fdroid/fdroidclient.git
- https://gitlab.com/inkscape/inkscape.git
- https://github.com/gnachman/iTerm2.git
- ]
-
- def create_project(url, force_latest_storage: false)
- group_path, project_path = url.split('/')[-2..-1]
-
- group = Group.find_by(path: group_path)
-
- unless group
- group = Group.new(
- name: group_path.titleize,
- path: group_path
- )
- group.description = FFaker::Lorem.sentence
- group.save
-
- group.add_owner(User.first)
- end
-
- project_path.gsub!(".git", "")
-
- params = {
- import_url: url,
- namespace_id: group.id,
- name: project_path.titleize,
- description: FFaker::Lorem.sentence,
- visibility_level: Gitlab::VisibilityLevel.values.sample,
- skip_disk_validation: true
- }
-
- if force_latest_storage
- params[:storage_version] = Project::LATEST_STORAGE_VERSION
- end
-
- project = nil
-
- Sidekiq::Worker.skipping_transaction_check do
- project = Projects::CreateService.new(User.first, params).execute
-
- # Seed-Fu runs this entire fixture in a transaction, so the `after_commit`
- # hook won't run until after the fixture is loaded. That is too late
- # since the Sidekiq::Testing block has already exited. Force clearing
- # the `after_commit` queue to ensure the job is run now.
- project.send(:_run_after_commit_queue)
- project.import_state.send(:_run_after_commit_queue)
- end
-
- if project.valid? && project.valid_repo?
- print '.'
- else
- puts project.errors.full_messages
- print 'F'
- end
- end
-
- # You can specify how many projects you need during seed execution
- size = ENV['SIZE'].present? ? ENV['SIZE'].to_i : 8
-
- project_urls.first(size).each_with_index do |url, i|
- create_project(url, force_latest_storage: i.even?)
- end
-
- if ENV['LARGE_PROJECTS'].present?
- large_project_urls.each(&method(:create_project))
-
- if ENV['FORK'].present?
- puts "\nGenerating forks"
-
- project_name = ENV['FORK'] == 'true' ? 'torvalds/linux' : ENV['FORK']
-
- project = Project.find_by_full_path(project_name)
-
- User.offset(1).first(5).each do |user|
- new_project = Projects::ForkService.new(project, user).execute
-
- if new_project.valid? && (new_project.valid_repo? || new_project.import_state.scheduled?)
- print '.'
- else
- new_project.errors.full_messages.each do |error|
- puts "#{new_project.full_path}: #{error}"
- end
- print 'F'
- end
- end
- end
- end
- end
- end
-end
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index 16243b72f9a..926401d8b9e 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -3,13 +3,17 @@ require './spec/support/sidekiq'
Gitlab::Seeder.quiet do
Project.all.each do |project|
10.times do
+ label_ids = project.labels.pluck(:id).sample(3)
+ label_ids += project.group.labels.sample(3) if project.group
+
issue_params = {
title: FFaker::Lorem.sentence(6),
description: FFaker::Lorem.sentence,
state: ['opened', 'closed'].sample,
milestone: project.milestones.sample,
assignees: [project.team.users.sample],
- created_at: rand(12).months.ago
+ created_at: rand(12).months.ago,
+ label_ids: label_ids
}
Issues::CreateService.new(project, project.team.users.sample, issue_params).execute
diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb
index 2051bcff8f0..1952f84ed62 100644
--- a/db/fixtures/development/10_merge_requests.rb
+++ b/db/fixtures/development/10_merge_requests.rb
@@ -12,13 +12,17 @@ Gitlab::Seeder.quiet do
source_branch = branches.pop
target_branch = branches.pop
+ label_ids = project.labels.pluck(:id).sample(3)
+ label_ids += project.group.labels.sample(3) if project.group
+
params = {
source_branch: source_branch,
target_branch: target_branch,
title: FFaker::Lorem.sentence(6),
description: FFaker::Lorem.sentences(3).join(" "),
milestone: project.milestones.sample,
- assignee: project.team.users.sample
+ assignee: project.team.users.sample,
+ label_ids: label_ids
}
# Only create MRs with users that are allowed to create MRs
diff --git a/db/fixtures/development/22_labeled_issues_seed.rb b/db/fixtures/development/22_labeled_issues_seed.rb
deleted file mode 100644
index 3730e9c7958..00000000000
--- a/db/fixtures/development/22_labeled_issues_seed.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-# Creates a project with labeled issues for an user.
-# Run this single seed file using: rake db:seed_fu FILTER=labeled USER_ID=74.
-# If an USER_ID is not provided it will use the last created user.
-require './spec/support/sidekiq'
-
-class Gitlab::Seeder::LabeledIssues
- include ::Gitlab::Utils
-
- def initialize(user)
- @user = user
- end
-
- def seed!
- Sidekiq::Testing.inline! do
- group = create_group
-
- create_projects(group)
- create_labels(group)
- create_issues(group)
- end
-
- print '.'
- end
-
- private
-
- def create_group
- group_name = "group_of_#{@user.username}_#{SecureRandom.hex(4)}"
-
- group_params = {
- name: group_name,
- path: group_name,
- description: FFaker::Lorem.sentence
- }
-
- Groups::CreateService.new(@user, group_params).execute
- end
-
- def create_projects(group)
- 5.times do
- project_name = "project_#{SecureRandom.hex(6)}"
-
- params = {
- namespace_id: group.id,
- name: project_name,
- description: FFaker::Lorem.sentence,
- visibility_level: Gitlab::VisibilityLevel.values.sample
- }
-
- Projects::CreateService.new(@user, params).execute
- end
- end
-
- def create_labels(group)
- group.projects.each do |project|
- 5.times do
- label_title = FFaker::Vehicle.model
- Labels::CreateService.new(title: label_title, color: "#69D100").execute(project: project)
- end
- end
-
- 10.times do
- label_title = FFaker::Product.brand
- Labels::CreateService.new(title: label_title).execute(group: group)
- end
- end
-
- def create_issues(group)
- # Get only group labels
- group_labels =
- LabelsFinder.new(@user, group_id: group.id).execute.where.not(group_id: nil)
-
- group.projects.each do |project|
- label_ids = project.labels.pluck(:id).sample(5)
- label_ids.push(*group.labels.sample(4))
-
- 20.times do
- issue_params = {
- title: FFaker::Lorem.sentence(6),
- description: FFaker::Lorem.sentence,
- state: 'opened',
- label_ids: label_ids
-
- }
-
- Issues::CreateService.new(project, @user, issue_params).execute if project.project_feature.present?
- end
- end
- end
-end
-
-Gitlab::Seeder.quiet do
- user_id = ENV['USER_ID']
-
- user =
- if user_id.present?
- User.find(user_id)
- else
- User.last
- end
-
- Gitlab::Seeder::LabeledIssues.new(user).seed!
-end
diff --git a/db/fixtures/development/25_api_personal_access_token.rb b/db/fixtures/development/25_api_personal_access_token.rb
new file mode 100644
index 00000000000..a2e6c674c1f
--- /dev/null
+++ b/db/fixtures/development/25_api_personal_access_token.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require './spec/support/sidekiq'
+
+# Create an api access token for root user with the value: ypCa3Dzb23o5nvsixwPA
+Gitlab::Seeder.quiet do
+ PersonalAccessToken.create!(
+ user_id: User.find_by(username: 'root').id,
+ name: "seeded-api-token",
+ scopes: ["api"],
+ token_digest: "/O0jfLERYT/L5gG8nfByQxqTj43TeLlRzOtJGTzRsbQ="
+ )
+
+ print '.'
+end
diff --git a/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb b/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
index d40c61f24b1..b4658bc4017 100644
--- a/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
+++ b/db/migrate/20170530130129_project_foreign_keys_with_cascading_deletes.rb
@@ -126,11 +126,10 @@ class ProjectForeignKeysWithCascadingDeletes < ActiveRecord::Migration[4.2]
queues.each do |queue|
# Stealing is racy so it's possible a pop might be called on an
# already-empty queue.
- begin
- remove_orphans(*queue.pop(true))
- stolen = true
- rescue ThreadError
- end
+
+ remove_orphans(*queue.pop(true))
+ stolen = true
+ rescue ThreadError
end
break unless stolen
diff --git a/db/migrate/20180314145917_add_header_and_footer_banners_to_appearances_table.rb b/db/migrate/20180314145917_add_header_and_footer_banners_to_appearances_table.rb
new file mode 100644
index 00000000000..8aba3448035
--- /dev/null
+++ b/db/migrate/20180314145917_add_header_and_footer_banners_to_appearances_table.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddHeaderAndFooterBannersToAppearancesTable < ActiveRecord::Migration[4.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :appearances, :header_message, :text
+ add_column :appearances, :header_message_html, :text
+
+ add_column :appearances, :footer_message, :text
+ add_column :appearances, :footer_message_html, :text
+
+ add_column :appearances, :message_background_color, :text
+ add_column :appearances, :message_font_color, :text
+ end
+end
diff --git a/db/migrate/20181205171941_create_project_daily_statistics.rb b/db/migrate/20181205171941_create_project_daily_statistics.rb
new file mode 100644
index 00000000000..c9e2a1e1aa7
--- /dev/null
+++ b/db/migrate/20181205171941_create_project_daily_statistics.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class CreateProjectDailyStatistics < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ create_table :project_daily_statistics, id: :bigserial do |t|
+ t.integer :project_id, null: false
+ t.integer :fetch_count, null: false
+ t.date :date
+
+ t.index [:project_id, :date], unique: true, order: { date: :desc }
+ t.foreign_key :projects, on_delete: :cascade
+ end
+ end
+end
diff --git a/db/migrate/20190206193120_add_index_to_tags.rb b/db/migrate/20190206193120_add_index_to_tags.rb
new file mode 100644
index 00000000000..5257ebba003
--- /dev/null
+++ b/db/migrate/20190206193120_add_index_to_tags.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexToTags < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_tags_on_name_trigram'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :tags, :name, name: INDEX_NAME, using: :gin, opclasses: { name: :gin_trgm_ops }
+ end
+
+ def down
+ remove_concurrent_index_by_name(:tags, INDEX_NAME)
+ end
+end
diff --git a/db/migrate/20190215154930_add_merge_pipelines_enabled_to_ci_cd_settings.rb b/db/migrate/20190215154930_add_merge_pipelines_enabled_to_ci_cd_settings.rb
new file mode 100644
index 00000000000..2a2a216da7d
--- /dev/null
+++ b/db/migrate/20190215154930_add_merge_pipelines_enabled_to_ci_cd_settings.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class AddMergePipelinesEnabledToCiCdSettings < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :project_ci_cd_settings, :merge_pipelines_enabled, :boolean
+ end
+end
diff --git a/db/migrate/20190218134158_add_masked_to_ci_variables.rb b/db/migrate/20190218134158_add_masked_to_ci_variables.rb
new file mode 100644
index 00000000000..b4999d5b4a9
--- /dev/null
+++ b/db/migrate/20190218134158_add_masked_to_ci_variables.rb
@@ -0,0 +1,21 @@
+# 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 AddMaskedToCiVariables < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :ci_variables, :masked, :boolean, default: false, allow_null: false
+ end
+
+ def down
+ remove_column :ci_variables, :masked
+ end
+end
diff --git a/db/migrate/20190218134209_add_masked_to_ci_group_variables.rb b/db/migrate/20190218134209_add_masked_to_ci_group_variables.rb
new file mode 100644
index 00000000000..8633875b341
--- /dev/null
+++ b/db/migrate/20190218134209_add_masked_to_ci_group_variables.rb
@@ -0,0 +1,21 @@
+# 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 AddMaskedToCiGroupVariables < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ # Set this constant to true if this migration requires downtime.
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :ci_group_variables, :masked, :boolean, default: false, allow_null: false
+ end
+
+ def down
+ remove_column :ci_group_variables, :masked
+ end
+end
diff --git a/db/migrate/20190220142344_add_email_header_and_footer_enabled_flag_to_appearances_table.rb b/db/migrate/20190220142344_add_email_header_and_footer_enabled_flag_to_appearances_table.rb
new file mode 100644
index 00000000000..85b9e0580f4
--- /dev/null
+++ b/db/migrate/20190220142344_add_email_header_and_footer_enabled_flag_to_appearances_table.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddEmailHeaderAndFooterEnabledFlagToAppearancesTable < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ disable_ddl_transaction!
+
+ DOWNTIME = false
+
+ def up
+ add_column_with_default(:appearances, :email_header_and_footer_enabled, :boolean, default: false)
+ end
+
+ def down
+ remove_column(:appearances, :email_header_and_footer_enabled)
+ end
+end
diff --git a/db/migrate/20190220150130_add_extra_shas_to_ci_pipelines.rb b/db/migrate/20190220150130_add_extra_shas_to_ci_pipelines.rb
new file mode 100644
index 00000000000..45c7c0949c6
--- /dev/null
+++ b/db/migrate/20190220150130_add_extra_shas_to_ci_pipelines.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+class AddExtraShasToCiPipelines < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ add_column :ci_pipelines, :source_sha, :binary
+ add_column :ci_pipelines, :target_sha, :binary
+ end
+end
diff --git a/db/migrate/20190225152525_add_auto_dev_ops_enabled_to_namespaces.rb b/db/migrate/20190225152525_add_auto_dev_ops_enabled_to_namespaces.rb
new file mode 100644
index 00000000000..93e7a84fb02
--- /dev/null
+++ b/db/migrate/20190225152525_add_auto_dev_ops_enabled_to_namespaces.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddAutoDevOpsEnabledToNamespaces < ActiveRecord::Migration[5.0]
+ DOWNTIME = false
+
+ def change
+ add_column :namespaces, :auto_devops_enabled, :boolean
+ end
+end
diff --git a/db/migrate/20190225160300_steal_encrypt_runners_tokens.rb b/db/migrate/20190225160300_steal_encrypt_runners_tokens.rb
new file mode 100644
index 00000000000..18c0d2a2e1b
--- /dev/null
+++ b/db/migrate/20190225160300_steal_encrypt_runners_tokens.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class StealEncryptRunnersTokens < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ # This cleans after `EncryptRunnersTokens`
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ Gitlab::BackgroundMigration.steal('EncryptRunnersTokens')
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/migrate/20190225160301_add_runner_tokens_indexes.rb b/db/migrate/20190225160301_add_runner_tokens_indexes.rb
new file mode 100644
index 00000000000..3230c2809de
--- /dev/null
+++ b/db/migrate/20190225160301_add_runner_tokens_indexes.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class AddRunnerTokensIndexes < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ # It seems that `ci_runners.token_encrypted` and `projects.runners_token_encrypted`
+ # are non-unique
+
+ def up
+ add_concurrent_index :ci_runners, :token_encrypted
+ add_concurrent_index :projects, :runners_token_encrypted
+ add_concurrent_index :namespaces, :runners_token_encrypted, unique: true
+ end
+
+ def down
+ remove_concurrent_index :ci_runners, :token_encrypted
+ remove_concurrent_index :projects, :runners_token_encrypted
+ remove_concurrent_index :namespaces, :runners_token_encrypted, unique: true
+ end
+end
diff --git a/db/migrate/20190228192410_add_multi_line_attributes_to_suggestion.rb b/db/migrate/20190228192410_add_multi_line_attributes_to_suggestion.rb
new file mode 100644
index 00000000000..856dfc89fa3
--- /dev/null
+++ b/db/migrate/20190228192410_add_multi_line_attributes_to_suggestion.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+class AddMultiLineAttributesToSuggestion < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_column_with_default :suggestions, :lines_above, :integer, default: 0, allow_null: false
+ add_column_with_default :suggestions, :lines_below, :integer, default: 0, allow_null: false
+ add_column_with_default :suggestions, :outdated, :boolean, default: false, allow_null: false
+ end
+
+ def down
+ remove_columns :suggestions, :outdated, :lines_above, :lines_below
+ end
+end
diff --git a/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb b/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb
new file mode 100644
index 00000000000..2c3a54b12a9
--- /dev/null
+++ b/db/migrate/20190301182457_add_external_hostname_to_ingress_and_knative.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+class AddExternalHostnameToIngressAndKnative < ActiveRecord::Migration[5.0]
+ DOWNTIME = false
+
+ def change
+ add_column :clusters_applications_ingress, :external_hostname, :string
+ add_column :clusters_applications_knative, :external_hostname, :string
+ end
+end
diff --git a/db/migrate/20190315191339_create_merge_request_assignees_table.rb b/db/migrate/20190315191339_create_merge_request_assignees_table.rb
new file mode 100644
index 00000000000..6fc4463f281
--- /dev/null
+++ b/db/migrate/20190315191339_create_merge_request_assignees_table.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+class CreateMergeRequestAssigneesTable < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ INDEX_NAME = 'index_merge_request_assignees_on_merge_request_id_and_user_id'
+
+ def up
+ create_table :merge_request_assignees do |t|
+ t.references :user, foreign_key: { on_delete: :cascade }, index: true, null: false
+ t.references :merge_request, foreign_key: { on_delete: :cascade }, null: false
+ end
+
+ add_index :merge_request_assignees, [:merge_request_id, :user_id], unique: true, name: INDEX_NAME
+ end
+
+ def down
+ drop_table :merge_request_assignees
+ end
+end
diff --git a/db/post_migrate/20181101091005_steal_digest_column.rb b/db/post_migrate/20181101091005_steal_digest_column.rb
new file mode 100644
index 00000000000..58ea710c18a
--- /dev/null
+++ b/db/post_migrate/20181101091005_steal_digest_column.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class StealDigestColumn < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ Gitlab::BackgroundMigration.steal('DigestColumn')
+ end
+
+ def down
+ # raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/post_migrate/20181101091124_remove_token_from_personal_access_tokens.rb b/db/post_migrate/20181101091124_remove_token_from_personal_access_tokens.rb
new file mode 100644
index 00000000000..415373068d5
--- /dev/null
+++ b/db/post_migrate/20181101091124_remove_token_from_personal_access_tokens.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class RemoveTokenFromPersonalAccessTokens < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ def change
+ remove_column :personal_access_tokens, :token, :string
+ end
+end
diff --git a/db/post_migrate/20190301081611_migrate_project_migrate_sidekiq_queue.rb b/db/post_migrate/20190301081611_migrate_project_migrate_sidekiq_queue.rb
new file mode 100644
index 00000000000..6af7902e0c4
--- /dev/null
+++ b/db/post_migrate/20190301081611_migrate_project_migrate_sidekiq_queue.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class MigrateProjectMigrateSidekiqQueue < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ DOWNTIME = false
+
+ def up
+ sidekiq_queue_migrate 'project_migrate_hashed_storage', to: 'hashed_storage:hashed_storage_project_migrate'
+ end
+
+ def down
+ sidekiq_queue_migrate 'hashed_storage:hashed_storage_project_migrate', to: 'project_migrate_hashed_storage'
+ end
+end
diff --git a/db/post_migrate/20190322132835_schedule_populate_merge_request_assignees_table.rb b/db/post_migrate/20190322132835_schedule_populate_merge_request_assignees_table.rb
new file mode 100644
index 00000000000..1ecb38e1a86
--- /dev/null
+++ b/db/post_migrate/20190322132835_schedule_populate_merge_request_assignees_table.rb
@@ -0,0 +1,23 @@
+# 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 SchedulePopulateMergeRequestAssigneesTable < ActiveRecord::Migration[5.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ BATCH_SIZE = 10_000
+ MIGRATION = 'PopulateMergeRequestAssigneesTable'
+ DELAY_INTERVAL = 8.minutes.to_i
+
+ disable_ddl_transaction!
+
+ def up
+ say 'Scheduling `PopulateMergeRequestAssigneesTable` jobs'
+ # We currently have ~4_500_000 merge request records on GitLab.com.
+ # This means it'll schedule ~450 jobs (10k MRs each) with a 8 minutes gap,
+ # so this should take ~60 hours for all background migrations to complete.
+ queue_background_migration_jobs_by_range_at_intervals(MergeRequest, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 626d8ed9646..c4d51c4b15b 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20190214112022) do
+ActiveRecord::Schema.define(version: 20190322132835) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -37,7 +37,14 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.integer "cached_markdown_version"
t.text "new_project_guidelines"
t.text "new_project_guidelines_html"
+ t.text "header_message"
+ t.text "header_message_html"
+ t.text "footer_message"
+ t.text "footer_message_html"
+ t.text "message_background_color"
+ t.text "message_font_color"
t.string "favicon"
+ t.boolean "email_header_and_footer_enabled", default: false, null: false
end
create_table "application_setting_terms", force: :cascade do |t|
@@ -399,6 +406,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.boolean "protected", default: false, null: false
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
+ t.boolean "masked", default: false, null: false
t.index ["group_id", "key"], name: "index_ci_group_variables_on_group_id_and_key", unique: true, using: :btree
end
@@ -491,6 +499,8 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.integer "failure_reason"
t.integer "iid"
t.integer "merge_request_id"
+ t.binary "source_sha"
+ t.binary "target_sha"
t.index ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
t.index ["merge_request_id"], name: "index_ci_pipelines_on_merge_request_id", where: "(merge_request_id IS NOT NULL)", using: :btree
t.index ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree
@@ -546,6 +556,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.index ["locked"], name: "index_ci_runners_on_locked", using: :btree
t.index ["runner_type"], name: "index_ci_runners_on_runner_type", using: :btree
t.index ["token"], name: "index_ci_runners_on_token", using: :btree
+ t.index ["token_encrypted"], name: "index_ci_runners_on_token_encrypted", using: :btree
end
create_table "ci_stages", force: :cascade do |t|
@@ -594,6 +605,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.integer "project_id", null: false
t.boolean "protected", default: false, null: false
t.string "environment_scope", default: "*", null: false
+ t.boolean "masked", default: false, null: false
t.index ["project_id", "key", "environment_scope"], name: "index_ci_variables_on_project_id_and_key_and_environment_scope", unique: true, using: :btree
end
@@ -696,6 +708,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.string "cluster_ip"
t.text "status_reason"
t.string "external_ip"
+ t.string "external_hostname"
t.index ["cluster_id"], name: "index_clusters_applications_ingress_on_cluster_id", unique: true, using: :btree
end
@@ -721,6 +734,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.string "hostname"
t.text "status_reason"
t.string "external_ip"
+ t.string "external_hostname"
t.index ["cluster_id"], name: "index_clusters_applications_knative_on_cluster_id", unique: true, using: :btree
end
@@ -1188,6 +1202,14 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.index ["user_id"], name: "index_members_on_user_id", using: :btree
end
+ create_table "merge_request_assignees", force: :cascade do |t|
+ t.integer "user_id", null: false
+ t.integer "merge_request_id", null: false
+ t.index ["merge_request_id", "user_id"], name: "index_merge_request_assignees_on_merge_request_id_and_user_id", unique: true, using: :btree
+ t.index ["merge_request_id"], name: "index_merge_request_assignees_on_merge_request_id", using: :btree
+ t.index ["user_id"], name: "index_merge_request_assignees_on_user_id", using: :btree
+ end
+
create_table "merge_request_diff_commits", id: false, force: :cascade do |t|
t.datetime_with_timezone "authored_date"
t.datetime_with_timezone "committed_date"
@@ -1365,6 +1387,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.integer "cached_markdown_version"
t.string "runners_token"
t.string "runners_token_encrypted"
+ t.boolean "auto_devops_enabled"
t.index ["created_at"], name: "index_namespaces_on_created_at", using: :btree
t.index ["name", "parent_id"], name: "index_namespaces_on_name_and_parent_id", unique: true, using: :btree
t.index ["name"], name: "index_namespaces_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
@@ -1374,6 +1397,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.index ["path"], name: "index_namespaces_on_path_trigram", using: :gin, opclasses: {"path"=>"gin_trgm_ops"}
t.index ["require_two_factor_authentication"], name: "index_namespaces_on_require_two_factor_authentication", using: :btree
t.index ["runners_token"], name: "index_namespaces_on_runners_token", unique: true, using: :btree
+ t.index ["runners_token_encrypted"], name: "index_namespaces_on_runners_token_encrypted", unique: true, using: :btree
t.index ["type"], name: "index_namespaces_on_type", using: :btree
end
@@ -1517,7 +1541,6 @@ ActiveRecord::Schema.define(version: 20190214112022) do
create_table "personal_access_tokens", force: :cascade do |t|
t.integer "user_id", null: false
- t.string "token"
t.string "name", null: false
t.boolean "revoked", default: false
t.date "expires_at"
@@ -1526,7 +1549,6 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.string "scopes", default: "--- []\n", null: false
t.boolean "impersonation", default: false, null: false
t.string "token_digest"
- t.index ["token"], name: "index_personal_access_tokens_on_token", unique: true, using: :btree
t.index ["token_digest"], name: "index_personal_access_tokens_on_token_digest", unique: true, using: :btree
t.index ["user_id"], name: "index_personal_access_tokens_on_user_id", using: :btree
end
@@ -1569,6 +1591,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
create_table "project_ci_cd_settings", force: :cascade do |t|
t.integer "project_id", null: false
t.boolean "group_runners_enabled", default: true, null: false
+ t.boolean "merge_pipelines_enabled"
t.index ["project_id"], name: "index_project_ci_cd_settings_on_project_id", unique: true, using: :btree
end
@@ -1582,6 +1605,13 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.index ["project_id", "key"], name: "index_project_custom_attributes_on_project_id_and_key", unique: true, using: :btree
end
+ create_table "project_daily_statistics", id: :bigserial, force: :cascade do |t|
+ t.integer "project_id", null: false
+ t.integer "fetch_count", null: false
+ t.date "date"
+ t.index ["project_id", "date"], name: "index_project_daily_statistics_on_project_id_and_date", unique: true, order: { date: :desc }, using: :btree
+ end
+
create_table "project_deploy_tokens", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "deploy_token_id", null: false
@@ -1737,6 +1767,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.index ["repository_storage", "created_at"], name: "idx_project_repository_check_partial", where: "(last_repository_check_at IS NULL)", using: :btree
t.index ["repository_storage"], name: "index_projects_on_repository_storage", using: :btree
t.index ["runners_token"], name: "index_projects_on_runners_token", using: :btree
+ t.index ["runners_token_encrypted"], name: "index_projects_on_runners_token_encrypted", using: :btree
t.index ["star_count"], name: "index_projects_on_star_count", using: :btree
t.index ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
end
@@ -2010,6 +2041,9 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.string "commit_id"
t.text "from_content", null: false
t.text "to_content", null: false
+ t.integer "lines_above", default: 0, null: false
+ t.integer "lines_below", default: 0, null: false
+ t.boolean "outdated", default: false, null: false
t.index ["note_id", "relative_order"], name: "index_suggestions_on_note_id_and_relative_order", unique: true, using: :btree
end
@@ -2040,6 +2074,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
t.string "name"
t.integer "taggings_count", default: 0
t.index ["name"], name: "index_tags_on_name", unique: true, using: :btree
+ t.index ["name"], name: "index_tags_on_name_trigram", using: :gin, opclasses: {"name"=>"gin_trgm_ops"}
end
create_table "term_agreements", force: :cascade do |t|
@@ -2432,6 +2467,8 @@ ActiveRecord::Schema.define(version: 20190214112022) do
add_foreign_key "lists", "boards", name: "fk_0d3f677137", on_delete: :cascade
add_foreign_key "lists", "labels", name: "fk_7a5553d60f", on_delete: :cascade
add_foreign_key "members", "users", name: "fk_2e88fb7ce9", on_delete: :cascade
+ add_foreign_key "merge_request_assignees", "merge_requests", on_delete: :cascade
+ add_foreign_key "merge_request_assignees", "users", on_delete: :cascade
add_foreign_key "merge_request_diff_commits", "merge_request_diffs", on_delete: :cascade
add_foreign_key "merge_request_diff_files", "merge_request_diffs", on_delete: :cascade
add_foreign_key "merge_request_diffs", "merge_requests", name: "fk_8483f3258f", on_delete: :cascade
@@ -2465,6 +2502,7 @@ ActiveRecord::Schema.define(version: 20190214112022) do
add_foreign_key "project_auto_devops", "projects", on_delete: :cascade
add_foreign_key "project_ci_cd_settings", "projects", name: "fk_24c15d2f2e", on_delete: :cascade
add_foreign_key "project_custom_attributes", "projects", on_delete: :cascade
+ add_foreign_key "project_daily_statistics", "projects", on_delete: :cascade
add_foreign_key "project_deploy_tokens", "deploy_tokens", on_delete: :cascade
add_foreign_key "project_deploy_tokens", "projects", on_delete: :cascade
add_foreign_key "project_error_tracking_settings", "projects", on_delete: :cascade
diff --git a/doc/README.md b/doc/README.md
index f87ff925e94..aead50ea97e 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -38,6 +38,7 @@ Have a look at some of our most popular documentation resources:
| [GitLab CI/CD examples](ci/examples/README.md) | Get up to speed quickly with common CI/CD scenarios. |
| [GitLab Container Registry](user/project/container_registry.md) | Host containers within GitLab. |
| [GitLab Pages](user/project/pages/index.md) | Host static websites for your projects with GitLab. |
+| [GitLab.com settings](user/gitlab_com/index.md) | Settings for [GitLab.com](#gitlabcom). |
| [Kubernetes integration](user/project/clusters/index.md) | Use GitLab with Kubernetes. |
| [SSH authentication](ssh/README.md) | Secure your network communications. |
| [Using Docker images](ci/docker/using_docker_images.md) | Build and test your applications with Docker. |
@@ -221,7 +222,7 @@ The following documentation relates to the DevOps **Verify** stage:
|:---------------------------------------------------|:-----------------------------------------------------------------------------|
| [GitLab CI/CD](ci/README.md) | Explore the features and capabilities of Continuous Integration with GitLab. |
| [JUnit test reports](ci/junit_test_reports.md) | Display JUnit test reports on merge requests. |
-| [Pipeline Graphs](ci/pipelines.md#pipeline-graphs) | Visualize builds. |
+| [Pipeline Graphs](ci/pipelines.md#visualizing-pipelines) | Visualize builds. |
| [Review Apps](ci/review_apps/index.md) | Preview changes to your application right from a merge request. |
<div align="right">
@@ -284,9 +285,11 @@ The following documentation relates to the DevOps **Configure** stage:
| [Auto DevOps](topics/autodevops/index.md) | Automatically employ a complete DevOps lifecycle. |
| [Easy creation of Kubernetes<br/>clusters on GKE](user/project/clusters/index.md#adding-and-creating-a-new-gke-cluster-via-gitlab) | Use Google Kubernetes Engine and GitLab. |
| [Executable Runbooks](user/project/clusters/runbooks/index.md) | Documented procedures that explain how to carry out particular processes. |
+| [GitLab ChatOps](ci/chatops/README.md) | Interact with CI/CD jobs through chat services. |
| [Installing Applications](user/project/clusters/index.md#installing-applications) | Deploy Helm, Ingress, and Prometheus on Kubernetes. |
| [Mattermost slash commands](user/project/integrations/mattermost_slash_commands.md) | Enable and use slash commands from within Mattermost. |
| [Protected variables](ci/variables/README.md#protected-variables) | Restrict variables to protected branches and tags. |
+| [Serverless](user/project/clusters/serverless/index.md) | Run serverless workloads on Kubernetes. |
| [Slack slash commands](user/project/integrations/slack_slash_commands.md) | Enable and use slash commands from within Slack. |
<div align="right">
diff --git a/doc/administration/auth/authentiq.md b/doc/administration/auth/authentiq.md
index 94a8803fff1..726622d8599 100644
--- a/doc/administration/auth/authentiq.md
+++ b/doc/administration/auth/authentiq.md
@@ -51,7 +51,7 @@ Authentiq will generate a Client ID and the accompanying Client Secret for you t
```
1. The `scope` is set to request the user's name, email (required and signed), and permission to send push notifications to sign in on subsequent visits.
-See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq/wiki/Scopes,-callback-url-configuration-and-responses) for more information on scopes and modifiers.
+ See [OmniAuth Authentiq strategy](https://github.com/AuthentiqID/omniauth-authentiq/wiki/Scopes,-callback-url-configuration-and-responses) for more information on scopes and modifiers.
1. Change `YOUR_CLIENT_ID` and `YOUR_CLIENT_SECRET` to the Client credentials you received in step 1.
diff --git a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
index 0d03b481881..15276d364a0 100644
--- a/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
+++ b/doc/administration/auth/how_to_configure_ldap_gitlab_ce/index.md
@@ -14,7 +14,7 @@ Managing a large number of users in GitLab can become a burden for system admini
In this guide we will focus on configuring GitLab with Active Directory. [Active Directory](https://en.wikipedia.org/wiki/Active_Directory) is a popular LDAP compatible directory service provided by Microsoft, included in all modern Windows Server operating systems.
-GitLab has supported LDAP integration since [version 2.2](https://about.gitlab.com/2012/02/22/gitlab-version-2-2/). With GitLab LDAP [group syncing](#group-syncing-ee) being added to GitLab Enterprise Edition in [version 6.0](https://about.gitlab.com/2013/08/20/gitlab-6-dot-0-released/). LDAP integration has become one of the most popular features in GitLab.
+GitLab has supported LDAP integration since [version 2.2](https://about.gitlab.com/2012/02/22/gitlab-version-2-2/). With GitLab LDAP [group syncing](https://docs.gitlab.com/ee/administration/auth/how_to_configure_ldap_gitlab_ee/index.html#group-sync) being added to GitLab Enterprise Edition in [version 6.0](https://about.gitlab.com/2013/08/20/gitlab-6-dot-0-released/). LDAP integration has become one of the most popular features in GitLab.
## Getting started
@@ -111,7 +111,7 @@ The initial configuration of LDAP in GitLab requires changes to the `gitlab.rb`
The two Active Directory specific values are `active_directory: true` and `uid: 'sAMAccountName'`. `sAMAccountName` is an attribute returned by Active Directory used for GitLab usernames. See the example output from `ldapsearch` for a full list of attributes a "person" object (user) has in **AD** - [`ldapsearch` example](#using-ldapsearch-unix)
-> Both group_base and admin_group configuration options are only available in GitLab Enterprise Edition. See [GitLab EE - LDAP Features](#gitlab-enterprise-edition---ldap-features)
+> Both group_base and admin_group configuration options are only available in GitLab Enterprise Edition. See [GitLab EE - LDAP Features](https://docs.gitlab.com/ee/administration/auth/how_to_configure_ldap_gitlab_ee/index.html#gitlab-enterprise-edition---ldap-features)
### Example `gitlab.rb` LDAP
diff --git a/doc/administration/auth/ldap.md b/doc/administration/auth/ldap.md
index 37e596f198f..440c2b1285a 100644
--- a/doc/administration/auth/ldap.md
+++ b/doc/administration/auth/ldap.md
@@ -81,6 +81,9 @@ library. `tls` corresponds to StartTLS, not to be confused with regular TLS.
Normally, if you specify `ssl` it will be on port 636, while `tls` (StartTLS)
would be on port 389. `plain` also operates on port 389.
+NOTE: **Note:**
+LDAP users must have an email address set, regardless of whether it is used to log in.
+
**Omnibus configuration**
```ruby
@@ -136,14 +139,54 @@ main:
##
verify_certificates: true
- ##
- ## Specifies the SSL version for OpenSSL to use, if the OpenSSL default
- ## is not appropriate.
- ##
- ## Example: 'TLSv1_1'
- ##
- ##
- ssl_version: ''
+ # OpenSSL::SSL::SSLContext options.
+ tls_options:
+ # Specifies the path to a file containing a PEM-format CA certificate,
+ # e.g. if you need to use an internal CA.
+ #
+ # Example: '/etc/ca.pem'
+ #
+ ca_file: ''
+
+ # Specifies the SSL version for OpenSSL to use, if the OpenSSL default
+ # is not appropriate.
+ #
+ # Example: 'TLSv1_1'
+ #
+ ssl_version: ''
+
+ # Specific SSL ciphers to use in communication with LDAP servers.
+ #
+ # Example: 'ALL:!EXPORT:!LOW:!aNULL:!eNULL:!SSLv2'
+ ciphers: ''
+
+ # Client certificate
+ #
+ # Example:
+ # cert: |
+ # -----BEGIN CERTIFICATE-----
+ # MIIDbDCCAlSgAwIBAgIGAWkJxLmKMA0GCSqGSIb3DQEBCwUAMHcxFDASBgNVBAoTC0dvb2dsZSBJ
+ # bmMuMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQDEwtMREFQIENsaWVudDEPMA0GA1UE
+ # CxMGR1N1aXRlMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTAeFw0xOTAyMjAwNzE4
+ # rntnF4d+0dd7zP3jrWkbdtoqjLDT/5D7NYRmVCD5vizV98FJ5//PIHbD1gL3a9b2MPAc6k7NV8tl
+ # ...
+ # 4SbuJPAiJxC1LQ0t39dR6oMCAMab3hXQqhL56LrR6cRBp6Mtlphv7alu9xb/x51y2x+g2zWtsf80
+ # Jrv/vKMsIh/sAyuogb7hqMtp55ecnKxceg==
+ # -----END CERTIFICATE -----
+ cert: ''
+
+ # Client private key
+ # key: |
+ # -----BEGIN PRIVATE KEY-----
+ # MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3DmJtLRmJGY4xU1QtI3yjvxO6
+ # bNuyE4z1NF6Xn7VSbcAaQtavWQ6GZi5uukMo+W5DHVtEkgDwh92ySZMuJdJogFbNvJvHAayheCdN
+ # 7mCQ2UUT9jGXIbmksUn9QMeJVXTZjgJWJzPXToeUdinx9G7+lpVa62UATEd1gaI3oyL72WmpDy/C
+ # rntnF4d+0dd7zP3jrWkbdtoqjLDT/5D7NYRmVCD5vizV98FJ5//PIHbD1gL3a9b2MPAc6k7NV8tl
+ # ...
+ # +9IhSYX+XIg7BZOVDeYqlPfxRvQh8vy3qjt/KUihmEPioAjLaGiihs1Fk5ctLk9A2hIUyP+sEQv9
+ # l6RG+a/mW+0rCWn8JAd464Ps9hE=
+ # -----END PRIVATE KEY-----
+ key: ''
##
## Set a timeout, in seconds, for LDAP queries. This helps avoid blocking
@@ -451,7 +494,7 @@ ldapsearch -H ldaps://$host:$port -D "$bind_dn" -y bind_dn_password.txt -b "$ba
### Invalid credentials when logging in
- Make sure the user you are binding with has enough permissions to read the user's
-tree and traverse it.
+ tree and traverse it.
- Check that the `user_filter` is not blocking otherwise valid users.
- Run the following check command to make sure that the LDAP settings are
correct and GitLab can see your users:
diff --git a/doc/administration/auth/okta.md b/doc/administration/auth/okta.md
index 3136923fa96..638405126a5 100644
--- a/doc/administration/auth/okta.md
+++ b/doc/administration/auth/okta.md
@@ -1,6 +1,6 @@
# Okta SSO provider
-Okta is a [Single Sign-on provider][okta-sso] that can be used to authenticate
+Okta is a [Single Sign-on provider](https://www.okta.com/products/single-sign-on/) that can be used to authenticate
with GitLab.
The following documentation enables Okta as a SAML provider.
@@ -140,7 +140,7 @@ Now that the Okta app is configured, it's time to enable it in GitLab.
}
```
-1. [Reconfigure][reconf] or [restart] GitLab for Omnibus and installations
+1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) or [restart](../restart_gitlab.md#installations-from-source) GitLab for Omnibus and installations
from source respectively for the changes to take effect.
You might want to try this out on an incognito browser window.
@@ -150,10 +150,5 @@ You might want to try this out on an incognito browser window.
>**Note:**
Make sure the groups exist and are assigned to the Okta app.
-You can take a look of the [SAML documentation][saml] on external groups since
+You can take a look of the [SAML documentation](../../integration/saml.md#marking-users-as-external-based-on-saml-groups) on external groups since
it works the same.
-
-[okta-sso]: https://www.okta.com/products/single-sign-on/
-[saml]: ../../integration/saml.md#external-groups
-[reconf]: ../restart_gitlab.md#omnibus-gitlab-reconfigure
-[restart]: ../restart_gitlab.md#installations-from-source
diff --git a/doc/administration/build_artifacts.md b/doc/administration/build_artifacts.md
index 623a5321f32..85ae2946bc8 100644
--- a/doc/administration/build_artifacts.md
+++ b/doc/administration/build_artifacts.md
@@ -1 +1,5 @@
+---
+redirect_to: 'job_artifacts.md'
+---
+
This document was moved to [job_artifacts](job_artifacts.md).
diff --git a/doc/administration/compliance.md b/doc/administration/compliance.md
index 0414b3ec12e..72cb57fb36c 100644
--- a/doc/administration/compliance.md
+++ b/doc/administration/compliance.md
@@ -6,12 +6,12 @@ GitLab’s [security features](../security/README.md) may also help you meet rel
|Feature |GitLab tier |GitLab.com |
| ---------| :--------: | :-------: |
-|**[Restrict SSH Keys](../README.html#administrator-documentation)**<br>Control the technology and key length of SSH keys used to access GitLab|Core+||
-|**[Granular user roles and flexible permissions](../user/permissions.html)**<br>Manage access and permissions with five different user roles and settings for external users. Set permissions according to people's role, rather than either read or write access to a repository. Don't share the source code with people that only need access to the issue tracker.|Core+|✓|
-|**[Enforce TOS acceptance](../user/admin_area/settings/terms.html)**<br>Enforce your users accepting new terms of service by blocking GitLab traffic.|Core+||
-|**[Email all users of a project, group, or entire server](../user/admin_area/settings/terms.html)**<br>An admin can email groups of users based on project or group membership, or email everyone using the GitLab instance. This is great for scheduled maintenance or upgrades.|Starter+||
+|**[Restrict SSH Keys](../security/ssh_keys_restrictions.md)**<br>Control the technology and key length of SSH keys used to access GitLab|Core+||
+|**[Granular user roles and flexible permissions](../user/permissions.md)**<br>Manage access and permissions with five different user roles and settings for external users. Set permissions according to people's role, rather than either read or write access to a repository. Don't share the source code with people that only need access to the issue tracker.|Core+|✓|
+|**[Enforce TOS acceptance](../user/admin_area/settings/terms.md)**<br>Enforce your users accepting new terms of service by blocking GitLab traffic.|Core+||
+|**[Email all users of a project, group, or entire server](../user/admin_area/settings/terms.md)**<br>An admin can email groups of users based on project or group membership, or email everyone using the GitLab instance. This is great for scheduled maintenance or upgrades.|Starter+||
|**[Omnibus package supports log forwarding](https://docs.gitlab.com/omnibus/settings/logs.html#udp-log-forwarding)**<br>Forward your logs to a central system.|Starter+||
-|**[Lock project membership to group](../workflow/groups.html#lock-project-membership-to-members-of-this-group)**<br>Group owners can prevent new members from being added to projects within a group.|Starter+|✓|
+|**[Lock project membership to group](https://docs.gitlab.com/ee/user/group/index.html#member-lock-starter)**<br>Group owners can prevent new members from being added to projects within a group.|Starter+|✓|
|**[LDAP group sync](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#group-sync)**<br>GitLab Enterprise Edition gives admins the ability to automatically sync groups and manage SSH keys, permissions, and authentication, so you can focus on building your product, not configuring your tools.|Starter+||
|**[LDAP group sync filters](https://docs.gitlab.com/ee/administration/auth/ldap-ee.html#group-sync)**<br>GitLab Enterprise Edition Premium gives more flexibility to synchronize with LDAP based on filters, meaning you can leverage LDAP attributes to map GitLab permissions.|Premium+||
|**[Audit logs](https://docs.gitlab.com/ee/administration/audit_events.html)**<br>To maintain the integrity of your code, GitLab Enterprise Edition Premium gives admins the ability to view any modifications made within the GitLab server in an advanced audit log system, so you can control, analyze and track every change.|Premium+||
diff --git a/doc/administration/container_registry.md b/doc/administration/container_registry.md
index a1ac4a2a57c..b21bfafc096 100644
--- a/doc/administration/container_registry.md
+++ b/doc/administration/container_registry.md
@@ -1,6 +1,7 @@
# GitLab Container Registry administration
> **Notes:**
+>
> - [Introduced][ce-4040] in GitLab 8.8.
> - Container Registry manifest `v1` support was added in GitLab 8.9 to support
> Docker versions earlier than 1.10.
diff --git a/doc/administration/custom_hooks.md b/doc/administration/custom_hooks.md
index 60ad4bf4e8f..28afaf84f5a 100644
--- a/doc/administration/custom_hooks.md
+++ b/doc/administration/custom_hooks.md
@@ -51,7 +51,7 @@ Hooks can be also placed in `hooks/<hook_name>.d` (global) or
execution of the hooks.
NOTE: **Note:** `<hook_name>.d` would need to be either `pre-receive.d`,
-`post-receive.d`, or `update.d` to work properly. Any other names will be ignored.
+`post-receive.d`, or `update.d` to work properly. Any other names will be ignored.
To look in a different directory for the global custom hooks (those in
`hooks/<hook_name.d>`), set `custom_hooks_dir` in gitlab-shell config. For
@@ -76,9 +76,21 @@ first script exiting with a non-zero value.
> [Introduced][5073] in GitLab 8.10.
-If the commit is declined or an error occurs during the Git hook check,
-the STDERR or STDOUT message of the hook will be present in GitLab's UI.
-STDERR takes precedence over STDOUT.
+To have custom error messages appear in GitLab's UI when the commit is
+declined or an error occurs during the Git hook, your script should:
+
+- Send the custom error messages to either the script's `stdout` or `stderr`.
+- Prefix each message with `GL-HOOK-ERR:` with no characters appearing before the prefix.
+
+### Example custom error message
+
+This hook script written in bash will generate the following message in GitLab's UI:
+
+```bash
+#!/bin/sh
+echo "GL-HOOK-ERR: My custom error message.";
+exit 1
+```
![Custom message from custom Git hook](img/custom_hooks_error_msg.png)
diff --git a/doc/administration/git_protocol.md b/doc/administration/git_protocol.md
index 11b2adeeeb8..345e8646f9b 100644
--- a/doc/administration/git_protocol.md
+++ b/doc/administration/git_protocol.md
@@ -5,13 +5,16 @@ description: "Set and configure Git protocol v2"
# Configuring Git Protocol v2
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/46555) in GitLab 11.4.
-> [Temporarily disabled](https://gitlab.com/gitlab-org/gitlab-ce/issues/55769) in GitLab 11.5.8, 11.6.6, 11.7.1, and 11.8+
+> Temporarily disabled (see [confidential issue](../user/project/issues/confidential_issues.md)
+> `https://gitlab.com/gitlab-org/gitlab-ce/issues/55769`) in GitLab 11.5.8, 11.6.6, 11.7.1, and 11.8+.
NOTE: **Note:**
-Git protocol v2 support has been [temporarily disabled](https://gitlab.com/gitlab-org/gitlab-ce/issues/55769),
-as a feature used to hide certain internal references does not function when it
+Git protocol v2 support has been temporarily disabled
+because a feature used to hide certain internal references does not function when it
is enabled, and this has a security impact. Once this problem has been resolved,
-protocol v2 support will be re-enabled.
+protocol v2 support will be re-enabled. For more information, see the
+[confidential issue](../user/project/issues/confidential_issues.md)
+`https://gitlab.com/gitlab-org/gitlab-ce/issues/55769`.
Git protocol v2 improves the v1 wire protocol in several ways and is
enabled by default in GitLab for HTTP requests. In order to enable SSH,
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 0795d3dad40..f1cedb85455 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -45,9 +45,13 @@ installations that are larger than a single machine. Most
installations will be better served with the default configuration
used by Omnibus and the GitLab source installation guide.
-Starting with GitLab 11.4, Gitaly is a replacement for NFS except
-when the [Elastic Search indexer](https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer)
-is used.
+Starting with GitLab 11.4, Gitaly is able to serve all Git requests without
+needed a shared NFS mount for Git repository data.
+Between 11.4 and 11.8 the exception was the
+[Elastic Search indexer](https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer).
+But since 11.8 the indexer uses Gitaly for data access as well. NFS can still
+be leveraged for redudancy on block level of the Git data. But only has to
+be mounted on the Gitaly server.
### Network architecture
@@ -62,14 +66,16 @@ is used.
- Gitaly addresses must be specified in such a way that they resolve
correctly for ALL Gitaly clients
- Gitaly clients are: unicorn, sidekiq, gitlab-workhorse,
- gitlab-shell, and Gitaly itself
+ gitlab-shell, Elasticsearch Indexer, and Gitaly itself
- special case: a Gitaly server must be able to make RPC calls **to
itself** via its own (Gitaly address, Gitaly token) pair as
specified in `gitlab-rails/config/gitlab.yml`
- Gitaly servers must not be exposed to the public internet
-Gitaly network traffic is unencrypted so you should use a firewall to
-restrict access to your Gitaly server.
+Gitaly network traffic is unencrypted by default, but supports
+[TLS](#tls-support). Authentication is done through a static token. For
+security in depth, its recommended to use a firewall to restrict access
+to your Gitaly server.
Below we describe how to configure a Gitaly server at address
`gitaly.internal:8075` with secret token `abc123secret`. We assume
@@ -194,17 +200,16 @@ server from reaching the Gitaly server then all Gitaly requests will
fail.
We assume that your Gitaly server can be reached at
-`gitaly.internal:8075` from your GitLab server, and that your GitLab
-NFS shares are mounted at `/mnt/gitlab/default` and
-`/mnt/gitlab/storage1` respectively.
+`gitaly.internal:8075` from your GitLab server, and that Gitaly can read and
+write to `/mnt/gitlab/default` and `/mnt/gitlab/storage1` respectively.
Omnibus installations:
```ruby
# /etc/gitlab/gitlab.rb
git_data_dirs({
- 'default' => { 'path' => '/mnt/gitlab/default', 'gitaly_address' => 'tcp://gitaly.internal:8075' },
- 'storage1' => { 'path' => '/mnt/gitlab/storage1', 'gitaly_address' => 'tcp://gitaly.internal:8075' },
+ 'default' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' },
+ 'storage1' => { 'gitaly_address' => 'tcp://gitaly.internal:8075' },
})
gitlab_rails['gitaly_token'] = 'abc123secret'
@@ -218,10 +223,8 @@ gitlab:
repositories:
storages:
default:
- path: /mnt/gitlab/default/repositories
gitaly_address: tcp://gitaly.internal:8075
storage1:
- path: /mnt/gitlab/storage1/repositories
gitaly_address: tcp://gitaly.internal:8075
gitaly:
@@ -236,14 +239,26 @@ repository from your GitLab server over HTTP.
## TLS support
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22602) in GitLab 11.7.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22602) in GitLab 11.8.
-Gitaly supports TLS credentials for GRPC authentication. To be able to communicate
+Gitaly supports TLS encryption. To be able to communicate
with a Gitaly instance that listens for secure connections you will need to use `tls://` url
scheme in the `gitaly_address` of the corresponding storage entry in the gitlab configuration.
The admin needs to bring their own certificate as we do not provide that automatically.
-The certificate to be used needs to be installed on all Gitaly nodes and on all client nodes that communicate with it following procedures described in [GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates)
+The certificate to be used needs to be installed on all Gitaly nodes and on all client nodes that communicate with it following procedures described in [GitLab custom certificate configuration](https://docs.gitlab.com/omnibus/settings/ssl.html#install-custom-public-certificates).
+
+Note that it is possible to configure Gitaly servers with both an
+unencrypted listening address `listen_addr` and an encrypted listening
+address `tls_listen_addr` at the same time. This allows you to do a
+gradual transition from unencrypted to encrypted traffic, if necessary.
+
+To observe what type of connections are actually being used in a
+production environment you can use the following Prometheus query:
+
+```
+sum(rate(gitaly_connections_total[5m])) by (type)
+```
### Example TLS configuration
@@ -300,6 +315,66 @@ certificate_path = '/path/to/cert.pem'
key_path = '/path/to/key.pem'
```
+## Gitaly-ruby
+
+Gitaly was developed to replace Ruby application code in gitlab-ce/ee.
+In order to save time and/or avoid the risk of rewriting existing
+application logic, in some cases we chose to copy some application code
+from gitlab-ce into Gitaly almost as-is. To be able to run that code, we
+made gitaly-ruby, which is a sidecar process for the main Gitaly Go
+process. Some examples of things that are implemented in gitaly-ruby are
+RPC's that deal with wiki's, and RPC's that create commits on behalf of
+a user, such as merge commits.
+
+### Number of gitaly-ruby workers
+
+Gitaly-ruby has much less capacity than Gitaly itself. If your Gitaly
+server has to handle a lot of request, the default setting of having
+just 1 active gitaly-ruby sidecar might not be enough. If you see
+ResourceExhausted errors from Gitaly it's very likely that you have not
+enough gitaly-ruby capacity.
+
+You can increase the number of gitaly-ruby processes on your Gitaly
+server with the following settings.
+
+Omnibus:
+
+```ruby
+# /etc/gitlab/gitlab.rb
+# Default is 2 workers. The minimum is 2; 1 worker is always reserved as
+# a passive stand-by.
+gitaly['ruby_num_workers'] = 4
+```
+
+Source:
+
+```toml
+# /home/git/gitaly/config.toml
+[gitaly-ruby]
+num_workers = 4
+```
+
+### Observing gitaly-ruby traffic
+
+Gitaly-ruby is a somewhat hidden, internal implementation detail of
+Gitaly. There is not that much visibility into what goes on inside
+gitaly-ruby processes.
+
+If you have Prometheus set up to scrape your Gitaly process, you can see
+request rates and error codes for individual RPC's in gitaly-ruby by
+querying `grpc_client_handled_total`. Strictly speaking this metric does
+not differentiate between gitaly-ruby and other RPC's, but in practice
+(as of GitLab 11.9), all gRPC calls made by Gitaly itself are internal
+calls from the main Gitaly process to one of its gitaly-ruby sidecars.
+
+Assuming your `grpc_client_handled_total` counter only observes Gitaly,
+the following query shows you RPC's are (most likely) internally
+implemented as calls to gitaly-ruby.
+
+```
+sum(rate(grpc_client_handled_total[5m])) by (grpc_method) > 0
+```
+
## Disabling or enabling the Gitaly service in a cluster environment
If you are running Gitaly [as a remote
diff --git a/doc/administration/high_availability/gitlab.md b/doc/administration/high_availability/gitlab.md
index e554c06532e..d95c3acec54 100644
--- a/doc/administration/high_availability/gitlab.md
+++ b/doc/administration/high_availability/gitlab.md
@@ -146,6 +146,14 @@ the share is exported and exists on the NFS server and try to remount.
---
+## Upgrading GitLab HA
+
+GitLab HA installations can be upgraded with no downtime, but the
+upgrade process must be carefully coordinated to avoid failures. See the
+[Omnibus GitLab multi-node upgrade
+document](https://docs.gitlab.com/omnibus/update/#multi-node--ha-deployment)
+for more details.
+
Read more on high-availability configuration:
1. [Configure the database](database.md)
diff --git a/doc/administration/high_availability/nfs.md b/doc/administration/high_availability/nfs.md
index 74b0e2c8184..f406163aea0 100644
--- a/doc/administration/high_availability/nfs.md
+++ b/doc/administration/high_availability/nfs.md
@@ -37,6 +37,30 @@ options:
circumstances it could lead to data loss if a failure occurs before data has
synced.
+### Improving NFS performance with GitLab
+
+NOTE: **Note:** This is only available with GitLab 11.9 and up.
+
+If you are using NFS to share Git data, we recommend that you enable a
+number of feature flags that will allow GitLab application processes to
+access Git data directly instead of going through the [Gitaly
+service](../gitaly/index.md). Depending on your workload and disk
+performance, these flags may help improve performance. See [the
+issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/57317) for more
+details.
+
+To do this, run the Rake task:
+
+```sh
+gitlab-rake gitlab:features:enable_rugged
+```
+
+If you need to undo this setting for some reason, run:
+
+```sh
+gitlab-rake gitlab:features:disable_rugged
+```
+
### Known issues
On some customer systems, we have seen NFS clients slow precipitously due to
@@ -47,9 +71,8 @@ bug](https://bugzilla.redhat.com/show_bug.cgi?id=1552203) that may be fixed in
[more recent kernels with this
commit](https://github.com/torvalds/linux/commit/95da1b3a5aded124dd1bda1e3cdb876184813140).
-Users encountering a similar issue may be advised to disable the NFS server
-delegation feature, which is an optimization to reduce the number of network
-round-trips needed to read or write files. To disable NFS server delegations
+GitLab recommends all NFS users disable the NFS server
+delegation feature. To disable NFS server delegations
on an Linux NFS server, do the following:
1. On the NFS server, run:
diff --git a/doc/administration/high_availability/redis.md b/doc/administration/high_availability/redis.md
index bf5d064d79d..3daebc4d84b 100644
--- a/doc/administration/high_availability/redis.md
+++ b/doc/administration/high_availability/redis.md
@@ -14,6 +14,7 @@ a hosted cloud solution or you can use the one that comes bundled with
Omnibus GitLab packages.
> **Notes:**
+>
> - Redis requires authentication for High Availability. See
> [Redis Security](https://redis.io/topics/security) documentation for more
> information. We recommend using a combination of a Redis password and tight
@@ -55,6 +56,7 @@ components below.
### High Availability with Sentinel
> **Notes:**
+>
> - Starting with GitLab `8.11`, you can configure a list of Redis Sentinel
> servers that will monitor a group of Redis servers to provide failover support.
> - Starting with GitLab `8.14`, the Omnibus GitLab Enterprise Edition package
@@ -231,6 +233,7 @@ Pick the one that suits your needs.
This is the section where we install and set up the new Redis instances.
> **Notes:**
+>
> - We assume that you have installed GitLab and all HA components from scratch. If you
> already have it installed and running, read how to
> [switch from a single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha).
@@ -359,11 +362,17 @@ following section assumes you are using Omnibus GitLab Enterprise Edition.
For the Omnibus Community Edition and installations from source, follow the
[Redis HA source install](redis_source.md) guide.
+NOTE: **Note:** If you are using an external Redis Sentinel instance, be sure
+to exclude the `requirepass` parameter from the Sentinel
+configuration. This parameter will cause clients to report `NOAUTH
+Authentication required.`. [Redis Sentinel 3.2.x does not support
+password authentication](https://github.com/antirez/redis/issues/3279).
+
Now that the Redis servers are all set up, let's configure the Sentinel
servers.
If you are not sure if your Redis servers are working and replicating
-correctly, please read the [Troubleshooting Replication](#troubleshooting-replication)
+correctly, please read the [Troubleshooting Replication](#troubleshooting-redis-replication)
and fix it before proceeding with Sentinel setup.
You must have at least `3` Redis Sentinel servers, and they need to
diff --git a/doc/administration/high_availability/redis_source.md b/doc/administration/high_availability/redis_source.md
index 14e2784c419..be6b547372a 100644
--- a/doc/administration/high_availability/redis_source.md
+++ b/doc/administration/high_availability/redis_source.md
@@ -46,7 +46,7 @@ valuable information for the general setup.
Assuming that the Redis master instance IP is `10.0.0.1`:
-1. [Install Redis](../../install/installation.md#6-redis)
+1. [Install Redis](../../install/installation.md#7-redis).
1. Edit `/etc/redis/redis.conf`:
```conf
@@ -72,7 +72,7 @@ Assuming that the Redis master instance IP is `10.0.0.1`:
Assuming that the Redis slave instance IP is `10.0.0.2`:
-1. [Install Redis](../../install/installation.md#6-redis)
+1. [Install Redis](../../install/installation.md#7-redis).
1. Edit `/etc/redis/redis.conf`:
```conf
diff --git a/doc/administration/img/custom_hooks_error_msg.png b/doc/administration/img/custom_hooks_error_msg.png
index 845f0de19ce..4f25c471908 100644
--- a/doc/administration/img/custom_hooks_error_msg.png
+++ b/doc/administration/img/custom_hooks_error_msg.png
Binary files differ
diff --git a/doc/administration/incoming_email.md b/doc/administration/incoming_email.md
index 05873e01a08..658b2f55d30 100644
--- a/doc/administration/incoming_email.md
+++ b/doc/administration/incoming_email.md
@@ -95,244 +95,249 @@ for a real-world example of this exploit.
### Omnibus package installations
-1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the
- feature and fill in the details for your specific IMAP server and email account:
+1. Find the `incoming_email` section in `/etc/gitlab/gitlab.rb`, enable the feature
+ and fill in the details for your specific IMAP server and email account (see [examples](#config-examples) below).
- Configuration for Postfix mail server, assumes mailbox
- incoming@gitlab.example.com
+1. Reconfigure GitLab for the changes to take effect:
- ```ruby
- gitlab_rails['incoming_email_enabled'] = true
+ ```sh
+ sudo gitlab-ctl reconfigure
+ sudo gitlab-ctl restart
+ ```
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
+1. Verify that everything is configured correctly:
- # Email account username
- # With third party providers, this is usually the full email address.
- # With self-hosted email servers, this is usually the user part of the email address.
- gitlab_rails['incoming_email_email'] = "incoming"
- # Email account password
- gitlab_rails['incoming_email_password'] = "[REDACTED]"
+ ```sh
+ sudo gitlab-rake gitlab:incoming_email:check
+ ```
- # IMAP server host
- gitlab_rails['incoming_email_host'] = "gitlab.example.com"
- # IMAP server port
- gitlab_rails['incoming_email_port'] = 143
- # Whether the IMAP server uses SSL
- gitlab_rails['incoming_email_ssl'] = false
- # Whether the IMAP server uses StartTLS
- gitlab_rails['incoming_email_start_tls'] = false
+Reply by email should now be working.
- # The mailbox where incoming mail will end up. Usually "inbox".
- gitlab_rails['incoming_email_mailbox_name'] = "inbox"
- # The IDLE command timeout.
- gitlab_rails['incoming_email_idle_timeout'] = 60
+### Installations from source
+
+1. Go to the GitLab installation directory:
+
+ ```sh
+ cd /home/git/gitlab
```
- Configuration for Gmail / Google Apps, assumes mailbox
- gitlab-incoming@gmail.com
+1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature
+ and fill in the details for your specific IMAP server and email account (see [examples](#config-examples) below).
- ```ruby
- gitlab_rails['incoming_email_enabled'] = true
+1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
+
+ ```sh
+ sudo mkdir -p /etc/default
+ echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
+ ```
+
+1. Restart GitLab:
+
+ ```sh
+ sudo service gitlab restart
+ ```
+
+1. Verify that everything is configured correctly:
+
+ ```sh
+ sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
+ ```
+
+Reply by email should now be working.
+
+### Config examples
+
+#### Postfix
+
+Example configuration for Postfix mail server. Assumes mailbox incoming@gitlab.example.com.
+
+Example for Omnibus installs:
+
+```ruby
+gitlab_rails['incoming_email_enabled'] = true
+
+# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+gitlab_rails['incoming_email_address'] = "incoming+%{key}@gitlab.example.com"
+
+# Email account username
+# With third party providers, this is usually the full email address.
+# With self-hosted email servers, this is usually the user part of the email address.
+gitlab_rails['incoming_email_email'] = "incoming"
+# Email account password
+gitlab_rails['incoming_email_password'] = "[REDACTED]"
+
+# IMAP server host
+gitlab_rails['incoming_email_host'] = "gitlab.example.com"
+# IMAP server port
+gitlab_rails['incoming_email_port'] = 143
+# Whether the IMAP server uses SSL
+gitlab_rails['incoming_email_ssl'] = false
+# Whether the IMAP server uses StartTLS
+gitlab_rails['incoming_email_start_tls'] = false
+
+# The mailbox where incoming mail will end up. Usually "inbox".
+gitlab_rails['incoming_email_mailbox_name'] = "inbox"
+# The IDLE command timeout.
+gitlab_rails['incoming_email_idle_timeout'] = 60
+```
+
+Example for source installs:
+
+```yaml
+incoming_email:
+ enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
+ address: "incoming+%{key}@gitlab.example.com"
# Email account username
# With third party providers, this is usually the full email address.
# With self-hosted email servers, this is usually the user part of the email address.
- gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"
+ user: "incoming"
# Email account password
- gitlab_rails['incoming_email_password'] = "[REDACTED]"
+ password: "[REDACTED]"
# IMAP server host
- gitlab_rails['incoming_email_host'] = "imap.gmail.com"
+ host: "gitlab.example.com"
# IMAP server port
- gitlab_rails['incoming_email_port'] = 993
+ port: 143
# Whether the IMAP server uses SSL
- gitlab_rails['incoming_email_ssl'] = true
+ ssl: false
# Whether the IMAP server uses StartTLS
- gitlab_rails['incoming_email_start_tls'] = false
+ start_tls: false
# The mailbox where incoming mail will end up. Usually "inbox".
- gitlab_rails['incoming_email_mailbox_name'] = "inbox"
+ mailbox: "inbox"
# The IDLE command timeout.
- gitlab_rails['incoming_email_idle_timeout'] = 60
- ```
+ idle_timeout: 60
+```
+
+#### Gmail
+
+Example configuration for Gmail/G Suite. Assumes mailbox gitlab-incoming@gmail.com.
+
+Example for Omnibus installs:
+
+```ruby
+gitlab_rails['incoming_email_enabled'] = true
- Configuration for Microsoft Exchange mail server w/ IMAP enabled, assumes the
- catch-all mailbox incoming@exchange.example.com
+# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+gitlab_rails['incoming_email_address'] = "gitlab-incoming+%{key}@gmail.com"
- ```ruby
- gitlab_rails['incoming_email_enabled'] = true
+# Email account username
+# With third party providers, this is usually the full email address.
+# With self-hosted email servers, this is usually the user part of the email address.
+gitlab_rails['incoming_email_email'] = "gitlab-incoming@gmail.com"
+# Email account password
+gitlab_rails['incoming_email_password'] = "[REDACTED]"
+
+# IMAP server host
+gitlab_rails['incoming_email_host'] = "imap.gmail.com"
+# IMAP server port
+gitlab_rails['incoming_email_port'] = 993
+# Whether the IMAP server uses SSL
+gitlab_rails['incoming_email_ssl'] = true
+# Whether the IMAP server uses StartTLS
+gitlab_rails['incoming_email_start_tls'] = false
+
+# The mailbox where incoming mail will end up. Usually "inbox".
+gitlab_rails['incoming_email_mailbox_name'] = "inbox"
+# The IDLE command timeout.
+gitlab_rails['incoming_email_idle_timeout'] = 60
+```
+
+Example for source installs:
+
+```yaml
+incoming_email:
+ enabled: true
# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- # Exchange does not support sub-addressing, so a catch-all mailbox must be used.
- gitlab_rails['incoming_email_address'] = "incoming-%{key}@exchange.example.com"
+ address: "gitlab-incoming+%{key}@gmail.com"
# Email account username
- # Typically this is the userPrincipalName (UPN)
- gitlab_rails['incoming_email_email'] = "incoming@ad-domain.example.com"
+ # With third party providers, this is usually the full email address.
+ # With self-hosted email servers, this is usually the user part of the email address.
+ user: "gitlab-incoming@gmail.com"
# Email account password
- gitlab_rails['incoming_email_password'] = "[REDACTED]"
+ password: "[REDACTED]"
# IMAP server host
- gitlab_rails['incoming_email_host'] = "exchange.example.com"
+ host: "imap.gmail.com"
# IMAP server port
- gitlab_rails['incoming_email_port'] = 993
+ port: 993
# Whether the IMAP server uses SSL
- gitlab_rails['incoming_email_ssl'] = true
- ```
-
-1. Reconfigure GitLab for the changes to take effect:
+ ssl: true
+ # Whether the IMAP server uses StartTLS
+ start_tls: false
- ```sh
- sudo gitlab-ctl reconfigure
- sudo gitlab-ctl restart
- ```
+ # The mailbox where incoming mail will end up. Usually "inbox".
+ mailbox: "inbox"
+ # The IDLE command timeout.
+ idle_timeout: 60
+```
-1. Verify that everything is configured correctly:
+#### MS Exchange
- ```sh
- sudo gitlab-rake gitlab:incoming_email:check
- ```
+Example configuration for Microsoft Exchange mail server with IMAP enabled. Assumes the
+catch-all mailbox incoming@exchange.example.com.
-1. Reply by email should now be working.
+Example for Omnibus installs:
-### Installations from source
+```ruby
+gitlab_rails['incoming_email_enabled'] = true
-1. Go to the GitLab installation directory:
+# The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+# The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+# Exchange does not support sub-addressing, so a catch-all mailbox must be used.
+gitlab_rails['incoming_email_address'] = "incoming-%{key}@exchange.example.com"
- ```sh
- cd /home/git/gitlab
- ```
+# Email account username
+# Typically this is the userPrincipalName (UPN)
+gitlab_rails['incoming_email_email'] = "incoming@ad-domain.example.com"
+# Email account password
+gitlab_rails['incoming_email_password'] = "[REDACTED]"
-1. Find the `incoming_email` section in `config/gitlab.yml`, enable the feature
- and fill in the details for your specific IMAP server and email account:
+# IMAP server host
+gitlab_rails['incoming_email_host'] = "exchange.example.com"
+# IMAP server port
+gitlab_rails['incoming_email_port'] = 993
+# Whether the IMAP server uses SSL
+gitlab_rails['incoming_email_ssl'] = true
+```
- ```sh
- sudo editor config/gitlab.yml
- ```
+Example for source installs:
- Configuration for Postfix mail server, assumes mailbox
- incoming@gitlab.example.com
-
- ```yaml
- incoming_email:
- enabled: true
-
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- address: "incoming+%{key}@gitlab.example.com"
-
- # Email account username
- # With third party providers, this is usually the full email address.
- # With self-hosted email servers, this is usually the user part of the email address.
- user: "incoming"
- # Email account password
- password: "[REDACTED]"
-
- # IMAP server host
- host: "gitlab.example.com"
- # IMAP server port
- port: 143
- # Whether the IMAP server uses SSL
- ssl: false
- # Whether the IMAP server uses StartTLS
- start_tls: false
-
- # The mailbox where incoming mail will end up. Usually "inbox".
- mailbox: "inbox"
- # The IDLE command timeout.
- idle_timeout: 60
- ```
+```yaml
+incoming_email:
+ enabled: true
- Configuration for Gmail / Google Apps, assumes mailbox
- gitlab-incoming@gmail.com
-
- ```yaml
- incoming_email:
- enabled: true
-
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- address: "gitlab-incoming+%{key}@gmail.com"
-
- # Email account username
- # With third party providers, this is usually the full email address.
- # With self-hosted email servers, this is usually the user part of the email address.
- user: "gitlab-incoming@gmail.com"
- # Email account password
- password: "[REDACTED]"
-
- # IMAP server host
- host: "imap.gmail.com"
- # IMAP server port
- port: 993
- # Whether the IMAP server uses SSL
- ssl: true
- # Whether the IMAP server uses StartTLS
- start_tls: false
-
- # The mailbox where incoming mail will end up. Usually "inbox".
- mailbox: "inbox"
- # The IDLE command timeout.
- idle_timeout: 60
- ```
-
- Configuration for Microsoft Exchange mail server w/ IMAP enabled, assumes the
- catch-all mailbox incoming@exchange.example.com
-
- ```yaml
- incoming_email:
- enabled: true
-
- # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
- # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
- # Exchange does not support sub-addressing, so a catch-all mailbox must be used.
- address: "incoming-%{key}@exchange.example.com"
-
- # Email account username
- # Typically this is the userPrincipalName (UPN)
- user: "incoming@ad-domain.example.com"
- # Email account password
- password: "[REDACTED]"
-
- # IMAP server host
- host: "exchange.example.com"
- # IMAP server port
- port: 993
- # Whether the IMAP server uses SSL
- ssl: true
- # Whether the IMAP server uses StartTLS
- start_tls: false
-
- # The mailbox where incoming mail will end up. Usually "inbox".
- mailbox: "inbox"
- # The IDLE command timeout.
- idle_timeout: 60
- ```
-
-1. Enable `mail_room` in the init script at `/etc/default/gitlab`:
-
- ```sh
- sudo mkdir -p /etc/default
- echo 'mail_room_enabled=true' | sudo tee -a /etc/default/gitlab
- ```
-
-1. Restart GitLab:
-
- ```sh
- sudo service gitlab restart
- ```
+ # The email address including the `%{key}` placeholder that will be replaced to reference the item being replied to.
+ # The placeholder can be omitted but if present, it must appear in the "user" part of the address (before the `@`).
+ # Exchange does not support sub-addressing, so a catch-all mailbox must be used.
+ address: "incoming-%{key}@exchange.example.com"
-1. Verify that everything is configured correctly:
+ # Email account username
+ # Typically this is the userPrincipalName (UPN)
+ user: "incoming@ad-domain.example.com"
+ # Email account password
+ password: "[REDACTED]"
- ```sh
- sudo -u git -H bundle exec rake gitlab:incoming_email:check RAILS_ENV=production
- ```
+ # IMAP server host
+ host: "exchange.example.com"
+ # IMAP server port
+ port: 993
+ # Whether the IMAP server uses SSL
+ ssl: true
+ # Whether the IMAP server uses StartTLS
+ start_tls: false
-1. Reply by email should now be working.
+ # The mailbox where incoming mail will end up. Usually "inbox".
+ mailbox: "inbox"
+ # The IDLE command timeout.
+ idle_timeout: 60
+```
diff --git a/doc/administration/index.md b/doc/administration/index.md
index ef692ea47ee..5f368ea8d49 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -41,6 +41,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [System hooks](../system_hooks/system_hooks.md): Notifications when users, projects and keys are changed.
- [Security](../security/README.md): Learn what you can do to further secure your GitLab instance.
- [Usage statistics, version check, and usage ping](../user/admin_area/settings/usage_statistics.md): Enable or disable information about your instance to be sent to GitLab, Inc.
+- [Global user settings](user_settings.md): Configure instance-wide user permissions.
- [Polling](polling.md): Configure how often the GitLab UI polls for updates.
- [GitLab Pages configuration](pages/index.md): Enable and configure GitLab Pages.
- [GitLab Pages configuration for GitLab source installations](pages/source.md): Enable and configure GitLab Pages on
@@ -51,8 +52,9 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Third party offers](../user/admin_area/settings/third_party_offers.md)
- [Compliance](compliance.md): A collection of features from across the application that you may configure to help ensure that your GitLab instance and DevOps workflow meet compliance standards.
- [Diff limits](../user/admin_area/diff_limits.md): Configure the diff rendering size limits of branch comparison pages.
-- [Merge request diffs](merge_request_diffs.md): Configure the diffs shown on merge requests
+- [Merge request diffs storage](merge_request_diffs.md): Configure merge requests diffs external storage.
- [Broadcast Messages](../user/admin_area/broadcast_messages.md): Send messages to GitLab users through the UI.
+- [Admin Area](../user/admin_area/index.md): for self-managed instance-wide configuration and maintenance.
#### Customizing GitLab's appearance
@@ -129,8 +131,8 @@ Learn how to install, configure, update, and maintain your GitLab instance.
- [Job artifacts](job_artifacts.md): Enable, disable, and configure job artifacts (a set of files and directories which are outputted by a job when it completes successfully).
- [Job traces](job_traces.md): Information about the job traces (logs).
- [Register Shared and specific Runners](../ci/runners/README.md#registering-a-shared-runner): Learn how to register and configure Shared and specific Runners to your own instance.
-- [Shared Runners pipelines quota](../user/admin_area/settings/continuous_integration.md#shared-runners-pipeline-minutes-quota): Limit the usage of pipeline minutes for Shared Runners.
-- [Enable/disable Auto DevOps](../topics/autodevops/index.md#enabling-auto-devops): Enable or disable Auto DevOps for your instance.
+- [Shared Runners pipelines quota](https://docs.gitlab.com/ee/user/admin_area/settings/continuous_integration.html#shared-runners-pipeline-minutes-quota-starter-only): Limit the usage of pipeline minutes for Shared Runners. **[STARTER ONLY]**
+- [Enable/disable Auto DevOps](../topics/autodevops/index.md#enablingdisabling-auto-devops): Enable or disable Auto DevOps for your instance.
## Git configuration options
diff --git a/doc/administration/job_artifacts.md b/doc/administration/job_artifacts.md
index 25ae535d1ec..e7792106f81 100644
--- a/doc/administration/job_artifacts.md
+++ b/doc/administration/job_artifacts.md
@@ -1,6 +1,7 @@
# Jobs artifacts administration
> **Notes:**
+>
> - Introduced in GitLab 8.2 and GitLab Runner 0.7.0.
> - Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format changed to `ZIP`.
> - Starting with GitLab 8.17, builds are renamed to jobs.
@@ -86,6 +87,7 @@ _The artifacts are stored by default in
### Using object storage
> **Notes:**
+>
> - [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),
@@ -274,7 +276,7 @@ you can flip the feature flag from a Rails console.
## Set the maximum file size of the artifacts
Provided the artifacts are enabled, you can change the maximum file size of the
-artifacts through the [Admin area settings](../user/admin_area/settings/continuous_integration.md#maximum-artifacts-size).
+artifacts through the [Admin area settings](../user/admin_area/settings/continuous_integration.md#maximum-artifacts-size-core-only).
## Storage statistics
diff --git a/doc/administration/merge_request_diffs.md b/doc/administration/merge_request_diffs.md
index 94620c3d3a0..c34a9519ace 100644
--- a/doc/administration/merge_request_diffs.md
+++ b/doc/administration/merge_request_diffs.md
@@ -1,7 +1,6 @@
-# Merge request diffs administration
+# Merge request diffs storage **[CORE ONLY]**
-> **Notes:**
-> - External merge request diffs introduced in GitLab 11.8
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/52568) in GitLab 11.8.
Merge request diffs are size-limited copies of diffs associated with merge
requests. When viewing a merge request, diffs are sourced from these copies
@@ -16,9 +15,7 @@ large, in which case, switching to external storage is recommended.
Merge request diffs can be stored on disk, or in object storage. In general, it
is better to store the diffs in the database than on disk.
-To enable external storage of merge request diffs:
-
----
+To enable external storage of merge request diffs, follow the instructions below.
**In Omnibus installations:**
@@ -29,17 +26,15 @@ To enable external storage of merge request diffs:
```
1. _The external diffs will be stored in in
- `/var/opt/gitlab/gitlab-rails/shared/external-diffs`._ To change the path,
- for example to `/mnt/storage/external-diffs`, edit `/etc/gitlab/gitlab.rb`
+ `/var/opt/gitlab/gitlab-rails/shared/external-diffs`._ To change the path,
+ for example, to `/mnt/storage/external-diffs`, edit `/etc/gitlab/gitlab.rb`
and add the following line:
```ruby
gitlab_rails['external_diffs_storage_path'] = "/mnt/storage/external-diffs"
```
-1. Save the file and [reconfigure GitLab][] for the changes to take effect.
-
----
+1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**In installations from source:**
@@ -52,7 +47,7 @@ To enable external storage of merge request diffs:
```
1. _The external diffs will be stored in
- `/home/git/gitlab/shared/external-diffs`._ To change the path, for example
+ `/home/git/gitlab/shared/external-diffs`._ To change the path, for example,
to `/mnt/storage/external-diffs`, edit `/home/git/gitlab/config/gitlab.yml`
and add or amend the following lines:
@@ -62,18 +57,18 @@ To enable external storage of merge request diffs:
storage_path: /mnt/storage/external-diffs
```
-1. Save the file and [restart GitLab][] for the changes to take effect.
+1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
### Using object storage
-Instead of storing the external diffs on disk, we recommended you use an object
+Instead of storing the external diffs on disk, we recommended the use of an object
store like AWS S3 instead. This configuration relies on valid AWS credentials to
be configured already.
### Object Storage Settings
For source installations, these settings are nested under `external_diffs:` and
-then `object_store:`. On omnibus installs, they are prefixed by
+then `object_store:`. On Omnibus installations, they are prefixed by
`external_diffs_object_store_`.
| Setting | Description | Default |
@@ -118,7 +113,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
}
```
- NOTE: if you are using AWS IAM profiles, be sure to omit the
+ Note that, if you are using AWS IAM profiles, be sure to omit the
AWS access key and secret access key/value pairs. For example:
```ruby
@@ -129,9 +124,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
}
```
-1. Save the file and [reconfigure GitLab][] for the changes to take effect.
-
----
+1. Save the file and [reconfigure GitLab](restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
**In installations from source:**
@@ -151,4 +144,4 @@ The connection settings match those provided by [Fog](https://github.com/fog), a
region: eu-central-1
```
-1. Save the file and [restart GitLab][] for the changes to take effect.
+1. Save the file and [restart GitLab](restart_gitlab.md#installations-from-source) for the changes to take effect.
diff --git a/doc/administration/monitoring/index.md b/doc/administration/monitoring/index.md
index d18dddf09c0..fa0459b24ff 100644
--- a/doc/administration/monitoring/index.md
+++ b/doc/administration/monitoring/index.md
@@ -7,4 +7,4 @@ Explore our features to monitor your GitLab instance:
- [GitHub imports](github_imports.md): Monitor the health and progress of GitLab's GitHub importer with various Prometheus metrics.
- [Monitoring uptime](../../user/admin_area/monitoring/health_check.md): Check the server status using the health check endpoint.
- [IP whitelists](ip_whitelist.md): Configure GitLab for monitoring endpoints that provide health check information when probed.
-- [nginx_status](https://docs.gitlab.com/omnibus/settings/nginx.html#enabling-disabling-nginx_status): Monitor your Nginx server status
+- [nginx_status](https://docs.gitlab.com/omnibus/settings/nginx.html#enablingdisabling-nginx_status): Monitor your Nginx server status
diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md
index 1f431f8bd62..ab43ec2cc4f 100644
--- a/doc/administration/monitoring/performance/grafana_configuration.md
+++ b/doc/administration/monitoring/performance/grafana_configuration.md
@@ -34,7 +34,7 @@ Test Connection to ensure the configuration is correct.
- **Default**: Checked
- **Type**: InfluxDB 0.9.x (Even if you're using InfluxDB 0.10.x)
- **Url**: `https://localhost:8086` (Or the remote URL if you've installed InfluxDB
-on a separate server)
+ on a separate server)
- **Access**: proxy
- **Database**: gitlab
- **User**: admin (Or the username configured when setting up InfluxDB)
diff --git a/doc/administration/monitoring/performance/introduction.md b/doc/administration/monitoring/performance/introduction.md
index 37a5388d2fc..7ace0ec5a93 100644
--- a/doc/administration/monitoring/performance/introduction.md
+++ b/doc/administration/monitoring/performance/introduction.md
@@ -1 +1,5 @@
+---
+redirect_to: 'index.md'
+---
+
This document was moved to [another location](index.md).
diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md
index 6a55dbe1eb4..95f497a1470 100644
--- a/doc/administration/monitoring/performance/performance_bar.md
+++ b/doc/administration/monitoring/performance/performance_bar.md
@@ -10,11 +10,11 @@ It allows you to see (from left to right):
- the current host serving the page
- the timing of the page (backend, frontend)
- time taken and number of DB queries, click through for details of these queries
-![SQL profiling using the Performance Bar](img/performance_bar_sql_queries.png)
+ ![SQL profiling using the Performance Bar](img/performance_bar_sql_queries.png)
- time taken and number of [Gitaly] calls, click through for details of these calls
-![Gitaly profiling using the Performance Bar](img/performance_bar_gitaly_calls.png)
+ ![Gitaly profiling using the Performance Bar](img/performance_bar_gitaly_calls.png)
- profile of the code used to generate the page, line by line. In the profile view, the numbers in the left panel represent wall time, cpu time, and number of calls (based on [rblineprof](https://github.com/tmm1/rblineprof)).
-![Line profiling using the Performance Bar](img/performance_bar_line_profiling.png)
+ ![Line profiling using the Performance Bar](img/performance_bar_line_profiling.png)
- time taken and number of calls to Redis
- time taken and number of background jobs created by Sidekiq
- time taken and number of Ruby GC calls
diff --git a/doc/administration/monitoring/performance/prometheus.md b/doc/administration/monitoring/performance/prometheus.md
index d73ef5d1789..2c5bab46dd9 100644
--- a/doc/administration/monitoring/performance/prometheus.md
+++ b/doc/administration/monitoring/performance/prometheus.md
@@ -1 +1,5 @@
+---
+redirect_to: '../prometheus/index.md'
+---
+
This document was moved to [monitoring/prometheus](../prometheus/index.md).
diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md
index 6ea0ac0d495..3bfcc9a289e 100644
--- a/doc/administration/monitoring/prometheus/gitlab_metrics.md
+++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md
@@ -17,10 +17,7 @@ GitLab monitors its own internal service metrics, and makes them available at th
`/-/metrics` endpoint. Unlike other [Prometheus] exporters, in order to access
it, the client IP needs to be [included in a whitelist][whitelist].
-Currently the embedded Prometheus server is not automatically configured to
-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.
+For Omnibus and Chart installations, these metrics are automatically enabled and collected as of [GitLab 9.4](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/1702). For source installations or earlier versions, these metrics will need to be enabled manually and collected by a Prometheus server.
## Unicorn Metrics available
diff --git a/doc/administration/monitoring/prometheus/index.md b/doc/administration/monitoring/prometheus/index.md
index 8f65cd6418c..f2ac155a694 100644
--- a/doc/administration/monitoring/prometheus/index.md
+++ b/doc/administration/monitoring/prometheus/index.md
@@ -1,6 +1,7 @@
# Monitoring GitLab with Prometheus
> **Notes:**
+>
> - Prometheus and the various exporters listed in this page are bundled in the
> Omnibus GitLab package. Check each exporter's documentation for the timeline
> they got added. For installations from source you will have to install them
@@ -17,7 +18,7 @@ access to high quality time-series monitoring of GitLab services.
## Overview
Prometheus works by periodically connecting to data sources and collecting their
-performance metrics via the [various exporters](#prometheus-exporters). To view
+performance metrics via the [various exporters](#bundled-software-metrics). To view
and work with the monitoring data, you can either
[connect directly to Prometheus](#viewing-performance-metrics) or utilize a
dashboard tool like [Grafana].
diff --git a/doc/administration/operations.md b/doc/administration/operations.md
index 4797d2a3206..9cd78105bbb 100644
--- a/doc/administration/operations.md
+++ b/doc/administration/operations.md
@@ -1 +1,5 @@
+---
+redirect_to: 'operations/index.md'
+---
+
This document was moved to [another location](operations/index.md).
diff --git a/doc/administration/operations/index.md b/doc/administration/operations/index.md
index a16fc7ae74f..32f36d68c50 100644
--- a/doc/administration/operations/index.md
+++ b/doc/administration/operations/index.md
@@ -3,20 +3,20 @@
Keep your GitLab instance up and running smoothly.
- [Clean up Redis sessions](cleaning_up_redis_sessions.md): Prior to GitLab 7.3,
-user sessions did not automatically expire from Redis. If
-you have been running a large GitLab server (thousands of users) since before
-GitLab 7.3 we recommend cleaning up stale sessions to compact the Redis
-database after you upgrade to GitLab 7.3.
+ user sessions did not automatically expire from Redis. If
+ you have been running a large GitLab server (thousands of users) since before
+ GitLab 7.3 we recommend cleaning up stale sessions to compact the Redis
+ database after you upgrade to GitLab 7.3.
- [Moving repositories](moving_repositories.md): Moving all repositories managed
-by GitLab to another file system or another server.
+ by GitLab to another file system or another server.
- [Sidekiq MemoryKiller](sidekiq_memory_killer.md): Configure Sidekiq MemoryKiller
-to restart Sidekiq.
+ to restart Sidekiq.
- [Unicorn](unicorn.md): Understand Unicorn and unicorn-worker-killer.
- Speed up SSH operations by [Authorizing SSH users via a fast,
-indexed lookup to the GitLab database](fast_ssh_key_lookup.md), and/or
-by [doing away with user SSH keys stored on GitLab entirely in favor
-of SSH certificates](ssh_certificates.md).
+ 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).
- [Filesystem Performance Benchmarking](filesystem_benchmarking.md): Filesystem
-performance can have a big impact on GitLab performance, especially for actions
-that read or write Git repositories. This information will help benchmark
-filesystem performance against known good and bad real-world systems.
+ performance can have a big impact on GitLab performance, especially for actions
+ that read or write Git repositories. This information will help benchmark
+ filesystem performance against known good and bad real-world systems.
diff --git a/doc/administration/operations/sidekiq_memory_killer.md b/doc/administration/operations/sidekiq_memory_killer.md
index cbffd883774..8eac42f2fe2 100644
--- a/doc/administration/operations/sidekiq_memory_killer.md
+++ b/doc/administration/operations/sidekiq_memory_killer.md
@@ -17,6 +17,11 @@ With the default settings, the MemoryKiller will cause a Sidekiq restart no
more often than once every 15 minutes, with the restart causing about one
minute of delay for incoming background jobs.
+Some background jobs rely on long-running external processes. To ensure these
+are cleanly terminated when Sidekiq is restarted, each Sidekiq process should be
+run as a process group leader (e.g., using `chpst -P`). If using Omnibus or the
+`bin/background_jobs` script with `runit` installed, this is handled for you.
+
## Configuring the MemoryKiller
The MemoryKiller is controlled using environment variables.
diff --git a/doc/administration/operations/speed_up_ssh.md b/doc/administration/operations/speed_up_ssh.md
index 89265b3018b..6dc83c42f53 100644
--- a/doc/administration/operations/speed_up_ssh.md
+++ b/doc/administration/operations/speed_up_ssh.md
@@ -1 +1,5 @@
+---
+redirect_to: 'fast_ssh_key_lookup.md'
+---
+
This document was moved to [another location](fast_ssh_key_lookup.md).
diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md
index 5c809f25fbd..288ce376687 100644
--- a/doc/administration/pages/index.md
+++ b/doc/administration/pages/index.md
@@ -5,6 +5,7 @@ description: 'Learn how to administer GitLab Pages.'
# GitLab Pages administration
> **Notes:**
+>
> - [Introduced][ee-80] in GitLab EE 8.3.
> - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
> - GitLab Pages [were ported][ce-14605] to Community Edition in GitLab 8.17.
@@ -337,10 +338,10 @@ The default is 100MB.
You may want to run GitLab Pages daemon on a separate server in order to decrease the load on your main application server.
Follow the steps below to configure GitLab Pages in a separate server.
-1. Suppose you have the main GitLab application server named `app1`. Prepare
-new Linux server (let's call it `app2`), create NFS share there and configure access to
-this share from `app1`. Let's use the default GitLab Pages folder `/var/opt/gitlab/gitlab-rails/shared/pages`
-as the shared folder on `app2` and mount it to `/mnt/pages` on `app1`.
+1. Suppose you have the main GitLab application server named `app1`. Prepare
+ new Linux server (let's call it `app2`), create NFS share there and configure access to
+ this share from `app1`. Let's use the default GitLab Pages folder `/var/opt/gitlab/gitlab-rails/shared/pages`
+ as the shared folder on `app2` and mount it to `/mnt/pages` on `app1`.
1. On `app2` install GitLab omnibus and modify `/etc/gitlab/gitlab.rb` this way:
@@ -365,7 +366,7 @@ as the shared folder on `app2` and mount it to `/mnt/pages` on `app1`.
pages_external_url "http://<your-pages-domain>"
gitlab_rails['pages_path'] = "/mnt/pages"
```
-
+
1. Run `sudo gitlab-ctl reconfigure`.
## Backup
diff --git a/doc/administration/pages/source.md b/doc/administration/pages/source.md
index 9f2b4d9075a..2100f7cd707 100644
--- a/doc/administration/pages/source.md
+++ b/doc/administration/pages/source.md
@@ -88,12 +88,13 @@ since that is needed in all configurations.
### Wildcard domains
->**Requirements:**
-> - [Wildcard DNS setup](#dns-configuration)
->
-> ---
->
-> URL scheme: `http://page.example.io`
+**Requirements:**
+
+- [Wildcard DNS setup](#dns-configuration)
+
+---
+
+URL scheme: `http://page.example.io`
This is the minimum setup that you can use Pages with. It is the base for all
other setups as described below. Nginx will proxy all requests to the daemon.
@@ -136,7 +137,7 @@ The Pages daemon doesn't listen to the outside world.
```
gitlab_pages_enabled=true
- gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090
+ gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090"
```
1. Copy the `gitlab-pages` Nginx configuration file:
diff --git a/doc/administration/polling.md b/doc/administration/polling.md
index 35aaa20df2c..a1077614677 100644
--- a/doc/administration/polling.md
+++ b/doc/administration/polling.md
@@ -10,15 +10,15 @@ say that issue notes poll every 2 seconds, and issue titles poll every 5
seconds; these are _not_ the actual values.
- 1 is the default, and recommended for most installations. (Issue notes poll
-every 2 seconds, and issue titles poll every 5 seconds.)
+ every 2 seconds, and issue titles poll every 5 seconds.)
- 0 will disable UI polling completely. (On the next poll, clients will stop
-polling for updates.)
+ polling for updates.)
- A value greater than 1 will slow polling down. If you see issues with
-database load from lots of clients polling for updates, increasing the
-multiplier from 1 can be a good compromise, rather than disabling polling
-completely. (For example: If this is set to 2, then issue notes poll every 4
-seconds, and issue titles poll every 10 seconds.)
+ database load from lots of clients polling for updates, increasing the
+ multiplier from 1 can be a good compromise, rather than disabling polling
+ completely. (For example: If this is set to 2, then issue notes poll every 4
+ seconds, and issue titles poll every 10 seconds.)
- A value between 0 and 1 will make the UI poll more frequently (so updates
-will show in other sessions faster), but is **not recommended**. 1 should be
-fast enough. (For example, if this is set to 0.5, then issue notes poll every
-1 second, and issue titles poll every 2.5 seconds.)
+ will show in other sessions faster), but is **not recommended**. 1 should be
+ fast enough. (For example, if this is set to 0.5, then issue notes poll every
+ 1 second, and issue titles poll every 2.5 seconds.)
diff --git a/doc/administration/raketasks/maintenance.md b/doc/administration/raketasks/maintenance.md
index 0d863594fc7..b295b7d5dc4 100644
--- a/doc/administration/raketasks/maintenance.md
+++ b/doc/administration/raketasks/maintenance.md
@@ -60,6 +60,7 @@ Runs the following rake tasks:
It will check that each component was set up according to the installation guide and suggest fixes for issues found.
You may also have a look at our Troubleshooting Guides:
+
- [Troubleshooting Guide (GitLab)](http://docs.gitlab.com/ee/README.html#troubleshooting)
- [Troubleshooting Guide (Omnibus Gitlab)](http://docs.gitlab.com/omnibus/README.html#troubleshooting)
diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md
index 7ad38abe4f5..c39fef907db 100644
--- a/doc/administration/raketasks/storage.md
+++ b/doc/administration/raketasks/storage.md
@@ -34,17 +34,59 @@ export ID_FROM=20
export ID_TO=50
```
-You can monitor the progress in the _Admin > Monitoring > Background jobs_ screen.
-There is a specific Queue you can watch to see how long it will take to finish: **project_migrate_hashed_storage**
+You can monitor the progress in the **Admin Area > Monitoring > Background Jobs** page.
+There is a specific Queue you can watch to see how long it will take to finish:
+`hashed_storage:hashed_storage_project_migrate`
After it reaches zero, you can confirm every project has been migrated by running the commands bellow.
If you find it necessary, you can run this migration script again to schedule missing projects.
-Any error or warning will be logged in the sidekiq's log file.
+Any error or warning will be logged in Sidekiq's log file.
You only need the `gitlab:storage:migrate_to_hashed` rake task to migrate your repositories, but we have additional
commands below that helps you inspect projects and attachments in both legacy and hashed storage.
+## Rollback from Hashed storage to Legacy storage
+
+If you need to rollback the storage migration for any reason, you can follow the steps described here.
+
+NOTE: **Note:** Hashed Storage will be required in future version of GitLab.
+
+To prevent new projects from being created in the Hashed storage,
+you need to undo the [enable hashed storage][storage-migration] changes.
+
+This task will schedule all your existing projects and associated attachments to be rolled back to the
+Legacy storage type.
+
+For Omnibus installations, run the following:
+
+```bash
+sudo gitlab-rake gitlab:storage:rollback_to_legacy
+```
+
+For source installations, run the following:
+
+```bash
+sudo -u git -H bundle exec rake gitlab:storage:rollback_to_legacy RAILS_ENV=production
+```
+
+Both commands accept a range as environment variable:
+
+```bash
+# to rollback any migrated project from ID 20 to 50.
+export ID_FROM=20
+export ID_TO=50
+```
+
+You can monitor the progress in the **Admin Area > Monitoring > Background Jobs** page.
+On the **Queues** tab, you can watch the `hashed_storage:hashed_storage_project_rollback` queue to see how long the process will take to finish.
+
+
+After it reaches zero, you can confirm every project has been rolled back by running the commands bellow.
+If some projects weren't rolled back, you can run this rollback script again to schedule further rollbacks.
+
+Any error or warning will be logged in Sidekiq's log file.
+
## List projects on Legacy storage
To have a simple summary of projects using **Legacy** storage:
diff --git a/doc/administration/raketasks/uploads/migrate.md b/doc/administration/raketasks/uploads/migrate.md
index b5c40478ea5..fd8ea8d3162 100644
--- a/doc/administration/raketasks/uploads/migrate.md
+++ b/doc/administration/raketasks/uploads/migrate.md
@@ -2,7 +2,7 @@
## Migrate to Object Storage
-After [configuring the object storage](../../uploads.md#using-object-storage) for GitLab's uploads, you may use this task to migrate existing uploads from the local storage to the remote storage.
+After [configuring the object storage](../../uploads.md#using-object-storage-core-only) for GitLab's uploads, you may use this task to migrate existing uploads from the local storage to the remote storage.
>**Note:**
All of the processing will be done in a background worker and requires **no downtime**.
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index 8b725e50f58..7cf8f20a9dc 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -31,7 +31,7 @@ panel.
If the repository check fails for some repository you should look up the error
in `repocheck.log`:
-- in the [admin panel](logs.md#repocheck-log)
+- in the [admin panel](logs.md#repochecklog)
- or on disk, see:
- `/var/log/gitlab/gitlab-rails` for Omnibus installations
- `/home/git/gitlab/log` for installations from source
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index 7f25423171f..1689b0a57d6 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -62,6 +62,8 @@ files and add the full paths of the alternative repository storage paths. In
the example below, we add two more mountpoints that are named `nfs` and `cephfs`
respectively.
+NOTE: **Note:** This example uses NFS and CephFS. We do not recommend using EFS for storage as it may impact GitLab's performance. See the [relevant documentation](./high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
+
**For installations from source**
1. Edit `gitlab.yml` and add the storage paths:
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 4934aaf39f7..4e1e363888d 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -2,6 +2,24 @@
> [Introduced][ce-28283] in GitLab 10.0.
+Two different storage layouts can be used
+to store the repositories on disk and their characteristics.
+
+GitLab can be configured to use one or multiple repository shard locations
+that can be:
+
+- Mounted to the local disk
+- Exposed as an NFS shared volume
+- Acessed via [gitaly] on its own machine.
+
+In GitLab, this is configured in `/etc/gitlab/gitlab.rb` by the `git_data_dirs({})`
+configuration hash. The storage layouts discussed here will apply to any shard
+defined in it.
+
+The `default` repository shard that is available in any installations
+that haven't customized it, points to the local folder: `/var/opt/gitlab/git-data`.
+Anything discussed below is expected to be part of that folder.
+
## Legacy Storage
Legacy Storage is the storage behavior prior to version 10.0. For historical
@@ -66,34 +84,12 @@ by another folder with the next 2 characters. They are both stored in a special
"@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}.wiki.git"
```
-### How to migrate to Hashed Storage
-
-In GitLab, go to **Admin > Settings**, find the **Repository Storage** section
-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].
-
-[ce-28283]: https://gitlab.com/gitlab-org/gitlab-ce/issues/28283
-[rake tasks]: raketasks/storage.md#migrate-existing-projects-to-hashed-storage
-[storage-paths]: repository_storage_types.md
-
-#### Rollback
-
-There is no automated rollback implemented. Below are the steps required to rollback
-from each storage migration.
-
-The rollback has to be performed in the reverse order. To get into "Legacy" state,
-you need to rollback Attachments first, then Project.
+### Hashed object pools
-Also note that if Geo is enabled, after the migration was triggered, an event is generated
-to replicate the operation on any Secondary node. That means the on disk changes will also
-need to be performed on these nodes as well. Database changes will propagate without issues.
-
-You must make sure the migration event was already processed or otherwise it may migrate
-the files back to Hashed state again.
-
-#### Hashed object pools
+CAUTION: **Beta:**
+Hashed objects pools are considered beta, and are not ready for production use.
+Follow [gitaly#1548](https://gitlab.com/gitlab-org/gitaly/issues/1548) for
+updates.
For deduplication of public forks and their parent repository, objects are pooled
in an object pool. These object pools are a third repository where shared objects
@@ -110,36 +106,60 @@ enabled for individual projects by executing
be on hashed storage, should not be a fork itself, and hashed storage should be
enabled for all new projects.
-##### Attachments
+### How to migrate to Hashed Storage
-To rollback single Attachment migration, rename `aa/bb/abcdef1234567890...` folder back to `namespace/project`.
+To start a migration, enable Hashed Storage for new projects:
+
+1. Go to **Admin > Settings** and expand the **Repository Storage** section.
+2. Select the **Use hashed storage paths for newly created and renamed projects** checkbox.
-Both folder names can be generated by the `FileUploader.absolute_base_dir(project)`, you
-just need to switch the version from the `project` back to the previous one.
+Check if the change breaks any existing integration you may have that
+either runs on the same machine as your repositories are located, or may login to that machine
+to access data (for example, a remote backup solution).
-```ruby
-project.storage_version
-# => 2
+To schedule a complete rollout, see the
+[rake task documentation for storage migration][rake/migrate-to-hashed] for instructions.
-FileUploader.absolute_base_dir(project)
-# => "/opt/gitlab/embedded/service/gitlab-rails/public/uploads/@hashed/d4/73/d4735e3a265e16eee03f59718b9b5d03019c07d8b6c51f90da3a666eec13ab35"
+If you do have any existing integration, you may want to do a small rollout first,
+to validate. You can do so by specifying a range with the operation.
-project.storage_version = 1
+This is an example of how to limit the rollout to Project IDs 50 to 100, running in
+an Omnibus Gitlab installation:
-FileUploader.absolute_base_dir(project)
-# => "/opt/gitlab/embedded/service/gitlab-rails/public/uploads/gitlab/gitlab-shell-renamed"
+```bash
+sudo gitlab-rake gitlab:storage:migrate_to_hashed ID_FROM=50 ID_TO=100
```
-##### Project
+Check the [documentation][rake/migrate-to-hashed] for additional information and instructions for
+source-based installation.
+
+#### Rollback
+
+Similar to the migration, to disable Hashed Storage for new
+projects:
-To rollback single Project migration, move `@hashed/aa/bb/aabbcdef1234567890abcdef.git` and `@hashed/aa/bb/aabbcdef1234567890abcdef.wiki.git`
-back to `namespace/project.git` and `namespace/project.wiki.git` respectively and switch the version from the `project` back to `null`.
+1. Go to **Admin > Settings** and expand the **Repository Storage** section.
+2. Uncheck the **Use hashed storage paths for newly created and renamed projects** checkbox.
+
+To schedule a complete rollback, see the
+[rake task documentation for storage rollback](raketasks/storage.md#rollback-from-hashed-storage-to-legacy-storage) for instructions.
+
+The rollback task also supports specifying a range of Project IDs. Here is an example
+of limiting the rollout to Project IDs 50 to 100, in an Omnibus Gitlab installation:
+
+```bash
+sudo gitlab-rake gitlab:storage:rollback_to_legacy ID_FROM=50 ID_TO=100
+```
+
+If you have a Geo setup, please note that the rollback will not be reflected automatically
+on the **secondary** node. You may need to wait for a backfill operation to kick-in and remove
+the remaining repositories from the special `@hashed/` folder manually.
### Hashed Storage coverage
We are incrementally moving every storable object in GitLab to the Hashed
Storage pattern. You can check the current coverage status below (and also see
-the [issue](https://gitlab.com/gitlab-com/infrastructure/issues/2821)).
+the [issue][ce-2821]).
Note that things stored in an S3 compatible endpoint will not have the downsides
mentioned earlier, if they are not prefixed with `#{namespace}/#{project_name}`,
@@ -156,6 +176,7 @@ which is true for CI Cache and LFS Objects.
| CI Artifacts | No | No | Yes | 9.4 / 10.6 |
| CI Cache | No | No | Yes | - |
| LFS Objects | Yes | Similar | Yes | 10.0 / 10.7 |
+| Repository pools| No | Yes | - | 11.6 |
#### Implementation Details
@@ -180,3 +201,9 @@ LFS Objects implements a similar storage pattern using 2 chars, 2 level folders,
```
They are also S3 compatible since **10.0** (GitLab Premium), and available in GitLab Core since **10.7**.
+
+[ce-2821]: https://gitlab.com/gitlab-com/infrastructure/issues/2821
+[ce-28283]: https://gitlab.com/gitlab-org/gitlab-ce/issues/28283
+[rake/migrate-to-hashed]: raketasks/storage.md#migrate-existing-projects-to-hashed-storage
+[storage-paths]: repository_storage_types.md
+[gitaly]: gitaly/index.md
diff --git a/doc/administration/repository_storages.md b/doc/administration/repository_storages.md
index cf6de15743f..af7a385e5a0 100644
--- a/doc/administration/repository_storages.md
+++ b/doc/administration/repository_storages.md
@@ -1 +1,5 @@
+---
+redirect_to: 'repository_storage_paths.md'
+---
+
This document was moved to [another location](repository_storage_paths.md).
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index 9dfe085425f..708b59a273b 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -18,7 +18,7 @@ below.
>**Notes:**
For historical reasons, uploads are stored into a base directory, which by default is `uploads/-/system`. It is strongly discouraged to change this configuration option on an existing GitLab installation.
-_The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads/-/system`._
+_The uploads are stored by default in `/var/opt/gitlab/gitlab-rails/uploads`._
1. To change the storage path for example to `/mnt/storage/uploads`, edit
`/etc/gitlab/gitlab.rb` and add the following line:
@@ -53,7 +53,7 @@ _The uploads are stored by default in
> **Notes:**
>
> - [Introduced][ee-3867] in [GitLab Premium][eep] 10.5.
-> - [Introduced][ce17358] in [GitLab Core][ce] 10.7.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17358) in [GitLab Core][ce] 10.7.
> - 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
@@ -152,4 +152,3 @@ _The uploads are stored by default in
[eep]: https://about.gitlab.com/gitlab-ee/ "GitLab Premium"
[ce]: https://about.gitlab.com/gitlab-ce/ "GitLab Community Edition"
[ee-3867]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/3867
-[ce-17358]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17358
diff --git a/doc/administration/user_settings.md b/doc/administration/user_settings.md
new file mode 100644
index 00000000000..f9654655949
--- /dev/null
+++ b/doc/administration/user_settings.md
@@ -0,0 +1,35 @@
+# Modifying global user settings
+
+GitLab administrators can modify user settings for the entire GitLab instance.
+
+## Disallow users creating top-level groups
+
+By default, new users can create top-level groups. To disable this, modify the appropriate configuration file.
+
+For Omnibus installations, add the following to `/etc/gitlab/gitlab.rb`:
+
+```ruby
+gitlab_rails['gitlab_default_can_create_group'] = false
+```
+
+For source installations, uncomment the following line in `config/gitlab.yml`:
+
+```yaml
+# default_can_create_group: false # default: true
+```
+
+## Disallow users changing usernames
+
+By default, new users can change their usernames. To disable this, modify the appropriate configuration file.
+
+For Omnibus installations, add the following to `/etc/gitlab/gitlab.rb`:
+
+```ruby
+gitlab_rails['gitlab_username_changing_enabled'] = false
+```
+
+For source installations, uncomment the following line in `config/gitlab.yml`:
+
+```yaml
+# username_changing_enabled: false # default: true - User can change her username/namespace
+```
diff --git a/doc/api/README.md b/doc/api/README.md
index 89069fe60e1..7ec7955c596 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -134,7 +134,7 @@ Endpoints are available for:
## Road to GraphQL
Going forward, we will start on moving to
-[GraphQL](http://graphql.org/learn/best-practices/) and deprecate the use of
+[GraphQL](graphql/index.md) and deprecate the use of
controller-specific endpoints. GraphQL has a number of benefits:
1. We avoid having to maintain two different APIs.
@@ -274,7 +274,7 @@ personal access tokens, and to using the [Sudo](#sudo) feature, since the user's
password/token may not be known or may change over time.
For more information, refer to the
-[users API](users.md#retrieve-user-impersonation-tokens) docs.
+[users API](users.md#create-an-impersonation-token) docs.
Impersonation tokens are used exactly like regular personal access tokens, and can be passed in either the
`private_token` parameter or the `Private-Token` header.
diff --git a/doc/api/boards.md b/doc/api/boards.md
index 2a2622736c3..28c73db6b98 100644
--- a/doc/api/boards.md
+++ b/doc/api/boards.md
@@ -37,7 +37,7 @@ Example response:
"web_url": "http://example.com/diaspora/diaspora-project-site"
},
"milestone": {
- "id": 12
+ "id": 12,
"title": "10.0"
},
"lists" : [
@@ -95,7 +95,7 @@ Example response:
```json
{
"id": 1,
- "name:": "project issue board",
+ "name": "project issue board",
"project": {
"id": 5,
"name": "Diaspora Project Site",
@@ -106,7 +106,7 @@ Example response:
"web_url": "http://example.com/diaspora/diaspora-project-site"
},
"milestone": {
- "id": 12
+ "id": 12,
"title": "10.0"
},
"lists" : [
diff --git a/doc/api/build_triggers.md b/doc/api/build_triggers.md
index 20d924ab35e..bad7a655d08 100644
--- a/doc/api/build_triggers.md
+++ b/doc/api/build_triggers.md
@@ -1 +1,5 @@
-This document was moved to [Pipeline Triggers](pipeline_triggers.md).
+---
+redirect_to: 'pipeline_triggers.md'
+---
+
+This document was moved to [another location](pipeline_triggers.md).
diff --git a/doc/api/builds.md b/doc/api/builds.md
index a6edda68bc4..0154d35cab6 100644
--- a/doc/api/builds.md
+++ b/doc/api/builds.md
@@ -1 +1,5 @@
+---
+redirect_to: 'jobs.md'
+---
+
This document was moved to [another location](jobs.md).
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 8d36ae7d559..09546fcac3f 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -79,6 +79,7 @@ POST /projects/:id/repository/commits
| `author_email` | string | no | Specify the commit author's email address |
| `author_name` | string | no | Specify the commit author's name |
| `stats` | boolean | no | Include commit stats. Default is true |
+| `force` | boolean | no | When `true` overwrites the target branch with a new commit based on the `start_branch` |
| `actions[]` Attribute | Type | Required | Description |
| --------------------- | ---- | -------- | ----------- |
@@ -474,7 +475,7 @@ GET /projects/:id/repository/commits/:sha/statuses
| `sha` | string | yes | The commit SHA
| `ref` | string | no | The name of a repository branch or tag or, if not given, the default branch
| `stage` | string | no | Filter by [build stage](../ci/yaml/README.md#stages), e.g., `test`
-| `name` | string | no | Filter by [job name](../ci/yaml/README.md#jobs), e.g., `bundler:audit`
+| `name` | string | no | Filter by [job name](../ci/yaml/README.md#introduction), e.g., `bundler:audit`
| `all` | boolean | no | Return all statuses, not only the latest ones
```bash
diff --git a/doc/api/group_milestones.md b/doc/api/group_milestones.md
index eb974267084..260eb09cc38 100644
--- a/doc/api/group_milestones.md
+++ b/doc/api/group_milestones.md
@@ -1,6 +1,5 @@
# Group milestones API
-> **Notes:**
> [Introduced][ce-12819] in GitLab 9.5.
## List group milestones
@@ -13,6 +12,7 @@ GET /groups/:id/milestones?iids[]=42
GET /groups/:id/milestones?iids[]=42&iids[]=43
GET /groups/:id/milestones?state=active
GET /groups/:id/milestones?state=closed
+GET /groups/:id/milestones?title=1.0
GET /groups/:id/milestones?search=version
```
@@ -23,6 +23,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 |
| `iids[]` | Array[integer] | optional | Return only the milestones having the given `iid` |
| `state` | string | optional | Return only `active` or `closed` milestones |
+| `title` | string | optional | Return only the milestones having the given `title` |
| `search` | string | optional | Return only milestones with a title or description matching the provided string |
```bash
diff --git a/doc/api/issues.md b/doc/api/issues.md
index 0571f280d2a..cb5789e76b7 100644
--- a/doc/api/issues.md
+++ b/doc/api/issues.md
@@ -32,6 +32,7 @@ GET /issues?author_id=5
GET /issues?assignee_id=5
GET /issues?my_reaction_emoji=star
GET /issues?search=foo&in=title
+GET /issues?confidential=true
```
| Attribute | Type | Required | Description |
@@ -52,6 +53,7 @@ GET /issues?search=foo&in=title
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
+| `confidential ` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/issues
@@ -148,6 +150,7 @@ GET /groups/:id/issues?search=issue+title+or+description
GET /groups/:id/issues?author_id=5
GET /groups/:id/issues?assignee_id=5
GET /groups/:id/issues?my_reaction_emoji=star
+GET /groups/:id/issues?confidential=true
```
| Attribute | Type | Required | Description |
@@ -168,6 +171,7 @@ GET /groups/:id/issues?my_reaction_emoji=star
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
+| `confidential ` | Boolean | no | Filter confidential or public issues. |
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/4/issues
@@ -264,6 +268,7 @@ GET /projects/:id/issues?search=issue+title+or+description
GET /projects/:id/issues?author_id=5
GET /projects/:id/issues?assignee_id=5
GET /projects/:id/issues?my_reaction_emoji=star
+GET /projects/:id/issues?confidential=true
```
| Attribute | Type | Required | Description |
@@ -284,6 +289,8 @@ GET /projects/:id/issues?my_reaction_emoji=star
| `created_before` | datetime | no | Return issues created on or before the given time |
| `updated_after` | datetime | no | Return issues updated on or after the given time |
| `updated_before` | datetime | no | Return issues updated on or before the given time |
+| `confidential ` | Boolean | no | Filter confidential or public issues. |
+
```bash
curl --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues
diff --git a/doc/api/jobs.md b/doc/api/jobs.md
index 085e321b35f..877cd99723a 100644
--- a/doc/api/jobs.md
+++ b/doc/api/jobs.md
@@ -10,7 +10,7 @@ GET /projects/:id/jobs
| 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. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `scope` | string **or** array of strings | no | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, or `manual`. All jobs are returned if `scope` is not provided. |
```sh
@@ -142,8 +142,8 @@ GET /projects/:id/pipelines/:pipeline_id/jobs
| Attribute | Type | Required | Description |
|---------------|--------------------------------|----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-| `pipeline_id` | integer | yes | The ID of a pipeline. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `pipeline_id` | integer | yes | ID of a pipeline. |
| `scope` | string **or** array of strings | no | Scope of jobs to show. Either one of or an array of the following: `created`, `pending`, `running`, `failed`, `success`, `canceled`, `skipped`, or `manual`. All jobs are returned if `scope` is not provided. |
```sh
@@ -275,8 +275,8 @@ GET /projects/:id/jobs/:job_id
| Attribute | Type | Required | Description |
|-----------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
-| `job_id` | integer | yes | The ID of a job. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `job_id` | integer | yes | ID of a job. |
```sh
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/8"
@@ -350,8 +350,8 @@ GET /projects/:id/jobs/:job_id/artifacts
| 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. |
-| `job_id` | integer | yes | The ID of a job. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `job_id` | integer | yes | ID of a job. |
Example requests:
@@ -385,7 +385,7 @@ Parameters
| 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. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
| `job` | string | yes | The name of the job. |
@@ -420,7 +420,7 @@ Parameters
| 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. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `job_id ` | integer | yes | The unique job identifier. |
| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
@@ -454,7 +454,7 @@ Parameters:
| 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. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
| `ref_name` | string | yes | Branch or tag name in repository. HEAD or SHA references are not supported. |
| `artifact_path` | string | yes | Path to a file inside the artifacts archive. |
| `job` | string | yes | The name of the job. |
@@ -483,8 +483,8 @@ GET /projects/:id/jobs/:job_id/trace
| 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. |
-| job_id | integer | yes | The ID of a job. |
+| id | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| job_id | integer | yes | ID of a job. |
```sh
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/8/trace"
@@ -507,8 +507,8 @@ POST /projects/:id/jobs/:job_id/cancel
| 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. |
-| `job_id` | integer | yes | The ID of a job. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `job_id` | integer | yes | ID of a job. |
```sh
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/cancel"
@@ -555,8 +555,8 @@ POST /projects/:id/jobs/:job_id/retry
| 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. |
-| `job_id` | integer | yes | The ID of a job. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `job_id` | integer | yes | ID of a job. |
```sh
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/retry"
@@ -605,8 +605,8 @@ Parameters
| 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. |
-| `job_id` | integer | yes | The ID of a job. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `job_id` | integer | yes | ID of a job. |
Example of request
@@ -658,8 +658,8 @@ Parameters
| 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. |
-| `job_id` | integer | yes | The ID of a job. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `job_id` | integer | yes | ID of a job. |
Example request:
@@ -699,6 +699,33 @@ Example response:
}
```
+## Delete artifacts
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/25522) in GitLab 11.9.
+
+Delete artifacts of a job.
+
+```
+DELETE /projects/:id/jobs/:job_id/artifacts
+```
+
+| Attribute | Type | Required | Description |
+|-----------|----------------|----------|------------------------------------------------------------------------------------------------------------------|
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+| `job_id` | integer | yes | ID of a job. |
+
+
+Example request:
+
+```sh
+curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/artifacts"
+```
+
+NOTE: **Note:**
+At least Maintainer role is required to delete artifacts.
+
+If the artifacts were deleted successfully, a response with status `204 No Content` is returned.
+
## Play a job
Triggers a manual action to start a job.
@@ -709,8 +736,8 @@ POST /projects/:id/jobs/:job_id/play
| 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. |
-| `job_id` | integer | yes | The ID of a job. |
+| `id` | integer/string | yes | ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user. |
+| `job_id` | integer | yes | ID of a job. |
```sh
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/jobs/1/play"
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index e176cdffc5f..ed4b6281acc 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -1101,6 +1101,40 @@ Parameters:
}
```
+## Merge to default merge ref path
+
+Merge the changes between the merge request source and target branches into `refs/merge-requests/:iid/merge`
+ref, of the target project repository. This ref will have the state the target branch would have if
+a regular merge action was taken.
+
+This is not a regular merge action given it doesn't change the merge request state in any manner.
+
+This ref (`refs/merge-requests/:iid/merge`) is **always** overwritten when submitting
+requests to this API, so none of its state is kept or used in the process.
+
+If the merge request has conflicts, is empty or already merged,
+you'll get a `400` and a descriptive error message. If you don't have permissions to do so,
+you'll get a `403`.
+
+It returns the HEAD commit of `refs/merge-requests/:iid/merge` in the response body in
+case of `200`.
+
+```
+PUT /projects/:id/merge_requests/:merge_request_iid/merge_to_ref
+```
+
+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) - Internal ID of MR
+- `merge_commit_message` (optional) - Custom merge commit message
+
+```json
+{
+ "commit_id": "854a3a7a17acbcc0bbbea170986df1eb60435f34"
+}
+```
+
## Cancel Merge When Pipeline Succeeds
If you don't have permissions to accept this merge request - you'll get a `401`
diff --git a/doc/api/milestones.md b/doc/api/milestones.md
index fa8f8a0bcf0..3b76c19dc07 100644
--- a/doc/api/milestones.md
+++ b/doc/api/milestones.md
@@ -10,6 +10,7 @@ GET /projects/:id/milestones?iids[]=42
GET /projects/:id/milestones?iids[]=42&iids[]=43
GET /projects/:id/milestones?state=active
GET /projects/:id/milestones?state=closed
+GET /projects/:id/milestones?title=1.0
GET /projects/:id/milestones?search=version
```
@@ -20,6 +21,7 @@ Parameters:
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `iids[]` | Array[integer] | optional | Return only the milestones having the given `iid` |
| `state` | string | optional | Return only `active` or `closed` milestones |
+| `title` | string | optional | Return only the milestones having the given `title` |
| `search` | string | optional | Return only milestones with a title or description matching the provided string |
```bash
@@ -130,3 +132,18 @@ Parameters:
- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
- `milestone_id` (required) - The ID of a project milestone
+
+## Promote project milestone to a group milestone
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/53861) in GitLab 11.9
+
+Only for users with developer access to the group.
+
+```
+POST /projects/:id/milestones/:milestone_id/promote
+```
+
+Parameters:
+
+- `id` (required) - The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user
+- `milestone_id` (required) - The ID of a project milestone
diff --git a/doc/api/namespaces.md b/doc/api/namespaces.md
index b8bc4c40124..b66a3198ffb 100644
--- a/doc/api/namespaces.md
+++ b/doc/api/namespaces.md
@@ -64,9 +64,9 @@ Get all namespaces that match a string in their name or path.
GET /namespaces?search=foobar
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `search` | string | no | Returns a list of namespaces the user is authorized to see based on the search criteria |
+| Attribute | Type | Required | Description |
+| --------- | ------ | -------- | ----------- |
+| `search` | string | no | Returns a list of namespaces the user is authorized to see based on the search criteria |
Example request:
@@ -98,9 +98,9 @@ Get a namespace by ID.
GET /namespaces/:id
```
-| Attribute | Type | Required | Description |
-| --------- | ---- | -------- | ----------- |
-| `id` | integer/string | yes | ID or path of the namespace |
+| Attribute | Type | Required | Description |
+| --------- | -------------- | -------- | ----------- |
+| `id` | integer/string | yes | ID or [URL-encoded path of the namespace](README.md#namespaced-path-encoding) |
Example request:
diff --git a/doc/api/pipeline_schedules.md b/doc/api/pipeline_schedules.md
index 137f1fdddec..50d9e007ecc 100644
--- a/doc/api/pipeline_schedules.md
+++ b/doc/api/pipeline_schedules.md
@@ -1,4 +1,4 @@
-# Pipeline schedules
+# Pipeline schedules API
You can read more about [pipeline schedules](../user/project/pipelines/schedules.md).
@@ -278,9 +278,9 @@ curl --request DELETE --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" "https://gi
}
```
-## Pipeline schedule variable
+## Pipeline schedule variables
-> [Introduced][ce-34518] in GitLab 10.0.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/34518) in GitLab 10.0.
## Create a new pipeline schedule variable
@@ -358,5 +358,3 @@ curl --request DELETE --header "PRIVATE-TOKEN: k5ESFgWY2Qf5xEvDcFxZ" "https://gi
"value": "updated value"
}
```
-
-[ce-34518]: https://gitlab.com/gitlab-org/gitlab-ce/issues/34518 \ No newline at end of file
diff --git a/doc/api/project_snippets.md b/doc/api/project_snippets.md
index f02674adfe2..0ccb0517e08 100644
--- a/doc/api/project_snippets.md
+++ b/doc/api/project_snippets.md
@@ -121,7 +121,6 @@ Parameters:
## Get user agent details
-> **Notes:**
> [Introduced][ce-29508] in GitLab 9.4.
Available only for admins.
diff --git a/doc/api/project_statistics.md b/doc/api/project_statistics.md
new file mode 100644
index 00000000000..34d73abfcbf
--- /dev/null
+++ b/doc/api/project_statistics.md
@@ -0,0 +1,49 @@
+# Project statistics API
+
+Every API call to [project](../user/project/index.md) statistics must be authenticated.
+
+## Get the statistics of the last 30 days
+
+Retrieving the statistics requires write access to the repository.
+Currently only HTTP fetches statistics are returned.
+Fetches statistics includes both clones and pulls count and are HTTP only, SSH fetches are not included.
+
+```
+GET /projects/:id/statistics
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ------ | -------- | ----------- |
+| `id ` | integer / string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
+
+Example response:
+
+```json
+{
+ "fetches": {
+ "total": 50,
+ "days": [
+ {
+ "count": 10,
+ "date": "2018-01-10"
+ },
+ {
+ "count": 10,
+ "date": "2018-01-09"
+ },
+ {
+ "count": 10,
+ "date": "2018-01-08"
+ },
+ {
+ "count": 10,
+ "date": "2018-01-07"
+ },
+ {
+ "count": 10,
+ "date": "2018-01-06"
+ }
+ ]
+ }
+}
+```
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 3c0c956ddc2..0a950352ecf 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -771,6 +771,8 @@ POST /projects/:id/fork
| --------- | ---- | -------- | ----------- |
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) |
| `namespace` | integer/string | yes | The ID or path of the namespace that the project will be forked to |
+| `path` | string | no | The path that will be assigned to the resultant project after forking |
+| `name` | string | no | The name that will be assigned to the resultant project after forking |
## List Forks of a project
diff --git a/doc/api/releases/links.md b/doc/api/releases/links.md
index fd7b9d6e6e2..9c91264ed65 100644
--- a/doc/api/releases/links.md
+++ b/doc/api/releases/links.md
@@ -15,7 +15,7 @@ GET /projects/:id/releases/:tag_name/assets/links
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. |
Example request:
@@ -53,7 +53,7 @@ GET /projects/:id/releases/:tag_name/assets/links/:link_id
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. |
| `link_id` | integer | yes | The id of the link. |
@@ -84,7 +84,7 @@ POST /projects/:id/releases/:tag_name/assets/links
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. |
| `name` | string | yes | The name of the link. |
| `url` | string | yes | The URL of the link. |
@@ -120,7 +120,7 @@ PUT /projects/:id/releases/:tag_name/assets/links/:link_id
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. |
| `link_id` | integer | yes | The id of the link. |
| `name` | string | no | The name of the link. |
@@ -156,7 +156,7 @@ DELETE /projects/:id/releases/:tag_name/assets/links/:link_id
| Attribute | Type | Required | Description |
| ------------- | -------------- | -------- | --------------------------------------- |
-| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
+| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../README.md#namespaced-path-encoding). |
| `tag_name` | string | yes | The tag associated with the Release. |
| `link_id` | integer | yes | The id of the link. |
diff --git a/doc/api/repository_files.md b/doc/api/repository_files.md
index 8c1d982f394..6fcc06ea8cd 100644
--- a/doc/api/repository_files.md
+++ b/doc/api/repository_files.md
@@ -171,6 +171,7 @@ Parameters:
If the commit fails for any reason we return a 400 error with a non-specific
error message. Possible causes for a failed commit include:
+
- the `file_path` contained `/../` (attempted directory traversal);
- the new file contents were identical to the current file contents, i.e. the
user tried to make an empty commit;
diff --git a/doc/api/runners.md b/doc/api/runners.md
index 4aa0e4543e5..7d7215e6b80 100644
--- a/doc/api/runners.md
+++ b/doc/api/runners.md
@@ -13,13 +13,15 @@ GET /runners
GET /runners?scope=active
GET /runners?type=project_type
GET /runners?status=active
+GET /runners?tag_list=tag1,tag2
```
-| Attribute | Type | Required | Description |
-|-----------|---------|----------|---------------------|
-| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
-| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
+| Attribute | Type | Required | Description |
+|-------------|----------------|----------|---------------------|
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
+| `tag_list` | Array[String] | no | List of of the runner's tags |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners"
@@ -62,13 +64,15 @@ GET /runners/all
GET /runners/all?scope=online
GET /runners/all?type=project_type
GET /runners/all?status=active
+GET /runners/all?tag_list=tag1,tag2
```
-| Attribute | Type | Required | Description |
-|-----------|---------|----------|---------------------|
-| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online`, `offline`; showing all runners if none provided |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
-| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
+| Attribute | Type | Required | Description |
+|-------------|----------------|----------|---------------------|
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of runners to show, one of: `specific`, `shared`, `active`, `paused`, `online`, `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
+| `tag_list` | Array[String] | no | List of of the runner's tags |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/runners/all"
@@ -347,14 +351,16 @@ GET /projects/:id/runners
GET /projects/:id/runners?scope=active
GET /projects/:id/runners?type=project_type
GET /projects/:id/runners?status=active
+GET /projects/:id/runners?tag_list=tag1,tag2
```
-| 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 |
-| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
-| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
-| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
+| 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 |
+| `scope` | string | no | Deprecated: Use `type` or `status` instead. The scope of specific runners to show, one of: `active`, `paused`, `online`, `offline`; showing all runners if none provided |
+| `type` | string | no | The type of runners to show, one of: `instance_type`, `group_type`, `project_type` |
+| `status` | string | no | The status of runners to show, one of: `active`, `paused`, `online`, `offline` |
+| `tag_list` | Array[String] | no | List of of the runner's tags |
```
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/9/runners"
@@ -478,7 +484,7 @@ Example response:
## Delete a registered Runner
-Deletes a registed Runner.
+Deletes a registered Runner.
```
DELETE /runners
diff --git a/doc/api/search.md b/doc/api/search.md
index aa601648b2c..6ee3d32d8bc 100644
--- a/doc/api/search.md
+++ b/doc/api/search.md
@@ -17,7 +17,7 @@ GET /search
| `scope` | string | yes | The scope to search in |
| `search` | string | yes | The search query |
-Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs.
+Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs, users.
The response depends on the requested scope.
@@ -253,7 +253,7 @@ Example response:
### Scope: snippet_blobs
```bash
-curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/search?scope=snippet_blos&search=test
+curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/search?scope=snippet_blobs&search=test
```
Example response:
@@ -281,6 +281,27 @@ Example response:
]
```
+### Scope: users
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/search?scope=users&search=doe
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 1,
+ "name": "John Doe1",
+ "username": "user1",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
+ "web_url": "http://localhost/user1"
+ }
+]
+```
+
## Group Search API
Search within the specified group.
@@ -297,7 +318,7 @@ GET /groups/:id/search
| `scope` | string | yes | The scope to search in |
| `search` | string | yes | The search query |
-Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones.
+Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, users.
The response depends on the requested scope.
@@ -499,6 +520,27 @@ Example response:
]
```
+### Scope: users
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/3/search?scope=users&search=doe
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 1,
+ "name": "John Doe1",
+ "username": "user1",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
+ "web_url": "http://localhost/user1"
+ }
+]
+```
+
## Project Search API
Search within the specified project.
@@ -515,7 +557,7 @@ GET /projects/:id/search
| `scope` | string | yes | The scope to search in |
| `search` | string | yes | The search query |
-Search the expression within the specified scope. Currently these scopes are supported: issues, merge_requests, milestones, notes, wiki_blobs, commits, blobs.
+Search the expression within the specified scope. Currently these scopes are supported: issues, merge_requests, milestones, notes, wiki_blobs, commits, blobs, users.
The response depends on the requested scope.
@@ -828,4 +870,25 @@ Example response:
]
```
+### Scope: users
+
+```bash
+curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/6/search?scope=users&search=doe
+```
+
+Example response:
+
+```json
+[
+ {
+ "id": 1,
+ "name": "John Doe1",
+ "username": "user1",
+ "state": "active",
+ "avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
+ "web_url": "http://localhost/user1"
+ }
+]
+```
+
[ce-41763]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41763
diff --git a/doc/api/services.md b/doc/api/services.md
index 5d5aa3e5b3e..03d0a80aa64 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -505,10 +505,9 @@ GET /projects/:id/services/jira
Set JIRA service for a project.
-> **Notes:**
-> - Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
-> `project_url` are replaced by `project_key`, `url`. If you are using an
-> older version, [follow this documentation][old-jira-api].
+> Starting with GitLab 8.14, `api_url`, `issues_url`, `new_issue_url` and
+> `project_url` are replaced by `project_key`, `url`. If you are using an
+> older version, [follow this documentation][old-jira-api].
```
PUT /projects/:id/services/jira
@@ -522,6 +521,7 @@ Parameters:
| `project_key` | string | yes | The short identifier for your JIRA project, all uppercase, e.g., `PROJ`. |
| `username` | string | yes | The username of the user created to be used with GitLab/JIRA. |
| `password` | string | yes | The password of the user created to be used with GitLab/JIRA. |
+| `active` | boolean | no | Activates or deactivates the service. Defaults to false (deactivated). |
| `jira_issue_transition_id` | integer | no | The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`. |
### Delete JIRA service
@@ -1101,3 +1101,39 @@ GET /projects/:id/services/mock-ci
```
[11435]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/11435
+
+## YouTrack
+
+YouTrack issue tracker
+
+### Create/Edit YouTrack service
+
+Set YouTrack service for a project.
+
+```
+PUT /projects/:id/services/youtrack
+```
+
+Parameters:
+
+| Parameter | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `issues_url` | string | true | Issue url |
+| `project_url` | string | true | Project url |
+| `description` | string | false | Description |
+
+### Delete YouTrack Service
+
+Delete YouTrack service for a project.
+
+```
+DELETE /projects/:id/services/youtrack
+```
+
+### Get YouTrack Service Settings
+
+Get YouTrack service settings for a project.
+
+```
+GET /projects/:id/services/youtrack
+```
diff --git a/doc/api/settings.md b/doc/api/settings.md
index 2e0a2a09133..c2a1f7feefd 100644
--- a/doc/api/settings.md
+++ b/doc/api/settings.md
@@ -161,7 +161,7 @@ are listed in the descriptions of the relevant settings.
| `email_author_in_body` | boolean | no | Some email servers do not support overriding the email sender name. Enable this option to include the name of the author of the issue, merge request or comment in the email body instead. |
| `enabled_git_access_protocol` | string | no | Enabled protocols for Git access. Allowed values are: `ssh`, `http`, and `nil` to allow both protocols. |
| `enforce_terms` | boolean | no | (**If enabled, requires:** `terms`) Enforce application ToS to all users. |
-| `first_day_of_week` | integer | no | Start day of the week for calendar views and date pickers. Valid values are `0` (default) for Sunday and `1` for Monday. |
+| `first_day_of_week` | integer | no | Start day of the week for calendar views and date pickers. Valid values are `0` (default) for Sunday, `1` for Monday, and `6` for Saturday. |
| `gitaly_timeout_default` | integer | no | Default Gitaly timeout, in seconds. This timeout is not enforced for git fetch/push operations or Sidekiq jobs. Set to `0` to disable timeouts. |
| `gitaly_timeout_fast` | integer | no | Gitaly fast operation timeout, in seconds. Some Gitaly operations are expected to be fast. If they exceed this threshold, there may be a problem with a storage shard and 'failing fast' can help maintain the stability of the GitLab instance. Set to `0` to disable timeouts. |
| `gitaly_timeout_medium` | integer | no | Medium Gitaly timeout, in seconds. This should be a value between the Fast and the Default timeout. Set to `0` to disable timeouts. |
diff --git a/doc/api/suggestions.md b/doc/api/suggestions.md
index e88d536282a..188989bc94e 100644
--- a/doc/api/suggestions.md
+++ b/doc/api/suggestions.md
@@ -24,8 +24,6 @@ Example response:
```json
{
"id": 36,
- "from_original_line": 10,
- "to_original_line": 10,
"from_line": 10,
"to_line": 10,
"appliable": false,
diff --git a/doc/api/users.md b/doc/api/users.md
index b0977810120..606003a75e2 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -140,7 +140,8 @@ GET /users
"can_create_project": true,
"two_factor_enabled": true,
"external": false,
- "private_profile": false
+ "private_profile": false,
+ "highest_role":10
}
]
```
diff --git a/doc/articles/artifactory_and_gitlab/index.md b/doc/articles/artifactory_and_gitlab/index.md
index 6a590b53727..ed9fd135e7c 100644
--- a/doc/articles/artifactory_and_gitlab/index.md
+++ b/doc/articles/artifactory_and_gitlab/index.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../ci/examples/artifactory_and_gitlab/index.md'
+---
+
This document was moved to [another location](../../ci/examples/artifactory_and_gitlab/index.md)
diff --git a/doc/articles/how_to_configure_ldap_gitlab_ce/index.md b/doc/articles/how_to_configure_ldap_gitlab_ce/index.md
index a8320c12e6b..8e2e54711e7 100644
--- a/doc/articles/how_to_configure_ldap_gitlab_ce/index.md
+++ b/doc/articles/how_to_configure_ldap_gitlab_ce/index.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../administration/auth/how_to_configure_ldap_gitlab_ce/index.md'
+---
+
This document was moved to [another location](../../administration/auth/how_to_configure_ldap_gitlab_ce/index.md).
diff --git a/doc/articles/how_to_install_git/index.md b/doc/articles/how_to_install_git/index.md
index 3e6003a33b7..62598101895 100644
--- a/doc/articles/how_to_install_git/index.md
+++ b/doc/articles/how_to_install_git/index.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../topics/git/how_to_install_git/index.md'
+---
+
This document was moved to [another location](../../topics/git/how_to_install_git/index.md).
diff --git a/doc/articles/index.md b/doc/articles/index.md
index 87ee17bb6de..162db11d6ac 100644
--- a/doc/articles/index.md
+++ b/doc/articles/index.md
@@ -4,8 +4,8 @@ comments: false
# Technical articles list (deprecated)
-[Technical articles](../development/documentation/index.md#technical-articles) are
-topic-related documentation, written with an user-friendly approach and language, aiming
+Technical articles are
+topic-related documentation, written with a user-friendly approach and language, aiming
to provide the community with guidance on specific processes to achieve certain objectives.
The list of technical articles was [deprecated](https://gitlab.com/gitlab-org/gitlab-ce/issues/41138) in favor of having them linked from their topic-related documentation:
diff --git a/doc/articles/laravel_with_gitlab_and_envoy/index.md b/doc/articles/laravel_with_gitlab_and_envoy/index.md
index b092cdb0f7a..fa4f6243410 100644
--- a/doc/articles/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/articles/laravel_with_gitlab_and_envoy/index.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../ci/examples/laravel_with_gitlab_and_envoy/index.md'
+---
+
This document was moved to [another location](../../ci/examples/laravel_with_gitlab_and_envoy/index.md).
diff --git a/doc/articles/numerous_undo_possibilities_in_git/index.md b/doc/articles/numerous_undo_possibilities_in_git/index.md
index 3f46ee9a5e6..83aac82db4e 100644
--- a/doc/articles/numerous_undo_possibilities_in_git/index.md
+++ b/doc/articles/numerous_undo_possibilities_in_git/index.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../topics/git/numerous_undo_possibilities_in_git/index.md'
+---
+
This document was moved to [another location](../../topics/git/numerous_undo_possibilities_in_git/index.md).
diff --git a/doc/articles/runner_autoscale_aws/index.md b/doc/articles/runner_autoscale_aws/index.md
index e2667aebc5f..fb769731256 100644
--- a/doc/articles/runner_autoscale_aws/index.md
+++ b/doc/articles/runner_autoscale_aws/index.md
@@ -1 +1,5 @@
+---
+redirect_to: 'https://docs.gitlab.com/runner/configuration/runner_autoscale_aws/index.html'
+---
+
This document was moved to [another location](https://docs.gitlab.com/runner/configuration/runner_autoscale_aws/index.html).
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 4e066a0df97..47810a8b7b6 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -5,131 +5,124 @@ description: "Learn how to use GitLab CI/CD, the GitLab built-in Continuous Inte
# GitLab Continuous Integration (GitLab CI/CD)
-![Pipeline graph](img/cicd_pipeline_infograph.png)
+GitLab CI/CD is GitLab's built-in tool for software development using continuous methodology:
-The benefits of Continuous Integration are huge when automation plays an
-integral part of your workflow. GitLab comes with built-in Continuous
-Integration, Continuous Deployment, and Continuous Delivery support
-to build, test, and deploy your application.
+- Continuous integration (CI).
+- Continuous delivery and deployment (CD).
-Here's some info we've gathered to get you started.
+Within the [DevOps lifecycle](../README.md#the-entire-devops-lifecycle), GitLab CI/CD spans
+the [Verify (CI)](../README.md#verify) and [Release (CD)](../README.md#release) stages.
-## Getting started
+## Overview
-The first steps towards your GitLab CI/CD journey.
+CI/CD is a vast area, so GitLab provides documentation for all levels of expertise. Consult the following table to find the right documentation for you:
-- [Getting started with GitLab CI/CD](quick_start/README.md): understand how GitLab CI/CD works.
-- [GitLab CI/CD configuration file: `.gitlab-ci.yml`](yaml/README.md) - Learn all about the ins and outs of `.gitlab-ci.yml`.
-- [Pipelines and jobs](pipelines.md): configure your GitLab CI/CD pipelines to build, test, and deploy your application.
-- Runners: The [GitLab Runner](https://docs.gitlab.com/runner/) is responsible by running the jobs in your CI/CD pipeline. On GitLab.com, Shared Runners are enabled by default, so
-you don't need to set up anything to start to use them with GitLab CI/CD.
+| Level of expertise | Resource |
+|:------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------|
+| New to the concepts of CI and CD | For a high-level overview, read an [introduction to CI/CD with GitLab](introduction/index.md). |
+| Familiar with GitLab CI/CD concepts | After getting familiar with GitLab CI/CD, let us walk you through a simple example in our [getting started guide](quick_start/README.md). |
+| A GitLab CI/CD expert | Jump straight to our [`.gitlab.yml`](yaml/README.md) reference. |
-### Introduction to GitLab CI/CD
+Familiarity with GitLab Runner is also useful because it is responsible for running the jobs in your
+CI/CD pipeline. On GitLab.com, shared Runners are enabled by default so you won't need to set this up to get started.
-- Article (2016-08-05): [Continuous Integration, Delivery, and Deployment with GitLab - Intro to CI/CD](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/)
-- Article (2015-12-14): [Getting started with GitLab and GitLab CI - Intro to CI](https://about.gitlab.com/2015/12/14/getting-started-with-gitlab-and-gitlab-ci/)
-- Article (2017-07-13): [Making CI Easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/)
-- Article (2017-05-22): [Fast and Natural Continuous Integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/)
-- **Videos:**
- - Demo (Streamed live on Jul 17, 2017): [GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195)
- - Demo (March, 2017): [How to get started using CI/CD with GitLab](https://about.gitlab.com/2017/03/13/ci-cd-demo/)
- - Webcast (April, 2016): [Getting started with CI in GitLab](https://about.gitlab.com/2016/04/20/webcast-recording-and-slides-introduction-to-ci-in-gitlab/)
-- **Third-party videos:**
- - [Intégration continue avec GitLab (September, 2016)](https://www.youtube.com/watch?v=URcMBXjIr24&t=13s)
- - [GitLab CI for Minecraft Plugins (July, 2016)](https://www.youtube.com/watch?v=Z4pcI9F8yf8)
+## CI/CD with Auto DevOps
-### Why GitLab CI/CD?
+[Auto DevOps](../topics/autodevops/index.md) is the default minimum-configuration method for
+implementing CI/CD. Auto DevOps:
- - Article (2016-10-17): [Why We Chose GitLab CI for our CI/CD Solution](https://about.gitlab.com/2016/10/17/gitlab-ci-oohlala/)
- - Article (2016-07-22): [Building our web-app on GitLab CI: 5 reasons why Captain Train migrated from Jenkins to GitLab CI](https://about.gitlab.com/2016/07/22/building-our-web-app-on-gitlab-ci/)
+- Provides simplified setup and execution of CI/CD.
+- Allows GitLab to automatically detect, build, test, deploy, and monitor your applications.
-## Exploring GitLab CI/CD
+## Manually configured CI/CD
-- [CI/CD Variables](variables/README.md) - Learn how to use variables defined in
- your `.gitlab-ci.yml` or the ones defined in your project's settings
- - [Where variables can be used](variables/where_variables_can_be_used.md) - A
- deeper look on where and how the CI/CD variables can be used
-- **The permissions model** - Learn about the access levels a user can have for
- performing certain CI actions
- - [User permissions](../user/permissions.md#gitlab-ci)
- - [Job permissions](../user/permissions.md#job-permissions)
-- [Configure a Runner, the application that runs your jobs](runners/README.md)
-- Article (2016-03-01): [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
-- Article (2016-07-29): [GitLab CI: Run jobs sequentially, in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
-- Article (2016-08-26): [GitLab CI: Deployment & environments](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
-- Article (2016-05-23): [Introduction to GitLab Container Registry](https://about.gitlab.com/2016/05/23/gitlab-container-registry/)
+For complete control, you can manually configure GitLab CI/CD.
-## Advanced use
+### Usage
-Once you get familiar with the basics of GitLab CI/CD, it's time to dive in and
-learn how to leverage its potential even more.
+With basic knowledge of how GitLab CI/CD works, the following documentation extends your knowledge
+into more features:
-- [Environments and deployments](environments.md): Separate your jobs into
- environments and use them for different purposes like testing, building and
- deploying
-- [Job artifacts](../user/project/pipelines/job_artifacts.md)
-- [Caching dependencies](caching/index.md)
-- [Git submodules](git_submodules.md) - How to run your CI jobs when Git
- submodules are involved
-- [Pipelines for merge requests](merge_request_pipelines/index.md)
-- [Use SSH keys in your build environment](ssh_keys/README.md)
-- [Trigger pipelines through the GitLab API](triggers/README.md)
-- [Trigger pipelines on a schedule](../user/project/pipelines/schedules.md)
-- [Kubernetes clusters](../user/project/clusters/index.md) - Integrate one or
- more Kubernetes clusters to your project
-- [Interactive web terminal](interactive_web_terminal/index.md) - Open an interactive
- web terminal to debug the running jobs
+| Topic | Description |
+|:--------------------------------------------------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------|
+| [Creating and using CI/CD pipelines](pipelines.md) | Understand, visualize, create, and use CI/CD pipelines. |
+| [CI/CD Variables](variables/README.md) | How environment variables can be configured and made available in pipelines. |
+| [Where variables can be used](variables/where_variables_can_be_used.md) | A deeper look into where and how CI/CD variables can be used. |
+| [User](../user/permissions.md#gitlab-cicd-permissions) and [job](../user/permissions.md#job-permissions) permissions | Learn about the access levels a user can have for performing certain CI actions. |
+| [Configuring GitLab Runners](runners/README.md) | Documentation for configuring [GitLab Runner](https://docs.gitlab.com/runner/). |
+| [Introduction to environments and deployments](environments.md) | Learn how to separate your jobs into environments and use them for different purposes like testing, building and, deploying. |
+| [Job artifacts](../user/project/pipelines/job_artifacts.md) | Learn about the output of jobs. |
+| [Cache dependencies in GitLab CI/CD](caching/index.md) | Discover how to speed up pipelines using caching. |
+| [Using Git submodules with GitLab CI](git_submodules.md) | How to run your CI jobs when using Git submodules. |
+| [Pipelines for merge requests](merge_request_pipelines/index.md) | Create pipelines specifically for merge requests. |
+| [Using SSH keys with GitLab CI/CD](ssh_keys/README.md) | Use SSH keys in your build environment. |
+| [Triggering pipelines through the API](triggers/README.md) | Use the GitLab API to trigger a pipeline. |
+| [Pipeline schedules](../user/project/pipelines/schedules.md) | Trigger pipelines on a schedule. |
+| [Connecting GitLab with a Kubernetes cluster](../user/project/clusters/index.md) | Integrate one or more Kubernetes clusters to your project. |
+| [ChatOps](chatops/README.md) | Trigger CI jobs from chat, with results sent back to the channel. |
+| [Interactive web terminals](interactive_web_terminal/index.md) | Open an interactive web terminal to debug the running jobs. |
+| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes in a per-branch basis. |
+| [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html) **[PREMIUM]** | Check the current health and status of each CI/CD environment running on Kubernetes. |
+| [GitLab CI/CD for external repositories](https://docs.gitlab.com/ee/ci/ci_cd_for_external_repos/index.html) **[PREMIUM]** | Get the benefits of GitLab CI/CD combined with repositories in GitHub and BitBucket Cloud. |
+| [Protected environments](https://docs.gitlab.com/ce/ci/environments/protected_environments.html) **[PREMIUM]** | Ensure that only people with the right privileges can deploy to an environment. |
-## GitLab CI/CD for Docker
+### GitLab Pages
-Leverage the power of Docker to run your CI pipelines.
+GitLab CI/CD can be used to build and host static websites. For more information, see the
+documentation on [GitLab Pages](../user/project/pages/index.md),
+or dive right into the [CI/CD step-by-step guide for Pages](../user/project/pages/getting_started_part_four.md).
-- [Use Docker images with GitLab Runner](docker/using_docker_images.md)
-- [Use CI to build Docker images](docker/using_docker_build.md)
-- [CI services (linked Docker containers)](services/README.md)
-- Article (2016-03-01): [Setting up GitLab Runner For Continuous Integration](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
+### Examples
-## Review Apps
+GitLab provides examples of configuring GitLab CI/CD in the form of:
-- [Review Apps documentation](review_apps/index.md)
-- Article (2016-11-22): [Introducing Review Apps](https://about.gitlab.com/2016/11/22/introducing-review-apps/)
-- [Example project that shows how to use Review Apps](https://gitlab.com/gitlab-examples/review-apps-nginx/)
+- A collection of [examples and other resources](examples/README.md).
+- Example projects that are available at the [`gitlab-examples`](https://gitlab.com/gitlab-examples) group. For example, see:
+ - [`multi-project-pipelines`](https://gitlab.com/gitlab-examples/multi-project-pipelines) for examples of implementing multi-project pipelines.
+ - [`review-apps-nginx`](https://gitlab.com/gitlab-examples/review-apps-nginx/) provides an example of using Review Apps.
-## Auto DevOps
+### Administration
-- [Auto DevOps](../topics/autodevops/index.md): Auto DevOps automatically detects, builds, tests, deploys, and monitors your applications.
+As a GitLab administrator, you can change the default behavior of GitLab CI/CD for:
-## GitLab CI for GitLab Pages
+- An [entire GitLab instance](../user/admin_area/settings/continuous_integration.md).
+- Specific projects, using [pipelines settings](../user/project/pipelines/settings.md).
-See the documentation on [GitLab Pages](../user/project/pages/index.md).
+See also:
-## Examples
+- [How to enable or disable GitLab CI/CD](enable_or_disable_ci.md).
+- Other [CI administration settings](../administration/index.md#continuous-integration-settings).
-Check the [GitLab CI/CD examples](examples/README.md) for a collection of tutorials and guides on setting up your CI/CD pipeline for various programming languages, frameworks,
-and operating systems.
+### Using Docker
-## Integrations
+Docker is commonly used with GitLab CI/CD. Learn more about how to to accomplish this with the following
+documentation:
-- Article (2016-06-09): [Continuous Delivery with GitLab and Convox](https://about.gitlab.com/2016/06/09/continuous-delivery-with-gitlab-and-convox/)
-- Article (2016-05-05): [Getting Started with GitLab and Shippable Continuous Integration](https://about.gitlab.com/2016/05/05/getting-started-gitlab-and-shippable/)
-- Article (2016-04-19): [GitLab Partners with DigitalOcean to make Continuous Integration faster, safer, and more affordable](https://about.gitlab.com/2016/04/19/gitlab-partners-with-digitalocean-to-make-continuous-integration-faster-safer-and-more-affordable/)
+| Topic | Description |
+|:-------------------------------------------------------------------------|:-------------------------------------------------------------------------|
+| [Using Docker images](docker/using_docker_images.md) | Use GitLab and GitLab Runner with Docker to build and test applications. |
+| [Building Docker images with GitLab CI/CD](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
-## Special configuration (GitLab admin)
+Related topics include:
-As a GitLab administrator, you can change the default behavior of GitLab CI/CD in
-your whole GitLab instance as well as in each project.
-
-- [Continuous Integration admin settings](../administration/index.md#continuous-integration-settings)
-- **Project specific:**
- - [Pipelines settings](../user/project/pipelines/settings.md)
- - [Learn how to enable or disable GitLab CI](enable_or_disable_ci.md)
-- **Affecting the whole GitLab instance:**
- - [Continuous Integration admin settings](../user/admin_area/settings/continuous_integration.md)
+- [Docker integration](docker/README.md).
+- [CI services (linked Docker containers)](services/README.md).
+
+## Why GitLab CI/CD?
+
+The following articles explain reasons to use GitLab CI/CD for your CI/CD infrastructure:
+
+- [Why we chose GitLab CI for our CI/CD solution](https://about.gitlab.com/2016/10/17/gitlab-ci-oohlala/)
+- [Building our web-app on GitLab CI](https://about.gitlab.com/2016/07/22/building-our-web-app-on-gitlab-ci/)
+
+See also the [Why CI/CD?](https://docs.google.com/presentation/d/1OGgk2Tcxbpl7DJaIOzCX4Vqg3dlwfELC3u2jEeCBbDk) presentation.
## Breaking changes
-- [CI variables renaming for GitLab 9.0](variables/README.md#9-0-renaming) Read about the
+As GitLab CI/CD has evolved, certain breaking changes have been necessary. These are:
+
+- [CI variables renaming for GitLab 9.0](variables/README.md#gitlab-90-renaming). Read about the
deprecated CI variables and what you should use for GitLab 9.0+.
-- [New CI job permissions model](../user/project/new_ci_build_permissions_model.md)
- Read about what changed in GitLab 8.12 and how that affects your jobs.
+- [New CI job permissions model](../user/project/new_ci_build_permissions_model.md).
+ See what changed in GitLab 8.12 and how that affects your jobs.
There's a new way to access your Git submodules and LFS objects in jobs.
diff --git a/doc/ci/autodeploy/index.md b/doc/ci/autodeploy/index.md
index 985ec4b972c..5221cbf8609 100644
--- a/doc/ci/autodeploy/index.md
+++ b/doc/ci/autodeploy/index.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../topics/autodevops/index.md#auto-deploy'
+---
+
This document was moved to [another location](../../topics/autodevops/index.md#auto-deploy).
diff --git a/doc/ci/autodeploy/quick_start_guide.md b/doc/ci/autodeploy/quick_start_guide.md
index 985ec4b972c..5221cbf8609 100644
--- a/doc/ci/autodeploy/quick_start_guide.md
+++ b/doc/ci/autodeploy/quick_start_guide.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../topics/autodevops/index.md#auto-deploy'
+---
+
This document was moved to [another location](../../topics/autodevops/index.md#auto-deploy).
diff --git a/doc/ci/build_artifacts/README.md b/doc/ci/build_artifacts/README.md
index 22b3872025f..b63659c1878 100644
--- a/doc/ci/build_artifacts/README.md
+++ b/doc/ci/build_artifacts/README.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/pipelines/job_artifacts.md'
+---
+
This document was moved to [pipelines/job_artifacts.md](../../user/project/pipelines/job_artifacts.md).
diff --git a/doc/ci/caching/index.md b/doc/ci/caching/index.md
index dbe3d9b62c9..e079483e2b5 100644
--- a/doc/ci/caching/index.md
+++ b/doc/ci/caching/index.md
@@ -52,7 +52,7 @@ artifacts are also available in between stages within a pipeline. So if you
build your application by downloading all the required modules, you might want
to declare them as artifacts so that each subsequent stage can depend on them
being there. There are some optimizations like declaring an
-[expiry time](../yaml/README.md#artifacts-expire_in) so you don't keep artifacts
+[expiry time](../yaml/README.md#artifactsexpire_in) so you don't keep artifacts
around too long, and using [dependencies](../yaml/README.md#dependencies) to
control exactly where artifacts are passed around.
@@ -87,7 +87,7 @@ cache, when declaring `cache` in your jobs, use one or a mix of the following:
that share their cache.
- [Use sticky Runners](../runners/README.md#locking-a-specific-runner-from-being-enabled-for-other-projects)
that will be only available to a particular project.
-- [Use a `key`](../yaml/README.md#cache-key) that fits your workflow (e.g.,
+- [Use a `key`](../yaml/README.md#cachekey) that fits your workflow (e.g.,
different caches on each branch). For that, you can take advantage of the
[CI/CD predefined variables](../variables/README.md#predefined-environment-variables).
@@ -169,7 +169,7 @@ job:
```
For more fine tuning, read also about the
-[`cache: policy`](../yaml/README.md#cache-policy).
+[`cache: policy`](../yaml/README.md#cachepolicy).
## Common use cases
diff --git a/doc/ci/chatops/README.md b/doc/ci/chatops/README.md
new file mode 100644
index 00000000000..a06fe6961a7
--- /dev/null
+++ b/doc/ci/chatops/README.md
@@ -0,0 +1,61 @@
+# GitLab ChatOps
+
+> **Notes:**
+>
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
+>
+> - ChatOps is currently in alpha, with some important features missing like access control.
+
+GitLab ChatOps provides a method to interact with CI/CD jobs through chat services like Slack. Many organizations' discussion, collaboration, and troubleshooting is taking place in chat services these days, and having a method to run CI/CD jobs with output posted back to the channel can significantly augment a team's workflow.
+
+## How it works
+
+GitLab ChatOps is built upon two existing features, [GitLab CI/CD](../README.md) and [Slack Slash Commmands](../../user/project/integrations/slack_slash_commands.md).
+
+A new `run` action has been added to the [slash commands](../../integration/slash_commands.md), which takes two arguments: a `<job name>` to execute and the `<job arguments>`. When executed, ChatOps will look up the specified job name and attempt to match it to a corresponding job in [.gitlab-ci.yml](../yaml/README.md). If a matching job is found on `master`, a pipeline containing just that job is scheduled. Two additional [CI/CD variables](../variables/README.md#predefined-environment-variables) are passed to the job: `CHAT_INPUT` contains any additional arguments, and `CHAT_CHANNEL` is set to the name of channel the action was triggered in.
+
+After the job has finished, its output is sent back to Slack provided it has completed within 30 minutes. If a job takes more than 30 minutes to run it must use the Slack API to manually send data back to a channel.
+
+[Developer access and above](../../user/permissions.html#project-members-permissions) is required to use the `run` command. If a job should not be able to be triggered from chat, it can be set to `except: [chat]`.
+
+## Creating a ChatOps CI job
+
+Since ChatOps is built upon GitLab CI/CD, the job has all the same features and functions available. There a few best practices to consider however when creating ChatOps jobs:
+
+* It is strongly recommended to set `only: [chat]` so the job does not run as part of the standard CI pipeline.
+* If the job is set to `when: manual`, the pipeline will be created however the job will wait to be started.
+* It is important to keep in mind that there is very limited support for access control. If the user who triggered the slash command is a developer in the project, the job will run. The job itself can utilize existing [CI/CD variables](../variables/README.html#predefined-environment-variables) like `GITLAB_USER_ID` to perform additional rights validation, however these variables can be [overridden](https://docs.gitlab.com/ce/ci/variables/README.html#priority-of-variables).
+
+### Controlling the ChatOps reply
+
+For jobs with a single command, its output is automatically sent back to the channel as a reply. For example the chat reply of the following job is simply `Hello World.`
+
+```yaml
+hello-world:
+ stage: chatops
+ only: [chat]
+ script:
+ - echo "Hello World."
+```
+
+Jobs that contain multiple commands, or have a `before_script`, include additional content in the chat reply. In these cases both the commands and their output are included, with the commands wrapped in ANSI colors codes.
+
+To selectively reply with the output of one command, its output must be bounded by the `chat_reply` section. For example, the following job will list the files in the current directory.
+
+```yaml
+ls:
+ stage: chatops
+ only: [chat]
+ script:
+ - echo "This command will not be shown."
+ - echo -e "section_start:$( date +%s ):chat_reply\r\033[0K\n$( ls -la )\nsection_end:$( date +%s ):chat_reply\r\033[0K"
+```
+
+## GitLab ChatOps icon
+
+Say Hi to our ChatOps bot.
+You can find and download the official GitLab ChatOps icon here.
+
+![GitLab ChatOps bot icon](img/gitlab-chatops-icon-small.png)
+
+[Download bigger image](img/gitlab-chatops-icon.png)
diff --git a/doc/ci/chatops/img/gitlab-chatops-icon-small.png b/doc/ci/chatops/img/gitlab-chatops-icon-small.png
new file mode 100644
index 00000000000..71cc5dba5cf
--- /dev/null
+++ b/doc/ci/chatops/img/gitlab-chatops-icon-small.png
Binary files differ
diff --git a/doc/ci/chatops/img/gitlab-chatops-icon.png b/doc/ci/chatops/img/gitlab-chatops-icon.png
new file mode 100644
index 00000000000..3ba8bd308e3
--- /dev/null
+++ b/doc/ci/chatops/img/gitlab-chatops-icon.png
Binary files differ
diff --git a/doc/ci/docker/README.md b/doc/ci/docker/README.md
index 8ae80b2bc02..446f5b54f0c 100644
--- a/doc/ci/docker/README.md
+++ b/doc/ci/docker/README.md
@@ -4,6 +4,8 @@ comments: false
# Docker integration
-- [Using Docker Images](using_docker_images.md)
-- [Using Docker Build](using_docker_build.md)
-- [Using kaniko](using_kaniko.md)
+The following documentation is available for using GitLab CI/CD with Docker:
+
+- [Using Docker images](using_docker_images.md).
+- [Building Docker images with GitLab CI/CD](using_docker_build.md).
+- [Building images with kaniko and GitLab CI/CD](using_kaniko.md).
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index a462c75f2f5..9266c4511be 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -17,7 +17,7 @@ used to create and test an image:
```bash
docker build -t my-image dockerfiles/
-docker run my-docker-image /script/to/run/tests
+docker run my-image /script/to/run/tests
docker tag my-image my-registry:5000/my-image
docker push my-registry:5000/my-image
```
@@ -389,6 +389,7 @@ If you're running multiple Runners you will have to modify all configuration fil
## Using the GitLab Container Registry
> **Notes:**
+>
> - This feature requires GitLab 8.8 and GitLab Runner 1.2.
> - Starting from GitLab 8.12, if you have [2FA] enabled in your account, you need
> to pass a [personal access token][pat] instead of your password in order to
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 959271d8abc..3314c76d234 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -391,9 +391,9 @@ CI jobs:
from `Dockerfile` that may be overridden in `.gitlab-ci.yml`)
1. The Runner attaches itself to a running container.
1. The Runner prepares a script (the combination of
- [`before_script`](../yaml/README.md#before_script),
+ [`before_script`](../yaml/README.md#before_script-and-after_script),
[`script`](../yaml/README.md#script),
- and [`after_script`](../yaml/README.md#after_script)).
+ and [`after_script`](../yaml/README.md#before_script-and-after_script)).
1. The Runner sends the script to the container's shell STDIN and receives the
output.
diff --git a/doc/ci/environments.md b/doc/ci/environments.md
index 6a9917f6430..05d392d54cb 100644
--- a/doc/ci/environments.md
+++ b/doc/ci/environments.md
@@ -21,7 +21,7 @@ all within GitLab. All you need to do is define them in your project's
history of your deployments per every environment.
Environments are like tags for your CI jobs, describing where code gets deployed.
-Deployments are created when [jobs] deploy versions of code to environments,
+Deployments are created when [jobs](yaml/README.md#introduction) deploy versions of code to environments,
so every environment can have one or more deployments. GitLab keeps track of
your deployments, so you always know what is currently being deployed on your
servers. If you have a deployment service such as [Kubernetes][kube]
@@ -103,7 +103,7 @@ the Git SHA and environment name.
To sum up, with the above `.gitlab-ci.yml` we have achieved that:
- All branches will run the `test` and `build` jobs.
-- The `deploy_staging` job will run [only](yaml/README.md#only) on the `master`
+- The `deploy_staging` job will run [only](yaml/README.md#onlyexcept-basic) on the `master`
branch which means all merge requests that are created from branches don't
get to deploy to the staging server
- When a merge request is merged, all jobs will run and the `deploy_staging`
@@ -298,8 +298,8 @@ here because it is guaranteed to be unique, but if you're using a workflow like
environment names to be more closely based on the branch name - the example
above would give you an URL like `https://100-do-the-thing.example.com`
-Last but not least, we tell the job to run [`only`][only] on branches
-[`except`][only] master.
+Last but not least, we tell the job to run [`only`](yaml/README.md#onlyexcept-basic) on branches
+[`except`](yaml/README.md#onlyexcept-basic) master.
>**Note:**
You are not bound to use the same prefix or only slashes in the dynamic
@@ -401,7 +401,7 @@ Let's briefly see where URL that's defined in the environments is exposed.
## Making use of the environment URL
-The [environment URL](yaml/README.md#environments-url) is exposed in a few
+The [environment URL](yaml/README.md#environmenturl) is exposed in a few
places within GitLab.
| In a merge request widget as a link | In the Environments view as a button | In the Deployments view as a button |
@@ -613,15 +613,13 @@ Below are some links you may find interesting:
- [Review Apps - Use dynamic environments to deploy your code for every branch](review_apps/index.md)
[Pipelines]: pipelines.md
-[jobs]: yaml/README.md#jobs
[yaml]: yaml/README.md
[environments]: #environments
[deployments]: #deployments
[permissions]: ../user/permissions.md
[variables]: variables/README.md
-[env-name]: yaml/README.md#environment-name
-[only]: yaml/README.md#only-and-except
-[onstop]: yaml/README.md#environment-on_stop
+[env-name]: yaml/README.md#environmentname
+[onstop]: yaml/README.md#environmenton_stop
[ce-7015]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7015
[gitlab-flow]: ../workflow/gitlab_flow.md
[gitlab runner]: https://docs.gitlab.com/runner/
diff --git a/doc/ci/examples/README.md b/doc/ci/examples/README.md
index 87e86bef44b..7f686781e3c 100644
--- a/doc/ci/examples/README.md
+++ b/doc/ci/examples/README.md
@@ -4,88 +4,118 @@ comments: false
# GitLab CI/CD Examples
-A collection of [`.gitlab-ci.yml` template files][gitlab-ci-templates] is maintained in GitLab. When you create a new file via the UI,
-GitLab will give you the option to choose one of these templates.
-If your favorite programming language or framework are missing we would love your
-help by sending a merge request with a new `.gitlab-ci.yml` to this project.
-
-There's also a collection of repositories with [example projects](https://gitlab.com/gitlab-examples) for various languages. You can fork and adjust them to your own needs.
-
-## Languages, frameworks, OSs
-
-- **PHP**:
- - [Testing a PHP application](php.md)
- - [Run PHP Composer & NPM scripts then deploy them to a staging server](deployment/composer-npm-deploy.md)
- - [How to test and deploy Laravel/PHP applications with GitLab CI/CD and Envoy](laravel_with_gitlab_and_envoy/index.md)
-- **Ruby**: [Test and deploy a Ruby application to Heroku](test-and-deploy-ruby-application-to-heroku.md)
-- **Python**: [Test and deploy a Python application to Heroku](test-and-deploy-python-application-to-heroku.md)
-- **Java**:
- - [Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD](deploy_spring_boot_to_cloud_foundry/index.md)
- - [Continuous Delivery of a Spring Boot application with GitLab CI and Kubernetes](https://about.gitlab.com/2016/12/14/continuous-delivery-of-a-spring-boot-application-with-gitlab-ci-and-kubernetes/)
-- **Scala**: [Test a Scala application](test-scala-application.md)
-- **Clojure**: [Test a Clojure application](test-clojure-application.md)
-- **Elixir**:
- - [Testing a Phoenix application with GitLab CI/CD](test_phoenix_app_with_gitlab_ci_cd/index.md)
- - [Building an Elixir Release into a Docker image using GitLab CI](https://about.gitlab.com/2016/08/11/building-an-elixir-release-into-docker-image-using-gitlab-ci-part-1/)
-- **iOS and macOS**:
- - [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
- - [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/)
-- **Android**: [Setting up GitLab CI for Android projects](https://about.gitlab.com/2016/11/30/setting-up-gitlab-ci-for-android-projects/)
-- **Debian**: [Continuous Deployment with GitLab: how to build and deploy a Debian Package with GitLab CI](https://about.gitlab.com/2016/10/12/automated-debian-package-build-with-gitlab-ci/)
-- **Maven**: [How to deploy Maven projects to Artifactory with GitLab CI/CD](artifactory_and_gitlab/index.md)
-
-### Game development
-
-- [DevOps and Game Dev with GitLab CI/CD](devops_and_game_dev_with_gitlab_ci_cd/index.md)
-
-### Miscellaneous
+Examples are a useful way of understanding how to implement GitLab CI/CD for your specific use case.
+
+Examples are available in several forms. As a collection of:
+
+- `.gitlab-ci.yml` [template files](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates) maintained in GitLab. When you create a new file via the UI,
+ GitLab will give you the option to choose one of these templates. This will allow you to quickly bootstrap your project for CI/CD.
+ If your favorite programming language or framework are missing, we would love your help by sending a merge request with a new `.gitlab-ci.yml` to this project.
+- Repositories with [example projects](https://gitlab.com/gitlab-examples) for various languages. You can fork and adjust them to your own needs.
+- Examples and [other resources](#other-resources) listed below.
+
+## CI/CD examples
+
+The following table lists examples for different use cases:
+
+| Use case | Resource |
+|:-----------------------------------------------|:-----------------------------------------------------------------------------------------------------------------------------------|
+| Browser performance testing | [Browser Performance Testing with the Sitespeed.io container](browser_performance.md). |
+| Clojure | [Test a Clojure application with GitLab CI/CD](test-clojure-application.md). |
+| Code quality analysis | [Analyze your project's Code Quality](code_quality.md). **[STARTER]** |
+| Container scanning | [Container Scanning with GitLab CI/CD](container_scanning.md). |
+| Dependency scanning | [Dependency Scanning with GitLab CI/CD](https://docs.gitlab.com/ee/ci/examples/dependency_scanning.html). **[ULTIMATE]** |
+| Deployment with `dpl` | [Using `dpl` as deployment tool](deployment/README.md). |
+| Dynamic application<br>security testing (DAST) | [Dynamic Application Security Testing with GitLab CI/CD](dast.md) **[ULTIMATE]** |
+| Elixir | [Testing a Phoenix application with GitLab CI/CD](test_phoenix_app_with_gitlab_ci_cd/index.md). |
+| Game development | [DevOps and Game Dev with GitLab CI/CD](devops_and_game_dev_with_gitlab_ci_cd/index.md). |
+| GitLab Pages | See the [GitLab Pages](../../user/project/pages/index.md) documentation for a complete example. |
+| Java | [Deploy a Spring Boot application to Cloud Foundry with GitLab CI/CD](deploy_spring_boot_to_cloud_foundry/index.md). |
+| JUnit | [JUnit test reports](../junit_test_reports.md). |
+| License management | [Dependencies license management with GitLab CI/CD](https://docs.gitlab.com/ee/ci/examples/license_management.html) **[ULTIMATE]** |
+| Maven | [How to deploy Maven projects to Artifactory with GitLab CI/CD](artifactory_and_gitlab/index.md). |
+| PHP | [Testing PHP projects](php.md). |
+| PHP | [Running Composer and NPM scripts with deployment via SCP in GitLab CI/CD](deployment/composer-npm-deploy.md). |
+| PHP | [Test and deploy Laravel applications with GitLab CI/CD and Envoy](laravel_with_gitlab_and_envoy/index.md). |
+| Python | [Test and deploy a Python application with GitLab CI/CD](test-and-deploy-python-application-to-heroku.md). |
+| Ruby | [Test and deploy a Ruby application with GitLab CI/CD](test-and-deploy-ruby-application-to-heroku.md). |
+| Scala | [Test and deploy a Scala application to Heroku](test-scala-application.md). |
+| Static application<br>security testing (SAST) | [Static Application Security Testing with GitLab CI/CD](https://docs.gitlab.com/ee/ci/examples/sast.html) **[ULTIMATE]** |
+| Testing | [End-to-end testing with GitLab CI/CD and WebdriverIO](end_to_end_testing_webdriverio/index.md). |
+
+### Contributing examples
+
+Contributions are welcome! You can help your favorite programming
+language users and GitLab by sending a merge request with a guide for that language.
+You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/)
+to get paid for writing complete articles for GitLab.
-- [End-to-end testing with GitLab CI/CD and WebdriverIO](end_to_end_testing_webdriverio/index.md)
-- [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)
+### Adding templates to your GitLab installation **[PREMIUM ONLY]**
-## Test Reports
+If you want to have customized examples and templates for your own self-managed GitLab instance available to your team, your GitLab administrator can [designate an instance template repository](https://docs.gitlab.com/ee/user/admin_area/settings/instance_template_repository.html) that contains examples and templates specific to your enterprise.
-[Collect test reports in Verify stage](../junit_test_reports.md)
+## Other resources
-## Code Quality analysis
+This section provides further resources to help you get familiar with different aspects of GitLab CI/CD.
-**(Starter)** [Analyze your project's Code Quality](code_quality.md)
+NOTE: **Note:**
+These resources may no longer reflect the current state of GitLab CI/CD.
-## Static Application Security Testing (SAST)
+### CI/CD in the cloud
-**(Ultimate)** [Scan your code for vulnerabilities](https://docs.gitlab.com/ee/ci/examples/sast.html)
+For examples of setting up GitLab CI/CD for cloud-based environments, see:
-## Dependency Scanning
+- [How to set up multi-account AWS SAM deployments with GitLab CI](https://about.gitlab.com/2019/02/04/multi-account-aws-sam-deployments-with-gitlab-ci/)
+- [How to autoscale continuous deployment with GitLab Runner on DigitalOcean](https://about.gitlab.com/2018/06/19/autoscale-continuous-deployment-gitlab-runner-digital-ocean/)
+- [How to create a CI/CD pipeline with Auto Deploy to Kubernetes using GitLab and Helm](https://about.gitlab.com/2017/09/21/how-to-create-ci-cd-pipeline-with-autodeploy-to-kubernetes-using-gitlab-and-helm/)
-**(Ultimate)** [Scan your dependencies for vulnerabilities](https://docs.gitlab.com/ee/ci/examples/dependency_scanning.html)
+### Customer stories
-## Container Scanning
+For some customer experiences with GitLab CI/CD, see:
-[Scan your Docker images for vulnerabilities](container_scanning.md)
+- [How Verizon Connect reduced datacenter deploys from 30 days to under 8 hours with GitLab](https://about.gitlab.com/2019/02/14/verizon-customer-story/)
+- [How Wag! cut their release process from 40 minutes to just 6](https://about.gitlab.com/2019/01/16/wag-labs-blog-post/)
+- [How Jaguar Land Rover embraced CI to speed up their software lifecycle](https://about.gitlab.com/2018/07/23/chris-hill-devops-enterprise-summit-talk/)
-## Dynamic Application Security Testing (DAST)
+### Getting started
-Scan your app for vulnerabilities with GitLab [Dynamic Application Security Testing (DAST)](dast.md)
+For some examples to help get you started, see:
-## Browser Performance Testing with Sitespeed.io
+- [GitLab CI/CD's 2018 highlights](https://about.gitlab.com/2019/01/21/gitlab-ci-cd-features-improvements/)
+- [A beginner's guide to continuous integration](https://about.gitlab.com/2018/01/22/a-beginners-guide-to-continuous-integration/)
+- [Making CI easier with GitLab](https://about.gitlab.com/2017/07/13/making-ci-easier-with-gitlab/)
-Analyze your [browser performance with Sitespeed.io](browser_performance.md)
+### Implementing GitLab CI/CD
-## GitLab CI/CD for Review Apps
+For examples of others who have implemented GitLab CI/CD, see:
-- [Example project](https://gitlab.com/gitlab-examples/review-apps-nginx/) that shows how to use GitLab CI/CD for [Review Apps](../review_apps/index.html)
+- [How to streamline interactions between multiple repositories with multi-project pipelines](https://about.gitlab.com/2018/10/31/use-multiproject-pipelines-with-gitlab-cicd/)
+- [How we used GitLab CI to build GitLab faster](https://about.gitlab.com/2018/05/02/using-gitlab-ci-to-build-gitlab-faster/)
+- [Test all the things in GitLab CI with Docker by example](https://about.gitlab.com/2018/02/05/test-all-the-things-gitlab-ci-docker-examples/)
+- [A Craftsman looks at continuous integration](https://about.gitlab.com/2018/01/17/craftsman-looks-at-continuous-integration/)
+- [Go tools and GitLab: How to do continuous integration like a boss](https://about.gitlab.com/2017/11/27/go-tools-and-gitlab-how-to-do-continuous-integration-like-a-boss/)
+- [GitBot – automating boring Git operations with CI](https://about.gitlab.com/2017/11/02/automating-boring-git-operations-gitlab-ci/)
+- [How to use GitLab CI for Vue.js](https://about.gitlab.com/2017/09/12/vuejs-app-gitlab/)
+- Video: [GitLab CI/CD Deep Dive](https://youtu.be/pBe4t1CD8Fc?t=195)
- [Dockerizing GitLab Review Apps](https://about.gitlab.com/2017/07/11/dockerizing-review-apps/)
+- [Fast and natural continuous integration with GitLab CI](https://about.gitlab.com/2017/05/22/fast-and-natural-continuous-integration-with-gitlab-ci/)
+- [Demo: CI/CD with GitLab in action](https://about.gitlab.com/2017/03/13/ci-cd-demo/)
-## GitLab CI/CD for GitLab Pages
+### Integrating GitLab CI/CD with other systems
-See the documentation on [GitLab Pages](../../user/project/pages/index.md) for a complete overview.
+To see how you can integrate GitLab CI/CD with third-party systems, see:
-## Contributing
+- [Streamline and shorten error remediation with Sentry’s new GitLab integration](https://about.gitlab.com/2019/01/25/sentry-integration-blog-post/)
+- [How to simplify your smart home configuration with GitLab CI/CD](https://about.gitlab.com/2018/08/02/using-the-gitlab-ci-slash-cd-for-smart-home-configuration-management/)
+- [Demo: GitLab + Jira + Jenkins](https://about.gitlab.com/2018/07/30/gitlab-workflow-with-jira-jenkins/)
+- [Introducing Auto Breakfast from GitLab (sort of)](https://about.gitlab.com/2018/06/29/introducing-auto-breakfast-from-gitlab/)
-Contributions are very welcome! You can help your favorite programming
-language users and GitLab by sending a merge request with a guide for that language.
-You may want to apply for the [GitLab Community Writers Program](https://about.gitlab.com/community-writers/)
-to get paid for writing complete articles for GitLab.
+### Mobile development
+
+For help with using GitLab CI/CD for mobile application development, see:
-[gitlab-ci-templates]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/lib/gitlab/ci/templates
+- [How to publish Android apps to the Google Play Store with GitLab and fastlane](https://about.gitlab.com/2019/01/28/android-publishing-with-gitlab-and-fastlane/)
+- [Setting up GitLab CI for Android projects](https://about.gitlab.com/2018/10/24/setting-up-gitlab-ci-for-android-projects/)
+- [Working with YAML in GitLab CI from the Android perspective](https://about.gitlab.com/2017/11/20/working-with-yaml-gitlab-ci-android/)
+- [How to use GitLab CI and MacStadium to build your macOS or iOS projects](https://about.gitlab.com/2017/05/15/how-to-use-macstadium-and-gitlab-ci-to-build-your-macos-or-ios-projects/)
+- [Setting up GitLab CI for iOS projects](https://about.gitlab.com/2016/03/10/setting-up-gitlab-ci-for-ios-projects/)
diff --git a/doc/ci/examples/artifactory_and_gitlab/index.md b/doc/ci/examples/artifactory_and_gitlab/index.md
index 9e657275d50..589912e7a2a 100644
--- a/doc/ci/examples/artifactory_and_gitlab/index.md
+++ b/doc/ci/examples/artifactory_and_gitlab/index.md
@@ -2,7 +2,7 @@
redirect_from: 'https://docs.gitlab.com/ee/articles/artifactory_and_gitlab/index.html'
author: Fabio Busatto
author_gitlab: bikebilly
-level: intermediary
+level: intermediate
article_type: tutorial
date: 2017-08-15
---
@@ -11,7 +11,7 @@ date: 2017-08-15
## Introduction
-In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/)
+In this article, we will show how you can leverage the power of [GitLab CI/CD](https://about.gitlab.com/product/continuous-integration/)
to build a [Maven](https://maven.apache.org/) project, deploy it to [Artifactory](https://www.jfrog.com/artifactory/), and then use it from another Maven application as a dependency.
You'll create two different projects:
@@ -19,7 +19,7 @@ You'll create two different projects:
- `simple-maven-dep`: the app built and deployed to Artifactory (available at <https://gitlab.com/gitlab-examples/maven/simple-maven-dep>)
- `simple-maven-app`: the app using the previous one as a dependency (available at <https://gitlab.com/gitlab-examples/maven/simple-maven-app>)
-We assume that you already have a GitLab account on [GitLab.com](https://gitlab.com/), and that you know the basic usage of Git and [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/).
+We assume that you already have a GitLab account on [GitLab.com](https://gitlab.com/), and that you know the basic usage of Git and [GitLab CI/CD](https://about.gitlab.com/product/continuous-integration/).
We also assume that an Artifactory instance is available and reachable from the internet, and that you have valid credentials to deploy on it.
## Create the simple Maven dependency
@@ -102,7 +102,7 @@ parameter in `.gitlab-ci.yml` to use the custom location instead of the default
### Configure GitLab CI/CD for `simple-maven-dep`
-Now it's time we set up [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) to automatically build, test and deploy the dependency!
+Now it's time we set up [GitLab CI/CD](https://about.gitlab.com/product/continuous-integration/) to automatically build, test and deploy the dependency!
GitLab CI/CD uses a file in the root of the repo, named `.gitlab-ci.yml`, to read the definitions for jobs
that will be executed by the configured GitLab Runners. You can read more about this file in the [GitLab Documentation](https://docs.gitlab.com/ee/ci/yaml/).
@@ -230,7 +230,7 @@ Now you are ready to use the Artifactory repository to resolve dependencies and
You need a last step to have everything in place: configure the `.gitlab-ci.yml` file for this project, as you already did for `simple-maven-dep`.
-You want to leverage [GitLab CI/CD](https://about.gitlab.com/features/gitlab-ci-cd/) to automatically build, test and run your awesome application,
+You want to leverage [GitLab CI/CD](https://about.gitlab.com/product/continuous-integration/) to automatically build, test and run your awesome application,
and see if you can get the greeting as expected!
All you need to do is to add the following `.gitlab-ci.yml` to the repo:
diff --git a/doc/ci/examples/container_scanning.md b/doc/ci/examples/container_scanning.md
index e8e9c73d1b2..36fdc29fe65 100644
--- a/doc/ci/examples/container_scanning.md
+++ b/doc/ci/examples/container_scanning.md
@@ -30,7 +30,7 @@ container_scanning:
- docker:stable-dind
script:
- docker run -d --name db arminc/clair-db:latest
- - docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.1
+ - docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.6
- apk add -U wget ca-certificates
- docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
- wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
@@ -95,7 +95,7 @@ container_scanning:
- docker:stable-dind
script:
- docker run -d --name db arminc/clair-db:latest
- - docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.1
+ - docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.6
- apk add -U wget ca-certificates
- docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
- wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
diff --git a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
index 474a481836a..cf281605f5e 100644
--- a/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
+++ b/doc/ci/examples/deploy_spring_boot_to_cloud_foundry/index.md
@@ -1,7 +1,7 @@
---
author: Dylan Griffith
author_gitlab: DylanGriffith
-level: intermediary
+level: intermediate
article_type: tutorial
date: 2018-06-07
description: "Continuous Deployment of a Spring Boot application to Cloud Foundry with GitLab CI/CD"
@@ -99,7 +99,7 @@ We've used the `java:8` [docker
image](../../docker/using_docker_images.md) to build
our application as it provides the up-to-date Java 8 JDK on [Docker
Hub](https://hub.docker.com/). We've also added the [`only`
-clause](../../yaml/README.md#only-and-except-simplified)
+clause](../../yaml/README.md#onlyexcept-basic)
to ensure our deployments only happen when we push to the master branch.
Now, since the steps defined in `.gitlab-ci.yml` require credentials to login
diff --git a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
index 1e2be2e8475..908cf85980e 100644
--- a/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/devops_and_game_dev_with_gitlab_ci_cd/index.md
@@ -1,9 +1,10 @@
---
author: Ryan Hall
author_gitlab: blitzgren
-level: intermediary
+level: intermediate
article_type: tutorial
date: 2018-03-07
+last_updated: 2019-03-11
---
# DevOps and Game Dev with GitLab CI/CD
@@ -20,7 +21,7 @@ and the basics of game development.
Our [demo game](http://gitlab-game-demo.s3-website-us-east-1.amazonaws.com/) consists of a simple spaceship traveling in space that shoots by clicking the mouse in a given direction.
-Creating a strong CI/CD pipeline at the beginning of developing another game, [Dark Nova](http://darknova.io/about),
+Creating a strong CI/CD pipeline at the beginning of developing another game, [Dark Nova](http://darknova.io/),
was essential for the fast pace the team worked at. This tutorial will build upon my
[previous introductory article](https://ryanhallcs.wordpress.com/2017/03/15/devops-and-game-dev/) and go through the following steps:
@@ -37,8 +38,8 @@ This will also provide
boilerplate code for starting a browser-based game with the following components:
- Written in [Typescript](https://www.typescriptlang.org/) and [PhaserJs](https://phaser.io)
-- Building, running, and testing with [Gulp](http://gulpjs.com/)
-- Unit tests with [Chai](http://chaijs.com/) and [Mocha](https://mochajs.org/)
+- Building, running, and testing with [Gulp](https://gulpjs.com)
+- Unit tests with [Chai](https://www.chaijs.com) and [Mocha](https://mochajs.org/)
- CI/CD with GitLab
- Hosting the codebase on GitLab.com
- Hosting the game on AWS
@@ -317,7 +318,7 @@ allowing us to run our tests by every push.
Our entire `.gitlab-ci.yml` file should now look like this:
```yml
-image: node:6
+image: node:10
build:
stage: build
@@ -386,7 +387,7 @@ Uploading artifacts to coordinator... ok id=17095874 responseStatus=2
We have our codebase built and tested on every push. To complete the full pipeline with Continuous Deployment,
let's set up [free web hosting with AWS S3](https://aws.amazon.com/s/dm/optimization/server-side-test/free-tier/free_np/) and a job through which our build artifacts get
-deployed. GitLab also has a free static site hosting service we could use, [GitLab Pages](https://about.gitlab.com/features/pages/),
+deployed. GitLab also has a free static site hosting service we could use, [GitLab Pages](https://about.gitlab.com/product/pages/),
however Dark Nova specifically uses other AWS tools that necessitates using `AWS S3`.
Read through this article that describes [deploying to both S3 and GitLab Pages](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/)
and further delves into the principles of GitLab CI/CD than discussed in this article.
@@ -412,7 +413,7 @@ use root security credentials. Proper IAM credential management is beyond the sc
article, but AWS will remind you that using root credentials is unadvised and against their
best practices, as they should. Feel free to follow best practices and use a custom IAM user's
credentials, which will be the same two credentials (Key ID and Secret). It's a good idea to
-fully understand [IAM Best Practices in AWS](http://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html). We need to add these credentials to GitLab:
+fully understand [IAM Best Practices in AWS](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html). We need to add these credentials to GitLab:
1. Log into your AWS account and go to the [Security Credentials page](https://console.aws.amazon.com/iam/home#/security_credential)
1. Click the **Access Keys** section and **Create New Access Key**. Create the key and keep the id and secret around, you'll need them later
@@ -454,7 +455,7 @@ Be sure to update the region and S3 URL in that last script command to fit your
Our final configuration file `.gitlab-ci.yml` looks like:
```yml
-image: node:6
+image: node:10
build:
stage: build
@@ -502,7 +503,7 @@ deploy:
## Conclusion
Within the [demo repository](https://gitlab.com/blitzgren/gitlab-game-demo) you can also find a handful of boilerplate code to get
-[Typescript](https://www.typescriptlang.org/), [Mocha](https://mochajs.org/), [Gulp](http://gulpjs.com/) and [Phaser](https://phaser.io) all playing
+[Typescript](https://www.typescriptlang.org/), [Mocha](https://mochajs.org/), [Gulp](https://gulpjs.com/) and [Phaser](https://phaser.io) all playing
together nicely with GitLab CI/CD, which is the result of lessons learned while making [Dark Nova](http://darknova.io/).
Using a combination of free and open source software, we have a full CI/CD pipeline, a game foundation,
and unit tests, all running and deployed at every push to master - with shockingly little code.
@@ -520,7 +521,7 @@ a lot of breathing room in quickly getting changes to players.
Here are some ideas to further investigate that can speed up or improve your pipeline:
- [Yarn](https://yarnpkg.com) instead of npm
-- Set up a custom [Docker](../../../ci/docker/using_docker_images.md#define-image-and-services-from-gitlab-ci-yml) image that can preload dependencies and tools (like AWS CLI)
-- Forward a [custom domain](http://docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html) to your game's S3 static website
+- Set up a custom [Docker](../../../ci/docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) image that can preload dependencies and tools (like AWS CLI)
+- Forward a [custom domain](https:/docs.aws.amazon.com/AmazonS3/latest/dev/website-hosting-custom-domain-walkthrough.html) to your game's S3 static website
- Combine jobs if you find it unnecessary for a small project
- Avoid the queues and set up your own [custom GitLab CI/CD runner](https://about.gitlab.com/2016/03/01/gitlab-runner-with-docker/)
diff --git a/doc/ci/examples/end_to_end_testing_webdriverio/index.md b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
index 9f3b8d9ad14..bd221b7145e 100644
--- a/doc/ci/examples/end_to_end_testing_webdriverio/index.md
+++ b/doc/ci/examples/end_to_end_testing_webdriverio/index.md
@@ -89,7 +89,7 @@ page and to interact with them - for example, to click on the link back to the h
The simple test shown above
can already give us a lot of confidence if it passes: we know our deployment has succeeded, that the
elements are visible on the page and that actual browsers can interact with it, and that routing
-works as expected. And all that in just 10 lines with gratituous whitespace! Add to that succeeding
+works as expected. And all that in just 10 lines with gratuitous whitespace! Add to that succeeding
unit tests and a successfully completed pipeline, and you can be fairly confident that the
dependency upgrade did not break anything without even having to look at your website.
@@ -139,7 +139,7 @@ new browser window interacting with your app as you specified.
Which brings us to the exciting part: how do we run this in GitLab CI/CD? There are two things we
need to do for this:
-1. Set up [CI/CD jobs](../../yaml/README.md#jobs) that actually have a browser available.
+1. Set up [CI/CD jobs](../../yaml/README.md#introduction) that actually have a browser available.
2. Update our WebdriverIO configuration to use those browsers to visit the review apps.
For the scope of this article, we've defined an additional [CI/CD stage](../../yaml/README.md#stages)
@@ -184,7 +184,7 @@ option as an argument to `npm run confidence-check` on the command line.
However, we still need to tell WebdriverIO which browser is available for it to use.
[GitLab CI/CD makes
-a number of variables available](../../variables/README.html#predefined-variables-environment-variables)
+a number of variables available](../../variables/README.html#predefined-environment-variables)
with information about the current CI job. We can use this information to dynamically set
up our WebdriverIO configuration according to the job that is running. More specifically, we can
tell WebdriverIO what browser to execute the test on depending on the name of the currently running
diff --git a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
index d3b6650b0f4..0f809ed05ca 100644
--- a/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
+++ b/doc/ci/examples/laravel_with_gitlab_and_envoy/index.md
@@ -2,9 +2,10 @@
redirect_from: 'https://docs.gitlab.com/ee/articles/laravel_with_gitlab_and_envoy/index.html'
author: Mehran Rasulian
author_gitlab: mehranrasulian
-level: intermediary
+level: intermediate
article_type: tutorial
date: 2017-08-31
+last_updated: 2019-03-06
---
# Test and deploy Laravel applications with GitLab CI/CD and Envoy
@@ -268,7 +269,7 @@ The `releases` directory will hold all our deployments:
echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
- cd {{ $releases_dir }}
+ cd {{ $new_release_dir }}
git reset --hard {{ $commit }}
@endtask
@@ -346,7 +347,7 @@ At the end, our `Envoy.blade.php` file will look like this:
echo 'Cloning repository'
[ -d {{ $releases_dir }} ] || mkdir {{ $releases_dir }}
git clone --depth 1 {{ $repository }} {{ $new_release_dir }}
- cd {{ $releases_dir }}
+ cd {{ $new_release_dir }}
git reset --hard {{ $commit }}
@endtask
@@ -374,7 +375,7 @@ You might want to create another Envoy task to do that for you.
We also create the `.env` file in the same path to set up our production environment variables for Laravel.
These are persistent data and will be shared to every new release.
-Now, we would need to deploy our app by running `envoy run deploy`, but it won't be necessary since GitLab can handle that for us with CI's [environments](../../environments.md), which will be described [later](#setting-up-gitlab-ci-cd) in this tutorial.
+Now, we would need to deploy our app by running `envoy run deploy`, but it won't be necessary since GitLab can handle that for us with CI's [environments](../../environments.md), which will be described [later](#setting-up-gitlab-cicd) in this tutorial.
Now it's time to commit [Envoy.blade.php](https://gitlab.com/mehranrasulian/laravel-sample/blob/master/Envoy.blade.php) and push it to the `master` branch.
To keep things simple, we commit directly to `master`, without using [feature-branches](../../../workflow/gitlab_flow.md#github-flow-as-a-simpler-alternative) since collaboration is beyond the scope of this tutorial.
@@ -557,7 +558,7 @@ So we should adjust the configuration of MySQL instance by defining `MYSQL_DATAB
Find out more about MySQL variables at the [official MySQL Docker Image](https://hub.docker.com/r/_/mysql/).
Also set the variables `DB_HOST` to `mysql` and `DB_USERNAME` to `root`, which are Laravel specific variables.
-We define `DB_HOST` as `mysql` instead of `127.0.0.1`, as we use MySQL Docker image as a service which [is linked to the main Docker image](../../docker/using_docker_images.md#how-services-are-linked-to-the-build).
+We define `DB_HOST` as `mysql` instead of `127.0.0.1`, as we use MySQL Docker image as a service which [is linked to the main Docker image](../../docker/using_docker_images.md#how-services-are-linked-to-the-job).
```yaml
...
diff --git a/doc/ci/examples/sast_docker.md b/doc/ci/examples/sast_docker.md
index 3a657b3a3d5..70b269046e5 100644
--- a/doc/ci/examples/sast_docker.md
+++ b/doc/ci/examples/sast_docker.md
@@ -1 +1,5 @@
-This document was moved to [another location](./container_scanning.md).
+---
+redirect_to: 'container_scanning.md'
+---
+
+This document was moved to [another location](container_scanning.md).
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 61bf68fa0e8..99a4316ab0d 100644
--- a/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-python-application-to-heroku.md
@@ -1,4 +1,4 @@
-# Test and Deploy a python application with GitLab CI/CD
+# Test and deploy a Python application with GitLab CI/CD
This example will guide you how to run tests in your Python application and deploy it automatically as Heroku application.
@@ -65,7 +65,7 @@ First install [Docker Engine](https://docs.docker.com/installation/).
To build this project you also need to have [GitLab Runner](https://docs.gitlab.com/runner).
You can use public runners available on `gitlab.com`, but you can register your own:
-```
+```sh
gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
diff --git a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
index 46e6efccaf8..3a0ddf001b8 100644
--- a/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
+++ b/doc/ci/examples/test-and-deploy-ruby-application-to-heroku.md
@@ -1,4 +1,4 @@
-# Test and Deploy a ruby application with GitLab CI/CD
+# Test and deploy a Ruby application with GitLab CI/CD
This example will guide you how to run tests in your Ruby on Rails application and deploy it automatically as Heroku application.
@@ -58,10 +58,10 @@ You can do this through the [Dashboard](https://dashboard.heroku.com/).
## Create Runner
First install [Docker Engine](https://docs.docker.com/installation/).
-To build this project you also need to have [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner).
+To build this project you also need to have [GitLab Runner](https://docs.gitlab.com/runner/).
You can use public runners available on `gitlab.com`, but you can register your own:
-```
+```sh
gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
diff --git a/doc/ci/examples/test-scala-application.md b/doc/ci/examples/test-scala-application.md
index 66bfa41cad9..fa18cb22aed 100644
--- a/doc/ci/examples/test-scala-application.md
+++ b/doc/ci/examples/test-scala-application.md
@@ -1,4 +1,4 @@
-# Test and deploy to Heroku a Scala application
+# Test and deploy a Scala application to Heroku
This example demonstrates the integration of GitLab CI with Scala
applications using SBT. Checkout the example
@@ -55,8 +55,8 @@ You can use other versions of Scala and SBT by defining them in
Add the `Coverage was \[\d+.\d+\%\]` regular expression in the
**Settings âž” Pipelines âž” Coverage report** project setting to
-retrieve the [test coverage] rate from the build trace and have it
-displayed with your jobs.
+retrieve the [test coverage](../../user/project/pipelines/settings.md#test-coverage-report-badge)
+rate from the build trace and have it displayed with your jobs.
**Pipelines** must be enabled for this option to appear.
@@ -69,8 +69,5 @@ in the `.gitlab-ci.yml` file with your application's name.
## Heroku API key
You can look up your Heroku API key in your
-[account](https://dashboard.heroku.com/account). Add a secure [variable] with
+[account](https://dashboard.heroku.com/account). Add a [protected variable](../variables/README.md#protected-variables) with
this value in **Project âž” Variables** with key `HEROKU_API_KEY`.
-
-[variable]: ../variables/README.md#user-defined-variables-secure-variables
-[test coverage]: ../../user/project/pipelines/settings.md#test-coverage-report-badge
diff --git a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
index c0346d78141..1a909e8892a 100644
--- a/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
+++ b/doc/ci/examples/test_phoenix_app_with_gitlab_ci_cd/index.md
@@ -4,6 +4,7 @@ author_gitlab: Hostert
level: beginner
article_type: tutorial
date: 2018-02-20
+last_updated: 2019-03-06
---
# Testing a Phoenix application with GitLab CI/CD
@@ -75,7 +76,7 @@ When we call `mix` command, we'll pass two arguments:
- The task we want it to run: `phoenix.new`
- And the parameter `phoenix.new` requires, which is the name of the new project. In this case,
-we're calling it `hello_gitlab_ci`, but you're free to set your own name:
+ we're calling it `hello_gitlab_ci`, but you're free to set your own name:
```bash
mix phoenix.new hello_gitlab_ci
@@ -177,7 +178,7 @@ environment it can run. Since we will work with a single environment, we'll edit
configuration file (`test.exs`).
But, why do we need to adjust our configuration? Well, GitLab CI/CD builds and tests our code in one
-isolated virtual machine, called [Runner][runner-site], using Docker technology. In this Runner,
+isolated virtual machine, called [Runner](../../runners/README.md), using Docker technology. In this Runner,
GitLab CI/CD has access to everything our Phoenix application need to run, exactly as we have in our
`localhost`, but we have to tell GitLab CI/CD where to create and find this database using system
variables. This way, GitLab CI/CD will create our test database inside the Runner, just like we do
@@ -249,7 +250,7 @@ project.
![Set up CI](img/setup-ci.png)
- On next screen, we can select a template ready to go. Click on **Apply a GitLab CI/CD Yaml
-template** and select **Elixir**:
+ template** and select **Elixir**:
![Select template](img/select-template.png)
@@ -417,7 +418,6 @@ other reasons][ci-reasons] to keep using GitLab CI/CD. The benefits to our teams
[ci-docs]: ../../README.md "GitLab CI/CD Documentation"
[skipping-jobs]: ../../yaml/README.md#skipping-jobs "Skipping Jobs"
[gitlab-runners]: ../../runners/README.md "GitLab Runners Documentation"
-[runner-site]: ../../runners/README.md#runners "Runners"
[docker-image]: https://hub.docker.com/r/trenpixster/elixir/ "Elixir Docker Image"
[using-docker]: ../../docker/using_docker_images.md "Using Docker Images"
[hello-gitlab]: https://gitlab.com/Hostert/hello_gitlab_ci "Hello GitLab CI/CD"
diff --git a/doc/ci/img/cicd_pipeline_infograph.png b/doc/ci/img/cicd_pipeline_infograph.png
deleted file mode 100644
index 9ddd4aa828b..00000000000
--- a/doc/ci/img/cicd_pipeline_infograph.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/img/pipelines-goal.png b/doc/ci/img/pipelines-goal.png
deleted file mode 100644
index f15716d0b8f..00000000000
--- a/doc/ci/img/pipelines-goal.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/img/types-of-pipelines.png b/doc/ci/img/types-of-pipelines.png
deleted file mode 100644
index 829a53d5d52..00000000000
--- a/doc/ci/img/types-of-pipelines.png
+++ /dev/null
Binary files differ
diff --git a/doc/ci/introduction/img/gitlab_workflow_example.png b/doc/ci/introduction/img/gitlab_workflow_example.png
new file mode 100644
index 00000000000..94e7753c3b2
--- /dev/null
+++ b/doc/ci/introduction/img/gitlab_workflow_example.png
Binary files differ
diff --git a/doc/ci/introduction/img/job_running.png b/doc/ci/introduction/img/job_running.png
new file mode 100755
index 00000000000..d5f922ceb8c
--- /dev/null
+++ b/doc/ci/introduction/img/job_running.png
Binary files differ
diff --git a/doc/ci/introduction/img/pipeline_status.png b/doc/ci/introduction/img/pipeline_status.png
new file mode 100755
index 00000000000..96881f072e1
--- /dev/null
+++ b/doc/ci/introduction/img/pipeline_status.png
Binary files differ
diff --git a/doc/ci/introduction/img/rollback.png b/doc/ci/introduction/img/rollback.png
new file mode 100755
index 00000000000..38e0552f4f1
--- /dev/null
+++ b/doc/ci/introduction/img/rollback.png
Binary files differ
diff --git a/doc/ci/introduction/index.md b/doc/ci/introduction/index.md
new file mode 100644
index 00000000000..1b423a4696e
--- /dev/null
+++ b/doc/ci/introduction/index.md
@@ -0,0 +1,193 @@
+---
+description: "An overview of Continuous Integration, Continuous Delivery, and Continuous Deployment, as well as an introduction to GitLab CI/CD."
+---
+
+# Introduction to CI/CD with GitLab
+
+In this document we'll present an overview of the concepts of Continuous Integration,
+Continuous Delivery, and Continuous Deployment, as well as an introduction to
+GitLab CI/CD.
+
+## Introduction to CI/CD methodologies
+
+The continuous methodologies of software development are based on
+automating the execution of scripts to minimize the chance of
+introducing errors while developing applications. They require
+less human intervention or even no intervention at all, from the
+development of new code until its deployment.
+
+It involves continuously building, testing, and deploying code
+changes at every small iteration, reducing the chance of developing
+new code based on bugged or failed previous versions.
+
+There are three main approaches to this methodology, each of them
+to be applied according to what best suits your strategy.
+
+### Continuous Integration
+
+Consider an application which has its code stored in a Git
+repository in GitLab. Developers push code changes every day,
+multiple times a day. For every push to the repository, you
+can create a set of scripts to build and test your application
+automatically, decreasing the chance of introducing errors to your app.
+
+This practice is known as [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration);
+for every change submitted to an application - even to development branches -
+it's built and tested automatically and continuously, ensuring the
+introduced changes pass all tests, guidelines, and code compliance
+standards you established for your app.
+
+[GitLab itself](https://gitlab.com/gitlab-org/gitlab-ce) is an
+example of using Continuous Integration as a software
+development method. For every push to the project, there's a set
+of scripts the code is checked against.
+
+### Continuous Delivery
+
+[Continuous Delivery](https://continuousdelivery.com/) is a step
+beyond Continuous Integration. Your application is not only
+built and tested at every code change pushed to the codebase,
+but, as an additional step, it's also deployed continuously, though
+the deployments are triggered manually.
+
+This method ensures the code is checked automatically but requires
+human intervention to manually and strategically trigger the deployment
+of the changes.
+
+### Continuous Deployment
+
+[Continuous Deployment](https://www.airpair.com/continuous-deployment/posts/continuous-deployment-for-practical-people)
+is also a further step beyond Continuous Integration, similar to
+Continuous Delivery. The difference is that instead of deploying your
+application manually, you set it to be deployed automatically. It does
+not require human intervention at all to have your application
+deployed.
+
+## Introduction to GitLab CI/CD
+
+GitLab CI/CD is a powerful tool built into GitLab that allows you
+to apply all the continuous methods (Continuous Integration,
+Delivery, and Deployment) to your software with no third-party
+application or integration needed.
+
+### How GitLab CI/CD works
+
+To use GitLab CI/CD, all you need is an application codebase hosted in a
+Git repository, and for your build, test, and deployment
+scripts to be specified in a file called [`.gitlab-ci.yml`](../yaml/README.md),
+located in the root path of your repository.
+
+In this file, you can define the scripts you want to run, define include and
+cache dependencies, choose commands you want to run in sequence
+and those you want to run in parallel, define where you want to
+deploy your app, and specify whether you will want to run the scripts automatically
+or trigger any of them manually. Once you're familiar with
+GitLab CI/CD you can add more advanced steps into the configuration file.
+
+To add scripts to that file, you'll need to organize them in a
+sequence that suits your application and are in accordance with
+the tests you wish to perform. To visualize the process, imagine
+that all the scripts you add to the configuration file are the
+same as the commands you run on a terminal in your computer.
+
+Once you've added your `.gitlab-ci.yml` configuration file to your
+repository, GitLab will detect it and run your scripts with the
+tool called [GitLab Runner](https://docs.gitlab.com/runner/), which
+works similarly to your terminal.
+
+The scripts are grouped into **jobs**, and together they compose
+a **pipeline**. A minimalist example of `.gitlab-ci.yml` file
+could contain:
+
+```yml
+before_script:
+ - apt-get install rubygems ruby-dev -y
+
+run-test:
+ script:
+ - ruby --version
+```
+
+The `before_script` attribute would install the dependencies
+for your app before running anything, and a **job** called
+`run-test` would print the Ruby version of the current system.
+Both of them compose a **pipeline** triggered at every push
+to any branch of the repository.
+
+GitLab CI/CD not only executes the jobs you've
+set, but also shows you what's happening during execution, as you
+would see in your terminal:
+
+![job running](img/job_running.png)
+
+You create the strategy for your app and GitLab runs the pipeline
+for you according to what you've defined. Your pipeline status is also
+displayed by GitLab:
+
+![pipeline status](img/pipeline_status.png)
+
+At the end, if anything goes wrong, you can easily
+[roll back](../environments.md#rolling-back-changes) all the changes:
+
+![rollback button](img/rollback.png)
+
+### Basic CI/CD workflow
+
+This is a very simple example for how GitLab CI/CD fits in a common
+development workflow.
+
+Assume that you have discussed a code implementation in an issue
+and worked locally on your proposed changes. Once you push your
+commits to a feature branch in a remote repository in GitLab,
+the CI/CD pipeline set for your project is triggered. By doing
+so, GitLab CI/CD:
+
+- Runs automated scripts (sequential or parallel) to:
+ - Build and test your app.
+ - Preview the changes per merge request with Review Apps, as you
+ would see in your `localhost`.
+
+Once you're happy with your implementation:
+
+- Get your code reviewed and approved.
+- Merge the feature branch into the default branch.
+ - GitLab CI/CD deploys your changes automatically to a production environment.
+- And finally, you and your team can easily roll it back if something goes wrong.
+
+<img src="img/gitlab_workflow_example.png" alt="GitLab workflow example" class="image-noshadow">
+
+GitLab CI/CD is capable of a doing a lot more, but this workflow
+exemplifies GitLab's ability to track the entire process,
+without the need of any external tool to deliver your software.
+And, most usefully, you can visualize all the steps through
+the GitLab UI.
+
+### Setting up GitLab CI/CD for the first time
+
+To get started with GitLab CI/CD, you need to familiarize yourself
+with the [`.gitlab-ci.yml`](../yaml/README.md) configuration file
+syntax and with its attributes.
+
+This document [introduces the concepts of GitLab CI/CD in the scope of GitLab Pages](../../user/project/pages/getting_started_part_four.md), for deploying static websites.
+Although it's meant for users who want to write their own Pages
+script from scratch, it also serves as an introduction to the setup process for GitLab CI/CD.
+It covers the very first general steps of writing a CI/CD configuration
+file, so we recommend you read through it to understand GitLab's CI/CD
+logic, and learn how to write your own script (or tweak an
+existing one) for any application.
+
+For a deep view of GitLab's CI/CD configuration options, check the
+[`.gitlab-ci.yml` full reference](../yaml/README.md).
+
+### GitLab CI/CD feature set
+
+- Easily set up your app's entire lifecycle with [Auto DevOps](../../topics/autodevops/index.md).
+- Deploy static websites with [GitLab Pages](../../user/project/pages/index.md).
+- Deploy your app to different [environments](../environments.md).
+- Preview changes per merge request with [Review Apps](../review_apps/index.md).
+- Develop secure and private Docker images with [Container Registry](../../user/project/container_registry.md).
+- Install your own [GitLab Runner](https://docs.gitlab.com/runner/).
+- [Schedule pipelines](../../user/project/pipelines/schedules.md).
+- Check for app vulnerabilities with [Security Test reports](https://docs.gitlab.com/ee/user/project/merge_requests/#security-reports-ultimate). **[ULTIMATE]**
+
+To see all CI/CD features, navigate back to the [CI/CD index](../README.md).
diff --git a/doc/ci/junit_test_reports.md b/doc/ci/junit_test_reports.md
index cf18c6d9660..d03c0b68daf 100644
--- a/doc/ci/junit_test_reports.md
+++ b/doc/ci/junit_test_reports.md
@@ -113,8 +113,8 @@ There are a few tools that can produce JUnit reports in Java.
In the following example, `gradle` is used to generate the test reports.
If there are multiple test tasks defined, `gradle` will generate multiple
-directories under `build/test-results/`. In that case, you can leverage regex
-matching by defining the following path: `build/test-results/test/TEST-*.xml`:
+directories under `build/test-results/`. In that case, you can leverage glob
+matching by defining the following path: `build/test-results/test/**/TEST-*.xml`:
```yaml
java:
@@ -123,7 +123,7 @@ java:
- gradle test
artifacts:
reports:
- junit: build/test-results/test/TEST-*.xml
+ junit: build/test-results/test/**/TEST-*.xml
```
#### Maven
diff --git a/doc/ci/merge_request_pipelines/index.md b/doc/ci/merge_request_pipelines/index.md
index b7b5c660586..e8953d235a7 100644
--- a/doc/ci/merge_request_pipelines/index.md
+++ b/doc/ci/merge_request_pipelines/index.md
@@ -9,13 +9,18 @@ For example, unit tests, lint checks, and [Review Apps](../review_apps/index.md)
are often used in this cycle.
With pipelines for merge requests, you can design a specific pipeline structure
-for merge requests. All you need to do is just adding `only: [merge_requests]` to
-the jobs that you want it to run for only merge requests.
-Every time, when developers create or update merge requests, a pipeline runs on
-their new commits at every push to GitLab.
+for merge requests.
+
+## Configuring pipelines for merge requests
+
+To configure pipelines for merge requests, add the `only: merge_requests` parameter to
+the jobs that you want to run only for merge requests.
+
+Then, when developers create or update merge requests, a pipeline runs
+every time a commit is pushed to GitLab.
NOTE: **Note**:
-If you use both this feature and [Merge When Pipeline Succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md),
+If you use this feature with [merge when pipeline succeeds](../../user/project/merge_requests/merge_when_pipeline_succeeds.md),
pipelines for merge requests take precedence over the other regular pipelines.
For example, consider the following [`.gitlab-ci.yml`](../yaml/README.md):
@@ -40,15 +45,17 @@ deploy:
script: ./deploy
```
-After the merge request is updated with new commits, GitLab detects that changes
-have occurred and creates a new pipeline for the merge request.
-The pipeline fetches the latest code from the source branch and run tests against it.
+After the merge request is updated with new commits:
+
+- GitLab detects that changes have occurred and creates a new pipeline for the merge request.
+- The pipeline fetches the latest code from the source branch and run tests against it.
+
In the above example, the pipeline contains only `build` and `test` jobs.
-Since the `deploy` job doesn't have the `only: [merge_requests]` rule,
+Since the `deploy` job doesn't have the `only: merge_requests` parameter,
deployment jobs will not happen in the merge request.
-Pipelines tagged as **merge request** indicate that they were triggered
-when a merge request was created or updated.
+Pipelines tagged with the **merge request** badge indicate that they were triggered
+when a merge request was created or updated. For example:
![Merge request page](img/merge_request.png)
@@ -58,13 +65,18 @@ The same tag is shown on the pipeline's details:
## Excluding certain jobs
-The behavior of the `only: merge_requests` rule is such that _only_ jobs with
-that rule are run in the context of a merge request; no other jobs will be run.
+The behavior of the `only: merge_requests` parameter is such that _only_ jobs with
+that parameter are run in the context of a merge request; no other jobs will be run.
-However, you may want to reverse this behaviour, having all of your jobs to run _except_
-for one or two. Consider the following pipeline, with jobs `A`, `B`, and `C`. If you want
-all pipelines to always run `A` and `B`, but only want `C` to run for a merge request,
-you can configure your `.gitlab-ci.yml` file as follows:
+However, you may want to reverse this behavior, having all of your jobs to run _except_
+for one or two.
+
+Consider the following pipeline, with jobs `A`, `B`, and `C`. Imagine you want:
+
+- All pipelines to always run `A` and `B`.
+- `C` to run only for merge requests.
+
+To achieve this, you can configure your `.gitlab-ci.yml` file as follows:
``` yaml
.only-default: &only-default
@@ -90,9 +102,11 @@ C:
- merge_requests
```
-Since `A` and `B` are getting the `only:` rule to execute in all cases, they will
-always run. `C` specifies that it should only run for merge requests, so for any
-pipeline except a merge request pipeline, it will not run.
+Therefore:
+
+- Since `A` and `B` are getting the `only:` rule to execute in all cases, they will always run.
+- Since `C` specifies that it should only run for merge requests, it will not run for any pipeline
+ except a merge request pipeline.
As you can see, this will help you avoid a lot of boilerplate where you'd need
to add that `only:` rule to all of your jobs in order to make them always run. You
@@ -106,10 +120,10 @@ flow, external contributors follow the following steps:
1. Fork a parent project.
1. Create a merge request from the forked project that targets the `master` branch
-in the parent project.
+ in the parent project.
1. A pipeline runs on the merge request.
1. A maintainer from the parent project checks the pipeline result, and merge
-into a target branch if the latest pipeline has passed.
+ into a target branch if the latest pipeline has passed.
Currently, those pipelines are created in a **forked** project, not in the
parent project. This means you cannot completely trust the pipeline result,
diff --git a/doc/ci/permissions/README.md b/doc/ci/permissions/README.md
index 80d8e46f29c..bc1e6ce3e0b 100644
--- a/doc/ci/permissions/README.md
+++ b/doc/ci/permissions/README.md
@@ -1 +1,5 @@
-This document was moved to [user/permissions.md](../../user/permissions.md#gitlab-ci).
+---
+redirect_to: '../../user/permissions.md#gitlab-cicd-permissions'
+---
+
+This document was moved to [user/permissions.md](../../user/permissions.md#gitlab-cicd-permissions).
diff --git a/doc/ci/pipelines.md b/doc/ci/pipelines.md
index c41f3b7e82d..38cd58f11ac 100644
--- a/doc/ci/pipelines.md
+++ b/doc/ci/pipelines.md
@@ -1,271 +1,331 @@
-# Introduction to pipelines and jobs
+# Creating and using CI/CD pipelines
> Introduced in GitLab 8.8.
+## Introduction
+
+Pipelines are the top-level component of continuous integration, delivery, and deployment.
+
+Pipelines comprise:
+
+- Jobs that define what to run. For example, code compilation or test runs.
+- Stages that define when and how to run. For example, that tests run only after code compilation.
+
+Multiple jobs in the same stage are executed by [Runners](runners/README.md) in parallel, if there are enough concurrent [Runners](runners/README.md).
+
+If all the jobs in a stage:
+
+- Succeed, the pipeline moves on to the next stage.
+- Fail, the next stage is not (usually) executed and the pipeline ends early.
+
NOTE: **Note:**
-If you have a [mirrored repository where GitLab pulls from](https://docs.gitlab.com/ee/workflow/repository_mirroring.html#pulling-from-a-remote-repository-starter),
+If you have a [mirrored repository that GitLab pulls from](https://docs.gitlab.com/ee/workflow/repository_mirroring.html#pulling-from-a-remote-repository-starter),
you may need to enable pipeline triggering in your project's
**Settings > Repository > Pull from a remote repository > Trigger pipelines for mirror updates**.
-## Pipelines
+### Simple pipeline example
-A pipeline is a group of [jobs] that get executed in [stages].
-All of the jobs in a stage are executed in parallel (if there are enough
-concurrent [Runners]), and if they all succeed, the pipeline moves on to the
-next stage. If one of the jobs fails, the next stage is not (usually)
-executed. You can access the pipelines page in your project's **Pipelines** tab.
+As an example, imagine a pipeline consisting of four stages, executed in the following order:
-In the following image you can see that the pipeline consists of four stages
-(`build`, `test`, `staging`, `production`) each one having one or more jobs.
+- `build`, with a job called `compile`.
+- `test`, with two jobs called `test` and `test2`.
+- `staging`, with a job called `deploy-to-stage`.
+- `production`, with a job called `deploy-to-prod`.
->**Note:**
-GitLab capitalizes the stages' names when shown in the [pipeline graphs](#pipeline-graphs).
+## Visualizing pipelines
-![Pipelines example](img/pipelines.png)
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5742) in GitLab 8.11.
-## Types of pipelines
+Pipelines can be complex structures with many sequential and parallel jobs.
-There are three types of pipelines that often use the single shorthand of "pipeline". People often talk about them as if each one is "the" pipeline, but really, they're just pieces of a single, comprehensive pipeline.
+To make it easier to understand the flow of a pipeline, GitLab has pipeline graphs for viewing pipelines
+and their statuses.
-![Types of Pipelines](img/types-of-pipelines.png)
+Pipeline graphs can be displayed in two different ways, depending on the page you
+access the graph from.
-1. **CI Pipeline**: Build and test stages defined in `.gitlab-ci.yml`.
-1. **Deploy Pipeline**: Deploy stage(s) defined in `.gitlab-ci.yml` The flow of deploying code to servers through various stages: e.g. development to staging to production.
-1. **Project Pipeline**: Cross-project CI dependencies [triggered via API][triggers], particularly for micro-services, but also for complicated build dependencies: e.g. api -> front-end, ce/ee -> omnibus.
+NOTE: **Note:**
+GitLab capitalizes the stages' names when shown in the pipeline graphs (below).
-## Development workflows
+### Regular pipeline graphs
-Pipelines accommodate several development workflows:
+Regular pipeline graphs show the names of the jobs of each stage. Regular pipeline graphs can
+be found when you are on a [single pipeline page](#accessing-pipelines). For example:
-1. **Branch Flow** (e.g. different branch for dev, qa, staging, production).
-1. **Trunk-based Flow** (e.g. feature branches and single master branch, possibly with tags for releases).
-1. **Fork-based Flow** (e.g. merge requests come from forks).
+![Pipelines example](img/pipelines.png)
-Example continuous delivery flow:
+### Pipeline mini graphs
-![CD Flow](img/pipelines-goal.png)
+Pipeline mini graphs take less space and can tell you at a
+quick glance if all jobs passed or something failed. The pipeline mini graph can
+be found when you navigate to:
-## Jobs
+- The pipelines index page.
+- A single commit page.
+- A merge request page.
-Jobs can be defined in the [`.gitlab-ci.yml`][jobs-yaml] file. Not to be
-confused with a `build` job or `build` stage.
+Pipeline mini graphs allow you to see all related jobs for a single commit and the net result
+of each stage of your pipeline. This allows you to quickly see what failed and
+fix it.
-## Defining pipelines
+Stages in pipeline mini graphs are collapsible. Hover your mouse over them and click to expand their jobs.
-Pipelines are defined in `.gitlab-ci.yml` by specifying [jobs] that run in
-[stages].
+| Mini graph | Mini graph expanded |
+|:-------------------------------------------------------------|:---------------------------------------------------------------|
+| ![Pipelines mini graph](img/pipelines_mini_graph_simple.png) | ![Pipelines mini graph extended](img/pipelines_mini_graph.png) |
-See the reference [documentation for jobs](yaml/README.md#jobs).
+### Job ordering in pipeline graphs
-## Manually executing pipelines
+Job ordering depends on the type of pipeline graph. For [regular pipeline graphs](#regular-pipeline-graphs), jobs are sorted by name.
-Pipelines can be manually executed, with predefined or manually-specified [variables](variables/README.md).
+For [pipeline mini graphs](#pipeline-mini-graphs) ([introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9760)
+in GitLab 9.0), jobs are sorted by severity and then by name.
-To execute a pipeline manually:
+The order of severity is:
-1. Navigate to your project's **CI/CD > Pipelines**.
-1. Click on the **Run Pipeline** button.
-1. Select the branch to run the pipeline for and enter any environment variables required for the pipeline run.
+- failed
+- warning
+- pending
+- running
+- manual
+- scheduled
+- canceled
+- success
+- skipped
+- created
-## Seeing pipeline status
+For example:
-You can find the current and historical pipeline runs under your project's
-**Pipelines** tab. Clicking on a pipeline will show the jobs that were run for
-that pipeline.
-
-![Pipelines index page](img/pipelines_index.png)
+![Pipeline mini graph sorting](img/pipelines_mini_graph_sorting.png)
-## Seeing job status
+### How pipeline duration is calculated
-When you visit a single pipeline you can see the related jobs for that pipeline.
-Clicking on an individual job will show you its job trace, and allow you to
-cancel the job, retry it, or erase the job trace.
+Total running time for a given pipeline excludes retries and pending
+(queued) time.
-![Pipelines example](img/pipelines.png)
+Each job is represented as a `Period`, which consists of:
-## Seeing the failure reason for jobs
+- `Period#first` (when the job started).
+- `Period#last` (when the job finished).
-> [Introduced][ce-17782] in GitLab 10.7.
+A simple example is:
-When a pipeline fails or is allowed to fail, there are several places where you
-can quickly check the reason it failed:
+- A (1, 3)
+- B (2, 4)
+- C (6, 7)
-- **In the pipeline graph** present on the pipeline detail view.
-- **In the pipeline widgets** present in the merge requests and commit pages.
-- **In the job views** present in the global and detailed views of a job.
+In the example:
-In any case, if you hover over the failed job you can see the reason it failed.
+- A begins at 1 and ends at 3.
+- B begins at 2 and ends at 4.
+- C begins at 6 and ends at 7.
-![Pipeline detail](img/job_failure_reason.png)
+Visually, it can be viewed as:
-From [GitLab 10.8][ce-17814] you can also see the reason it failed on the Job detail page.
+```text
+0 1 2 3 4 5 6 7
+ AAAAAAA
+ BBBBBBB
+ CCCC
+```
-## Pipeline graphs
+The union of A, B, and C is (1, 4) and (6, 7). Therefore, the total running time is:
-> [Introduced][ce-5742] in GitLab 8.11.
+```text
+(4 - 1) + (7 - 6) => 4
+```
-Pipelines can be complex structures with many sequential and parallel jobs.
-To make it a little easier to see what is going on, you can view a graph
-of a single pipeline and its status.
+## Configuring pipelines
-A pipeline graph can be shown in two different ways depending on what page you
-are on.
+Pipelines, and their component jobs and stages, are defined in the [`.gitlab-ci.yml`](yaml/README.md) file for each project.
----
+In particular:
-The regular pipeline graph that shows the names of the jobs of each stage can
-be found when you are on a [single pipeline page](#seeing-pipeline-status).
+- Jobs are the [basic configuration](yaml/README.html#introduction) component.
+- Stages are defined using the [`stages`](yaml/README.html#stages) keyword.
-![Pipelines example](img/pipelines.png)
+For all available configuration options, see the [GitLab CI/CD Pipeline Configuration Reference](yaml/README.md).
-Then, there is the pipeline mini graph which takes less space and can give you a
-quick glance if all jobs pass or something failed. The pipeline mini graph can
-be found when you visit:
+### Settings and schedules
-- The pipelines index page.
-- A single commit page.
-- A merge request page.
+In addition to configuring jobs through `.gitlab-ci.yml`, additional configuration options are available
+through the GitLab UI:
-That way, you can see all related jobs for a single commit and the net result
-of each stage of your pipeline. This allows you to quickly see what failed and
-fix it. Stages in pipeline mini graphs are collapsible. Hover your mouse over
-them and click to expand their jobs.
+- Pipeline settings for each project. For more information, see [Pipeline settings](../user/project/pipelines/settings.md).
+- Schedules for pipelines. For more information, see [Pipeline schedules](../user/project/pipelines/schedules.md).
-| **Mini graph** | **Mini graph expanded** |
-| :------------: | :---------------------: |
-| ![Pipelines mini graph](img/pipelines_mini_graph_simple.png) | ![Pipelines mini graph extended](img/pipelines_mini_graph.png) |
+### Grouping jobs
-### Grouping similar jobs in the pipeline graph
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6242) in GitLab 8.12.
-> [Introduced][ce-6242] in GitLab 8.12.
+If you have many similar jobs, your [pipeline graph](#visualizing-pipelines) becomes long and hard
+to read.
-If you have many similar jobs, your pipeline graph becomes very long and hard
-to read. For that reason, similar jobs can automatically be grouped together.
+For that reason, similar jobs can automatically be grouped together.
If the job names are formatted in certain ways, they will be collapsed into
a single group in regular pipeline graphs (not the mini graphs).
+
You'll know when a pipeline has grouped jobs if you don't see the retry or
cancel button inside them. Hovering over them will show the number of grouped
jobs. Click to expand them.
![Grouped pipelines](img/pipelines_grouped.png)
-The basic requirements is that there are two numbers separated with one of
+#### Configuring grouping
+
+In the pipeline [configuration file](yaml/README.md), job names must include two numbers separated with one of
the following (you can even use them interchangeably):
-- A space (` `)
-- A slash (`/`)
-- A colon (`:`)
+- A space.
+- A slash (`/`).
+- A colon (`:`).
+
+NOTE: **Note:**
+More specifically, it uses [this](https://gitlab.com/gitlab-org/gitlab-ce/blob/2f3dc314f42dbd79813e6251792853bc231e69dd/app/models/commit_status.rb#L99) regular expression: `\d+[\s:\/\\]+\d+\s*`.
->**Note:**
-More specifically, [it uses][regexp] this regular expression: `\d+[\s:\/\\]+\d+\s*`.
+#### How grouping works
The jobs will be ordered by comparing those two numbers from left to right. You
usually want the first to be the index and the second the total.
For example, the following jobs will be grouped under a job named `test`:
-- `test 0 3` => `test`
-- `test 1 3` => `test`
-- `test 2 3` => `test`
+- `test 0 3`
+- `test 1 3`
+- `test 2 3`
The following jobs will be grouped under a job named `test ruby`:
-- `test 1:2 ruby` => `test ruby`
-- `test 2:2 ruby` => `test ruby`
+- `test 1:2 ruby`
+- `test 2:2 ruby`
The following jobs will be grouped under a job named `test ruby` as well:
-- `1/3 test ruby` => `test ruby`
-- `2/3 test ruby` => `test ruby`
-- `3/3 test ruby` => `test ruby`
+- `1/3 test ruby`
+- `2/3 test ruby`
+- `3/3 test ruby`
-### Manual actions from the pipeline graph
+### Pipelines for merge requests
-> [Introduced][ce-7931] in GitLab 8.15.
+GitLab supports configuring pipelines that run only for merge requests. For more information, see
+[Pipelines for merge requests](merge_request_pipelines/index.md).
-[Manual actions][manual] allow you to require manual interaction before moving
-forward with a particular job in CI. Your entire pipeline can run automatically,
-but the actual [deploy to production][env-manual] will require a click.
+### Badges
-You can do this straight from the pipeline graph. Just click on the play button
-to execute that particular job. For example, in the image below, the `production`
-stage has a job with a manual action.
+Pipeline status and test coverage report badges are available and configurable for each project.
-![Pipelines example](img/pipelines.png)
+For information on adding pipeline badges to projects, see [Pipeline badges](../user/project/pipelines/settings.md#pipeline-badges).
-### Delay a particular job in the pipeline graph
+## Multi-project pipelines **[PREMIUM]**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21767) in GitLab 11.4.
+Pipelines for different projects can be combined and visualized together.
-When you do not want to run a job immediately, you can [delay the job to run after a certain period](yaml/README.md#when-delayed).
-This is especially useful for timed incremental rollout that new code is rolled out gradually.
-For example, if you start rolling out new code and users do not experience trouble, GitLab automatically completes the deployment from 0% to 100%.
-Alternatively, if you start rolling out and you noticed that a few users experience trouble with the version,
-you can stop the timed incremental rollout by canceling the pipeline, and [rolling](environments.md#rolling-back-changes) it back to the stable version.
+For more information, see [Multi-project pipelines](https://docs.gitlab.com/ee/ci/multi_project_pipelines.html).
-![Pipelines example](img/pipeline_incremental_rollout.png)
+## Working with pipelines
-### Ordering of jobs in pipeline graphs
+In general, pipelines are executed automatically and require no intervention once created.
-**Regular pipeline graph**
+However, there are instances where you'll need to interact with pipelines. These are documented below.
-In the single pipeline page, jobs are sorted by name.
+### Manually executing pipelines
-**Mini pipeline graph**
+Pipelines can be manually executed, with predefined or manually-specified [variables](variables/README.md).
-> [Introduced][ce-9760] in GitLab 9.0.
+You might do this if the results of a pipeline (for example, a code build) is required outside the normal
+operation of the pipeline.
-In the pipeline mini graphs, the jobs are sorted first by severity and then
-by name. The order of severity is:
+To execute a pipeline manually:
-- failed
-- warning
-- pending
-- running
-- manual
-- scheduled
-- canceled
-- success
-- skipped
-- created
+1. Navigate to your project's **CI/CD > Pipelines**.
+1. Click on the **Run Pipeline** button.
+1. On the **Run Pipeline** page:
+ 1. Select the branch to run the pipeline for in the **Create for** field.
+ 1. Enter any [environment variables](variables/README.md) required for the pipeline run.
+ 1. Click the **Create pipeline** button.
-![Pipeline mini graph sorting](img/pipelines_mini_graph_sorting.png)
+The pipeline will execute the jobs as configured.
-## How the pipeline duration is calculated
+### Accessing pipelines
-Total running time for a given pipeline would exclude retries and pending
-(queue) time. We could reduce this problem down to finding the union of
-periods.
+You can find the current and historical pipeline runs under your project's
+**CI/CD > Pipelines** page. Clicking on a pipeline will show the jobs that were run for
+that pipeline.
-So each job would be represented as a `Period`, which consists of
-`Period#first` as when the job started and `Period#last` as when the
-job was finished. A simple example here would be:
+![Pipelines index page](img/pipelines_index.png)
-- A (1, 3)
-- B (2, 4)
-- C (6, 7)
+You can also access pipelines for a merge request by navigating to its **Pipelines** tab.
-Here A begins from 1, and ends to 3. B begins from 2, and ends to 4.
-C begins from 6, and ends to 7. Visually it could be viewed as:
+### Accessing individual jobs
-```
-0 1 2 3 4 5 6 7
- AAAAAAA
- BBBBBBB
- CCCC
-```
+When you access a pipeline, you can see the related jobs for that pipeline.
-The union of A, B, and C would be (1, 4) and (6, 7), therefore the
-total running time should be:
+Clicking on an individual job will show you its job trace, and allow you to:
-```
-(4 - 1) + (7 - 6) => 4
-```
+- Cancel the job.
+- Retry the job.
+- Erase the job trace.
+
+### Seeing the failure reason for jobs
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17782) in GitLab 10.7.
+
+When a pipeline fails or is allowed to fail, there are several places where you
+can quickly check the reason it failed:
+
+- In the pipeline graph, on the pipeline detail view.
+- In the pipeline widgets, in the merge requests and commit pages.
+- In the job views, in the global and detailed views of a job.
+
+In each place, if you hover over the failed job you can see the reason it failed.
-## Badges
+![Pipeline detail](img/job_failure_reason.png)
+
+From [GitLab 10.8](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17814),
+you can also see the reason it failed on the Job detail page.
+
+### Manual actions from pipeline graphs
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7931) in GitLab 8.15.
-Pipeline status and test coverage report badges are available. You can find their
-respective link in the [Pipelines settings] page.
+Manual actions, configured using the [`when:manual`](yaml/README.md#whenmanual) parameter,
+allow you to require manual interaction before moving forward in the pipeline.
+
+You can do this straight from the pipeline graph. Just click on the play button
+to execute that particular job.
+
+For example, your pipeline start automatically, but require manual action to
+[deploy to production](environments.md#manually-deploying-to-environments). In the example below, the `production`
+stage has a job with a manual action.
+
+![Pipelines example](img/pipelines.png)
+
+### Delay a job in a pipeline graph
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21767) in GitLab 11.4.
+
+When you do not want to run a job immediately, you can use the [`when:delayed`](yaml/README.md#whendelayed) parameter to
+delay a job's execution for a certain period.
+
+This is especially useful for timed incremental rollout where new code is rolled out gradually.
+
+For example, if you start rolling out new code and:
+
+- Users do not experience trouble, GitLab can automatically complete the deployment from 0% to 100%.
+- Users experience trouble with the new code, you can stop the timed incremental rollout by canceling the pipeline
+ and [rolling](environments.md#rolling-back-changes) back to the last stable version.
+
+![Pipelines example](img/pipeline_incremental_rollout.png)
+
+### Using the API
+
+GitLab provides API endpoints to:
+
+- Perform basic functions. For more information, see [Pipelines API](../api/pipelines.md).
+- Maintain pipeline schedules. For more information, see [Pipeline schedules API](../api/pipeline_schedules.md).
+- Trigger pipeline runs. For more information, see:
+ - [Triggering pipelines through the API](triggers/README.md).
+ - [Pipeline triggers API](../api/pipeline_triggers.md).
## Security on protected branches
@@ -276,14 +336,14 @@ The following actions are allowed on protected branches only if the user is
[allowed to merge or push](../user/project/protected_branches.md#using-the-allowed-to-merge-and-allowed-to-push-settings)
on that specific branch:
-- Run **manual pipelines** (using [Web UI](#manually-executing-pipelines) or Pipelines API).
-- Run **scheduled pipelines**.
-- Run pipelines using **triggers**.
-- Trigger **manual actions** on existing pipelines.
-- **Retry/cancel** existing jobs (using Web UI or Pipelines API).
+- Run manual pipelines (using the [Web UI](#manually-executing-pipelines) or pipelines API).
+- Run scheduled pipelines.
+- Run pipelines using triggers.
+- Trigger manual actions on existing pipelines.
+- Retry or cancel existing jobs (using the Web UI or pipelines API).
**Variables** marked as **protected** are accessible only to jobs that
-run on protected branches, avoiding untrusted users to get unintended access to
+run on protected branches, preventing untrusted users getting unintended access to
sensitive information like deployment credentials and tokens.
**Runners** marked as **protected** can run jobs only on protected
@@ -291,19 +351,3 @@ branches, avoiding untrusted code to be executed on the protected runner and
preserving deployment keys and other credentials from being unintentionally
accessed. In order to ensure that jobs intended to be executed on protected
runners will not use regular runners, they must be tagged accordingly.
-
-[jobs]: #jobs
-[jobs-yaml]: yaml/README.md#jobs
-[manual]: yaml/README.md#whenmanual
-[env-manual]: environments.md#manually-deploying-to-environments
-[stages]: yaml/README.md#stages
-[runners]: runners/README.html
-[pipelines settings]: ../user/project/pipelines/settings.md
-[triggers]: triggers/README.md
-[ce-5742]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5742
-[ce-6242]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6242
-[ce-7931]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7931
-[ce-9760]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9760
-[ce-17782]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17782
-[ce-17814]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17814
-[regexp]: https://gitlab.com/gitlab-org/gitlab-ce/blob/2f3dc314f42dbd79813e6251792853bc231e69dd/app/models/commit_status.rb#L99
diff --git a/doc/ci/review_apps/index.md b/doc/ci/review_apps/index.md
index f4d7b9ad194..9dbcf9d1155 100644
--- a/doc/ci/review_apps/index.md
+++ b/doc/ci/review_apps/index.md
@@ -12,8 +12,8 @@ Review Apps are a collaboration tool that takes the hard work out of providing a
Review Apps:
- Provide an automatic live preview of changes made in a feature branch by spinning up a dynamic environment for your merge requests.
-- Allow designers and product manages to see your changes without needing to check out your branch and run your changes in a sandbox environment.
-- Are fully integrated with the [GitLab DevOps LifeCycle](../../README.md#complete-devops-with-gitlab).
+- Allow designers and product managers to see your changes without needing to check out your branch and run your changes in a sandbox environment.
+- Are fully integrated with the [GitLab DevOps LifeCycle](../../README.md#the-entire-devops-lifecycle).
- Allow you to deploy your changes wherever you want.
![Review Apps Workflow](img/continuous-delivery-review-apps.svg)
diff --git a/doc/ci/services/README.md b/doc/ci/services/README.md
index d94b472b768..2eda5d23976 100644
--- a/doc/ci/services/README.md
+++ b/doc/ci/services/README.md
@@ -10,4 +10,4 @@ be linked with your base image. Below is a list of examples you may use.
- [Using MySQL](mysql.md)
- [Using PostgreSQL](postgres.md)
- [Using Redis](redis.md)
-- [Using Other Services](../docker/using_docker_images.md#how-to-use-other-images-as-services)
+- [Using Other Services](../docker/using_docker_images.md#what-is-a-service)
diff --git a/doc/ci/services/postgres.md b/doc/ci/services/postgres.md
index 3899b555f32..2e6d7ae94d2 100644
--- a/doc/ci/services/postgres.md
+++ b/doc/ci/services/postgres.md
@@ -31,7 +31,7 @@ Database: nice_marmot
```
If you are wondering why we used `postgres` for the `Host`, read more at
-[How is service linked to the job](../docker/using_docker_images.md#how-is-service-linked-to-the-job).
+[How services are linked to the job](../docker/using_docker_images.md#how-services-are-linked-to-the-job).
You can also use any other docker image available on [Docker Hub][hub-pg].
For example, to use PostgreSQL 9.3 the service becomes `postgres:9.3`.
diff --git a/doc/ci/ssh_keys/README.md b/doc/ci/ssh_keys/README.md
index 0c3b0bf6990..ff63d0bd69f 100644
--- a/doc/ci/ssh_keys/README.md
+++ b/doc/ci/ssh_keys/README.md
@@ -92,7 +92,7 @@ to access it. This is where an SSH key pair comes in handy.
```
NOTE: **Note:**
- The [`before_script`](../yaml/README.md#before-script) can be set globally
+ The [`before_script`](../yaml/README.md#before_script-and-after_script) can be set globally
or per-job.
1. Make sure the private server's [SSH host keys are verified](#verifying-the-ssh-host-keys).
diff --git a/doc/ci/triggers/README.md b/doc/ci/triggers/README.md
index 61037360326..398b017277f 100644
--- a/doc/ci/triggers/README.md
+++ b/doc/ci/triggers/README.md
@@ -4,7 +4,7 @@
>
> - [Introduced](https://about.gitlab.com/2015/08/22/gitlab-7-14-released/) in GitLab 7.14.
> - GitLab 8.12 has a completely redesigned job permissions system. Read all
-> about the [new model and its implications](../../user/project/new_ci_build_permissions_model.md#job-triggers).
+> about the [new model and its implications](../../user/project/new_ci_build_permissions_model.md#pipeline-triggers).
Triggers can be used to force a pipeline rerun of a specific `ref` (branch or
tag) with an API call.
@@ -17,6 +17,12 @@ The following methods of authentication are supported.
A unique trigger token can be obtained when [adding a new trigger](#adding-a-new-trigger).
+DANGER: **Danger:**
+Passing plain text tokens in public projects is a security issue. Potential
+attackers can impersonate the user that exposed their trigger token publicly in
+their `.gitlab-ci.yml` file. Use [variables](../variables/README.md#variables)
+to protect trigger tokens.
+
## Adding a new trigger
You can add a new trigger by going to your project's
@@ -53,9 +59,6 @@ The action is irreversible.
>
> - Valid refs are only the branches and tags. If you pass a commit SHA as a ref,
> it will not trigger a job.
-> - If your project is public, passing the token in plain text is probably not the
-> wisest idea, so you might want to use a
-> [variable](../variables/README.md#variables) for that purpose.
To trigger a job you need to send a `POST` request to GitLab's API endpoint:
@@ -219,7 +222,7 @@ Old triggers, created before GitLab 9.0 will be marked as legacy.
Triggers with the legacy label do not have an associated user and only have
access to the current project. They are considered deprecated and will be
removed with one of the future versions of GitLab. You are advised to
-[take ownership](#taking-ownership) of any legacy triggers.
+[take ownership](#taking-ownership-of-a-trigger) of any legacy triggers.
[ee-2017]: https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/2017
[ee]: https://about.gitlab.com/pricing/
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 7498617ed2c..814185f7732 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -16,7 +16,7 @@ Variables of different types can take precedence over other variables, depending
The order of precedence for variables is (from highest to lowest):
-1. [Trigger variables](../triggers/README.md#pass-job-variables-to-a-trigger) or [scheduled pipeline variables](../../user/project/pipelines/schedules.md#making-use-of-scheduled-pipeline-variables).
+1. [Trigger variables](../triggers/README.md#making-use-of-trigger-variables) or [scheduled pipeline variables](../../user/project/pipelines/schedules.md#making-use-of-scheduled-pipeline-variables).
1. Project-level [variables](#variables) or [protected variables](#protected-variables).
1. Group-level [variables](#variables) or [protected variables](#protected-variables).
1. YAML-defined [job-level variables](../yaml/README.md#variables).
@@ -46,13 +46,15 @@ version of Runner required.
NOTE: **Note:**
Starting with GitLab 9.0, we have deprecated some variables. Read the
-[9.0 Renaming](#9-0-renaming) section to find out their replacements. **You are
+[9.0 Renaming](#gitlab-90-renaming) section to find out their replacements. **You are
strongly advised to use the new variables as we will remove the old ones in
future GitLab releases.**
| Variable | GitLab | Runner | Description |
|-------------------------------------------|--------|--------|-------------|
| **ARTIFACT_DOWNLOAD_ATTEMPTS** | 8.15 | 1.9 | Number of attempts to download artifacts running a job |
+| **CHAT_INPUT** | 10.6 | all | Additional arguments passed in the [ChatOps](../chatops/README.md) command |
+| **CHAT_CHANNEL** | 10.6 | all | Source chat channel which triggered the [ChatOps](../chatops/README.md) command |
| **CI** | all | 0.4 | Mark that job is executed in CI environment |
| **CI_COMMIT_BEFORE_SHA** | 11.2 | all | The previous latest commit present on a branch before a push request. |
| **CI_COMMIT_DESCRIPTION** | 10.8 | all | The description of the commit: the message without first line, if the title is shorter than 100 characters; full message in other case. |
@@ -77,17 +79,23 @@ future GitLab releases.**
| **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][registry] and downloading [dependent repositories][dependent-repositories] |
| **CI_JOB_URL** | 11.1 | 0.5 | Job details URL |
-| **CI_MERGE_REQUEST_ID** | 11.6 | all | The ID of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) |
-| **CI_MERGE_REQUEST_IID** | 11.6 | all | The IID of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) |
-| **CI_MERGE_REQUEST_PROJECT_ID** | 11.6 | all | The ID of the project of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) |
-| **CI_MERGE_REQUEST_PROJECT_PATH** | 11.6 | all | The path of the project of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) (e.g. `namespace/awesome-project`) |
-| **CI_MERGE_REQUEST_PROJECT_URL** | 11.6 | all | The URL of the project of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) (e.g. `http://192.168.10.15:3000/namespace/awesome-project`) |
-| **CI_MERGE_REQUEST_REF_PATH** | 11.6 | all | The ref path of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md). (e.g. `refs/merge-requests/1/head`) |
-| **CI_MERGE_REQUEST_SOURCE_BRANCH_NAME** | 11.6 | all | The source branch name of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) |
-| **CI_MERGE_REQUEST_SOURCE_PROJECT_ID** | 11.6 | all | The ID of the source project of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) |
-| **CI_MERGE_REQUEST_SOURCE_PROJECT_PATH** | 11.6 | all | The path of the source project of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) |
-| **CI_MERGE_REQUEST_SOURCE_PROJECT_URL** | 11.6 | all | The URL of the source project of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) |
-| **CI_MERGE_REQUEST_TARGET_BRANCH_NAME** | 11.6 | all | The target branch name of the merge request if it's [pipelines for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_ID** | 11.6 | all | The ID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_IID** | 11.6 | all | The IID of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_PROJECT_ID** | 11.6 | all | The ID of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_PROJECT_PATH** | 11.6 | all | The path of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `namespace/awesome-project`) |
+| **CI_MERGE_REQUEST_PROJECT_URL** | 11.6 | all | The URL of the project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) (e.g. `http://192.168.10.15:3000/namespace/awesome-project`) |
+| **CI_MERGE_REQUEST_REF_PATH** | 11.6 | all | The ref path of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). (e.g. `refs/merge-requests/1/head`) |
+| **CI_MERGE_REQUEST_SOURCE_BRANCH_NAME** | 11.6 | all | The source branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_SOURCE_BRANCH_SHA** | 11.9 | all | The HEAD sha of the source branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_SOURCE_PROJECT_ID** | 11.6 | all | The ID of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_SOURCE_PROJECT_PATH** | 11.6 | all | The path of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_SOURCE_PROJECT_URL** | 11.6 | all | The URL of the source project of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_TARGET_BRANCH_NAME** | 11.6 | all | The target branch name of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_TARGET_BRANCH_SHA** | 11.9 | all | The HEAD sha of the target branch of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_TITLE** | 11.9 | all | The title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_ASSIGNEES** | 11.9 | all | Comma-separated list of usernames of assignees for the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md). [Multiple assignees for merge requests](https://gitlab.com/gitlab-org/gitlab-ee/issues/2004) is scheduled for a future release |
+| **CI_MERGE_REQUEST_MILESTONE** | 11.9 | all | The milestone title of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
+| **CI_MERGE_REQUEST_LABELS** | 11.9 | all | Comma-separated label names of the merge request if [the pipelines are for merge requests](../merge_request_pipelines/index.md) |
| **CI_NODE_INDEX** | 11.5 | all | Index of the job in the job set. If the job is not parallelized, this variable is not set. |
| **CI_NODE_TOTAL** | 11.5 | all | Total number of instances of this job running in parallel. If the job is not parallelized, this variable is set to `1`. |
| **CI_API_V4_URL** | 11.7 | all | The GitLab API v4 root URL |
@@ -303,7 +311,7 @@ variables that were set, etc.
Before enabling this, you should ensure jobs are visible to
[team members only](../../user/permissions.md#project-features). You should
-also [erase](../pipelines.md#seeing-build-status) all generated job traces
+also [erase](../pipelines.md#accessing-individual-jobs) all generated job traces
before making them visible again.
To enable debug traces, set the `CI_DEBUG_TRACE` variable to `true`:
@@ -565,7 +573,7 @@ If any of the conditions in `variables` evaluates to truth when using `only`,
a new job is going to be created. If any of the expressions evaluates to truth
when `except` is being used, a job is not going to be created.
-This follows usual rules for [`only` / `except` policies][builds-policies].
+This follows usual rules for [`only` / `except` policies](../yaml/README.md#onlyexcept-advanced).
### Supported syntax
@@ -631,7 +639,6 @@ Below you can find supported syntax reference:
[protected tags]: ../../user/project/protected_tags.md
[shellexecutors]: https://docs.gitlab.com/runner/executors/
[triggered]: ../triggers/README.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
diff --git a/doc/ci/variables/where_variables_can_be_used.md b/doc/ci/variables/where_variables_can_be_used.md
index 1d98e8426fe..ceca4af1bee 100644
--- a/doc/ci/variables/where_variables_can_be_used.md
+++ b/doc/ci/variables/where_variables_can_be_used.md
@@ -106,9 +106,9 @@ The following variables are known as "persisted":
They are:
-- Supported for definitions where the ["Expansion place"](#gitlab-ci-yml-file) is:
+- Supported for definitions where the ["Expansion place"](#gitlab-ciyml-file) is:
- Runner.
- Script execution shell.
- Not supported:
- - For definitions where the ["Expansion place"](#gitlab-ci-yml-file) is GitLab.
+ - For definitions where the ["Expansion place"](#gitlab-ciyml-file) is GitLab.
- In the `only` and `except` [variables expressions](README.md#variables-expressions).
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 31f99cd5e68..9eb694a2c64 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -1,26 +1,39 @@
-# Configuration of your jobs with .gitlab-ci.yml
+# GitLab CI/CD Pipeline Configuration Reference
-This document describes the usage of `.gitlab-ci.yml`, the file that is used by
-GitLab Runner to manage your project's jobs.
+GitLab CI/CD [pipelines](../pipelines.md) are configured using a YAML file called `.gitlab-ci.yml` within each project.
-From version 7.12, GitLab CI uses a [YAML](https://en.wikipedia.org/wiki/YAML)
-file (`.gitlab-ci.yml`) for the project configuration. It is placed in the root
-of your repository and contains definitions of how your project should be built.
+The `.gitlab-ci.yml` file defines the structure and order of the pipelines and determines:
-If you want a quick introduction to GitLab CI, follow our
-[quick start guide](../quick_start/README.md).
+- What to execute using [GitLab Runner](https://docs.gitlab.com/runner/).
+- What decisions to make when specific conditions are encountered. For example, when a process succeeds or fails.
+
+This topic covers CI/CD pipeline configuration. For other CI/CD configuration information, see:
+
+- [GitLab CI/CD Variables](../variables/README.md), for configuring the environment the pipelines run in.
+- [GitLab Runner advanced configuration](https://docs.gitlab.com/runner/configuration/advanced-configuration.html), for configuring GitLab Runner.
+
+We have complete examples of configuring pipelines:
+
+- For a quick introduction to GitLab CI, follow our [quick start guide](../quick_start/README.md).
+- For a collection of examples, see [GitLab CI/CD Examples](../examples/README.md).
+- To see a large `.gitlab-ci.yml` file used in an enterprise, see the [`.gitlab-ci.yml` file for `gitlab-ce`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/.gitlab-ci.yml).
NOTE: **Note:**
If you have a [mirrored repository where GitLab pulls from](https://docs.gitlab.com/ee/workflow/repository_mirroring.html#pulling-from-a-remote-repository-starter),
you may need to enable pipeline triggering in your project's
**Settings > Repository > Pull from a remote repository > Trigger pipelines for mirror updates**.
-## Jobs
+## Introduction
+
+Pipeline configuration begins with jobs. Jobs are the most fundamental element of a `.gitlab-ci.yml` file.
+
+Jobs are:
-The YAML file defines a set of jobs with constraints stating when they should
-be run. You can specify an unlimited number of jobs which are defined as
-top-level elements with an arbitrary name and always have to contain at least
-the `script` clause.
+- Defined with constraints stating under what conditions they should be executed.
+- Top-level elements with an arbitrary name and must contain at least the [`script`](#script) clause.
+- Not limited in how many can be defined.
+
+For example:
```yaml
job1:
@@ -39,6 +52,14 @@ Jobs are picked up by [Runners](../runners/README.md) and executed within the
environment of the Runner. What is important, is that each job is run
independently from each other.
+### Validate the .gitlab-ci.yml
+
+Each instance of GitLab CI has an embedded debug tool called Lint, which validates the
+content of your `.gitlab-ci.yml` files. You can find the Lint under the page `ci/lint` of your
+project namespace. For example, `http://gitlab.example.com/gitlab-org/project-123/-/ci/lint`.
+
+### Unavailable names for jobs
+
Each job must have a unique name, but there are a few **reserved `keywords` that
cannot be used as job names**:
@@ -51,42 +72,137 @@ cannot be used as job names**:
- `variables`
- `cache`
-A job is defined by a list of parameters that define the job behavior.
-
-| Keyword | Required | Description |
-|---------------|----------|-------------|
-| [script](#script) | yes | Defines a shell script which is executed by Runner |
-| [extends](#extends) | no | Defines a configuration entry that this job is going to inherit from |
-| [include](#include) | no | Defines a configuration entry that allows this job to include external YAML files |
-| [image](#image-and-services) | no | Use docker image, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) |
-| [services](#image-and-services) | no | Use docker services, covered in [Using Docker Images](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml) |
-| [stage](#stage) | no | Defines a job stage (default: `test`) |
-| type | no | Alias for `stage` |
-| [variables](#variables) | no | Define job variables on a job level |
-| [only](#only-and-except-simplified) | no | Defines a list of git refs for which job is created |
-| [except](#only-and-except-simplified) | no | Defines a list of git refs for which job is not created |
-| [tags](#tags) | no | Defines a list of tags which are used to select Runner |
-| [allow_failure](#allow_failure) | no | Allow job to fail. Failed job doesn't contribute to commit status |
-| [when](#when) | no | Define when to run job. Can be `on_success`, `on_failure`, `always` or `manual` |
-| [dependencies](#dependencies) | no | Define other jobs that a job depends on so that you can pass artifacts between them|
-| [artifacts](#artifacts) | no | Define list of [job artifacts](#artifacts) |
-| [cache](#cache) | no | Define list of files that should be cached between subsequent runs |
-| [before_script](#before_script-and-after_script) | no | Override a set of commands that are executed before job |
-| [after_script](#before_script-and-after_script) | no | Override a set of commands that are executed after job |
-| [environment](#environment) | no | Defines a name of environment to which deployment is done by this job |
-| [coverage](#coverage) | no | Define code coverage settings for a given job |
-| [retry](#retry) | no | Define when and how many times a job can be auto-retried in case of a failure |
-| [parallel](#parallel) | no | Defines how many instances of a job should be run in parallel |
-
-## `image` and `services`
-
-This allows to specify a custom Docker image and a list of services that can be
-used for time of the job. The configuration of this feature is covered in
-[a separate document](../docker/README.md).
-
-## `before_script` and `after_script`
-
-> Introduced in GitLab 8.7 and requires GitLab Runner v1.2
+### Using reserved keywords
+
+If you get validation error when using specific values (for example, `true` or `false`), try to:
+
+- Quote them.
+- Change them to a different form. For example, `/bin/true`.
+
+## Configuration parameters
+
+A job is defined as a list of parameters that define the job's behavior.
+
+The following table lists available parameters for jobs:
+
+| Keyword | Description |
+|:---------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [`script`](#script) | Shell script which is executed by Runner. |
+| [`image`](#image) | Use docker images. Also available: `image:name` and `image:entrypoint`. |
+| [`services`](#services) | Use docker services images. Also available: `services:name`, `services:alias`, `services:entrypoint`, and `services:command`. |
+| [`before_script`](#before_script-and-after_script) | Override a set of commands that are executed before job. |
+| [`after_script`](#before_script-and-after_script) | Override a set of commands that are executed after job. |
+| [`stages`](#stages) | Define stages in a pipeline. |
+| [`stage`](#stage) | Defines a job stage (default: `test`). |
+| [`only`](#onlyexcept-basic) | Limit when jobs are created. Also available: [`only:refs`, `only:kubernetes`, `only:variables`, and `only:changes`](#onlyexcept-advanced). |
+| [`except`](#onlyexcept-basic) | Limit when jobs are not created. Also available: [`except:refs`, `except:kubernetes`, `except:variables`, and `except:changes`](#onlyexcept-advanced). |
+| [`tags`](#tags) | List of tags which are used to select Runner. |
+| [`allow_failure`](#allow_failure) | Allow job to fail. Failed job doesn't contribute to commit status. |
+| [`when`](#when) | When to run job. Also available: `when:manual` and `when:delayed`. |
+| [`environment`](#environment) | Name of an environment to which the job deploys. Also available: `environment:name`, `environment:url`, `environment:on_stop`, and `environment:action`. |
+| [`cache`](#cache) | List of files that should be cached between subsequent runs. Also available: `cache:paths`, `cache:key`, `cache:untracked`, and `cache:policy`. |
+| [`artifacts`](#artifacts) | List of files and directories to attach to a job on success. Also available: `artifacts:paths`, `artifacts:name`, `artifacts:untracked`, `artifacts:when`, `artifacts:expire_in`, `artifacts:reports`, and `artifacts:reports:junit`.<br><br>In GitLab [Enterprise Edition](https://about.gitlab.com/pricing/), these are available: `artifacts:reports:codequality`, `artifacts:reports:sast`, `artifacts:reports:dependency_scanning`, `artifacts:reports:container_scanning`, `artifacts:reports:dast`, `artifacts:reports:license_management`, and `artifacts:reports:performance`. |
+| [`dependencies`](#dependencies) | Other jobs that a job depends on so that you can pass artifacts between them. |
+| [`coverage`](#coverage) | Code coverage settings for a given job. |
+| [`retry`](#retry) | When and how many times a job can be auto-retried in case of a failure. |
+| [`parallel`](#parallel) | How many instances of a job should be run in parallel. |
+| [`trigger`](#trigger-premium) | Defines a downstream pipeline trigger. |
+| [`include`](#include) | Allows this job to include external YAML files. Also available: `include:local`, `include:file`, `include:template`, and `include:remote`. |
+| [`extends`](#extends) | Configuration entry that this job is going to inherit from. |
+| [`pages`](#pages) | Upload the result of a job to use with GitLab Pages. |
+| [`variables`](#variables) | Define job variables on a job level. |
+
+NOTE: **Note:**
+Parameters `types` and `type` are [deprecated](#deprecated-parameters).
+
+## Parameter details
+
+The following are detailed explanations for parameters used to configure CI/CD pipelines.
+
+### `script`
+
+`script` is the only required keyword that a job needs. It's a shell script
+which is executed by the Runner. For example:
+
+```yaml
+job:
+ script: "bundle exec rspec"
+```
+
+This parameter can also contain several commands using an array:
+
+```yaml
+job:
+ script:
+ - uname -a
+ - bundle exec rspec
+```
+
+NOTE: **Note:**
+Sometimes, `script` commands will need to be wrapped in single or double quotes.
+For example, commands that contain a colon (`:`) need to be wrapped in quotes so
+that the YAML parser knows to interpret the whole thing as a string rather than
+a "key: value" pair. Be careful when using special characters:
+`:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` ``.
+
+### `image`
+
+Used to specify [a Docker image](../docker/using_docker_images.md#what-is-an-image) to use for the job.
+
+For:
+
+- Simple definition examples, see [Define `image` and `services` from .gitlab-ci.yml](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml).
+- Detailed usage information, refer to [Docker integration](../docker/README.md) documentation.
+
+#### `image:name`
+
+An [extended docker configuration option](../docker/using_docker_images.md#extended-docker-configuration-options).
+
+For more information, see [Available settings for `image`](../docker/using_docker_images.md#available-settings-for-image).
+
+#### `image:entrypoint`
+
+An [extended docker configuration option](../docker/using_docker_images.md#extended-docker-configuration-options).
+
+For more information, see [Available settings for `image`](../docker/using_docker_images.md#available-settings-for-image).
+
+### `services`
+
+Used to specify a [service Docker image](../docker/using_docker_images.md#what-is-a-service), linked to a base image specified in [`image`](#image).
+
+For:
+
+- Simple definition examples, see [Define `image` and `services` from .gitlab-ci.yml](../docker/using_docker_images.md#define-image-and-services-from-gitlab-ciyml).
+- Detailed usage information, refer to [Docker integration](../docker/README.md) documentation.
+- For example services, see [GitLab CI Services](../services/README.md).
+
+#### `services:name`
+
+An [extended docker configuration option](../docker/using_docker_images.md#extended-docker-configuration-options).
+
+For more information, see see [Available settings for `services`](../docker/using_docker_images.md#available-settings-for-services).
+
+#### `services:alias`
+
+An [extended docker configuration option](../docker/using_docker_images.md#extended-docker-configuration-options).
+
+For more information, see see [Available settings for `services`](../docker/using_docker_images.md#available-settings-for-services).
+
+#### `services:entrypoint`
+
+An [extended docker configuration option](../docker/using_docker_images.md#extended-docker-configuration-options).
+
+For more information, see see [Available settings for `services`](../docker/using_docker_images.md#available-settings-for-services).
+
+#### `services:command`
+
+An [extended docker configuration option](../docker/using_docker_images.md#extended-docker-configuration-options).
+
+For more information, see see [Available settings for `services`](../docker/using_docker_images.md#available-settings-for-services).
+
+### `before_script` and `after_script`
+
+> Introduced in GitLab 8.7 and requires GitLab Runner v1.2.
`before_script` is used to define the command that should be run before all
jobs, including deploy jobs, but after the restoration of [artifacts](#artifacts).
@@ -116,7 +232,7 @@ job:
- execute this after my script
```
-## `stages`
+### `stages`
`stages` is used to define stages that can be used by jobs and is defined
globally.
@@ -150,11 +266,11 @@ There are also two edge cases worth mentioning:
`test` and `deploy` are allowed to be used as job's stage by default.
1. If a job doesn't specify a `stage`, the job is assigned the `test` stage.
-## `stage`
+### `stage`
`stage` is defined per-job and relies on [`stages`](#stages) which is defined
globally. It allows to group jobs into different stages, and jobs of the same
-`stage` are executed in `parallel`. For example:
+`stage` are executed in parallel (subject to [certain conditions](#using-your-own-runners)). For example:
```yaml
stages:
@@ -179,38 +295,18 @@ job 4:
script: make deploy
```
-## `types`
-
-CAUTION: **Deprecated:**
-`types` is deprecated, and could be removed in one of the future releases.
-Use [stages](#stages) instead.
-
-## `script`
+#### Using your own Runners
-`script` is the only required keyword that a job needs. It's a shell script
-which is executed by the Runner. For example:
-
-```yaml
-job:
- script: "bundle exec rspec"
-```
+When using your own Runners, GitLab Runner runs only one job at a time by default (see the
+`concurrent` flag in [Runner global settings](https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-global-section)
+for more information).
-This parameter can also contain several commands using an array:
-
-```yaml
-job:
- script:
- - uname -a
- - bundle exec rspec
-```
+Jobs will run on your own Runners in parallel only if:
-Sometimes, `script` commands will need to be wrapped in single or double quotes.
-For example, commands that contain a colon (`:`) need to be wrapped in quotes so
-that the YAML parser knows to interpret the whole thing as a string rather than
-a "key: value" pair. Be careful when using special characters:
-`:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` ``.
+- Run on different Runners.
+- The Runner's `concurrent` setting has been changed.
-## `only` and `except` (simplified)
+### `only`/`except` (basic)
`only` and `except` are two parameters that set a job policy to limit when
jobs are created:
@@ -276,10 +372,11 @@ job:
- branches@gitlab-org/gitlab-ce
except:
- master@gitlab-org/gitlab-ce
+ - release/.*@gitlab-org/gitlab-ce
```
The above example will run `job` for all branches on `gitlab-org/gitlab-ce`,
-except master.
+except `master` and those with names prefixed with `release/`.
If a job does not have an `only` rule, `only: ['branches', 'tags']` is set by
default. If it doesn't have an `except` rule, it is empty.
@@ -299,7 +396,7 @@ job:
only: ['branches', 'tags']
```
-## `only` and `except` (complex)
+### `only`/`except` (advanced)
> - `refs` and `kubernetes` policies introduced in GitLab 10.0.
> - `variables` policy introduced in GitLab 10.7.
@@ -323,10 +420,10 @@ If you use multiple keys under `only` or `except`, they act as an AND. The logic
> (any of refs) AND (any of variables) AND (any of changes) AND (if kubernetes is active)
-### `only:refs` and `except:refs`
+#### `only:refs`/`except:refs`
The `refs` strategy can take the same values as the
-[simplified only/except configuration](#only-and-except-simplified).
+[simplified only/except configuration](#onlyexcept-basic).
In the example below, the `deploy` job is going to be created only when the
pipeline has been [scheduled][schedules] or runs for the `master` branch:
@@ -339,7 +436,7 @@ deploy:
- schedules
```
-### `only:kubernetes` and `except:kubernetes`
+#### `only:kubernetes`/`except:kubernetes`
The `kubernetes` strategy accepts only the `active` keyword.
@@ -352,7 +449,7 @@ deploy:
kubernetes: active
```
-### `only:variables` and `except:variables`
+#### `only:variables`/`except:variables`
The `variables` keyword is used to define variables expressions. In other words,
you can use predefined variables / project / group or
@@ -384,7 +481,7 @@ end-to-end:
Learn more about [variables expressions](../variables/README.md#variables-expressions).
-### `only:changes` and `except:changes`
+#### `only:changes`/`except:changes`
Using the `changes` keyword with `only` or `except`, makes it possible to define if
a job should be created based on files modified by a git push event.
@@ -415,7 +512,7 @@ CAUTION: **Warning:**
There are some caveats when using this feature with new branches and tags. See
the section below.
-#### Using `changes` with new branches and tags
+##### Using `changes` with new branches and tags
If you are pushing a **new** branch or a **new** tag to GitLab, the policy
always evaluates to true and GitLab will create a job. This feature is not
@@ -423,7 +520,7 @@ connected with merge requests yet, and because GitLab is creating pipelines
before an user can create a merge request we don't know a target branch at
this point.
-#### Using `changes` with `merge_requests`
+##### Using `changes` with `merge_requests`
With [pipelines for merge requests](../merge_request_pipelines/index.md),
make it possible to define if a job should be created base on files modified
@@ -446,7 +543,7 @@ In the scenario above, if you create or update a merge request that changes
either files in `service-one` folder or `Dockerfile`, GitLab creates and triggers
the `docker build service one` job.
-## `tags`
+### `tags`
`tags` is used to select specific Runners from the list of all Runners that are
allowed to run this project.
@@ -489,7 +586,7 @@ osx job:
- echo "Hello, $USER!"
```
-## `allow_failure`
+### `allow_failure`
`allow_failure` allows a job to fail without impacting the rest of the CI
suite.
@@ -525,7 +622,7 @@ job3:
- deploy_to_staging
```
-## `when`
+### `when`
`when` is used to implement jobs that are run in case of failure or despite the
failure.
@@ -587,10 +684,8 @@ The above script will:
success or failure.
1. Allow you to manually execute `deploy_job` from GitLab's UI.
-### `when:manual`
+#### `when:manual`
-> **Notes:**
->
> - Introduced in GitLab 8.10.
> - Blocking manual actions were introduced in GitLab 9.0.
> - Protected actions were introduced in GitLab 9.2.
@@ -618,11 +713,11 @@ action fails, the pipeline will eventually succeed.
Manual actions are considered to be write actions, so permissions for
[protected branches](../../user/project/protected_branches.md) are used when
-user wants to trigger an action. In other words, in order to trigger a manual
-action assigned to a branch that the pipeline is running for, user needs to
-have ability to merge to this branch.
+a user wants to trigger an action. In other words, in order to trigger a manual
+action assigned to a branch that the pipeline is running for, the user needs to
+have the ability to merge to this branch.
-### `when:delayed`
+#### `when:delayed`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21767) in GitLab 11.4.
@@ -658,10 +753,8 @@ This job will never be executed in the future unless you execute the job manuall
You can start a delayed job immediately by clicking the **Play** button.
GitLab runner will pick your job soon and start the job.
-## `environment`
+### `environment`
-> **Notes:**
->
> - Introduced in GitLab 8.9.
> - You can read more about environments and find more examples in the
> [documentation about environments][environment].
@@ -683,10 +776,8 @@ deploy to production:
In the above example, the `deploy to production` job will be marked as doing a
deployment to the `production` environment.
-### `environment:name`
+#### `environment:name`
-> **Notes:**
->
> - Introduced in GitLab 8.11.
> - Before GitLab 8.11, the name of an environment could be defined as a string like
> `environment: production`. The recommended way now is to define it under the
@@ -722,10 +813,8 @@ deploy to production:
name: production
```
-### `environment:url`
+#### `environment:url`
-> **Notes:**
->
> - Introduced in GitLab 8.11.
> - Before GitLab 8.11, the URL could be added only in GitLab's UI. The
> recommended way now is to define it in `.gitlab-ci.yml`.
@@ -749,10 +838,8 @@ deploy to production:
url: https://prod.example.com
```
-### `environment:on_stop`
+#### `environment:on_stop`
-> **Notes:**
->
> - [Introduced][ce-6669] in GitLab 8.13.
> - Starting with GitLab 8.14, when you have an environment that has a stop action
> defined, GitLab will automatically trigger a stop action when the associated
@@ -764,7 +851,7 @@ the environment.
Read the `environment:action` section for an example.
-### `environment:action`
+#### `environment:action`
> [Introduced][ce-6669] in GitLab 8.13.
@@ -805,10 +892,8 @@ The `stop_review_app` job is **required** to have the following keywords defined
- `stage` should be the same as the `review_app` in order for the environment
to stop automatically when the branch is deleted
-### Dynamic environments
+#### Dynamic environments
-> **Notes:**
->
> - [Introduced][ce-6323] in GitLab 8.12 and GitLab Runner 1.6.
> - The `$CI_ENVIRONMENT_SLUG` was [introduced][ce-7983] in GitLab 8.15.
> - The `name` and `url` parameters can use any of the defined CI variables,
@@ -841,10 +926,8 @@ The common use case is to create dynamic environments for branches and use them
as Review Apps. You can see a simple example using Review Apps at
<https://gitlab.com/gitlab-examples/review-apps-nginx/>.
-## `cache`
+### `cache`
-> **Notes:**
->
> - Introduced in GitLab Runner v0.7.0.
> - `cache` can be set globally and per-job.
> - From GitLab 9.0, caching is enabled and shared between pipelines and jobs
@@ -862,7 +945,7 @@ workspace.
If `cache` is defined outside the scope of jobs, it means it is set
globally and all jobs will use that definition.
-### `cache:paths`
+#### `cache:paths`
Use the `paths` directive to choose which files or directories will be cached.
Wildcards can be used as well.
@@ -898,7 +981,7 @@ Note that since cache is shared between jobs, if you're using different
paths for different jobs, you should also set a different **cache:key**
otherwise cache content can be overwritten.
-### `cache:key`
+#### `cache:key`
> Introduced in GitLab Runner v1.0.0.
@@ -939,7 +1022,7 @@ cache:
- binaries/
```
-### `cache:untracked`
+#### `cache:untracked`
Set `untracked: true` to cache all files that are untracked in your Git
repository:
@@ -962,7 +1045,7 @@ rspec:
- binaries/
```
-### `cache:policy`
+#### `cache:policy`
> Introduced in GitLab 9.4.
@@ -1009,10 +1092,8 @@ Additionally, if you have a job that unconditionally recreates the cache without
reference to its previous contents, you can use `policy: push` in that job to
skip the download step.
-## `artifacts`
+### `artifacts`
-> **Notes:**
->
> - Introduced in GitLab Runner v0.7.0 for non-Windows platforms.
> - Windows support was added in GitLab Runner v.1.0.0.
> - From GitLab 9.2, caches are restored before artifacts.
@@ -1025,9 +1106,9 @@ attached to the job after success.
The artifacts will be sent to GitLab after the job finishes successfully and will
be available for download in the GitLab UI.
-[Read more about artifacts.](../../user/project/pipelines/job_artifacts.md)
+[Read more about artifacts](../../user/project/pipelines/job_artifacts.md).
-### `artifacts:paths`
+#### `artifacts:paths`
You can only use paths that are within the project workspace. To pass artifacts
between different jobs, see [dependencies](#dependencies).
@@ -1072,7 +1153,7 @@ release-job:
- tags
```
-### `artifacts:name`
+#### `artifacts:name`
> Introduced in GitLab 8.6 and GitLab Runner v1.1.0.
@@ -1153,14 +1234,13 @@ job:
- binaries/
```
-### `artifacts:untracked`
+#### `artifacts:untracked`
`artifacts:untracked` is used to add all Git untracked files as artifacts (along
to the paths defined in `artifacts:paths`).
NOTE: **Note:**
-To exclude the folders/files which should not be a part of `untracked` just
-add them to `.gitignore`.
+`artifacts:untracked` ignores configuration in the repository's `.gitignore` file.
Send all Git untracked files:
@@ -1178,7 +1258,7 @@ artifacts:
- binaries/
```
-### `artifacts:when`
+#### `artifacts:when`
> Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
@@ -1199,14 +1279,14 @@ job:
when: on_failure
```
-### `artifacts:expire_in`
+#### `artifacts:expire_in`
> Introduced in GitLab 8.9 and GitLab Runner v1.3.0.
`expire_in` allows you to specify how long artifacts should live before they
expire and therefore deleted, counting from the time they are uploaded and
stored on GitLab. If the expiry time is not defined, it defaults to the
-[instance wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration)
+[instance wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration-core-only)
(30 days by default, forever on GitLab.com).
You can use the **Keep** button on the job page to override expiration and
@@ -1234,7 +1314,7 @@ job:
expire_in: 1 week
```
-### `artifacts:reports`
+#### `artifacts:reports`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20390) in
GitLab 11.2. Requires GitLab Runner 11.2 and above.
@@ -1245,14 +1325,14 @@ this with [JUnit reports](#artifactsreportsjunit).
NOTE: **Note:**
The test reports are collected regardless of the job results (success or failure).
-You can use [`artifacts:expire_in`](#artifacts-expire_in) to set up an expiration
+You can use [`artifacts:expire_in`](#artifactsexpire_in) to set up an expiration
date for their artifacts.
NOTE: **Note:**
If you also want the ability to browse the report output files, include the
[`artifacts:paths`](#artifactspaths) keyword.
-#### `artifacts:reports:junit`
+##### `artifacts:reports:junit`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/20390) in
GitLab 11.2. Requires GitLab Runner 11.2 and above.
@@ -1286,7 +1366,7 @@ concatenated into a single file. Use a filename pattern (`junit: rspec-*.xml`),
an array of filenames (`junit: [rspec-1.xml, rspec-2.xml, rspec-3.xml]`), or a
combination thereof (`junit: [rspec.xml, test-results/TEST-*.xml]`).
-#### `artifacts:reports:codequality` **[STARTER]**
+##### `artifacts:reports:codequality` **[STARTER]**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1296,7 +1376,7 @@ as artifacts.
The collected Code Quality report will be uploaded to GitLab as an artifact and will
be automatically shown in merge requests.
-#### `artifacts:reports:sast` **[ULTIMATE]**
+##### `artifacts:reports:sast` **[ULTIMATE]**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1307,7 +1387,7 @@ The collected SAST report will be uploaded to GitLab as an artifact and will
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-#### `artifacts:reports:dependency_scanning` **[ULTIMATE]**
+##### `artifacts:reports:dependency_scanning` **[ULTIMATE]**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1318,7 +1398,7 @@ The collected Dependency Scanning report will be uploaded to GitLab as an artifa
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-#### `artifacts:reports:container_scanning` **[ULTIMATE]**
+##### `artifacts:reports:container_scanning` **[ULTIMATE]**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1329,7 +1409,7 @@ The collected Container Scanning report will be uploaded to GitLab as an artifac
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-#### `artifacts:reports:dast` **[ULTIMATE]**
+##### `artifacts:reports:dast` **[ULTIMATE]**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1340,7 +1420,7 @@ The collected DAST report will be uploaded to GitLab as an artifact and will
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-#### `artifacts:reports:license_management` **[ULTIMATE]**
+##### `artifacts:reports:license_management` **[ULTIMATE]**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1351,7 +1431,7 @@ The collected License Management report will be uploaded to GitLab as an artifac
be automatically shown in merge requests, pipeline view and provide data for security
dashboards.
-#### `artifacts:reports:performance` **[PREMIUM]**
+##### `artifacts:reports:performance` **[PREMIUM]**
> Introduced in GitLab 11.5. Requires GitLab Runner 11.5 and above.
@@ -1361,7 +1441,7 @@ as artifacts.
The collected Performance report will be uploaded to GitLab as an artifact and will
be automatically shown in merge requests.
-## `dependencies`
+### `dependencies`
> Introduced in GitLab 8.6 and GitLab Runner v1.1.1.
@@ -1420,12 +1500,12 @@ deploy:
script: make deploy
```
-### When a dependent job will fail
+#### When a dependent job will fail
> Introduced in GitLab 10.3.
If the artifacts of the job that is set as a dependency have been
-[expired](#artifacts-expire_in) or
+[expired](#artifactsexpire_in) or
[erased](../../user/project/pipelines/job_artifacts.md#erasing-artifacts), then
the dependent job will fail.
@@ -1434,7 +1514,7 @@ You can ask your administrator to
[flip this switch](../../administration/job_artifacts.md#validation-for-dependencies)
and bring back the old behavior.
-## `coverage`
+### `coverage`
> [Introduced][ce-7447] in GitLab 8.17.
@@ -1454,7 +1534,7 @@ job1:
coverage: '/Code coverage: \d+\.\d+/'
```
-## `retry`
+### `retry`
> [Introduced][ce-12909] in GitLab 9.5.
> [Behaviour expanded](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/21758)
@@ -1528,7 +1608,7 @@ Possible values for `when` are:
- `missing_dependency_failure`: Retry if a dependency was missing.
- `runner_unsupported`: Retry if the runner was unsupported.
-## `parallel`
+### `parallel`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/22631) in GitLab 11.5.
@@ -1548,7 +1628,49 @@ test:
parallel: 5
```
-## `include`
+### `trigger` **[PREMIUM]**
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ee/issues/8997) in [GitLab Premium](https://about.gitlab.com/pricing/) 11.8.
+
+`trigger` allows you to define downstream pipeline trigger. When a job created
+from `trigger` definition is started by GitLab, a downstream pipeline gets
+created.
+
+Learn more about [multi-project pipelines](https://docs.gitlab.com/ee/ci/multi_project_pipelines.html#creating-cross-project-pipelines-from-gitlab-ci-yml).
+
+#### Simple `trigger` syntax
+
+The most simple way to configure a downstream trigger to use `trigger` keyword
+with a full path to a downstream project:
+
+```yaml
+rspec:
+ stage: test
+ script: bundle exec rspec
+
+staging:
+ stage: deploy
+ trigger: my/deployment
+```
+
+#### Complex `trigger` syntax
+
+It is possible to configure a branch name that GitLab will use to create
+a downstream pipeline with:
+
+```yaml
+rspec:
+ stage: test
+ script: bundle exec rspec
+
+staging:
+ stage: deploy
+ trigger:
+ project: my/deployment
+ branch: stable
+```
+
+### `include`
> - Introduced in [GitLab Premium](https://about.gitlab.com/pricing/) 10.5.
> - Available for Starter, Premium and Ultimate since 10.6.
@@ -1585,7 +1707,12 @@ of using YAML anchors, you can use the [`extends` keyword](#extends).
See [usage examples](#include-examples).
-### `include:local`
+NOTE: **Note:**
+`.gitlab-ci.yml` configuration included by all methods is evaluated at pipeline creation.
+The configuration is a snapshot in time and persisted in the database. Any changes to
+referenced `.gitlab-ci.yml` configuration will not be reflected in GitLab until the next pipeline is created.
+
+#### `include:local`
`include:local` includes a file from the same repository as `.gitlab-ci.yml`.
It's referenced using full paths relative to the root directory (`/`).
@@ -1594,6 +1721,9 @@ You can only use files that are currently tracked by Git on the same branch
your configuration file is on. In other words, when using a `include:local`, make
sure that both `.gitlab-ci.yml` and the local file are on the same branch.
+All [nested includes](#nested-includes) will be executed in the scope of the same project,
+so it is possible to use local, project, remote or template includes.
+
NOTE: **Note:**
Including local files through Git submodules paths is not supported.
@@ -1604,7 +1734,7 @@ include:
- local: '/templates/.gitlab-ci-template.yml'
```
-### `include:file`
+#### `include:file`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/53903) in GitLab 11.7.
@@ -1635,7 +1765,11 @@ include:
file: '/templates/.gitlab-ci-template.yml'
```
-### `include:template`
+All [nested includes](#nested-includes) will be executed in the scope of the target project,
+so it is possible to use local (relative to target project), project, remote
+or template includes.
+
+#### `include:template`
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/53445) in GitLab 11.7.
@@ -1650,7 +1784,10 @@ include:
- template: Auto-DevOps.gitlab-ci.yml
```
-### `include:remote`
+All [nested includes](#nested-includes) will be executed only with the permission of the user,
+so it is possible to use project, remote or template includes.
+
+#### `include:remote`
`include:remote` can be used to include a file from a different location,
using HTTP/HTTPS, referenced by using the full URL. The remote file must be
@@ -1662,16 +1799,22 @@ include:
- remote: 'https://gitlab.com/awesome-project/raw/master/.gitlab-ci-template.yml'
```
-NOTE: **Note for GitLab admins:**
-In order to include files from another repository inside your local network,
-you may need to enable the **Allow requests to the local network from hooks and services** checkbox
-located in the **Admin area > Settings > Network > Outbound requests** section.
+All nested includes will be executed without context as public user, so only another remote,
+or public project, or template is allowed.
-### `include` examples
+#### Nested includes
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/56836) in GitLab 11.9.
+
+Nested includes allow you to compose a set of includes.
+A total of 50 includes is allowed.
+Duplicate includes are considered a configuration error.
+
+#### `include` examples
Here are a few more `include` examples.
-#### Single string or array of multiple values
+##### Single string or array of multiple values
You can include your extra YAML file(s) either as a single string or
an array of multiple values. The following examples are all valid.
@@ -1725,7 +1868,7 @@ include:
file: '/templates/.gitlab-ci-template.yml'
```
-#### Re-using a `before_script` template
+##### Re-using a `before_script` template
In the following example, the content of `.before-script-template.yml` will be
automatically fetched and evaluated along with the content of `.gitlab-ci.yml`.
@@ -1749,7 +1892,7 @@ rspec:
- bundle exec rspec
```
-#### Overriding external template values
+##### Overriding external template values
The following example shows specific YAML-defined variables and details of the
`production` job from an include file being customized in `.gitlab-ci.yml`.
@@ -1834,7 +1977,52 @@ In this case, if `install_dependencies` and `deploy` were not repeated in
`.gitlab-ci.yml`, they would not be part of the script for the `production`
job in the combined CI configuration.
-## `extends`
+##### Using nested includes
+
+The examples below show how includes can be nested from different sources
+using a combination of different methods.
+
+In this example, `.gitlab-ci.yml` includes local the file `/.gitlab-ci/another-config.yml`:
+
+```yaml
+include:
+ - local: /.gitlab-ci/another-config.yml
+```
+
+The `/.gitlab-ci/another-config.yml` includes a template and the `/templates/docker-workflow.yml` file
+from another project:
+
+```yaml
+include:
+ - template: Bash.gitlab-ci.yml
+ - project: /group/my-project
+ file: /templates/docker-workflow.yml
+```
+
+The `/templates/docker-workflow.yml` present in `/group/my-project` includes two local files
+of the `/group/my-project`:
+
+```yaml
+include:
+ - local: : /templates/docker-build.yml
+ - local: : /templates/docker-testing.yml
+```
+
+Our `/templates/docker-build.yml` present in `/group/my-project` adds a `docker-build` job:
+
+```yaml
+docker-build:
+ script: docker build -t my-image .
+```
+
+Our second `/templates/docker-test.yml` present in `/group/my-project` adds a `docker-test` job:
+
+```yaml
+docker-test:
+ script: docker run my-image /run/tests.sh
+```
+
+### `extends`
> Introduced in GitLab 11.3.
@@ -1915,7 +2103,7 @@ spinach:
script: rake spinach
```
-## Using `extends` and `include` together
+### Using `extends` and `include` together
`extends` works across configuration files combined with `include`.
@@ -1940,7 +2128,7 @@ useTemplate:
This will run a job called `useTemplate` that runs `echo Hello!` as defined in
the `.template` job, and uses the `alpine` Docker image as defined in the local job.
-## `pages`
+### `pages`
`pages` is a special job that is used to upload static content to GitLab that
can be used to serve your website. It has a special syntax, so the two
@@ -1969,7 +2157,7 @@ pages:
Read more on [GitLab Pages user documentation](../../user/project/pages/index.md).
-## `variables`
+### `variables`
> Introduced in GitLab Runner v0.5.0.
@@ -2002,9 +2190,9 @@ you can set in `.gitlab-ci.yml`, there are also the so called
[Variables](../variables/README.md#variables)
which can be set in GitLab's UI.
-[Learn more about variables and their priority.][variables]
+Learn more about [variables and their priority][variables].
-### Git strategy
+#### Git strategy
> Introduced in GitLab 8.9 as an experimental feature. May change or be removed
> completely in future releases. `GIT_STRATEGY=none` requires GitLab Runner
@@ -2049,7 +2237,7 @@ NOTE: **Note:** `GIT_STRATEGY` is not supported for
but may be in the future. See the [support Git strategy with Kubernetes executor feature proposal](https://gitlab.com/gitlab-org/gitlab-runner/issues/3847)
for updates.
-### Git submodule strategy
+#### Git submodule strategy
> Requires GitLab Runner v1.10+.
@@ -2087,9 +2275,9 @@ Note that for this feature to work correctly, the submodules must be configured
- a relative path to another repository on the same GitLab server. See the
[Git submodules](../git_submodules.md) documentation.
-### Git checkout
+#### Git checkout
-> Introduced in GitLab Runner 9.3
+> Introduced in GitLab Runner 9.3.
The `GIT_CHECKOUT` variable can be used when the `GIT_STRATEGY` is set to either
`clone` or `fetch` to specify whether a `git checkout` should be run. If not
@@ -2116,7 +2304,7 @@ script:
- git merge $CI_BUILD_REF_NAME
```
-### Job stages attempts
+#### Job stages attempts
> Introduced in GitLab, it requires GitLab Runner v1.9+.
@@ -2140,7 +2328,7 @@ variables:
You can set them globally or per-job in the [`variables`](#variables) section.
-### Shallow cloning
+#### Shallow cloning
> Introduced in GitLab 8.9 as an experimental feature. May change in future
releases or be removed completely.
@@ -2150,7 +2338,7 @@ shallow cloning of the repository which can significantly speed up cloning for
repositories with a large number of commits or old, large binaries. The value is
passed to `git fetch` and `git clone`.
->**Note:**
+NOTE: **Note:**
If you use a depth of 1 and have a queue of jobs or retry
jobs, jobs may fail.
@@ -2173,6 +2361,22 @@ variables:
You can set it globally or per-job in the [`variables`](#variables) section.
+## Deprecated parameters
+
+The following parameters are deprecated.
+
+### `types`
+
+CAUTION: **Deprecated:**
+`types` is deprecated, and could be removed in a future release.
+Use [`stages`](#stages) instead.
+
+### `type`
+
+CAUTION: **Deprecated:**
+`type` is deprecated, and could be removed in one of the future releases.
+Use [`stage`](#stage) instead.
+
## Special YAML features
It's possible to use special YAML features like anchors (`&`), aliases (`*`)
@@ -2332,7 +2536,9 @@ You can see that the hidden keys are conveniently used as templates.
## Triggers
Triggers can be used to force a rebuild of a specific branch, tag or commit,
-with an API call.
+with an API call when a pipeline gets created using a trigger token.
+
+Not to be confused with [`trigger`](#trigger-premium).
[Read more in the triggers documentation.](../triggers/README.md)
@@ -2348,22 +2554,6 @@ using Git 2.10 or newer:
git push -o ci.skip
```
-## Validate the .gitlab-ci.yml
-
-Each instance of GitLab CI has an embedded debug tool called Lint, which validates the
-content of your `.gitlab-ci.yml` files. You can find the Lint under the page `ci/lint` of your
-project namespace (e.g, `http://gitlab-example.com/gitlab-org/project-123/-/ci/lint`)
-
-## Using reserved keywords
-
-If you get validation error when using specific values (e.g., `true` or `false`),
-try to quote them, or change them to a different form (e.g., `/bin/true`).
-
-## Examples
-
-See a [list of examples](../examples/README.md "CI/CD examples") for using
-GitLab CI/CD with various languages.
-
[ce-6323]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6323
[ce-6669]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/6669
[ce-7983]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7983
@@ -2372,4 +2562,4 @@ GitLab CI/CD with various languages.
[environment]: ../environments.md "CI/CD environments"
[schedules]: ../../user/project/pipelines/schedules.md "Pipelines schedules"
[variables]: ../variables/README.md "CI/CD variables"
-[push-option]: https://git-scm.com/docs/git-push#git-push--oltoptiongt
+[push-option]: https://git-scm.com/docs/git-push#Documentation/git-push.txt--oltoptiongt
diff --git a/doc/container_registry/README.md b/doc/container_registry/README.md
index 5d2f5edcb18..b31870df36d 100644
--- a/doc/container_registry/README.md
+++ b/doc/container_registry/README.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/container_registry.md'
+---
+
This document was moved to [another location](../user/project/container_registry.md).
diff --git a/doc/container_registry/troubleshooting.md b/doc/container_registry/troubleshooting.md
index 2f8cd37b488..c99d7011ac2 100644
--- a/doc/container_registry/troubleshooting.md
+++ b/doc/container_registry/troubleshooting.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/container_registry.md'
+---
+
This document was moved to [user/project/container_registry](../user/project/container_registry.md).
diff --git a/doc/customization/system_header_and_footer_messages.md b/doc/customization/system_header_and_footer_messages.md
new file mode 100644
index 00000000000..9d6931c730d
--- /dev/null
+++ b/doc/customization/system_header_and_footer_messages.md
@@ -0,0 +1,22 @@
+# Adding a system message to every page
+
+> [Introduced][ee-1283] in [GitLab Premium][eep] 10.7.
+
+Navigate to the **Admin** area and go to the **Appearance** page.
+
+Under **System header and footer** insert your header message and/or footer message.
+Both background and font color of the header and footer are customizable.
+
+You can also apply the header and footer messages to gitlab emails,
+by checking the **Enable header and footer in emails** checkbox.
+Note that color settings will only be applied within the app interface and not to emails
+
+![appearance](system_header_and_footer_messages/appearance.png)
+
+After saving, all GitLab pages will contain the custom system header and/or footer messages:
+
+![custom_header_footer](system_header_and_footer_messages/custom_header_footer.png)
+
+The GitLab sign in page will also show the header and the footer messages:
+
+![sign_up_custom_header_and_footer](system_header_and_footer_messages/sign_up_custom_header_and_footer.png)
diff --git a/doc/customization/system_header_and_footer_messages/appearance.png b/doc/customization/system_header_and_footer_messages/appearance.png
new file mode 100644
index 00000000000..fd315bb6c07
--- /dev/null
+++ b/doc/customization/system_header_and_footer_messages/appearance.png
Binary files differ
diff --git a/doc/customization/system_header_and_footer_messages/custom_header_footer.png b/doc/customization/system_header_and_footer_messages/custom_header_footer.png
new file mode 100644
index 00000000000..691ca8a3484
--- /dev/null
+++ b/doc/customization/system_header_and_footer_messages/custom_header_footer.png
Binary files differ
diff --git a/doc/customization/system_header_and_footer_messages/sign_up_custom_header_and_footer.png b/doc/customization/system_header_and_footer_messages/sign_up_custom_header_and_footer.png
new file mode 100644
index 00000000000..a9c59bc9f62
--- /dev/null
+++ b/doc/customization/system_header_and_footer_messages/sign_up_custom_header_and_footer.png
Binary files differ
diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md
index 9194f847cdf..45f1fed355e 100644
--- a/doc/customization/welcome_message.md
+++ b/doc/customization/welcome_message.md
@@ -1 +1,5 @@
+---
+redirect_to: 'branded_login_page.md'
+---
+
This document was moved to [another location](branded_login_page.md).
diff --git a/doc/development/README.md b/doc/development/README.md
index 13646cbfe48..5a33c46c620 100644
--- a/doc/development/README.md
+++ b/doc/development/README.md
@@ -54,6 +54,7 @@ description: 'Learn how to contribute to GitLab.'
- [Prometheus metrics](prometheus_metrics.md)
- [Guidelines for reusing abstractions](reusing_abstractions.md)
- [DeclarativePolicy framework](policies.md)
+- [How Git object deduplication works in GitLab](git_object_deduplication.md)
## Performance guides
diff --git a/doc/development/api_graphql_styleguide.md b/doc/development/api_graphql_styleguide.md
index 95722c027ba..501092ff2aa 100644
--- a/doc/development/api_graphql_styleguide.md
+++ b/doc/development/api_graphql_styleguide.md
@@ -12,24 +12,34 @@ add a `HTTP_PRIVATE_TOKEN` header.
### Authorization
Fields can be authorized using the same abilities used in the Rails
-app. This can be done using the `authorize` helper:
+app. This can be done by supplying the `authorize` option:
```ruby
module Types
class QueryType < BaseObject
graphql_name 'Query'
- field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver do
- authorize :read_project
- end
+ field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver, authorize: :read_project
end
+end
+```
+
+Fields can be authorized against multiple abilities, in which case all
+ability checks must pass. This requires explicitly passing a block to `field`:
+
+```ruby
+field :project, Types::ProjectType, null: true, resolver: Resolvers::ProjectResolver do
+ authorize [:read_project, :another_ability]
+end
```
The object found by the resolve call is used for authorization.
-This works for authorizing a single record, for authorizing
-collections, we should only load what the currently authenticated user
-is allowed to view. Preferably we use our existing finders for that.
+TIP: **Tip:**
+When authorizing collections, try to load only what the currently
+authenticated user is allowed to view with our existing finders first.
+This minimizes database queries and unnecessary authorization checks of
+the loaded records.
## Types
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index 63574b28edc..115c8cfb9ff 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -58,21 +58,21 @@ GitLab can be considered to have two layers from a process perspective:
- [Omnibus configuration options](https://gitlab.com/gitlab-org/gitaly/tree/master/doc/configuration)
- Layer: Core Service (Data)
-Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab. (Think GitLab.com or High Availablity Deployments) As of 11.3.0, This service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly).
+Gitaly is a service designed by GitLab to remove our need for NFS for Git storage in distributed deployments of GitLab (Think GitLab.com or High Availability Deployments). As of 11.3.0, this service handles all Git level access in GitLab. You can read more about the project [in the project's readme](https://gitlab.com/gitlab-org/gitaly).
### gitlab-monitor
- Omnibus configuration options
- Layer: Monitoring
-GitLab Monitor is a process disigned in house that allows us to export metrics about GitLab application internals to prometheus. You can read more [in the project's readme](https://gitlab.com/gitlab-org/gitlab-monitor)
+GitLab Monitor is a process designed in house that allows us to export metrics about GitLab application internals to prometheus. You can read more [in the project's readme](https://gitlab.com/gitlab-org/gitlab-monitor)
### gitlab-workhorse
- Omnibus configuration options
- Layer: Core Service (Processor)
-[GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) is a program designed at GitLab to help alieviate pressure from unicorn. You can read more about the [historical reasons for developing](https://about.gitlab.com/2016/04/12/a-brief-history-of-gitlab-workhorse/). It's designed to act as a smart reverse proxy to help speed up GitLab as a whole.
+[GitLab Workhorse](https://gitlab.com/gitlab-org/gitlab-workhorse) is a program designed at GitLab to help alleviate pressure from unicorn. You can read more about the [historical reasons for developing](https://about.gitlab.com/2016/04/12/a-brief-history-of-gitlab-workhorse/). It's designed to act as a smart reverse proxy to help speed up GitLab as a whole.
### logrotate
@@ -158,6 +158,18 @@ TODO
TODO
+### Registry
+
+The registry is what users use to store their own Docker images. The bundled
+registry uses nginx as a load balancer and GitLab as an authentication manager.
+Whenever a client requests to pull or push an image from the registry, it will
+return a `401` response along with a header detailing where to get an
+authentication token, in this case the GitLab instance. The client will then
+request a pull or push auth token from GitLab and retry the original request
+to the registry. Learn more about [token authentication](https://docs.docker.com/registry/spec/auth/token/).
+
+An external registry can also be configured to use GitLab as an auth endpoint.
+
## GitLab by Request Type
GitLab provides two "interfaces" for end users to access the service:
@@ -174,7 +186,7 @@ When making a request to an HTTP Endpoint (Think `/users/sign_in`) the request w
- nginx - Acts as our first line reverse proxy
- gitlab-workhorse - This determines if it needs to go to the Rails application or somewhere else to reduce load on unicorn.
- unicorn - Since this is a web request, and it needs to access the application it will go to Unicorn.
-- Postgres/Gitaly/Redis - Depending on the type of request, it may hit these services to store or retreive data.
+- Postgres/Gitaly/Redis - Depending on the type of request, it may hit these services to store or retrieve data.
### GitLab Git Request Cycle
diff --git a/doc/development/automatic_ce_ee_merge.md b/doc/development/automatic_ce_ee_merge.md
index fed772b9240..3ca841353e6 100644
--- a/doc/development/automatic_ce_ee_merge.md
+++ b/doc/development/automatic_ce_ee_merge.md
@@ -115,16 +115,16 @@ Now, every time you create an MR for CE and EE:
1. Continue cherry-picking: `git cherry-pick --continue`
1. Push to EE: `git push origin branch-example-ee`
1. Create the EE-equivalent MR and link to the CE MR from the
-description "Ports [CE-MR-LINK] to EE"
+ description "Ports [CE-MR-LINK] to EE"
1. Once all the jobs are passing in both CE and EE, you've addressed the
-feedback from your own team, and got them approved, the merge requests can be merged.
+ feedback from your own team, and got them approved, the merge requests can be merged.
1. When both MRs are ready, the EE merge request will be merged first, and the
-CE-equivalent will be merged next.
+ CE-equivalent will be merged next.
**Important notes:**
- The commit SHA can be easily found from the GitLab UI. From a merge request,
-open the tab **Commits** and click the copy icon to copy the commit SHA.
+ open the tab **Commits** and click the copy icon to copy the commit SHA.
- To cherry-pick a **commit range**, such as [A > B > C > D] use:
```shell
@@ -140,7 +140,7 @@ open the tab **Commits** and click the copy icon to copy the commit SHA.
```
- To cherry-pick a **merge commit**, use the flag `-m 1`. For example, suppose that the
-merge commit SHA is `138f5e2f20289bb376caffa0303adb0cac859ce1`:
+ merge commit SHA is `138f5e2f20289bb376caffa0303adb0cac859ce1`:
```shell
git cherry-pick -m 1 138f5e2f20289bb376caffa0303adb0cac859ce1
@@ -163,12 +163,12 @@ merge commit SHA is `138f5e2f20289bb376caffa0303adb0cac859ce1`:
commits and you want to cherry-pick all but the merge commit.
- If you push more commits to the CE branch, you can safely repeat the procedure
-to cherry-pick them to the EE-equivalent branch. You can do that as many times as
-necessary, using the same CE and EE branches.
+ to cherry-pick them to the EE-equivalent branch. You can do that as many times as
+ necessary, using the same CE and EE branches.
- If you submitted the merge request to the CE repo and the `ee-compat-check` job passed,
-you are not required to submit the EE-equivalent MR, but it's still recommended. If the
-job failed, you are required to submit the EE MR so that you can fix the conflicts in EE
-before merging your changes into CE.
+ you are not required to submit the EE-equivalent MR, but it's still recommended. If the
+ job failed, you are required to submit the EE MR so that you can fix the conflicts in EE
+ before merging your changes into CE.
## FAQ
diff --git a/doc/development/changelog.md b/doc/development/changelog.md
index cd0a1f46d27..273a7fceaf5 100644
--- a/doc/development/changelog.md
+++ b/doc/development/changelog.md
@@ -43,7 +43,7 @@ the `author` field. GitLab team members **should not**.
database records created during Cycle Analytics model spec."
- _Any_ contribution from a community member, no matter how small, **may** have
a changelog entry regardless of these guidelines if the contributor wants one.
- Example: "Fixed a typo on the search results page. (Jane Smith)"
+ Example: "Fixed a typo on the search results page."
- Performance improvements **should** have a changelog entry.
- Any change that introduces a database migration **must** have a
changelog entry.
@@ -135,21 +135,13 @@ If you're working on the GitLab EE repository, the entry will be added to
| Argument | Shorthand | Purpose |
| ----------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------- |
-| [`--amend`] | | Amend the previous commit |
-| [`--force`] | `-f` | Overwrite an existing entry |
-| [`--merge-request`] | `-m` | Set merge request ID |
-| [`--dry-run`] | `-n` | Don't actually write anything, just print |
-| [`--git-username`] | `-u` | Use Git user.name configuration as the author |
-| [`--type`] | `-t` | The category of the change, valid options are: `added`, `fixed`, `changed`, `deprecated`, `removed`, `security`, `performance`, `other` |
-| [`--help`] | `-h` | Print help message |
-
-[`--amend`]: #-amend
-[`--force`]: #-force-or-f
-[`--merge-request`]: #-merge-request-or-m
-[`--dry-run`]: #-dry-run-or-n
-[`--git-username`]: #-git-username-or-u
-[`--type`]: #-type-or-t
-[`--help`]: #-help
+| [`--amend`](#--amend) | | Amend the previous commit |
+| [`--force`](#--force-or--f) | `-f` | Overwrite an existing entry |
+| [`--merge-request`](#--merge-request-or--m) | `-m` | Set merge request ID |
+| [`--dry-run`](#--dry-run-or--n) | `-n` | Don't actually write anything, just print |
+| [`--git-username`](#--git-username-or--u) | `-u` | Use Git user.name configuration as the author |
+| [`--type`](#--type-or--t) | `-t` | The category of the change, valid options are: `added`, `fixed`, `changed`, `deprecated`, `removed`, `security`, `performance`, `other` |
+| `--help` | `-h` | Print help message |
##### `--amend`
diff --git a/doc/development/code_review.md b/doc/development/code_review.md
index 1b591c7c322..b1a32e0ed26 100644
--- a/doc/development/code_review.md
+++ b/doc/development/code_review.md
@@ -20,7 +20,7 @@ importance of involving reviewer(s) in the section on the responsibility of the
If you need some guidance (e.g. it's your first merge request), feel free to ask
one of the [Merge request coaches][team].
-If you need assistance with security scans or comments, feel free to include the
+If you need assistance with security scans or comments, feel free to include the
Security Team (`@gitlab-com/gl-security`) in the review.
The `danger-review` CI job will randomly pick a reviewer and a maintainer for
@@ -58,12 +58,7 @@ from teams other than your own.
#### Security requirements
- 1. If your merge request is processing, storing, or transferring any kind of [RED or ORANGE data](https://docs.google.com/document/d/15eNKGA3zyZazsJMldqTBFbYMnVUSQSpU14lo22JMZQY/edit) (this is a confidential document), it must be
- **approved by a [Security Engineer][team]**.
- 1. If your merge request involves implementing, utilizing, or is otherwise related to any type of authentication, authorization, or session handling mechanism, it must be
- **approved by a [Security Engineer][team]**.
- 1. If your merge request has a goal which requires a cryptographic function such as: confidentiality, integrity, authentication, or non-repudiation, it must be
- **approved by a [Security Engineer][team]**.
+View the updated documentation regarding [internal application security reviews](https://about.gitlab.com/handbook/engineering/security/index.html#internal-application-security-reviews) for **when** and **how** to request a security review.
### The responsibility of the merge request author
@@ -138,10 +133,10 @@ as a domain expert and/or reviewer, it is recommended that they are not also pic
as the maintainer to ultimately approve and merge it.
Try to review in a timely manner; doing so allows everyone involved in the merge
-request to iterate faster as the context is fresh in memory. Further, this
+request to iterate faster as the context is fresh in memory. Further, this
improves contributors' experiences significantly. Reviewers should aim to review
-within two working days from the date they were assigned the merge request. If
-you don't think you'll be able to review a merge request within that time, let
+within two working days from the date they were assigned the merge request. If
+you don't think you'll be able to review a merge request within that time, let
the author know as soon as possible. When the author of the merge request has not
heard anything after two days, a new reviewer should be assigned.
@@ -151,7 +146,7 @@ required approvers.
Maintainers must check before merging if the merge request is introducing new
vulnerabilities, by inspecting the list in the Merge Request [Security
Widget](https://docs.gitlab.com/ee/user/project/merge_requests/#security-reports-ultimate).
-When in doubt, a [Security Engineer][team] can be involved. The list of detected
+When in doubt, a [Security Engineer][team] can be involved. The list of detected
vulnerabilities must be either empty or containing:
- dismissed vulnerabilities in case of false positives
@@ -203,7 +198,10 @@ first time.
- Extract unrelated changes and refactorings into future merge requests/issues.
- Seek to understand the reviewer's perspective.
- Try to respond to every comment.
-- Let the reviewer select the "Resolve discussion" buttons.
+- The merge request author resolves only the discussions they have fully
+ addressed. If there's an open reply, an open discussion, a suggestion,
+ a question, or anything else, the discussion should be left to be resolved
+ by the reviewer.
- Push commits based on earlier rounds of feedback as isolated commits to the
branch. Do not squash until the branch is ready to merge. Reviewers should be
able to read individual updates based on their earlier feedback.
diff --git a/doc/development/contributing/index.md b/doc/development/contributing/index.md
index 7ac846e4c4d..bfe1ef75914 100644
--- a/doc/development/contributing/index.md
+++ b/doc/development/contributing/index.md
@@ -4,7 +4,7 @@ Thank you for your interest in contributing to GitLab. This guide details how
to contribute to GitLab in a way that is easy for everyone.
We want to create a welcoming environment for everyone who is interested in contributing.
-Please visit our [Code of Conduct page](https://about.gitlab.com/contributing/code-of-conduct) to learn more about our committment to an open and welcoming environment.
+Please visit our [Code of Conduct page](https://about.gitlab.com/contributing/code-of-conduct) to learn more about our commitment to an open and welcoming environment.
For a first-time step-by-step guide to the contribution process, please see
["Contributing to GitLab"](https://about.gitlab.com/contributing/).
@@ -119,6 +119,7 @@ This [documentation](merge_request_workflow.md) outlines the current merge reque
- [Merge request guidelines](merge_request_workflow.md#merge-request-guidelines)
- [Contribution acceptance criteria](merge_request_workflow.md#contribution-acceptance-criteria)
- [Definition of done](merge_request_workflow.md#definition-of-done)
+- [Dependencies](merge_request_workflow.md#dependencies)
## Style guides
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index c5344139ab4..4c53643ed9c 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -199,7 +199,7 @@ 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")
+ solve in the ~"Next Patch Release")
- Small ~feature
- Small ~"technical debt" issues
@@ -300,17 +300,17 @@ 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
+ 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.
+ 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.
+ 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.
+ issues or chunks. You can simply not set the weight of a parent issue and set
+ weights to children issues.
## Regression issues
diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md
index 4e766f37871..5e310092a6e 100644
--- a/doc/development/contributing/merge_request_workflow.md
+++ b/doc/development/contributing/merge_request_workflow.md
@@ -1,91 +1,95 @@
# 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 `Accepting merge requests` label](issue_workflow.md#label-for-community-contributors),
-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
+We welcome merge requests from everyone, with fixes and improvements
+to GitLab code, tests, and documentation. The issues that are specifically suitable
+for community contributions are listed with the [`Accepting merge requests`](issue_workflow.md#label-for-community-contributors)
+label, but you are free to contribute to any issue you want.
+
+Please note that if an issue is marked for the current milestone at any time, even
+when you are working on it, a GitLab Inc. 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
+If you want to add a new feature that is not labeled, it is best to first create
+an 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.
+wireframes of the proposed feature if it will also change the UI.
-Merge requests should be opened at [GitLab.com][gitlab-mr-tracker].
+Merge requests should be submitted to the appropriate project at GitLab.com, for example
+[GitLab CE](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests),
+[GitLab EE](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests),
+[GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/merge_requests),
+[GitLab Omnibus](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests), etc.
If you are new to GitLab development (or web development in general), see the
-[I want to contribute!](index.md#i-want-to-contribute) section to get you started with
+[I want to contribute!](index.md#i-want-to-contribute) section to get 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.
-
-[gitlab-mr-tracker]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests
-[gdk]: https://gitlab.com/gitlab-org/gitlab-development-kit
+To start developing GitLab, download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit)
+and see the [Development section](../../README.md) for the required 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
+If you find an issue, please submit a merge request with a fix or improvement, if
+you can, and include tests. If you don't know how to fix the issue but can write a test
+that exposes the issue, we will accept that as well. In general, bug fixes that
+include a regression test are merged quickly, while new features without proper
+tests might be slower to receive feedback. The workflow to make a merge
request is as follows:
-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. [Fork](../../workflow/forking_workflow.md#creating-a-fork) the project into
+ your personal namespace (or group) on GitLab.com.
+1. Create a feature branch in your fork (don't work off `master`).
+1. Write [tests](../rake_tasks.md#run-tests) and code.
+1. [Generate a changelog entry with `bin/changelog`](../changelog.md)
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
+ [documentation guidelines](../documentation/index.md).
+1. Follow the [commit messages guidelines](#commit-messages-guidelines).
+1. If you have multiple commits, combine them into a few logically organized
+ commits by [squashing them](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History#_squashing),
+ but do not change the commit history if you're working on shared branches though.
+1. Push the commit(s) to your working branch in your fork.
+1. Submit a merge request (MR) to the `master` branch in the main GitLab project.
+ 1. Your merge request needs at least 1 approval, but feel free to require more.
+ For instance if you're touching both 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.
+ maintainer.
+ 1. If you're submitting changes to documentation, you'll need approval from a technical
+ writer, based on the appropriate [product category](https://about.gitlab.com/handbook/product/categories/).
+ Only assign the MR to them when it's ready for docs review.
+ 1. You don't have to select any specific 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 reason for your change.
+ 1. If you are contributing code, fill in the description according to the default
+ 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.
+ "Choose a template" menu and fill in the description according to 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
+ `Closes #XXX` syntax to [auto-close](../../user/project/issues/automatic_issue_closing.md)
+ the issue(s) once the merge request is merged.
+1. If you're allowed to (Core team members, for example), set a relevant milestone
+ and [labels](issue_workflow.md).
+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, which
+ can be found by running `grep css-class ./app -R`.
+1. Be prepared to answer questions and incorporate feedback into your MR with new
+ commits. Once you have fully addressed a suggestion from a reviewer, click the
+ "Resolve discussion" button beneath it to mark it resolved.
+ 1. The merge request author resolves only the discussions they have fully addressed.
+ If there's an open reply or discussion, a suggestion, a question, or anything else,
+ the discussion should be left to be resolved by the reviewer.
+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).
+ to large changes in the MR, execute the migrations again once the review is complete.
+1. Write tests for more complex migrations.
+1. Merge requests **must** adhere to the [merge request performance guidelines](../merge_request_performance_guidelines.md).
+1. For tests that use Capybara, read
+ [how to write reliable, asynchronous integration tests](https://robots.thoughtbot.com/write-reliable-asynchronous-integration-tests-with-capybara).
1. If your merge request introduces changes that require additional steps when
installing GitLab from source, add them to `doc/install/installation.md` in
the same merge request.
@@ -95,109 +99,117 @@ request is as follows:
instructions are specific to a version, add them to the "Version specific
upgrading instructions" section.
-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.
-
-[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
-[team]: https://about.gitlab.com/team/
+If you would like quick feedback on your merge request feel free to mention someone
+from the [core team](https://about.gitlab.com/community/core-team/) or one of the
+[merge request coaches](https://about.gitlab.com/team/). When having your code reviewed
+and when reviewing merge requests, please keep the [code review guidelines](../code_review.md)
+in mind.
+
+### Keep it simple
+
+*Live by smaller iterations.* Please keep the amount of changes in a single MR **as small as possible**.
+If you want to contribute a large feature, think very carefully about what the
+[minimum viable change](https://about.gitlab.com/handbook/product/#the-minimally-viable-change)
+is. Can you split the functionality into two smaller MRs? Can you submit only the
+backend/API code? Can you start with a very simple UI? Can you do just a part of the
+refactor?
+
+Small MRs which are more easily reviewed, lead to higher code quality which is
+more important to GitLab than having a minimal commit log. The smaller an MR is,
+the more likely it will be merged quickly. After that you can send more MRs to
+enhance and expand the feature. The [How to get faster PR reviews](https://github.com/kubernetes/kubernetes/blob/release-1.5/docs/devel/faster_reviews.md)
+document from the Kubernetes team also has some great points regarding this.
+
+### Commit messages guidelines
+
+When writing commit messages, please follow the guidelines below:
+
+- The commit subject must contain at least 3 words.
+- The commit subject should ideally contain up to 50 characters,
+and must not be longer than 72 characters.
+- The commit subject must start with a capital letter.
+- The commit subject must not end with a period.
+- The commit subject and body must be separated by a blank line.
+- The commit body must not contain more than 72 characters per line.
+- Commits that change 30 or more lines across at least 3 files must
+describe these changes in the commit body.
+- The commit subject or body must not contain Emojis.
+- Use issues and merge requests' full URLs instead of short references,
+as they are displayed as plain text outside of GitLab.
+- The merge request must not contain more than 10 commit messages.
+
+If the guidelines are not met, the MR will not pass the
+[Danger checks](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/danger/commit_messages/Dangerfile).
+For more information see [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/).
## Contribution acceptance criteria
-1. The change is as small as possible
+To make sure that your merge request can be approved, please ensure that it meets
+the contribution acceptance criteria below:
+
+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.md) 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).
-
-[license-finder-doc]: ../licensing.md
-[polling-etag]: ../polling.md
+ - If a failing CI build seems to be unrelated to your contribution, you can try
+ restarting the failing CI job, rebasing from master to bring in updates that
+ may resolve the failure, or if it has not been fixed yet, ask a developer to
+ help you fix the test.
+1. The MR initially contains a a few logically organized commits.
+1. The changes can merge without problems. If not, you should rebase if you're the
+ only one working on your feature branch, otherwise merge `master`.
+1. Only one specific issue is fixed or one specific feature is implemented. Do not
+ combine things; send separate merge requests for each issue or feature.
+1. Migrations should do only one thing (e.g., create a table, move data to a new
+ table, or remove an old table) to aid retrying on failure.
+1. Contains functionality that other users will benefit from.
+1. Doesn't add configuration options or settings options since they complicate making
+ and testing future changes.
+1. Changes do not 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`](../merge_request_performance_guidelines.md).
+ - Avoid repeated access of the filesystem.
+ - Use [polling with ETag caching](../polling.md) if needed to support real-time features.
+1. If the merge request adds any new libraries (gems, JavaScript libraries, etc.),
+ they should conform to our [Licensing guidelines](../licensing.md). See those
+ instructions for help if the "license-finder" test fails with a
+ `Dependencies that need approval` error. Also, make the reviewer aware of the new
+ library and explain why you need it.
+1. The merge request meets GitLab's [definition of done](#definition-of-done), below.
## 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 by UX/FE/BE 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.)
-1. [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-or-end-to-end-tests) added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams) with any questions
+code. We use the following [definition of done](https://www.agilealliance.org/glossary/definition-of-done).
+Your contribution is not *done* until you have made sure it meets all of these
+requirements.
+
+1. Clear description explaining the relevancy of the contribution.
+1. Working and clean code that is commented where needed.
+1. [Unit, integration, and system tests](../testing_guide/index.md) that all pass
+ on the CI server.
+1. Performance/scalability implications have been considered, addressed, and tested.
+1. [Documented](../documentation/index.md) in the `/doc` directory.
+1. [Changelog entry added](../changelog.md), if necessary.
+1. Reviewed by relevant (UX/FE/BE/tech writing) reviewers and all concerns are addressed.
+1. Merged by a project maintainer.
+1. Added to the [release post](https://about.gitlab.com/handbook/marketing/blog/release-posts/),
+ if relevant.
+1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/data/features.yml), if relevant.
+1. [Black-box tests/end-to-end tests](../testing_guide/testing_levels.md#black-box-tests-at-the-system-level-aka-end-to-end-tests)
+ added if required. Please contact [the quality team](https://about.gitlab.com/handbook/engineering/quality/#teams)
+ with any questions.
+
+## Dependencies
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. 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>
-
-[definition-of-done]: http://guide.agilealliance.org/guide/definition-of-done.html
-[testing]: ../testing_guide/index.md
-
----
-
-[Return to Contributing documentation](index.md)
-
-[changelog]: ../changelog.md "Generate a changelog entry"
-[doc-guidelines]: ../documentation/index.md "Documentation guidelines"
+consider updating the following, and note the applicability of each in your merge
+request:
+
+1. Note the addition in the [release blog post](https://about.gitlab.com/handbook/marketing/blog/release-posts/)
+ (create one if it doesn't exist yet).
+1. [The upgrade guide](../../update/upgrading_from_source.md).
+1. The [GitLab Installation Guide](../../install/installation.md#1-packages-and-dependencies).
+1. The [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
+1. The [CI environment preparation](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/scripts/prepare_build.sh).
+1. The [Omnibus package creator](https://gitlab.com/gitlab-org/omnibus-gitlab).
diff --git a/doc/development/distributed_tracing.md b/doc/development/distributed_tracing.md
new file mode 100644
index 00000000000..038e3de10d7
--- /dev/null
+++ b/doc/development/distributed_tracing.md
@@ -0,0 +1,182 @@
+# Distributed Tracing - development guidelines
+
+GitLab is instrumented for distributed tracing.
+
+According to [Open Tracing](https://opentracing.io/docs/overview/what-is-tracing/):
+
+> Distributed tracing, also called distributed request tracing, is a method used to profile and
+> monitor applications, especially those built using a microservices architecture. Distributed
+> tracing helps to pinpoint where failures occur and what causes poor performance.
+
+Distributed tracing is especially helpful in understanding the lifecycle of a request as it passes
+through the different components of the GitLab application. At present, Workhorse, Rails, Sidekiq,
+and Gitaly support tracing instrumentation.
+
+Distributed tracing adds minimal overhead when disabled, but imposes only small overhead when
+enabled and is therefore capable in any environment, including production. For this reason, it can
+be useful in diagnosing production issues, particularly performance problems.
+
+## Enabling distributed tracing
+
+GitLab uses the `GITLAB_TRACING` environment variable to configure distributed tracing. The same
+configuration is used for all components (e.g., Workhorse, Rails, etc).
+
+When `GITLAB_TRACING` is not set, the application will not be instrumented, meaning that there is
+no overhead at all.
+
+To enable `GITLAB_TRACING`, a valid _"configuration-string"_ value should be set, with a URL-like
+form:
+
+```console
+GITLAB_TRACING=opentracing://<driver>?<param_name>=<param_value>&<param_name_2>=<param_value_2>
+```
+
+In this example, we have the following hypothetical values:
+
+- `driver`: the driver. [GitLab supports
+ `jaeger`](https://docs.gitlab.com/ee/user/project/operations/tracing.html). In future, other
+ tracing implementations may also be supported.
+- `param_name`, `param_value`: these are driver specific configuration values. Configuration
+ parameters for Jaeger are documented [further on in this
+ document](#2-configure-the-gitlab_tracing-environment-variable) they should be URL encoded.
+ Multiple values should be separated by `&` characters like a URL.
+
+## Using Jaeger in the GitLab Development Kit
+
+The first tracing implementation that GitLab supports is Jaeger, and the [GitLab Development
+Kit](https://gitlab.com/gitlab-org/gitlab-development-kit/) supports distributed tracing with
+Jaeger out-of-the-box.
+
+The easiest way to access tracing from a GDK environment is through the
+[performance-bar](../administration/monitoring/performance/performance_bar.md). This can be shown
+by typing `p` `b` in the browser window.
+
+![Jaeger Search UI](img/distributed_tracing_performance_bar.png)
+
+Once the performance bar is enabled, click on the **Trace** link in the performance bar to go to
+the Jaeger UI.
+
+The Jaeger search UI will return a query for the `Correlation-ID` of the current request. Normally,
+this search should return a single trace result. Clicking this result will show the detail of the
+trace in a hierarchical time-line.
+
+![Jaeger Search UI](img/distributed_tracing_jaeger_ui.png)
+
+## Using Jaeger without the GitLab Developer Kit
+
+Distributed Tracing can be enabled in non-GDK development environments as well as production or
+staging environments, for troubleshooting. Please note that at this time, this functionality is
+experimental, and not supported in production environments at present. In this first release, it is intended to be
+used for debugging in development environments only.
+
+Jaeger tracing can be enabled through a three-step process:
+
+1. [Start Jaeger](#1-start-jaeger).
+1. [Configure the `GITLAB_TRACING` environment variable](#2-configure-the-gitlab_tracing-environment-variable).
+1. [Start the GitLab application](#3-start-the-gitlab-application).
+1. [Go to the Jaeger Search UI in your browser](#4-open-the-jaeger-search-ui).
+
+### 1. Start Jaeger
+
+Jaeger has many configuration options, but is very easy to start in an "all-in-one" mode which uses
+memory for trace storage (and is therefore non-persistent). The main advantage of "all-in-one" mode
+being ease of use.
+
+For more detailed configuration options, refer to the [Jaeger
+documentation](https://www.jaegertracing.io/docs/1.9/getting-started/).
+
+#### Using Docker
+
+If you have Docker available, the easier approach to running the Jaeger all-in-one is through
+Docker, using the following command:
+
+```console
+$ docker run \
+ --rm \
+ -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 \
+ -p 5775:5775/udp \
+ -p 6831:6831/udp \
+ -p 6832:6832/udp \
+ -p 5778:5778 \
+ -p 16686:16686 \
+ -p 14268:14268 \
+ -p 9411:9411 \
+ jaegertracing/all-in-one:latest
+```
+
+#### Using the Jaeger process
+
+Without Docker, the all-in-one process is still easy to setup.
+
+1. Download the [latest Jaeger release](https://github.com/jaegertracing/jaeger/releases) for your
+ platform.
+1. Extract the archive and run the `bin/all-in-one` process.
+
+This should start the process with the default listening ports.
+
+### 2. Configure the `GITLAB_TRACING` environment variable
+
+Once you have Jaeger running, you'll need to configure the `GITLAB_TRACING` variable with the
+appropriate configuration string.
+
+**TL;DR:** If you are running everything on the same host, use the following value:
+
+```console
+$ export GITLAB_TRACING="opentracing://jaeger?http_endpoint=http%3A%2F%2Flocalhost%3A14268%2Fapi%2Ftraces&sampler=const&sampler_param=1"
+```
+
+This configuration string uses the Jaeger driver `opentracing://jaeger` with the following options:
+
+| Name | Value | Description |
+|------|-------|-------------|
+| `http_endpoint` | `http://localhost:14268/api/traces` | Configures Jaeger to send trace information to the HTTP endpoint running on `http://localhost:14268/`. Alternatively, the `upd_endpoint` can be used. |
+| `sampler` | `const` | Configures Jaeger to use the constant sampler (either on or off). |
+| `sampler_param` | `1` | Configures the `const` sampler to sample _all_ traces. Using `0` would sample _no_ traces. |
+
+**Other parameter values are also possible:**
+
+| Name | Example | Description |
+|------|-------|-------------|
+| `udp_endpoint` | `localhost:6831` | This is the default. Configures Jaeger to send trace information to the UDP listener on port `6831` using compact thrift protocol. Note that we've experienced some issues with the [Jaeger Client for Ruby](https://github.com/salemove/jaeger-client-ruby) when using this protocol. |
+| `sampler` | `probabalistic` | Configures Jaeger to use a probabilistic random sampler. The rate of samples is configured by the `sampler_param` value. |
+| `sampler_param` | `0.01` | Use a ratio of `0.01` to configure the `probabalistic` sampler to randomly sample _1%_ of traces. |
+
+NOTE: **Note:**
+The same `GITLAB_TRACING` value should to be configured in the environment
+variables for all GitLab processes, including Workhorse, Gitaly, Rails, and Sidekiq.
+
+### 3. Start the GitLab application
+
+Once the `GITLAB_TRACING` environment variable is exported to all GitLab services, start the
+application.
+
+When `GITLAB_TRACING` is configured properly, the application will log this on startup:
+
+```console
+13:41:53 gitlab-workhorse.1 | 2019/02/12 13:41:53 Tracing enabled
+...
+13:41:54 gitaly.1 | 2019/02/12 13:41:54 Tracing enabled
+...
+```
+
+If `GITLAB_TRACING` is not configured correctly, this will also be logged:
+
+```console
+13:43:45 gitaly.1 | 2019/02/12 13:43:45 skipping tracing configuration step: tracer: unable to load driver mytracer
+```
+
+By default, GitLab ships with the Jaeger tracer, but other tracers can be included at compile time.
+Details of how this can be done are included in the [LabKit tracing
+documentation](https://godoc.org/gitlab.com/gitlab-org/labkit/tracing).
+
+If no log messages about tracing are emitted, the `GITLAB_TRACING` environment variable is likely
+not set.
+
+### 4. Open the Jaeger Search UI
+
+By default, the Jaeger search UI is available at <http://localhost:16686/search>.
+
+TIP: **Tip:**
+Don't forget that you will need to generate traces by using the application before
+they appear in the Jaeger UI.
+
diff --git a/doc/development/documentation/feature-change-workflow.md b/doc/development/documentation/feature-change-workflow.md
index b5b325683a3..1f68b6a6a70 100644
--- a/doc/development/documentation/feature-change-workflow.md
+++ b/doc/development/documentation/feature-change-workflow.md
@@ -2,111 +2,178 @@
description: How to add docs for new or enhanced GitLab features.
---
-# Documentation process at GitLab
+# Documentation process for feature changes
At GitLab, developers contribute new or updated documentation along with their code, but product managers and technical writers also have essential roles in the process.
- **Developers**: Author/update documentation in the same MR as their code, and
-merge it by the feature freeze for the assigned milestone. Request technical writer
-assistance if needed.
+ merge it by the feature freeze for the assigned milestone. Request technical writer
+ assistance if needed. Other developers typically act as reviewers.
- **Product Managers** (PMs): In the issue for all new and enhanced features,
-confirm the documentation requirements, plus the mentioned feature description
-and use cases, which can be reused in docs. They can bring in a technical
-writer for discussion or help, and can be called upon themselves as a doc reviewer.
+ confirm the documentation requirements, plus the mentioned feature description
+ and use cases, which can be reused in docs. They can bring in a technical
+ writer for discussion or collaboration, and can be called upon themselves as a doc reviewer.
- **Technical Writers**: Review doc requirements in issues, track issues and MRs
-that contain docs changes, help with any questions throughout the authoring/editing process,
-and review all new and updated docs content after it's merged (unless a pre-merge
-review request is made).
+ that contain docs changes, help with any questions throughout the authoring/editing process,
+ work on special projects related to the documentation, and review all new and updated
+ docs content, whether before or after it is merged.
-Beyond this process, any member of the GitLab community can also author documentation
+Beyond this process, any member of the GitLab community can also author or request documentation
improvements that are not associated with a new or changed feature. See the [Documentation improvement workflow](improvement-workflow.md).
## When documentation is required
Documentation must be delivered whenever:
-- A new or enhanced feature is shipped that impacts the user/admin experience
-- There are changes to the UI or API
-- A process, workflow, or previously documented feature is changed
-- A feature is deprecated or removed
+- A new or enhanced feature is shipped that impacts the user/admin experience.
+- There are changes to the UI or API.
+- A process, workflow, or previously documented feature is changed.
+- A feature is deprecated or removed.
+
+For example, a UI restyling that offers no difference in functionality may require
+documentation updates if screenshots are now needed, or need to be updated.
Documentation is not required when a feature is changed on the backend
-only and does not directly affect the way that any user or
-administrator would interact with GitLab. For example, a UI restyling that offers
-no difference in functionality may require documentation updates if screenshots
-are now needed, or need to be updated.
+only and does not directly affect the way that any user or administrator would
+interact with GitLab.
NOTE: **Note:**
When revamping documentation, if unrelated to the feature change, this should be submitted
in its own MR (using the [documentation improvement workflow](improvement-workflow.md))
so that we can ensure the more time-sensitive doc updates are merged with code by the freeze.
+## Documentation requirements in feature issues
+
+Requirements for the documentation of a feature should be included as part of the
+issue for planning that feature, in a Documentation section within the issue description.
+
+This section is provided as part of the Feature Proposal template and should be added
+to the issue if it is not already present.
+
+Anyone can add these details, but the product manager who assigns the issue to a specific release
+milestone will ensure these details are present and finalized by the time of that milestone's kickoff.
+Developers, technical writers, and others may help further refine this plan at any time.
+
+### Details to include
+
+- What concepts and procedures should the docs guide and enable the user to understand or accomplish?
+- To this end, what new page(s) are needed, if any? What pages/subsections need updates? Consider user, admin, and API doc changes and additions.
+- For any guide or instruction set, should it help address a single use case, or be flexible to address a certain range of use cases?
+- Do we need to update a previously recommended workflow? Should we link the new feature from various relevant locations? Consider all ways documentation should be affected.
+- Are there any key terms or task descriptions that should be included so that the docs are found in relevant searches?
+- Include suggested titles of any pages or subsections, if applicable.
+
## Documenting a new or changed feature
To follow a consistent workflow every month, documentation changes
involve the Product Managers, the developer who shipped the feature,
-and the Technical Writing team. Each role is described below.
+and the technical writer for the DevOps stage. Each role is described below.
+
+The Documentation items in the GitLab CE/EE [Feature Proposal issue template](https://gitlab.com/gitlab-org/gitlab-ce/raw/template-improvements-for-documentation/.gitlab/issue_templates/Feature%20proposal.md)
+and default merge request template will assist you with following this process.
+
+### Product Manager role
-### 1. Product Manager's role
+For issues requiring any new or updated documentation, the Product Manager (PM)
+must:
-The Product Manager (PM) should confirm or add the following items in the issue:
+- Add the `Documentation` label.
+- Confirm or add the [documentation requirements](#documentation-requirements-in-feature-issues).
+- Ensure the issue contains any new or updated feature name, overview/description,
+ and use cases, as required per the [documentation structure and template](structure.md), when applicable.
-- New or updated feature name, overview/description, and use cases, all required per the [Documentation structure and template](structure.md).
-- The documentation requirements for the developer working on the docs.
- - What new page, new subsection of an existing page, or other update to an existing page/subsection is needed.
- - Just one page/section/update or multiple (perhaps there's an end user and admin change needing docs, or we need to update a previously recommended workflow, or we want to link the new feature from various places; consider and mention all ways documentation should be affected.
- - Suggested title of any page or subsection, if applicable.
-- Label the issue with `Documentation` and `docs:P1` in addition to the `Deliverable` label and correct milestone.
+Everyone is encouraged to draft the requirements in the issue, but a product manager will
+do the following:
-Anyone is welcome to draft the items above in the issue, but a product manager must review and update them whenever the issue is assigned a specific milestone.
+- When the issue is assigned a release milestone, review and update the Documentation details.
+- By the kickoff, finalizie the Documentation details.
-### 2. Developer's role
+### Developer and maintainer roles
+
+#### Authoring
As a developer, you must ship the documentation with the code of the feature that
you are creating or updating. The documentation is an essential part of the product.
+Technical writers are happy to help, as requested and planned on an issue-by-issue basis.
+
+Follow the process below unless otherwise agreed with the product manager and technical writer for a given issue:
-- New and edited docs should be included in the MR introducing the code, and planned
-in the issue that proposed the feature. However, if the new or changed doc requires
-extensive collaboration or conversation, a separate, linked issue can be used for the planning process.
+- Include any new and edited docs in the MR introducing the code.
+- Use the Documentation requirements confirmed by the Product Manager in the
+ issue and discuss any further doc plans or ideas as needed.
+ - If the new or changed doc requires extensive collaboration or conversation, a separate,
+ linked issue can be used for the planning process.
+ - We are trying to avoid using a separate MR, so that the docs stay with the code, but the
+ Technical Writing team is interested in discussing any potential exceptions that may be suggested.
- Use the [Documentation guidelines](index.md), as well as other resources linked from there,
-including the [Structure and template](structure.md) page, [Style Guide](styleguide.md), and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
+ including the Documentation [Structure and template](structure.md) page, [Style Guide](styleguide.md), and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
- If you need any help to choose the correct place for a doc, discuss a documentation
-idea or outline, or request any other help, ping the Technical Writer for the relevant
-[DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages)
-in your issue or MR, or write within `#docs` on the GitLab Slack.
+ idea or outline, or request any other help, ping the Technical Writer for the relevant
+ [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages)
+ in your issue or MR, or write within `#docs` on the GitLab Slack.
- The docs must be merged with the code **by the feature freeze date**, otherwise
-- the feature cannot be included with the release.<!-- TODO: Policy/process for feature-flagged issues -->
+ the feature cannot be included with the release. A policy for documenting feature-flagged
+ issues is forthcoming and you are welcome to join the [discussion](https://gitlab.com/gitlab-org/gitlab-ce/issues/56813).
+
+#### Reviews and merging
+
+All reviewers can help ensure accuracy, clarity, completeness, and adherence to the plans in the issue, as well as the [Documentation Guidelines](https://docs.gitlab.com/ee/development/documentation/) and [Style Guide](https://docs.gitlab.com/ee/development/documentation/styleguide.html).
+
+- **Prior to merging**, documentation changes committed by the developer must be reviewed by:
+
+ 1. **The code reviewer** for the MR, to confirm accuracy, clarity, and completeness.
+ 1. Optionally: Others involved in the work, such as other devs or the PM.
+ 1. Optionally: The technical writer for the DevOps stage. If not prior to merging, the technical writer will review after the merge.
+ This helps us ensure that the developer has time to merge good content by the freeze, and that it can be further refined by the release, if needed.
+ - To decide whether to request this review before the merge, consider the amount of time left before the code freeze, the size of the change,
+ and your degree of confidence in having users of an RC use your docs as written.
+ - Pre-merge tech writer reviews should be most common when the code is complete well in advance of the freeze and/or for larger documentation changes.
+ - You can request a review and if there is not sufficient time to complete it prior to the freeze,
+ the maintainer can merge the current doc changes (if complete) and create a follow-up doc review issue.
+ - The technical writer can also help decide what docs to merge before the freeze and whether to work on further changes in a follow up MR.
+ - **To request a pre-merge technical writer review**, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+ - **To request a post-merge technical writer review**, [create an issue for one using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review) and link it from the MR that makes the doc change.
+ 1. **The maintainer** who is assigned to merge the MR, to verify clarity, completeness, and quality, to the best of their ability.
+
+- Upon merging, if a technical writer review has not been performed and there is not yet a linked issue for a follow-up review, the maintainer should [create an issue using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review), link it from the MR, and
+ mention the original MR author in the new issue. Alternatively, the mainitainer can ask the MR author to create and link this issue before the MR is merged.
+
+- After merging, documentation changes are reviewed by:
+
+ 1. The technical writer--**if** their review was not performed prior to the merge.
+ 2. Optionally: by the PM (for accuracy and to ensure it's consistent with the vision for how the product will be used).
+ Any party can raise the item to the PM for review at any point: the dev, the technical writer, or the PM, who can request/plan a review at the outset.
+
+### Technical Writer role
+
+#### Planning
+
+- The technical writer monitors the documentation needs of issues assigned to the current and next milestone
+ for their DevOps stage(s), and participates in any needed discussion on docs planning and requirements refinement
+ with the dev, PM, and others.
+- The technical writer will review these requirements again upon the kickoff and provide feedback, as needed.
+ This is not a blocking review and developers should not wait to work on docs.
-Prior to merge, documentation changes commited by the developer must be reviewed by:
-* the person reviewing the code and merging the MR.
-* optionally: others involved in the work (such as other devs, the PM, or a technical writer), if requested.
+#### Collaboration
-After merging, documentation changing are reviewed by:
-* a technical writer (for clarity, structure, grammar, etc).
-* optionally: by the PM (for accuracy and to ensure it's consistent with the vision for how the product will be used).
-Any party can raise the item to the PM for review at any point: the dev, the technical writer, or the PM, who can request/plan a review at the outset.
+By default, the developer will work on documentation changes independently, but
+the developer, PM, or technicial writer can propose a broader collaboration for
+any given issue.
-### 3. Technical Writer's role
+Additionally, technical writers are available for questions at any time.
-**Planning**
-- Once an issue contains a Documentation label and an upcoming milestone, a
-technical writer reviews the listed documentation requirements, which should have
-already been reviewed by the PM. (These are non-blocking reviews; developers should
-not wait to work on docs.)
-- Monitor the documentation needs of issues assigned to the current and next milestone,
-and participate in any needed discussion on docs planning with the dev, PM, and others.
+#### Review
-**Review**
- Techncial writers provide non-blocking reviews of all documentation changes,
-typically after the change is merged. However, if the docs are ready in the MR while
-we are awaiting other work in order to merge, the technical writer's review can commence early.
+ before or after the change is merged. However, if the docs are ready in the MR while
+ there's time before the freeze, the technical writer's review can commence early, on request.
- The technical writer will confirm that the doc is clear, grammatically correct,
-and discoverable, while avoiding redundancy, bad file locations, typos, broken links,
-etc. The technical writer will review the documentation for the following, which
-the developer and code reviewer should have already made a good-faith effort to ensure:
+ and discoverable, while avoiding redundancy, bad file locations, typos, broken links,
+ etc. The technical writer will review the documentation for the following, which
+ the developer and code reviewer should have already made a good-faith effort to ensure:
- Clarity.
- - Relevance (make sure the content is appropriate given the impact of the feature).
- - Location (make sure the doc is in the correct dir and has the correct name).
+ - Adherence to the plans and goals in the issue.
+ - Location (make sure the docs are in the correct directorkes and has the correct name).
- Syntax, typos, and broken links.
- Improvements to the content.
- - Accordance to the [Documentation Style Guide](styleguide.md) and [structure/template](structure.md).
+ - Accordance with the [Documentation Style Guide](styleguide.md), and [Structure and Template](structure.md) doc.
diff --git a/doc/development/documentation/improvement-workflow.md b/doc/development/documentation/improvement-workflow.md
index ef6392c6f7f..a12c3d5ea7b 100644
--- a/doc/development/documentation/improvement-workflow.md
+++ b/doc/development/documentation/improvement-workflow.md
@@ -9,26 +9,30 @@ Anyone can contribute a merge request or create an issue for GitLab's documentat
This page covers the process for any contributions to GitLab's docs that are
not part of feature development. If you are looking for information on updating
GitLab's docs as is required with the development and release of a new feature
-or feature enhancement, see the [feature-change documentation workflow](feature-change-workflow.md).
+or feature enhancement, see the [documentation process for feature changes](feature-change-workflow.md).
## Who updates the docs
Anyone can contribute! You can create a merge request with documentation
when you find errors or other room for improvement in an existing doc, or when you
-have an idea for all-new documentation that would help a GitLab user or admin
-to achieve or improve their DevOps workflows.
+have an idea for all-new documentation that would help a GitLab user or administrator
+to accomplish their work with GitLab.
## How to update the docs
-- Follow the described standards and processes listed on the [GitLab Documentation guidelines](index.md) page,
-including linked resources: the [Structure and template](structure.md) page, [Style Guide](styleguide.md), and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
-- Follow GitLab's [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines).
-- If you need any help to choose the correct place for a doc, discuss a documentation
+1. Click "Edit this Page" at the bottom of any page on docs.gitlab.com, or navigate to
+ one of the repositories and doc paths listed on the [GitLab Documentation guidelines](index.md) page.
+ Work in a fork if you do not have developer access to the GitLab project.
+1. Follow the described standards and processes listed on that Guidelines page,
+ including the linked resources: the [Structure and template](structure.md) page, [Style Guide](styleguide.md), and [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
+1. Follow GitLab's [Merge Request Guidelines](../contributing/merge_request_workflow.md#merge-request-guidelines).
+
+If you need any help to choose the correct place for a doc, discuss a documentation
idea or outline, or request any other help, ping the Technical Writer for the relevant
[DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages)
in your issue or MR, or write within `#docs` if you are a member of GitLab's Slack workspace.
-## Merging
+## Review and merging
Anyone with master access to the affected GitLab project can merge documentation changes.
This person must make a good-faith effort to ensure that the content is clear
@@ -38,12 +42,22 @@ that it meets the [Documentation Guidelines](index.md) and [Style Guide](stylegu
If the author or reviewer has any questions, or would like a techncial writer's review
before merging, mention the writer who is assigned to the relevant [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
-## Technical Writer review
+The process can involve the following parties/phases, and is replicated in the `Documentation` MR template for GitLab CE and EE, to help with following the process.
+
+**1. Primary Reviewer** - Review by a [code reviewer](https://about.gitlab.com/handbook/engineering/projects/) or other appropriate colleague to confirm accuracy, clarity, and completeness. This can be skipped for minor fixes without substantive content changes.
+
+**2. Technical Writer** - Optional - If not requested for this MR, must be scheduled post-merge. To request a pre-merge review, assign the writer listed for the applicable [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+To request a post-merge review, [create an issue for one using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review) and link it from the MR that makes the doc change.
+
+**3. Maintainer**
-The technical writing team reviews changes after they are merged, unless a prior
-review is requested.
+1. Review by assigned maintainer, who can always request/require the above reviews. Maintainer review can occur before or after a technical writer review.
+2. Ensure a release milestone of the format XX.Y is set. If the freeze for that release has begun, add the label `pick into <XX.Y>` unless this change is not required for the release. In that case, simply change the milestone.
+3. If EE and CE MRs exist, merge the EE MR first, then the CE MR.
+4. After merging, if there has not been a technical writer review and an issue for a follow-up review was not already created and linked from the MR, [create the issue using the Doc Review template](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Doc%20Review) and link it from the MR.
## Other ways to help
If you have ideas for further documentation resources that would be best
-considered/handled by technical writers, devs, and other SMEs, please create an issue.
+considered/handled by technical writers, devs, and other SMEs, please [create an issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issuable_template=Documentation)
+using the Documentation template.
diff --git a/doc/development/documentation/index.md b/doc/development/documentation/index.md
index 287a472d0d8..a4da34a50ce 100644
--- a/doc/development/documentation/index.md
+++ b/doc/development/documentation/index.md
@@ -10,8 +10,8 @@ In addition to this page, the following resources to help craft and contribute d
- [Style Guide](styleguide.md) - What belongs in the docs, language guidelines, and more.
- [Structure and template](structure.md) - Learn the typical parts of a doc page and how to write each one.
-- [Workflow](workflow.md) - A landing page for our key workflows:
- - [Feature-change documentation workflow](feature-change-workflow.md) - Adding required documentation when developing a GitLab feature.
+- [Workflows](workflow.md) - A landing page for our key workflows:
+ - [Documentation process for feature changes](feature-change-workflow.md) - Adding required documentation when developing a GitLab feature.
- [Documentation improvement workflow](improvement-workflow.md) - New content not associated with a new feature.
- [Markdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) - A reference for the markdown implementation used by GitLab's documentation site and about.gitlab.com.
- [Site architecture](site_architecture/index.md) - How docs.gitlab.com is built.
@@ -511,10 +511,10 @@ Currently, the following tests are in place:
1. `docs lint`: Check that all internal (relative) links work correctly and
that all cURL examples in API docs use the full switches. It's recommended
- to [check locally](#previewing-locally) before pushing to GitLab by executing the command
+ to [check locally](#previewing-the-changes-live) before pushing to GitLab by executing the command
`bundle exec nanoc check internal_links` on your local
[`gitlab-docs`](https://gitlab.com/gitlab-com/gitlab-docs) directory.
-1. [`ee_compat_check`](../automatic_ce_ee_merge.md#avoiding-ce-gt-ee-merge-conflicts-beforehand) (runs on CE only):
+1. [`ee_compat_check`](../automatic_ce_ee_merge.md#avoiding-ce-ee-merge-conflicts-beforehand) (runs on CE only):
When you submit a merge request to GitLab Community Edition (CE),
there is this additional job that runs against Enterprise Edition (EE)
and checks if your changes can apply cleanly to the EE codebase.
diff --git a/doc/development/documentation/site_architecture/global_nav.md b/doc/development/documentation/site_architecture/global_nav.md
index 0aa3c41a225..f2f4f5f0e1c 100644
--- a/doc/development/documentation/site_architecture/global_nav.md
+++ b/doc/development/documentation/site_architecture/global_nav.md
@@ -62,7 +62,7 @@ the consent of one of the technical writers.
The global nav is built from two files:
- [Data](#data-file)
-- [Layout](#layout-file)
+- [Layout](#layout-file-logic)
The data file feeds the layout with the links to the docs. The layout organizes
the data among the nav in containers properly [styled](#css-classes).
diff --git a/doc/development/documentation/site_architecture/index.md b/doc/development/documentation/site_architecture/index.md
index 0ce5825fd61..ee3a9caf9a0 100644
--- a/doc/development/documentation/site_architecture/index.md
+++ b/doc/development/documentation/site_architecture/index.md
@@ -44,7 +44,7 @@ read through the [global navigation](global_nav.md) doc.
The docs site is deployed to production with GitLab Pages, and previewed in
merge requests with Review Apps.
-The deployment aspects will be soon transfered from the [original document](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md)
+The deployment aspects will be soon transferred from the [original document](https://gitlab.com/gitlab-com/gitlab-docs/blob/master/README.md)
to this page.
<!--
diff --git a/doc/development/documentation/structure.md b/doc/development/documentation/structure.md
index ee3bd5606a5..95b5fcd99a1 100644
--- a/doc/development/documentation/structure.md
+++ b/doc/development/documentation/structure.md
@@ -20,7 +20,7 @@ Every feature or use case document should include the following content in the f
with exceptions and details noted below and in the template included on this page.
- **Title**: Top-level heading with the feature name, or a use case name, which would start with
-a verb, like Configuring, Enabling, etc.
+ a verb, like Configuring, Enabling, etc.
- **Introduction**: A couple sentences about the subject matter and what's to be found on this page.
- **Overview** Describe what it is, what it does, and in what context it should be used.
- **Use cases**: describes real use case scenarios for that feature/configuration.
@@ -95,7 +95,7 @@ Link each one to an appropriate place for more information.
"Instructions" is usually not the name of the heading.
This is the part of the document where you can include one or more sets of instructions, each to accomplish a specific task.
Headers should describe the task the reader will achieve by following the instructions within, typically starting with a verb.
-Larger instruction sets may have subsections covering specific phases of the process.
+Larger instruction sets may have subsections covering specific phases of the process.
- Write a step-by-step guide, with no gaps between the steps.
- Start with an h2 (`##`), break complex steps into small steps using
diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md
index 7a3a8f25c2d..c0386290785 100644
--- a/doc/development/documentation/styleguide.md
+++ b/doc/development/documentation/styleguide.md
@@ -36,7 +36,7 @@ gem will support all [GFM markup](../../user/markdown.md) in the future. For now
use regular markdown markup, following the rules on this style guide. For a complete
Kramdown reference, check the [GitLab Markdown Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/).
Use Kramdown markup wisely: do not overuse its specific markup (e.g., `{:.class}`) as it will not render properly in
-[`/help`](#gitlab-help).
+[`/help`](index.md#gitlab-help).
## Content
@@ -80,11 +80,10 @@ yield a useful result, and ensuring content is helpful and easy to consume.
## Text
-- Split up long lines (wrap text), this makes it much easier to review and edit. Only
- double line breaks are shown as a full line break by creating new paragraphs.
- 80-100 characters is the recommended line length.
+- Splitting long lines (preferably up to 100 characters) can make it easier to provide feedback on small chunks of text.
+- Insert an empty line for new paragraphs.
- Use sentence case for titles, headings, labels, menu items, and buttons.
-- Jump a line between different markups (e.g., after every paragraph, header, list, etc). Example:
+- Insert an empty line between different markups (e.g., after every paragraph, header, list, etc). Example:
```md
## Header
@@ -236,6 +235,24 @@ For other punctuation rules, please refer to the
E.g., instead of writing something like `Read more about GitLab Issue Boards [here](LINK)`,
write `Read more about [GitLab Issue Boards](LINK)`.
+### Links to confidential issues
+
+Don't link directly to [confidential issues](../../user/project/issues/confidential_issues.md). These will fail for:
+
+- Those without sufficient permissions.
+- Automated link checkers.
+
+Instead:
+
+- Mention in the text that the information is contained in a confidential issue. This will reduce confusion.
+- Provide a link in back ticks (`` ` ``) so that those with access to the issue can easily navigate to it.
+
+Example:
+
+```md
+For more information, see the [confidential issue](https://docs.gitlab.com/ee/user/project/issues/confidential_issues.html) `https://gitlab.com/gitlab-org/gitlab-ce/issues/<issue_number>`.
+```
+
### Unlinking emails
By default, all email addresses will render in an email tag on docs.gitlab.com.
@@ -612,7 +629,7 @@ In this case:
- The code blocks are indented one or more spaces under the list item to render
correctly.
- Different highlighting languages are used for each config in the code block.
-- The [references](#references) guide is used for reconfigure/restart.
+- The [GitLab Restart](#gitlab-restart) section is used to explain a required restart/reconfigure of GitLab.
## API
diff --git a/doc/development/documentation/workflow.md b/doc/development/documentation/workflow.md
index 7c32c92b147..0abfe4b82a4 100644
--- a/doc/development/documentation/workflow.md
+++ b/doc/development/documentation/workflow.md
@@ -2,9 +2,9 @@
description: Learn the processes for contributing to GitLab's documentation.
---
-# Documentation workflows at GitLab
+# Documentation workflows
-Documentation workflows at GitLab differ depending on the reason for the change. The two types of documentation changes are:
+Documentation workflows at GitLab differ depending on the reason for the change:
-- [Feature-change documentation workflow](feature-change-workflow.md) - The documentation is being created or updated as part of the development and release of a new or enhanced feature. This process involves the developer of the feature (who includes new/updated documentation files as part of the same merge request containing the feature's code) and also involves the product manager and technical writer who are listed for the feature's [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
+- [Documentation process for feature changes](feature-change-workflow.md) - The documentation is being created or updated as part of the development and release of a new or enhanced feature. This process involves the developer of the feature (who includes new/updated documentation files as part of the same merge request containing the feature's code) and also involves the product manager and technical writer who are listed for the feature's [DevOps stage](https://about.gitlab.com/handbook/product/categories/#devops-stages).
- [Documentation improvement workflow](improvement-workflow.md) - All documentation additions not associated with a feature release. Documentation is being created or updated to improve accuracy, completeness, ease of use, or any reason other than a feature change. Anyone (and everyone) can contribute a merge request for this type of change at any time.
diff --git a/doc/development/ee_features.md b/doc/development/ee_features.md
index 3e85c0e1995..41a64044c68 100644
--- a/doc/development/ee_features.md
+++ b/doc/development/ee_features.md
@@ -161,7 +161,7 @@ still having access the class's implementation with `super`.
There are a few gotchas with it:
-- you should always [`extend ::Gitlab::Utils::Override`] and use `override` to
+- you should always [`extend ::Gitlab::Utils::Override`](utilities.md#overridehttpsgitlabcomgitlab-orggitlab-ceblobmasterlibgitlabutilsoverriderb) and use `override` to
guard the "overrider" method to ensure that if the method gets renamed in
CE, the EE override won't be silently forgotten.
- when the "overrider" would add a line in the middle of the CE
@@ -273,8 +273,6 @@ module EE
end
```
-[`extend ::Gitlab::Utils::Override`]: utilities.md#override
-
##### Overriding CE class methods
The same applies to class methods, except we want to use
@@ -880,12 +878,104 @@ import bundle from 'ee_else_ce/protected_branches/protected_branches_bundle.js';
See the frontend guide [performance section](./fe_guide/performance.md) for
information on managing page-specific javascript within EE.
+
+## Vue code in `assets/javascript`
+### script tag
+
+#### Child Component only used in EE
+To seperate Vue template differences we should [async import the components](https://vuejs.org/v2/guide/components-dynamic-async.html#Async-Components).
+
+Doing this allows for us to load the correct component in EE whilst in CE
+we can load a empty component that renders nothing. This code **should**
+exist in the CE repository as well as the EE repository.
+
+```html
+<script>
+export default {
+ components: {
+ EEComponent: () => import('ee_component/components/test.vue'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <ee-component />
+ </div>
+</template>
+```
+
+#### For JS code that is EE only, like props, computed properties, methods, etc, we will keep the current approach
+ - Since we [can't async load a mixin](https://github.com/vuejs/vue-loader/issues/418#issuecomment-254032223) we will use the [`ee_else_ce`](https://docs.gitlab.com/ee/development/ee_features.html#javascript-code-in-assetsjavascripts) alias we already have for webpack.
+ - This means all the EE specific props, computed properties, methods, etc that are EE only should be in a mixin in the `ee/` folder and we need to create a CE counterpart of the mixin
+
+ ##### Example:
+ ```javascript
+ import mixin from 'ee_else_ce/path/mixin';
+
+ {
+ mixins: [mixin]
+ }
+ ```
+- Computed Properties/methods and getters only used in the child import still need a counterpart in CE
+
+- For store modules, we will need a CE counterpart too.
+- You can see an MR with an example [here](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/9762)
+
+#### `template` tag
+* **EE Child components**
+ - Since we are using the async loading to check which component to load, we'd still use the component's name, check [this example](#child-component-only-used-in-ee).
+
+* **EE extra HTML**
+ - For the templates that have extra HTML in EE we will use the `ifEE` mixin with the `v-if` directive.
+ - You can either use the `template` tag as a wrapper or directly in the element, if there is only one element to be rendered in EE:
+
+```html
+ <template v-if="ifEE">
+ <p>Several</p>
+ <p>non wrapper</p>
+ <p>elements</p>
+ <p>that are rendered</p>
+ <p>in EE only</p>
+ </template>
+```
+
+
+```html
+ <ul v-if="renderIfEE">
+ <li>One wrapped</li>
+ <li>element</li>
+ <li>that is rendered</li>
+ <li>in EE only</li>
+ </template>
+```
+
+### Non Vue Files
+For regular JS files, the approach is similar.
+
+1. We will keep using the [`ee_else_ce`](https://docs.gitlab.com/ee/development/ee_features.html#javascript-code-in-assetsjavascripts) helper, this means that EE only code should be inside the `ee/` folder.
+ 1. An EE file should be created with the EE only code, and it should extend the CE counterpart.
+1. For code inside functions that can't be extended, we will use an `if` statement with the `ifEE` helper
+
+##### Example:
+
+```javascript
+import { ifEE } from '~/lib/utils/common_utils'
+if (renderIfEE) {
+ $('.js-import-git-toggle-button').on('click', () => {
+ const $projectMirror = $('#project_mirror');
+
+ $projectMirror.attr('disabled', !$projectMirror.attr('disabled'));
+ });
+}
+```
+
## SCSS code in `assets/stylesheets`
To separate EE-specific styles in SCSS files, if a component you're adding styles for
is limited to only EE, it is better to have a separate SCSS file in appropriate directory
within `app/assets/stylesheets`.
-See [backporting changes](#backporting-changes) for instructions on how to merge changes safely.
+See [backporting changes](#backporting-changes-from-EE-to-CE) for instructions on how to merge changes safely.
In some cases, this is not entirely possible or creating dedicated SCSS file is an overkill,
e.g. a text style of some component is different for EE. In such cases,
diff --git a/doc/development/fe_guide/architecture.md b/doc/development/fe_guide/architecture.md
index aebb22caa15..c67389b169e 100644
--- a/doc/development/fe_guide/architecture.md
+++ b/doc/development/fe_guide/architecture.md
@@ -11,12 +11,9 @@ Architectural decisions should be accessible to everyone, so please document
them in the relevant Merge Request discussion or by updating our documentation
when appropriate.
-You can find the Frontend Architecture experts on the [team page][team-page].
+You can find the Frontend Architecture experts on the [team page](https://about.gitlab.com/team).
## Examples
You can find documentation about the desired architecture for a new feature
-built with Vue.js [here][vue-section].
-
-[team-page]: https://about.gitlab.com/team
-[vue-section]: vue.md#frontend.html#how-to-build-a-new-feature-with-vue-js
+built with Vue.js [here](vue.md).
diff --git a/doc/development/fe_guide/development_process.md b/doc/development/fe_guide/development_process.md
index 905668eef58..f3fdaa3b883 100644
--- a/doc/development/fe_guide/development_process.md
+++ b/doc/development/fe_guide/development_process.md
@@ -61,17 +61,19 @@ Please use your best judgement when to use it and please contribute new points t
---
### Share your work early
+
1. Before writing code, ensure your vision of the architecture is aligned with
-GitLab's architecture.
+ GitLab's architecture.
1. Add a diagram to the issue and ask a frontend architect in the slack channel `#fe_architectural` about it.
![Diagram of Issue Boards Architecture](img/boards_diagram.png)
1. Don't take more than one week between starting work on a feature and
-sharing a Merge Request with a reviewer or a maintainer.
+ sharing a Merge Request with a reviewer or a maintainer.
### Vue features
+
1. Follow the steps in [Vue.js Best Practices](vue.md)
1. Follow the style guide.
1. Only a handful of people are allowed to merge Vue related features.
-Reach out to one of Vue experts early in this process.
+ Reach out to one of Vue experts early in this process.
diff --git a/doc/development/fe_guide/droplab/plugins/filter.md b/doc/development/fe_guide/droplab/plugins/filter.md
index ddc6a3386c7..1f188c64fe4 100644
--- a/doc/development/fe_guide/droplab/plugins/filter.md
+++ b/doc/development/fe_guide/droplab/plugins/filter.md
@@ -9,7 +9,7 @@ Add the `Filter` object to the plugins array of a `DropLab.prototype.init` or `D
- `Filter` requires a config value for `template`.
- `template` should be the key of the objects within your data array that you want to compare
-to the user input string, for filtering.
+ to the user input string, for filtering.
```html
<input href="#" id="trigger" data-dropdown-trigger="#list">
diff --git a/doc/development/fe_guide/droplab/plugins/input_setter.md b/doc/development/fe_guide/droplab/plugins/input_setter.md
index e229103e462..e4050213869 100644
--- a/doc/development/fe_guide/droplab/plugins/input_setter.md
+++ b/doc/development/fe_guide/droplab/plugins/input_setter.md
@@ -9,7 +9,7 @@ Add the `InputSetter` object to the plugins array of a `DropLab.prototype.init`
- `InputSetter` requires a config value for `input` and `valueAttribute`.
- `input` should be the DOM element that you want to manipulate.
- `valueAttribute` should be a string that is the name of an attribute on your list items that is used to get the value
-to update the `input` element with.
+ to update the `input` element with.
You can also set the `InputSetter` config to an array of objects, which will allow you to update multiple elements.
diff --git a/doc/development/fe_guide/event_tracking.md b/doc/development/fe_guide/event_tracking.md
new file mode 100644
index 00000000000..6ab3fa4acf3
--- /dev/null
+++ b/doc/development/fe_guide/event_tracking.md
@@ -0,0 +1,91 @@
+# Event Tracking
+
+We use [Snowplow](https://github.com/snowplow/snowplow) for tracking custom events (available in GitLab [Enterprise Edition](https://about.gitlab.com/pricing/) only).
+
+## Generic tracking function
+
+In addition to Snowplow's built-in method for tracking page views, we use a generic tracking function which enables us to selectively apply listeners to events.
+
+The generic tracking function can be imported in EE-specific JS files as follows:
+
+```javascript
+import { trackEvent } from `ee/stats`;
+```
+
+This gives the user access to the `trackEvent` method, which takes the following parameters:
+
+| parameter | type | description | required |
+| ---------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
+| `category` | string | Describes the page that you're capturing click events on. Unless infeasible, please use the Rails page attribute `document.body.dataset.page` by default. | true |
+| `eventName` | string | Describes the action the user is taking. The first word should always describe the action. For example, clicks should be `click` and activations should be `activate`. Use underscores to describe what was acted on. For example, activating a form field would be `activate_form_input`. Clicking on a dropdown is `click_dropdown`. | true |
+| `additionalData` | object | Additional data such as `label`, `property`, and `value` as described [in our Feature Instrumentation taxonomy](https://about.gitlab.com/handbook/product/feature-instrumentation/#taxonomy). | false |
+
+Read more about instrumentation and the taxonomy in the [Product Handbook](https://about.gitlab.com/handbook/product/feature-instrumentation).
+
+### Tracking in `.js` and `.vue` files
+
+The most simple use case is to add tracking programmatically to an event of interest in Javascript.
+
+The following example demonstrates how to track a click on a button in Javascript by calling the `trackEvent` method explicitly:
+
+```javascript
+import { trackEvent } from `ee/stats`;
+
+trackEvent('dashboard:projects:index', 'click_button', {
+ label: 'create_from_template',
+ property: 'template_preview',
+ value: 'rails',
+});
+```
+
+### Tracking in HAML templates
+
+Sometimes we want to track clicks for multiple elements on a page. Creating event handlers for all elements could soon turn into a tedious task.
+
+There's a more convenient solution to this problem. When working with HAML templates, we can add `data-track-*` attributes to elements of interest. This way, all elements that have both `data-track-label` and `data-track-event` attributes assigned get marked for event tracking. All we have to do is call the `bindTrackableContainer` method on a container which allows for better scoping.
+
+Below is an example of `data-track-*` attributes assigned to a button in HAML:
+
+```ruby
+%button.btn{ data: { track_label: "create_from_template", track_property: "template_preview", track_event: "click_button", track_value: "my-template" } }
+```
+
+By calling `bindTrackableContainer('.my-container')`, click handlers get bound to all elements located in `.my-container` provided that they have the necessary `data-track-*` attributes assigned to them.
+
+```javascript
+import Stats from 'ee/stats';
+
+document.addEventListener('DOMContentLoaded', () => {
+ Stats.bindTrackableContainer('.my-container', 'category');
+});
+```
+
+The second parameter in `bindTrackableContainer` is optional. If omitted, the value of `document.body.dataset.page` will be used as category instead.
+
+Below is a list of supported `data-track-*` attributes:
+
+| attribute | description | required |
+| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
+| `data-track-label` | The `label` in `trackEvent` | true |
+| `data-track-event` | The `eventName` in `trackEvent` | true |
+| `data-track-property` | The `property` in `trackEvent`. If omitted, an empty string will be used as a default value. | false |
+| `data-track-value` | The `value` in `trackEvent`. If omitted, this will be `target.value` or empty string. For checkboxes, the default value being tracked will be the element's checked attribute if `data-track-value` is omitted. | false |
+
+Since Snowplow is an Enterprise Edition feature, it's necessary to create a CE backport when adding `data-track-*` attributes to HAML templates in most cases.
+
+## Testing
+
+Snowplow can be enabled by navigating to:
+
+- **Admin area > Settings > Integrations** in the UI.
+- `admin/application_settings/integrations` in your browser.
+
+The following configuration is required:
+
+| Name | Value |
+| ------------- | ------------------------- |
+| Collector | `snowplow.trx.gitlab.net` |
+| Site ID | `gitlab` |
+| Cookie domain | `.gitlab.com` |
+
+Now the implemented tracking events can be inspected locally by looking at the network panel of the browser's development tools.
diff --git a/doc/development/fe_guide/graphql.md b/doc/development/fe_guide/graphql.md
index f55f01720fd..3290f29530a 100644
--- a/doc/development/fe_guide/graphql.md
+++ b/doc/development/fe_guide/graphql.md
@@ -9,7 +9,7 @@ read more about [Feature Flags][feature-flags].
## Apollo Client
To save duplicated clients getting created in different apps, we have a
-[default client][defualt-client] that should be used. This setups the
+[default client][default-client] that should be used. This setups the
Apollo client with the correct URL and also sets the CSRF headers.
## GraphQL Queries
@@ -27,11 +27,11 @@ the Vue application is mounted.
```javascript
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import defaultClient from '~/lib/graphql';
+import createDefaultClient from '~/lib/graphql';
Vue.use(VueApollo);
const apolloProvider = new VueApollo({
- defaultClient,
+ defaultClient: createDefaultClient(),
});
new Vue({
@@ -43,6 +43,29 @@ new Vue({
Read more about [Vue Apollo][vue-apollo] in the [Vue Apollo documentation][vue-apollo-docs].
+### Local state with `apollo-link-state`
+
+It is possible to use our Apollo setup with [apollo-link-state][apollo-link-state] by passing
+in the client state object when creating the default client.
+
+```javascript
+import Vue from 'vue';
+import VueApollo from 'vue-apollo';
+import createDefaultClient from '~/lib/graphql';
+Vue.use(VueApollo);
+
+const apolloProvider = new VueApollo({
+ defaultClient: createDefaultClient({
+ defaults: {
+ testing: true,
+ },
+ resolvers: {
+ ...
+ },
+ }),
+});
+```
+
### Testing
With [Vue test utils][vue-test-utils] it is easy to quickly test components that
@@ -81,3 +104,4 @@ Read more about the [Apollo] client in the [Apollo documentation][apollo-client-
[default-client]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/app/assets/javascripts/lib/graphql.js
[apollo-client-docs]: https://www.apollographql.com/docs/tutorial/client.html
[vue-test-utils]: https://vue-test-utils.vuejs.org/
+[apollo-link-state]: https://www.apollographql.com/docs/link/links/state.html
diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md
index 86b8972a69e..d5cc28dc00c 100644
--- a/doc/development/fe_guide/index.md
+++ b/doc/development/fe_guide/index.md
@@ -1,8 +1,5 @@
# Frontend Development Guidelines
-> **Notice:**
-> We are currently in the process of re-writing our development guide to make it easier to find information. The new guide is still WIP but viewable in [development/new_fe_guide](../new_fe_guide/index.md)
-
This document describes various guidelines to ensure consistency and quality
across GitLab's frontend team.
@@ -12,15 +9,6 @@ GitLab is built on top of [Ruby on Rails][rails] using [Haml][haml] and also a J
Be wary of [the limitations that come with using Hamlit][hamlit-limits]. We also use [SCSS][scss] and plain JavaScript with
modern ECMAScript standards supported through [Babel][babel] and ES module support through [webpack][webpack].
-### Javascript development
-
-[Vue.js][vue] is used for particularly advanced, dynamic elements and based on previous iterations [jQuery][jquery] is used in lot of places through the application's JavaScript.
-
-We also use [Axios][axios] to handle all of our network requests.
-
-We also utilize [webpack][webpack] to handle the bundling, minification, and
-compression of our assets.
-
Working with our frontend assets requires Node (v8.10.0 or greater) and Yarn
(v1.10.0 or greater). You can find information on how to install these on our
[installation guide][install].
@@ -31,6 +19,14 @@ For our currently-supported browsers, see our [requirements][requirements].
---
+## Initiatives
+
+Current high-level frontend goals are listed on [Frontend Epics](https://gitlab.com/groups/gitlab-org/-/epics?label_name%5B%5D=frontend).
+
+## [Principles](principles.md)
+
+High-level guidelines for contributing to GitLab.
+
## [Development Process](development_process.md)
How we plan and execute the work on the frontend.
@@ -73,6 +69,10 @@ How we use SVG for our Icons and Illustrations.
How we use UI components.
+## [Event Tracking](event_tracking.md)
+
+How we use Snowplow to track custom events.
+
---
## Style Guides
@@ -124,14 +124,3 @@ The [externalization part of the guide](../i18n/externalization.md) explains the
[scss-lint]: https://github.com/brigade/scss-lint
[install]: ../../install/installation.md#4-node
[requirements]: ../../install/requirements.md#supported-web-browsers
-
----
-
-## [DropLab](droplab/droplab.md)
-
-Our internal `DropLab` dropdown library.
-
-- [DropLab](droplab/droplab.md)
-- [Ajax plugin](droplab/plugins/ajax.md)
-- [Filter plugin](droplab/plugins/filter.md)
-- [InputSetter plugin](droplab/plugins/input_setter.md)
diff --git a/doc/development/fe_guide/performance.md b/doc/development/fe_guide/performance.md
index 0aba38aca8c..2628e95dbc1 100644
--- a/doc/development/fe_guide/performance.md
+++ b/doc/development/fe_guide/performance.md
@@ -5,6 +5,7 @@
### Realtime Components
When writing code for realtime features we have to keep a couple of things in mind:
+
1. Do not overload the server with requests.
1. It should feel realtime.
@@ -12,16 +13,16 @@ Thus, we must strike a balance between sending requests and the feeling of realt
Use the following rules when creating realtime solutions.
1. The server will tell you how much to poll by sending `Poll-Interval` in the header.
-Use that as your polling interval. This way it is [easy for system administrators to change the
-polling rate](../../administration/polling.md).
-A `Poll-Interval: -1` means you should disable polling, and this must be implemented.
+ 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 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
-controlled by the server.
+ controlled by the server.
1. The backend code will most likely be using etags. You do not and should not check for status
-`304 Not Modified`. The browser will transform it for you.
+ `304 Not Modified`. The browser will transform it for you.
### Lazy Loading Images
diff --git a/doc/development/fe_guide/principles.md b/doc/development/fe_guide/principles.md
new file mode 100644
index 00000000000..6fb3456222f
--- /dev/null
+++ b/doc/development/fe_guide/principles.md
@@ -0,0 +1,15 @@
+# Principles
+
+These principles will ensure that your frontend contribution starts off in the right direction.
+
+## Discuss architecture before implementation
+
+Discuss your architecture design in an issue before writing code. This helps decrease the review time and also provides good practice for writing and thinking about system design.
+
+## Be consistent
+
+There are multiple ways of writing code to accomplish the same results. We should be as consistent as possible in how we write code across our codebases. This will make it more easier us to maintain our code across GitLab.
+
+## Improve code [iteratively](https://about.gitlab.com/handbook/values/#iteration)
+
+Whenever you see with existing code that does not follow our current style guide, update it proactively. You don't need to fix everything, but each merge request should iteratively improve our codebase, and reduce technical debt where possible.
diff --git a/doc/development/fe_guide/style_guide_js.md b/doc/development/fe_guide/style_guide_js.md
index 1e0529262ad..060cd8baf7f 100644
--- a/doc/development/fe_guide/style_guide_js.md
+++ b/doc/development/fe_guide/style_guide_js.md
@@ -14,9 +14,9 @@ See [our current .eslintrc](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/
#### ESlint
1. **Never** disable eslint rules unless you have a good reason.
-You may see a lot of legacy files with `/* eslint-disable some-rule, some-other-rule */`
-at the top, but legacy files are a special case. Any time you develop a new feature or
-refactor an existing one, you should abide by the eslint rules.
+ You may see a lot of legacy files with `/* eslint-disable some-rule, some-other-rule */`
+ at the top, but legacy files are a special case. Any time you develop a new feature or
+ refactor an existing one, you should abide by the eslint rules.
1. **Never Ever EVER** disable eslint globally for a file
@@ -53,7 +53,7 @@ refactor an existing one, you should abide by the eslint rules.
1. [class-methods-use-this][eslint-this]
1. When they are needed _always_ place ESlint directive comment blocks on the first line of a script,
-followed by any global declarations, then a blank newline prior to any imports or code.
+ followed by any global declarations, then a blank newline prior to any imports or code.
```javascript
// bad
@@ -125,8 +125,8 @@ followed by any global declarations, then a blank newline prior to any imports o
```
1. Relative paths: when importing a module in the same directory, a child
-directory, or an immediate parent directory prefer relative paths. When
-importing a module which is two or more levels up, prefer either `~/` or `ee/`.
+ directory, or an immediate parent directory prefer relative paths. When
+ importing a module which is two or more levels up, prefer either `~/` or `ee/`.
In **app/assets/javascripts/my-feature/subdir**:
@@ -163,9 +163,9 @@ importing a module which is two or more levels up, prefer either `~/` or `ee/`.
```
1. Avoid using IIFE. Although we have a lot of examples of files which wrap their
-contents in IIFEs (immediately-invoked function expressions),
-this is no longer necessary after the transition from Sprockets to webpack.
-Do not use them anymore and feel free to remove them when refactoring legacy code.
+ contents in IIFEs (immediately-invoked function expressions),
+ this is no longer necessary after the transition from Sprockets to webpack.
+ Do not use them anymore and feel free to remove them when refactoring legacy code.
1. Avoid adding to the global namespace.
```javascript
@@ -484,8 +484,8 @@ Please check this [rules][eslint-plugin-vue-rules] for more documentation.
```
1. Default key should be provided if the prop is not required.
-_Note:_ There are some scenarios where we need to check for the existence of the property.
-On those a default key should not be provided.
+ _Note:_ There are some scenarios where we need to check for the existence of the property.
+ On those a default key should not be provided.
```javascript
// good
diff --git a/doc/development/fe_guide/testing.md b/doc/development/fe_guide/testing.md
index 98e499b8c0f..b23e37d1eef 100644
--- a/doc/development/fe_guide/testing.md
+++ b/doc/development/fe_guide/testing.md
@@ -1 +1,5 @@
-This document was moved to [../testing_guide/frontend_testing.md](../testing_guide/frontend_testing.md).
+---
+redirect_to: '../testing_guide/frontend_testing.md'
+---
+
+This document was moved to [another location](../testing_guide/frontend_testing.md).
diff --git a/doc/development/fe_guide/vue.md b/doc/development/fe_guide/vue.md
index 60f322c6e75..a34d1fcec89 100644
--- a/doc/development/fe_guide/vue.md
+++ b/doc/development/fe_guide/vue.md
@@ -120,9 +120,16 @@ You can read more about components in Vue.js site, [Component System][component-
#### Vuex
Check this [page](vuex.md) for more details.
+### Mixing Vue and jQuery
+
+- Mixing Vue and jQuery is not recommended.
+- If you need to use a specific jQuery plugin in Vue, [create a wrapper around it][https://vuejs.org/v2/examples/select2.html].
+- It is acceptable for Vue to listen to existing jQuery events using jQuery event listeners.
+- It is not recommended to add new jQuery events for Vue to interact with jQuery.
+
## Style guide
-Please refer to the Vue section of our [style guide](style_guide_js.md#vue-js)
+Please refer to the Vue section of our [style guide](style_guide_js.md#vuejs)
for best practices while writing your Vue components and templates.
## Testing Vue Components
@@ -132,7 +139,7 @@ Each Vue component has a unique output. This output is always present in the ren
Although we can test each method of a Vue component individually, our goal must be to test the output
of the render/template function, which represents the state at all times.
-Make use of the [axios mock adapter](axios.md#mock-axios-response-on-tests) to mock data returned.
+Make use of the [axios mock adapter](axios.md#mock-axios-response-in-tests) to mock data returned.
Here's how we would test the Todo App above:
@@ -223,6 +230,7 @@ need to test the rendered output. [Vue][vue-test] guide's to unit test show us e
## Vue.js Expert Role
One should apply to be a Vue.js expert by opening an MR when the Merge Request's they create and review show:
+
- Deep understanding of Vue and Vuex reactivy
- Vue and Vuex code are structured according to both official and our guidelines
- Full understanding of testing a Vue and Vuex application
diff --git a/doc/development/fe_guide/vuex.md b/doc/development/fe_guide/vuex.md
index 4b60ec80cb8..7b54fa6289c 100644
--- a/doc/development/fe_guide/vuex.md
+++ b/doc/development/fe_guide/vuex.md
@@ -52,7 +52,7 @@ The first thing you should do before writing any code is to design the state.
Often we need to provide data from haml to our Vue application. Let's store it in the state for better access.
```javascript
- export default {
+ export default () => ({
endpoint: null,
isLoading: false,
@@ -62,7 +62,7 @@ Often we need to provide data from haml to our Vue application. Let's store it i
errorAddingUser: false,
users: [],
- };
+ });
```
#### Access `state` properties
diff --git a/doc/development/feature_flags.md b/doc/development/feature_flags.md
index 67356a12eba..3271f9a7fb3 100644
--- a/doc/development/feature_flags.md
+++ b/doc/development/feature_flags.md
@@ -26,8 +26,9 @@ the time, you should execute `/chatops run feature set my_feature_flag 50`.
## Feature flags for user applications
-GitLab does not yet support the use of feature flags in deployed user applications.
-You can follow the progress on that [in the issue on our issue tracker](https://gitlab.com/gitlab-org/gitlab-ee/issues/779).
+This document only covers feature flags used in the development of GitLab
+itself. Feature flags in deployed user applications can be found at
+[Feature Flags](https://docs.gitlab.com/ee/user/project/operations/feature_flags.html)
## Developing with feature flags
diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md
new file mode 100644
index 00000000000..81c5f69c7b8
--- /dev/null
+++ b/doc/development/git_object_deduplication.md
@@ -0,0 +1,261 @@
+# How Git object deduplication works in GitLab
+
+When a GitLab user [forks a project](../workflow/forking_workflow.md),
+GitLab creates a new Project with an associated Git repository that is a
+copy of the original project at the time of the fork. If a large project
+gets forked often, this can lead to a quick increase in Git repository
+storage disk use. To counteract this problem, we are adding Git object
+deduplication for forks to GitLab. In this document, we will describe how
+GitLab implements Git object deduplication.
+
+## Enabling Git object deduplication via feature flags
+
+As of GitLab 11.9, Git object deduplication in GitLab is in beta. In this
+document, you can read about the caveats of enabling the feature. Also,
+note that Git object deduplication is limited to forks of public
+projects on hashed repository storage.
+
+You can enable deduplication globally by setting the `object_pools`
+feature flag to `true`:
+
+``` {.ruby}
+Feature.enable(:object_pools)
+```
+
+Or just for forks of a specific project:
+
+``` {.ruby}
+fork_parent = Project.find(MY_PROJECT_ID)
+Feature.enable(:object_pools, fork_parent)
+```
+
+To check if a project uses Git object deduplication, look in a Rails
+console if `project.pool_repository` is present.
+
+## Pool repositories
+
+### Understanding Git alternates
+
+At the Git level, we achieve deduplication by using [Git
+alternates](https://git-scm.com/docs/gitrepository-layout#gitrepository-layout-objects).
+Git alternates is a mechanism that lets a repository borrow objects from
+another repository on the same machine.
+
+If we want repository A to borrow from repository B, we first write a
+path that resolves to `B.git/objects` in the special file
+`A.git/objects/info/alternates`. This establishes the alternates link.
+Next, we must perform a Git repack in A. After the repack, any objects
+that are duplicated between A and B will get deleted from A. Repository
+A is now no longer self-contained, but it still has its own refs and
+configuration. Objects in A that are not in B will remain in A. For this
+to work, it is of course critical that **no objects ever get deleted from
+B** because A might need them.
+
+### Git alternates in GitLab: pool repositories
+
+GitLab organizes this object borrowing by creating special **pool
+repositories** which are hidden from the user. We then use Git
+alternates to let a collection of project repositories borrow from a
+single pool repository. We call such a collection of project
+repositories a pool. Pools form star-shaped networks of repositories
+that borrow from a single pool, which will resemble (but not be
+identical to) the fork networks that get formed when users fork
+projects.
+
+At the Git level, pool repositories are created and managed using Gitaly
+RPC calls. Just like with normal repositories, the authority on which
+pool repositories exist, and which repositories borrow from them, lies
+at the Rails application level in SQL.
+
+In conclusion, we need three things for effective object deduplication
+across a collection of GitLab project repositories at the Git level:
+
+1. A pool repository must exist.
+2. The participating project repositories must be linked to the pool
+ repository via their respective `objects/info/alternates` files.
+3. The pool repository must contain Git object data common to the
+ participating project repositories.
+
+### Deduplication factor
+
+The effectiveness of Git object deduplication in GitLab depends on the
+amount of overlap between the pool repository and each of its
+participants. As of GitLab 11.9, we have a somewhat optimistic system.
+The only data that will be deduplicated is the data in the source
+project repository at the time the pool repository is created. That is,
+the data in the source project at the time of the first fork *after* the
+deduplication feature has been enabled.
+
+When we enable the object deduplication feature for
+gitlab.com/gitlab-org/gitlab-ce, which is about 1GB at the time of
+writing, all new forks of that project would be 1GB smaller than they
+would have been without Git object deduplication. So even in its current
+optimistic form, we expect Git object deduplication in GitLab to make a
+difference.
+
+However, if a lot of Git objects get added to the project repositories
+in a pool after the pool repository was created these new Git objects
+will currently (GitLab 11.9) not get deduplicated. Over time, the
+deduplication factor of the pool will get worse and worse.
+
+As an extreme example, if we create an empty repository A, and fork that
+to repository B, behind the scenes we get an object pool P with no
+objects in it at all. If we then push 1GB of Git data to A, and push the
+same Git data to B, it will not get deduplicated, because that data was
+not in A at the time P was created.
+
+This also matters in less extreme examples. Consider a pool P with
+source project A and 500 active forks B1, B2,...,B500. Suppose,
+optimistically, that the forks are fully deduplicated at the start of
+our scenario. Now some time passes and 200MB of new Git data gets added
+to project A. Because of the forking workflow, this data makes also its way
+into the forks B1, ..., B500. That means we would now have 100GB of Git
+data sitting around (500 \* 200MB) across the forks, that could have
+been deduplicated. But because of the way we do deduplication this new
+data will not be deduplicated.
+
+> TODO Add periodic maintenance of object pools to prevent gradual loss
+> of deduplication over time.
+> https://gitlab.com/groups/gitlab-org/-/epics/524
+
+## SQL model
+
+As of GitLab 11.8, project repositories in GitLab do not have their own
+SQL table. They are indirectly identified by columns on the `projects`
+table. In other words, the only way to look up a project repository is to
+first look up its project, and then call `project.repository`.
+
+With pool repositories we made a fresh start. These live in their own
+`pool_repositories` SQL table. The relations between these two tables
+are as follows:
+
+- a `Project` belongs to at most one `PoolRepository`
+ (`project.pool_repository`)
+- as an automatic consequence of the above, a `PoolRepository` has
+ many `Project`s
+- a `PoolRepository` has exactly one "source `Project`"
+ (`pool.source_project`)
+
+### Assumptions
+
+- All repositories in a pool must use [hashed
+ storage](../administration/repository_storage_types.md). This is so
+ that we don't have to ever worry about updating paths in
+ `object/info/alternates` files.
+- All repositories in a pool must be on the same Gitaly storage shard.
+ The Git alternates mechanism relies on direct disk access across
+ multiple repositories, and we can only assume direct disk access to
+ be possible within a Gitaly storage shard.
+- All project repositories in a pool must have "Public" visibility in
+ GitLab at the time they join. There are gotchas around visibility of
+ Git objects across alternates links. This restriction is a defense
+ against accidentally leaking private Git data.
+- The only two ways to remove a member project from a pool are (1) to
+ delete the project or (2) to move the project to another Gitaly
+ storage shard.
+
+### Creating pools and pool memberships
+
+- When a pool gets created, it must have a source project. The initial
+ contents of the pool repository are a Git clone of the source
+ project repository.
+- The occasion for creating a pool is when an existing eligible
+ (public, hashed storage, non-forked) GitLab project gets forked and
+ this project does not belong to a pool repository yet. The fork
+ parent project becomes the source project of the new pool, and both
+ the fork parent and the fork child project become members of the new
+ pool.
+- Once project A has become the source project of a pool, all future
+ eligible forks of A will become pool members.
+- If the fork source is itself a fork, the resulting repository will
+ neither join the repository nor will a new pool repository be
+ seeded.
+
+ eg:
+
+ Suppose fork A is part of a pool repository, any forks created off
+ of fork A *will not* be a part of the pool repository that fork A is
+ a part of.
+
+ Suppose B is a fork of A, and A does not belong to an object pool.
+ Now C gets created as a fork of B. C will not be part of a pool
+ repository.
+
+> TODO should forks of forks be deduplicated?
+> https://gitlab.com/gitlab-org/gitaly/issues/1532
+
+### Consequences
+
+- If a normal Project participating in a pool gets moved to another
+ Gitaly storage shard, its "belongs to PoolRepository" relation must
+ be broken. Because of the way moving repositories between shard is
+ implemented, we will automatically get a fresh self-contained copy
+ of the project's repository on the new storage shard.
+- If the source project of a pool gets moved to another Gitaly storage
+ shard or is deleted, we may have to break the "PoolRepository has
+ one source Project" relation?
+
+> TODO What happens, or should happen, if a source project changes
+> visibility, is deleted, or moves to another storage shard?
+> https://gitlab.com/gitlab-org/gitaly/issues/1488
+
+## Consistency between the SQL pool relation and Gitaly
+
+As far as Gitaly is concerned, the SQL pool relations make two types of
+claims about the state of affairs on the Gitaly server: pool repository
+existence, and the existence of an alternates connection between a
+repository and a pool.
+
+### Pool existence
+
+If GitLab thinks a pool repository exists (i.e. it exists according to
+SQL), but it does not on the Gitaly server, then certain RPC calls that
+take the object pool as an argument will fail.
+
+> TODO What happens if SQL says the pool repo exists but Gitaly says it
+> does not? https://gitlab.com/gitlab-org/gitaly/issues/1533
+
+If GitLab thinks a pool does not exist, while it does exist on disk,
+that has no direct consequences on its own. However, if other
+repositories on disk borrow objects from this unknown pool repository
+then we risk data loss, see below.
+
+### Pool relation existence
+
+There are three different things that can go wrong here.
+
+#### 1. SQL says repo A belongs to pool P but Gitaly says A has no alternate objects
+
+In this case, we miss out on disk space savings but all RPC's on A itself
+will function fine. As long as Git can find all its objects, it does not
+matter exactly where those objects are.
+
+#### 2. SQL says repo A belongs to pool P1 but Gitaly says A has alternate objects in pool P2
+
+If we are not careful, this situation can lead to data loss. During some
+operations (repository maintenance), GitLab will try to re-link A to its
+pool P1. If this clobbers the existing link to P2, then A will loose Git
+objects and become invalid.
+
+Also, keep in mind that if GitLab's database got messed up, it may not
+even know that P2 exists.
+
+> TODO Ensure that Gitaly will not clobber existing, unexpected
+> alternates links. https://gitlab.com/gitlab-org/gitaly/issues/1534
+
+#### 3. SQL says repo A does not belong to any pool but Gitaly says A belongs to P
+
+This has the same data loss possibility as scenario 2 above.
+
+## Git object deduplication and GitLab Geo
+
+When a pool repository record is created in SQL on a Geo primary, this
+will eventually trigger an event on the Geo secondary. The Geo secondary
+will then create the pool repository in Gitaly. This leads to an
+"eventually consistent" situation because as each pool participant gets
+synchronized, Geo will eventuall trigger garbage collection in Gitaly on
+the secondary, at which stage Git objects will get deduplicated.
+
+> TODO How do we handle the edge case where at the time the Geo
+> secondary tries to create the pool repository, the source project does
+> not exist? https://gitlab.com/gitlab-org/gitaly/issues/1533
diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md
index d5fc403bf8b..27b69ba8278 100644
--- a/doc/development/gitaly.md
+++ b/doc/development/gitaly.md
@@ -3,10 +3,17 @@
[Gitaly](https://gitlab.com/gitlab-org/gitaly) is a high-level Git RPC service used by GitLab CE/EE,
Workhorse and GitLab-Shell.
+## Beginner's guide
+
+Start by reading the gitaly repository's
+[Beginner's guide to Gitaly contributions](https://gitlab.com/gitlab-org/gitaly/blob/master/doc/beginners_guide.md).
+It describes how to setup gitaly, the various components of gitaly and what they do, and how to run its test suites.
+
## Developing new Git features
-Starting with GitLab 10.8, all new Git features should be developed in
-Gitaly.
+To read or write Git data, a request has to be made to Gitaly. This means that
+if you're developing a new feature where you need data that's not yet available
+in `lib/gitlab/git` changes have to be made to Gitaly.
> This is a new process that is not clearly defined yet. If you want
to contribute a Git feature and you're getting stuck, reach out to the
@@ -56,6 +63,44 @@ If your test-suite is failing with Gitaly issues, as a first step, try running:
rm -rf tmp/tests/gitaly
```
+## Legacy Rugged code
+
+While Gitaly can handle all Git access, many of GitLab customers still
+run Gitaly atop NFS. The legacy Rugged implementation for Git calls may
+be faster than the Gitaly RPC due to N+1 Gitaly calls and other
+reasons. See [the
+issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/57317) for more
+details.
+
+Until GitLab has eliminated most of these inefficiencies or the use of
+NFS is discontinued for Git data, Rugged implementations of some of the
+most commonly-used RPCs can be enabled via feature flags:
+
+* `rugged_find_commit`
+* `rugged_get_tree_entries`
+* `rugged_tree_entry`
+* `rugged_commit_is_ancestor`
+
+A convenience Rake task can be used to enable or disable these flags
+all together. To enable:
+
+```sh
+bundle exec rake gitlab:features:enable_rugged
+```
+
+To disable:
+
+```sh
+bundle exec rake gitlab:features:disable_rugged
+```
+
+Most of this code exists in the `lib/gitlab/git/rugged_impl` directory.
+
+NOTE: **Note:** You should NOT need to add or modify code related to
+Rugged unless explicitly discussed with the [Gitaly
+Team](https://gitlab.com/groups/gl-gitaly/group_members). This code will
+NOT work on GitLab.com or other GitLab instances that do not use NFS.
+
## `TooManyInvocationsError` errors
During development and testing, you may experience `Gitlab::GitalyClient::TooManyInvocationsError` failures.
@@ -152,3 +197,82 @@ as a [CI environment variable](../ci/variables/README.md#variables).
---
[Return to Development documentation](README.md)
+
+## Wrapping RPCs in Feature Flags
+
+Here are the steps to gate a new feature in Gitaly behind a feature flag.
+
+### Gitaly
+
+1. Create a package scoped flag name:
+
+ ```go
+ var findAllTagsFeatureFlag = "go-find-all-tags"
+ ```
+
+1. Create a switch in the code using the `featureflag` package:
+
+ ```go
+ if featureflag.IsEnabled(ctx, findAllTagsFeatureFlag) {
+ // go implementation
+ } else {
+ // ruby implementation
+ }
+ ```
+
+1. Create prometheus metrics:
+
+ ```go
+ var findAllTagsRequests = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "gitaly_find_all_tags_requests_total",
+ Help: "Counter of go vs ruby implementation of FindAllTags",
+ },
+ []string{"implementation"},
+ )
+ )
+
+ func init() {
+ prometheus.Register(findAllTagsRequests)
+ }
+
+ if featureflag.IsEnabled(ctx, findAllTagsFeatureFlag) {
+ findAllTagsRequests.WithLabelValues("go").Inc()
+ // go implementation
+ } else {
+ findAllTagsRequests.WithLabelValues("ruby").Inc()
+ // ruby impelmentation
+ }
+ ```
+
+1. Set headers in tests:
+
+ ```go
+ import (
+ "google.golang.org/grpc/metadata"
+
+ "gitlab.com/gitlab-org/gitaly/internal/featureflag"
+ )
+
+ //...
+
+ md := metadata.New(map[string]string{featureflag.HeaderKey(findAllTagsFeatureFlag): "true"})
+ ctx = metadata.NewOutgoingContext(context.Background(), md)
+
+ c, err = client.FindAllTags(ctx, rpcRequest)
+ require.NoError(t, err)
+ ```
+
+### Gitlab-Rails
+
+1. Add feature flag to `lib/gitlab/gitaly_client.rb` (in gitlab-rails):
+
+ ```ruby
+ SERVER_FEATURE_FLAGS = %w[go-find-all-tags].freeze
+ ```
+
+1. Test in rails console by setting feature flag:
+
+ ```ruby
+ Feature.enable('gitaly_go-find-all-tags')
+ ```
diff --git a/doc/development/go_guide/index.md b/doc/development/go_guide/index.md
index cdc806a2d31..6dcade3bb51 100644
--- a/doc/development/go_guide/index.md
+++ b/doc/development/go_guide/index.md
@@ -26,7 +26,7 @@ Reviewers and maintainers should pay attention to:
- `defer` functions: ensure the presence when needed, and after `err` check.
- Inject dependencies as parameters.
-- Void structs when marshalling to JSON (generates `null` instead of `[]`).
+- Void structs when marshaling to JSON (generates `null` instead of `[]`).
### Security
@@ -161,12 +161,39 @@ in the code.
### Logging
-The usage of a logging library is strongly recommended for daemons. Even though
-there is a `log` package in the standard library, we generally use
-[logrus](https://github.com/sirupsen/logrus). Its plugin ("hooks") system
+The usage of a logging library is strongly recommended for daemons. Even
+though there is a `log` package in the standard library, we generally use
+[Logrus](https://github.com/sirupsen/logrus). Its plugin ("hooks") system
makes it a powerful logging library, with the ability to add notifiers and
formatters at the logger level directly.
+#### Structured (JSON) logging
+
+Every binary ideally must have structured (JSON) logging in place as it helps
+with searching and filtering the logs. At GitLab we use structured logging in
+JSON format, as all our infrastructure assumes that. When using
+[Logrus](https://github.com/sirupsen/logrus) you can turn on structured
+logging simply by using the build in [JSON
+formatter](https://github.com/sirupsen/logrus#formatters). This follows the
+same logging type we use in our [Ruby
+applications](../logging.md#use-structured-json-logging).
+
+#### How to use Logrus
+
+There are a few guidelines one should follow when using the
+[Logrus](https://github.com/sirupsen/logrus) package:
+
+- When printing an error use
+ [WithError](https://godoc.org/github.com/sirupsen/logrus#WithError). For
+ example, `logrus.WithError(err).Error("Failed to do something")`.
+- Since we use [structured logging](#structured-json-logging) we can log
+ fields in the context of that code path, such as the URI of the request using
+ [`WithField`](https://godoc.org/github.com/sirupsen/logrus#WithField) or
+ [`WithFields`](https://godoc.org/github.com/sirupsen/logrus#WithFields). For
+ example, `logrus.WithField("file", "/app/go).Info("Opening dir")`. If you
+ have to log multiple keys, always use `WithFields` instead of calling
+ `WithField` more than once.
+
### Tracing and Correlation
[LabKit](https://gitlab.com/gitlab-org/labkit) is a place to keep common
diff --git a/doc/development/i18n/merging_translations.md b/doc/development/i18n/merging_translations.md
index 2fa7558d30b..85284d8c714 100644
--- a/doc/development/i18n/merging_translations.md
+++ b/doc/development/i18n/merging_translations.md
@@ -32,8 +32,7 @@ clicking `Pause sync` on the [Crowdin integration settings
page](https://translate.gitlab.com/project/gitlab-ee/settings#integration).
When all failures are resolved, the translations need to be double
-checked once more [as discussed in this
-issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/37850).
+checked once more as discussed in [confidential issue](https://docs.gitlab.com/ee/user/project/issues/confidential_issues.html) `https://gitlab.com/gitlab-org/gitlab-ce/issues/37850`.
## Merging translations
diff --git a/doc/development/i18n/proofreader.md b/doc/development/i18n/proofreader.md
index 6054873cb46..dd338f6f67d 100644
--- a/doc/development/i18n/proofreader.md
+++ b/doc/development/i18n/proofreader.md
@@ -47,6 +47,7 @@ are very appreciative of the work done by translators and proofreaders!
- Hungarian
- Proofreaders needed.
- Indonesian
+ - Adi Ferdian - [GitLab](https://gitlab.com/adiferd), [Crowdin](https://crowdin.com/profile/adiferd)
- Ahmad Naufal Mukhtar - [GitLab](https://gitlab.com/anaufalm), [Crowdin](https://crowdin.com/profile/anaufalm)
- Italian
- Paolo Falomo - [GitLab](https://gitlab.com/paolofalomo), [Crowdin](https://crowdin.com/profile/paolo.falomo)
diff --git a/doc/development/i18n_guide.md b/doc/development/i18n_guide.md
index f6e949b5fd8..e588b47e203 100644
--- a/doc/development/i18n_guide.md
+++ b/doc/development/i18n_guide.md
@@ -1 +1,5 @@
-This document was moved to [a new location](i18n/index.md).
+---
+redirect_to: 'i18n/index.md'
+---
+
+This document was moved to [another location](i18n/index.md).
diff --git a/doc/development/img/distributed_tracing_jaeger_ui.png b/doc/development/img/distributed_tracing_jaeger_ui.png
new file mode 100644
index 00000000000..57517dacced
--- /dev/null
+++ b/doc/development/img/distributed_tracing_jaeger_ui.png
Binary files differ
diff --git a/doc/development/img/distributed_tracing_performance_bar.png b/doc/development/img/distributed_tracing_performance_bar.png
new file mode 100644
index 00000000000..c9998cedd2d
--- /dev/null
+++ b/doc/development/img/distributed_tracing_performance_bar.png
Binary files differ
diff --git a/doc/development/import_export.md b/doc/development/import_export.md
index e2108605d54..f7f48b03651 100644
--- a/doc/development/import_export.md
+++ b/doc/development/import_export.md
@@ -222,7 +222,7 @@ bundle exec rake gitlab:import_export:bump_version
### Renaming columns or models
-This is a relatively common occurence that will require a version bump.
+This is a relatively common occurrence that will require a version bump.
There is also the _RC problem_ - GitLab.com runs an RC, prior to any customers,
meaning that we want to bump the version up in the next version (or patch release).
diff --git a/doc/development/logging.md b/doc/development/logging.md
index 5c1d96b9e0c..d61441813b2 100644
--- a/doc/development/logging.md
+++ b/doc/development/logging.md
@@ -32,8 +32,8 @@ These logs suffer from a number of problems:
1. They often lack timestamps or other contextual information (e.g. project ID, user)
2. They may span multiple lines, which make them hard to find via Elasticsearch.
3. They lack a common structure, which make them hard to parse by log
-forwarders, such as Logstash or Fluentd. This also makes them hard to
-search.
+ forwarders, such as Logstash or Fluentd. This also makes them hard to
+ search.
Note that currently on GitLab.com, any messages in `production.log` will
NOT get indexed by Elasticsearch due to the sheer volume and noise. They
@@ -61,12 +61,12 @@ importer. You want to log issues created, merge requests, etc. as the
importer progresses. Here's what to do:
1. Look at [the list of GitLab Logs](../administration/logs.md) to see
-if your log message might belong with one of the existing log files.
+ if your log message might belong with one of the existing log files.
1. If there isn't a good place, consider creating a new filename, but
-check with a maintainer if it makes sense to do so. A log file should
-make it easy for people to search pertinent logs in one place. For
-example, `geo.log` contains all logs pertaining to GitLab Geo.
-To create a new file:
+ check with a maintainer if it makes sense to do so. A log file should
+ make it easy for people to search pertinent logs in one place. For
+ example, `geo.log` contains all logs pertaining to GitLab Geo.
+ To create a new file:
1. Choose a filename (e.g. `importer_json.log`).
1. Create a new subclass of `Gitlab::JsonLogger`:
@@ -130,15 +130,15 @@ To create a new file:
## Additional steps with new log files
1. Consider log retention settings. By default, Omnibus will rotate any
-logs in `/var/log/gitlab/gitlab-rails/*.log` every hour and [keep at
-most 30 compressed files](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate).
-On GitLab.com, that setting is only 6 compressed files. These settings should suffice
-for most users, but you may need to tweak them in [omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab).
+ logs in `/var/log/gitlab/gitlab-rails/*.log` every hour and [keep at
+ most 30 compressed files](https://docs.gitlab.com/omnibus/settings/logs.html#logrotate).
+ On GitLab.com, that setting is only 6 compressed files. These settings should suffice
+ for most users, but you may need to tweak them in [omnibus-gitlab](https://gitlab.com/gitlab-org/omnibus-gitlab).
1. If you add a new file, submit an issue to the [production
-tracker](https://gitlab.com/gitlab-com/gl-infra/production/issues) or
-a merge request to the [gitlab_fluentd](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
-project. See [this example](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd/merge_requests/51/diffs).
+ tracker](https://gitlab.com/gitlab-com/gl-infra/production/issues) or
+ a merge request to the [gitlab_fluentd](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd)
+ project. See [this example](https://gitlab.com/gitlab-cookbooks/gitlab_fluentd/merge_requests/51/diffs).
1. Be sure to update the [GitLab CE/EE documentation](../administration/logs.md) and the [GitLab.com
-runbooks](https://gitlab.com/gitlab-com/runbooks/blob/master/howto/logging.md).
+ runbooks](https://gitlab.com/gitlab-com/runbooks/blob/master/howto/logging.md).
diff --git a/doc/development/new_fe_guide/development/accessibility.md b/doc/development/new_fe_guide/development/accessibility.md
index 2a3a126ca5c..8420a504ec4 100644
--- a/doc/development/new_fe_guide/development/accessibility.md
+++ b/doc/development/new_fe_guide/development/accessibility.md
@@ -2,7 +2,7 @@
Using semantic HTML plays a key role when it comes to accessibility.
## Accessible Rich Internet Applications - ARIA
-WAI-ARIA, the Accessible Rich Internet Applications specification, defines a way to make Web content and Web applications more accessible to people with disabilities.
+WAI-ARIA, the Accessible Rich Internet Applications specification, defines a way to make Web content and Web applications more accessible to people with disabilities.
> Note: It is [recommended][using-aria] to use semantic elements as the primary method to achieve accessibility rather than adding aria attributes. Adding aria attributes should be seen as a secondary method for creating accessible elements.
@@ -15,6 +15,7 @@ Check the list of WAI-ARIA roles [here][roles]
When using icons or images that aren't absolutely needed to understand the context, we should use `aria-hidden="true"`.
On the other hand, if an icon is crucial to understand the context we should do one of the following:
+
1. Use `aria-label` in the element with a meaningful description
1. Use `aria-labelledby` to point to an element that contains the explanation for that icon
diff --git a/doc/development/new_fe_guide/development/design_patterns.md b/doc/development/new_fe_guide/development/design_patterns.md
deleted file mode 100644
index ee06566ed30..00000000000
--- a/doc/development/new_fe_guide/development/design_patterns.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Design patterns
-
-> TODO: Add content
diff --git a/doc/development/new_fe_guide/development/index.md b/doc/development/new_fe_guide/development/index.md
index cee8e43ebad..5dced3dc466 100644
--- a/doc/development/new_fe_guide/development/index.md
+++ b/doc/development/new_fe_guide/development/index.md
@@ -1,9 +1,5 @@
# Development
-## [Design patterns](design_patterns.md)
-
-Examples of proven design patterns used in our codebase.
-
## [Components](components.md)
Documentation on existing components and how to best create a new component.
@@ -12,14 +8,6 @@ Documentation on existing components and how to best create a new component.
Learn how to implement an accessible frontend.
-## [Network requests](network_requests.md)
-
-Learn how to handle network requests in our codebase.
-
-## [Security](security.md)
-
-Learn how to ensure that our frontend is secure.
-
## [Performance](performance.md)
Learn how to keep our frontend performant.
diff --git a/doc/development/new_fe_guide/development/network_requests.md b/doc/development/new_fe_guide/development/network_requests.md
deleted file mode 100644
index 047c00313bc..00000000000
--- a/doc/development/new_fe_guide/development/network_requests.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Network requests
-
-> TODO: Add content
diff --git a/doc/development/new_fe_guide/development/security.md b/doc/development/new_fe_guide/development/security.md
deleted file mode 100644
index 5bb38f17988..00000000000
--- a/doc/development/new_fe_guide/development/security.md
+++ /dev/null
@@ -1,14 +0,0 @@
-# Security
-
-## Avoid inline scripts and styles
-
-Inline scripts and styles should be avoided in almost all cases. In an effort to protect users from [XSS vulnerabilities](https://en.wikipedia.org/wiki/Cross-site_scripting), we will be disabling inline scripts using Content Security Policy.
-
-## Including external resources
-
-External fonts, CSS, and JavaScript should never be used with the exception of Google Analytics and Piwik - and only when the instance has enabled it. Assets should always be hosted and served locally from the GitLab instance. Embedded resources via `iframes` should never be used except in certain circumstances such as with ReCaptcha, which cannot be used without an `iframe`.
-
-## Resources for security testing
-
-- [Mozilla's HTTP Observatory CLI](https://github.com/mozilla/http-observatory-cli)
-- [Qualys SSL Labs Server Test](https://www.ssllabs.com/ssltest/analyze.html)
diff --git a/doc/development/new_fe_guide/event_tracking.md b/doc/development/new_fe_guide/event_tracking.md
deleted file mode 100644
index 1958f1ce528..00000000000
--- a/doc/development/new_fe_guide/event_tracking.md
+++ /dev/null
@@ -1,74 +0,0 @@
-# Event Tracking
-
-We use [Snowplow](https://github.com/snowplow/snowplow) for tracking custom events.
-
-## Generic tracking function
-
-In addition to Snowplow's built-in method for tracking page views, we use a generic tracking function which enables us to selectively apply listeners to events.
-
-The generic tracking function can be imported in EE-specific JS files as follows:
-
-```javascript
-import { trackEvent } from `ee/stats`;
-```
-
-This gives the user access to the `trackEvent` method, which takes the following parameters:
-
-| parameter | type | description | required |
-| ---------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
-| `category` | string | Describes the page that you're capturing click events on. Unless infeasible, please use the Rails page attribute `document.body.dataset.page` by default. | true |
-| `eventName` | string | Describes the action the user is taking. The first word should always describe the action. For example, clicks should be `click` and activations should be `activate`. Use underscores to describe what was acted on. For example, activating a form field would be `activate_form_input`. Clicking on a dropdown is `click_dropdown`. | true |
-| `additionalData` | object | Additional data such as `label`, `property`, and `value` as described [in our Feature Instrumentation taxonomy](https://about.gitlab.com/handbook/product/feature-instrumentation/#taxonomy). | false |
-
-Read more about instrumentation and the taxonomy in the [Product Handbook](https://about.gitlab.com/handbook/product/feature-instrumentation).
-
-### Tracking in `.js` and `.vue` files
-
-The most simple use case is to add tracking programmatically to an event of interest in Javascript.
-
-The following example demonstrates how to track a click on a button in Javascript by calling the `trackEvent` method explicitly:
-
-```javascript
-import { trackEvent } from `ee/stats`;
-
-trackEvent('dashboard:projects:index', 'click_button', {
- label: 'create_from_template',
- property: 'template_preview',
- value: 'rails',
-});
-```
-
-### Tracking in HAML templates
-
-Sometimes we want to track clicks for multiple elements on a page. Creating event handlers for all elements could soon turn into a tedious task.
-
-There's a more convenient solution to this problem. When working with HAML templates, we can add `data-track-*` attributes to elements of interest. This way, all elements that have both `data-track-label` and `data-track-event` attributes assigned get marked for event tracking. All we have to do is call the `bindTrackableContainer` method on a container which allows for better scoping.
-
-Below is an example of `data-track-*` attributes assigned to a button in HAML:
-
-```ruby
-%button.btn{ data: { track_label: "create_from_template", track_property: "template_preview", track_event: "click_button", track_value: "my-template" } }
-```
-
-By calling `bindTrackableContainer('.my-container')`, click handlers get bound to all elements located in `.my-container` provided that they have the necessary `data-track-*` attributes assigned to them.
-
-```javascript
-import Stats from 'ee/stats';
-
-document.addEventListener('DOMContentLoaded', () => {
- Stats.bindTrackableContainer('.my-container', 'category');
-});
-```
-
-The second parameter in `bindTrackableContainer` is optional. If omitted, the value of `document.body.dataset.page` will be used as category instead.
-
-Below is a list of supported `data-track-*` attributes:
-
-| attribute | description | required |
-| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
-| `data-track-label` | The `label` in `trackEvent` | true |
-| `data-track-event` | The `eventName` in `trackEvent` | true |
-| `data-track-property` | The `property` in `trackEvent`. If omitted, an empty string will be used as a default value. | false |
-| `data-track-value` | The `value` in `trackEvent`. If omitted, this will be `target.value` or empty string. For checkboxes, the default value being tracked will be the element's checked attribute if `data-track-value` is omitted. | false |
-
-Since Snowplow is an Enterprise Edition feature, it's necessary to create a CE backport when adding `data-track-*` attributes to HAML templates in most cases.
diff --git a/doc/development/new_fe_guide/index.md b/doc/development/new_fe_guide/index.md
index 5fd5af252ef..0e8f5486861 100644
--- a/doc/development/new_fe_guide/index.md
+++ b/doc/development/new_fe_guide/index.md
@@ -3,14 +3,6 @@
This guide contains all the information to successfully contribute to GitLab's frontend.
This is a living document, and we welcome contributions, feedback and suggestions.
-## [Principles](principles.md)
-
-Ensure that your frontend contribution starts off in the right direction.
-
-## [Initiatives](initiatives.md)
-
-High level overview of where we are going from a frontend perspective.
-
## [Development](development/index.md)
Guidance on topics related to development.
@@ -27,9 +19,6 @@ Learn about all the internal JavaScript modules that make up our frontend.
Style guides to keep our code consistent.
-## [Event Tracking with Snowplow](event_tracking.md)
-
-How we use Snowplow to track custom events.
## [Tips](tips.md)
diff --git a/doc/development/new_fe_guide/initiatives.md b/doc/development/new_fe_guide/initiatives.md
deleted file mode 100644
index c81ed3579f0..00000000000
--- a/doc/development/new_fe_guide/initiatives.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Initiatives
-
-> TODO: Add Initiatives
diff --git a/doc/development/new_fe_guide/principles.md b/doc/development/new_fe_guide/principles.md
deleted file mode 100644
index 0af5f506a91..00000000000
--- a/doc/development/new_fe_guide/principles.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Principles
-
-These principles will ensure that your frontend contribution starts off in the right direction.
-
-## Discuss architecture before implementation
-
-Discuss your architecture design in an issue before writing code. This helps decrease the review time and also provides good practice for writing and thinking about system design.
-
-## Be consistent
-
-There are multiple ways of writing code to accomplish the same results. We should be as consistent as possible in how we write code across our codebases. This will make it more easier us to maintain our code across GitLab.
-
-## Enhance progressively
-
-Whenever you see with existing code that does not follow our current style guide, update it proactively. Refrain from changing everything but each merge request should progressively enhance our codebase and reduce technical debt.
-
-## When to use Vue
-
-- Use Vue for feature that make use of heavy DOM manipulation
-- Use Vue for reusable components
-
-## When to use jQuery
-
-- Use jQuery to interact with Bootstrap JavaScript components
-- Avoid jQuery when a better alternative exists. We are slowly moving away from it [#43559][jquery-future]
-
-## Mixing Vue and jQuery
-
-- Mixing Vue and jQuery is not recommended.
-- If you need to use a specific jQuery plugin in Vue, [create a wrapper around it][select2].
-- It is acceptable for Vue to listen to existing jQuery events using jQuery event listeners.
-- It is not recommended to add new jQuery events for Vue to interact with jQuery.
-
-[jquery-future]: https://gitlab.com/gitlab-org/gitlab-ce/issues/43559
-[select2]: https://vuejs.org/v2/examples/select2.html
diff --git a/doc/development/new_fe_guide/style/html.md b/doc/development/new_fe_guide/style/html.md
index 2d5b7d048ab..e8c9c2ccebf 100644
--- a/doc/development/new_fe_guide/style/html.md
+++ b/doc/development/new_fe_guide/style/html.md
@@ -2,10 +2,11 @@
## Buttons
-<a name="button-type"></a><a name="1.1"></a>
-- [1.1](#button-type) **Use button type** Button tags requires a `type` attribute according to the [W3C HTML specification][button-type-spec].
+### Button type
-```
+Button tags requires a `type` attribute according to the [W3C HTML specification](https://www.w3.org/TR/2011/WD-html5-20110525/the-button-element.html#dom-button-type).
+
+```html
// bad
<button></button>
@@ -13,10 +14,11 @@
<button type="button"></button>
```
-<a name="button-role"></a><a name="1.2"></a>
-- [1.2](#button-role) **Use button role for non buttons** If an HTML element has an onClick handler but is not a button, it should have `role="button"`. This is more [accessible][button-role-accessible].
+### Button role
-```
+If an HTML element has an `onClick` handler but is not a button, it should have `role="button"`. This is [more accessible](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role).
+
+```html
// bad
<div onClick="doSomething"></div>
@@ -26,10 +28,11 @@
## Links
-<a name="blank-links"></a><a name="2.1"></a>
-- [2.1](#blank-links) **Use rel for target blank** Use `rel="noopener noreferrer"` whenever your links open in a new window i.e. `target="_blank"`. This prevents [the following][jitbit-target-blank] security vulnerability documented by JitBit
+### Blank target
-```
+Use `rel="noopener noreferrer"` whenever your links open in a new window, i.e. `target="_blank"`. This prevents a security vulnerability [documented by JitBit](https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/).
+
+```html
// bad
<a href="url" target="_blank"></a>
@@ -37,17 +40,14 @@
<a href="url" target="_blank" rel="noopener noreferrer"></a>
```
-<a name="fake-links"></a><a name="2.2"></a>
-- [2.2](#fake-links) **Do not use fake links** Use a button tag if a link only invokes JavaScript click event handlers. This is more semantic.
+### Fake links
-```
+**Do not use fake links.** Use a button tag if a link only invokes JavaScript click event handlers, which is more semantic.
+
+```html
// bad
<a class="js-do-something" href="#"></a>
// good
<button class="js-do-something" type="button"></button>
```
-
-[button-type-spec]: https://www.w3.org/TR/2011/WD-html5-20110525/the-button-element.html#dom-button-type
-[button-role-accessible]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role
-[jitbit-target-blank]: https://www.jitbit.com/alexblog/256-targetblank---the-most-underestimated-vulnerability-ever/
diff --git a/doc/development/new_fe_guide/style/javascript.md b/doc/development/new_fe_guide/style/javascript.md
index 922fd1e4ea4..3019eaa089c 100644
--- a/doc/development/new_fe_guide/style/javascript.md
+++ b/doc/development/new_fe_guide/style/javascript.md
@@ -1,194 +1,196 @@
# JavaScript style guide
-We use [Airbnb's JavaScript Style Guide][airbnb-style-guide] and it's accompanying linter to manage most of our JavaScript style guidelines.
+We use [Airbnb's JavaScript Style Guide](https://github.com/airbnb/javascript) and it's accompanying
+linter to manage most of our JavaScript style guidelines.
-In addition to the style guidelines set by Airbnb, we also have a few specific rules listed below.
+In addition to the style guidelines set by Airbnb, we also have a few specific rules
+listed below.
> **Tip:**
You can run eslint locally by running `yarn eslint`
-## Arrays
-
-<a name="avoid-foreach"></a><a name="1.1"></a>
-- [1.1](#avoid-foreach) **Avoid ForEach when mutating data** Use `map`, `reduce` or `filter` instead of `forEach` when mutating data. This will minimize mutations in functions ([which is aligned with Airbnb's style guide][airbnb-minimize-mutations])
-
- ```
- // bad
- users.forEach((user, index) => {
- user.id = index;
- });
-
- // good
- const usersWithId = users.map((user, index) => {
- return Object.assign({}, user, { id: index });
- });
- ```
-
-## Functions
-
-<a name="limit-params"></a><a name="2.1"></a>
-- [2.1](#limit-params) **Limit number of parameters** If your function or method has more than 3 parameters, use an object as a parameter instead.
-
- ```
- // bad
- function a(p1, p2, p3) {
- // ...
- };
-
- // good
- function a(p) {
- // ...
- };
- ```
-
-## Classes & constructors
-
-<a name="avoid-constructor-side-effects"></a><a name="3.1"></a>
-- [3.1](#avoid-constructor-side-effects) **Avoid side effects in constructors** Avoid making some operations in the `constructor`, such as asynchronous calls, API requests and DOM manipulations. Prefer moving them into separate functions. This will make tests easier to write and code easier to maintain.
-
- ```javascript
- // bad
- class myClass {
- constructor(config) {
- this.config = config;
- axios.get(this.config.endpoint)
- }
- }
-
- // good
- class myClass {
- constructor(config) {
- this.config = config;
- }
-
- makeRequest() {
- axios.get(this.config.endpoint)
- }
- }
- const instance = new myClass();
- instance.makeRequest();
-
- ```
-
-<a name="avoid-classes-to-handle-dom-events"></a><a name="3.2"></a>
-- [3.2](#avoid-classes-to-handle-dom-events) **Avoid classes to handle DOM events** If the only purpose of the class is to bind a DOM event and handle the callback, prefer using a function.
-
- ```
- // bad
- class myClass {
- constructor(config) {
- this.config = config;
- }
-
- init() {
- document.addEventListener('click', () => {});
- }
- }
-
- // good
-
- const myFunction = () => {
- document.addEventListener('click', () => {
- // handle callback here
- });
- }
- ```
-
-<a name="element-container"></a><a name="3.3"></a>
-- [3.3](#element-container) **Pass element container to constructor** When your class manipulates the DOM, receive the element container as a parameter.
+## Avoid forEach
+
+Avoid forEach when mutating data. Use `map`, `reduce` or `filter` instead of `forEach`
+when mutating data. This will minimize mutations in functions,
+which aligns with [Airbnb's style guide](https://github.com/airbnb/javascript#testing--for-real).
+
+```javascript
+// bad
+users.forEach((user, index) => {
+ user.id = index;
+});
+
+// good
+const usersWithId = users.map((user, index) => {
+ return Object.assign({}, user, { id: index });
+});
+```
+
+## Limit number of parameters
+
+If your function or method has more than 3 parameters, use an object as a parameter
+instead.
+
+```javascript
+// bad
+function a(p1, p2, p3) {
+ // ...
+};
+
+// good
+function a(p) {
+ // ...
+};
+```
+
+## Avoid side effects in constructors
+
+Avoid making asynchronous calls, API requests or DOM manipulations in the `constructor`.
+Move them into separate functions instead. This will make tests easier to write and
+code easier to maintain.
+
+```javascript
+// bad
+class myClass {
+ constructor(config) {
+ this.config = config;
+ axios.get(this.config.endpoint)
+ }
+}
+
+// good
+class myClass {
+ constructor(config) {
+ this.config = config;
+ }
+
+ makeRequest() {
+ axios.get(this.config.endpoint)
+ }
+}
+const instance = new myClass();
+instance.makeRequest();
+
+```
+
+## Avoid classes to handle DOM events
+
+If the only purpose of the class is to bind a DOM event and handle the callback, prefer
+using a function.
+
+```javascript
+// bad
+class myClass {
+ constructor(config) {
+ this.config = config;
+ }
+
+ init() {
+ document.addEventListener('click', () => {});
+ }
+}
+
+// good
+
+const myFunction = () => {
+ document.addEventListener('click', () => {
+ // handle callback here
+ });
+}
+```
+
+## Pass element container to constructor
+
+When your class manipulates the DOM, receive the element container as a parameter.
This is more maintainable and performant.
- ```
- // bad
- class a {
- constructor() {
- document.querySelector('.b');
- }
- }
-
- // good
- class a {
- constructor(options) {
- options.container.querySelector('.b');
- }
- }
- ```
-
-## Type Casting & Coercion
-
-<a name="use-parseint"></a><a name="4.1"></a>
-- [4.1](#use-parseint) **Use ParseInt** Use `ParseInt` when converting a numeric string into a number.
-
- ```
- // bad
- Number('10')
-
- // good
- parseInt('10', 10);
- ```
-
-## CSS Selectors
-
-<a name="use-js-prefix"></a><a name="5.1"></a>
-- [5.1](#use-js-prefix) **Use js prefix** If a CSS class is only being used in JavaScript as a reference to the element, prefix the class name with `js-`
-
- ```
- // bad
- <button class="add-user"></button>
-
- // good
- <button class="js-add-user"></button>
- ```
-
-## Modules
-
-<a name="use-absolute-paths"></a><a name="6.1"></a>
-- [6.1](#use-absolute-paths) **Use absolute paths for nearby modules** Use absolute paths if the module you are importing is less than two levels up.
-
- ```
- // bad
- import GitLabStyleGuide from '~/guides/GitLabStyleGuide';
-
- // good
- import GitLabStyleGuide from '../GitLabStyleGuide';
- ```
-
-<a name="use-relative-paths"></a><a name="6.2"></a>
-- [6.2](#use-relative-paths) **Use relative paths for distant modules** If the module you are importing is two or more levels up, use a relative path instead of an absolute path.
-
- ```
- // bad
- import GitLabStyleGuide from '../../../guides/GitLabStyleGuide';
-
- // good
- import GitLabStyleGuide from '~/GitLabStyleGuide';
- ```
-
-<a name="global-namespace"></a><a name="6.3"></a>
-- [6.3](#global-namespace) **Do not add to global namespace**
-
-<a name="domcontentloaded"></a><a name="6.4"></a>
-- [6.4](#domcontentloaded) **Do not use DOMContentLoaded in non-page modules** Imported modules should act the same each time they are loaded. `DOMContentLoaded` events are only allowed on modules loaded in the `/pages/*` directory because those are loaded dynamically with webpack.
-
-## Security
-
-<a name="avoid-xss"></a><a name="7.1"></a>
-- [7.1](#avoid-xss) **Avoid XSS** Do not use `innerHTML`, `append()` or `html()` to set content. It opens up too many vulnerabilities.
-
-## ESLint
-
-<a name="disable-eslint-file"></a><a name="8.1"></a>
-- [8.1](#disable-eslint-file) **Disabling ESLint in new files** Do not disable ESLint when creating new files. Existing files may have existing rules disabled due to legacy compatibility reasons but they are in the process of being refactored.
-
-<a name="disable-eslint-rule"></a><a name="8.2"></a>
-- [8.2](#disable-eslint-rule) **Disabling ESLint rule** Do not disable specific ESLint rules. Due to technical debt, you may disable the following rules only if you are invoking/instantiating existing code modules
-
- - [no-new][no-new]
- - [class-method-use-this][class-method-use-this]
-
-> Note: Disable these rules on a per line basis. This makes it easier to refactor in the future. E.g. use `eslint-disable-next-line` or `eslint-disable-line`
-
-[airbnb-style-guide]: https://github.com/airbnb/javascript
-[airbnb-minimize-mutations]: https://github.com/airbnb/javascript#testing--for-real
-[no-new]: http://eslint.org/docs/rules/no-new
-[class-method-use-this]: http://eslint.org/docs/rules/class-methods-use-this
+```javascript
+// bad
+class a {
+ constructor() {
+ document.querySelector('.b');
+ }
+}
+
+// good
+class a {
+ constructor(options) {
+ options.container.querySelector('.b');
+ }
+}
+```
+
+## Use ParseInt
+
+Use `ParseInt` when converting a numeric string into a number.
+
+```javascript
+// bad
+Number('10')
+
+// good
+parseInt('10', 10);
+```
+
+## CSS Selectors - Use `js-` prefix
+
+If a CSS class is only being used in JavaScript as a reference to the element, prefix
+the class name with `js-`.
+
+```html
+// bad
+<button class="add-user"></button>
+
+// good
+<button class="js-add-user"></button>
+```
+
+## Absolute vs relative paths for modules
+
+Use relative paths if the module you are importing is less than two levels up.
+
+```javascript
+// bad
+import GitLabStyleGuide from '~/guides/GitLabStyleGuide';
+
+// good
+import GitLabStyleGuide from '../GitLabStyleGuide';
+```
+
+If the module you are importing is two or more levels up, use an absolute path instead:
+
+```javascript
+// bad
+import GitLabStyleGuide from '../../../guides/GitLabStyleGuide';
+
+// good
+import GitLabStyleGuide from '~/GitLabStyleGuide';
+```
+
+Additionally, **do not add to global namespace**.
+
+## Do not use `DOMContentLoaded` in non-page modules
+
+Imported modules should act the same each time they are loaded. `DOMContentLoaded`
+events are only allowed on modules loaded in the `/pages/*` directory because those
+are loaded dynamically with webpack.
+
+## Avoid XSS
+
+Do not use `innerHTML`, `append()` or `html()` to set content. It opens up too many
+vulnerabilities.
+
+## Disabling ESLint in new files
+
+Do not disable ESLint when creating new files. Existing files may have existing rules
+disabled due to legacy compatibility reasons but they are in the process of being refactored.
+
+Do not disable specific ESLint rules. Due to technical debt, you may disable the following
+rules only if you are invoking/instantiating existing code modules.
+
+ - [no-new](http://eslint.org/docs/rules/no-new)
+ - [class-method-use-this](http://eslint.org/docs/rules/class-methods-use-this)
+
+> Note: Disable these rules on a per line basis. This makes it easier to refactor
+ in the future. E.g. use `eslint-disable-next-line` or `eslint-disable-line`.
diff --git a/doc/development/new_fe_guide/tips.md b/doc/development/new_fe_guide/tips.md
index 881ad1662ae..889a5aab2b7 100644
--- a/doc/development/new_fe_guide/tips.md
+++ b/doc/development/new_fe_guide/tips.md
@@ -7,3 +7,19 @@ To clear production compiled assets created with `yarn webpack-prod` you can run
```
yarn clean
```
+
+## Creating feature flags in development
+
+The process for creating a feature flag is the same as [enabling a feature flag in development](https://docs.gitlab.com/ee/development/feature_flags.html#enabling-a-feature-flag-in-development).
+
+Your feature flag can now be:
+
+- [made available to the frontend](https://docs.gitlab.com/ee/development/feature_flags.html#frontend) via the `gon`
+- queried in [tests](https://docs.gitlab.com/ee/development/feature_flags.html#specs)
+- queried in HAML templates and ruby files via the `Feature.enabled?(:my_shiny_new_feature_flag)` method
+
+### More on feature flags
+
+- [Deleting a feature flag](https://docs.gitlab.com/ee/api/features.html#delete-a-feature)
+- [Manage feature flags](https://docs.gitlab.com/ee/development/feature_flags.html)
+- [Feature flags API](https://docs.gitlab.com/ee/api/features.html)
diff --git a/doc/development/ordering_table_columns.md b/doc/development/ordering_table_columns.md
index 3e49a65f5ab..cbfd05e731d 100644
--- a/doc/development/ordering_table_columns.md
+++ b/doc/development/ordering_table_columns.md
@@ -19,7 +19,7 @@ The space between rows is also subject to alignment padding. The `user_id`
column takes only 4 bytes, and on 64-bit platform, 4 zeroes will be added for
alignment padding, to allow storing the next row beginning with the "clear" word.
-As a result, the actual size of each column would be (ommiting variable length
+As a result, the actual size of each column would be (omitting variable length
data and 24-byte tuple header): 8 bytes, variable, 8 bytes. This means that
each row will require at least 16 bytes for the two 4-byte integers. If a table
has a few rows this is not an issue. However, once you start storing millions of
diff --git a/doc/development/performance.md b/doc/development/performance.md
index 32970152911..0e21d45f57c 100644
--- a/doc/development/performance.md
+++ b/doc/development/performance.md
@@ -38,6 +38,7 @@ GitLab provides built-in tools to help improve performance and availability:
- [Profiling](profiling.md).
- [Sherlock](profiling.md#sherlock).
+- [Distributed Tracing](distributed_tracing.md)
- [GitLab Performance Monitoring](../administration/monitoring/performance/index.md).
- [Request Profiling](../administration/monitoring/performance/request_profiling.md).
- [QueryRecoder](query_recorder.md) for preventing `N+1` regressions.
@@ -410,6 +411,21 @@ there's nothing stopping somebody from doing this elsewhere in the code:
SOME_CONSTANT = 'bar'
```
+## How to seed a database with millions of rows
+
+You might want millions of project rows in your local database, for example,
+in order to compare relative query performance, or to reproduce a bug. You could
+do this by hand with SQL commands, but since you have ActiveRecord models, you
+might find using these gems more convenient:
+
+- [BulkInsert gem](https://github.com/jamis/bulk_insert)
+- [ActiveRecord::PgGenerateSeries gem](https://github.com/ryu39/active_record-pg_generate_series)
+
+### Examples
+
+You may find some useful examples in this snippet:
+https://gitlab.com/gitlab-org/gitlab-ce/snippets/33946
+
[#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/policies.md b/doc/development/policies.md
index 6a3b90f3604..c4ac42bb40a 100644
--- a/doc/development/policies.md
+++ b/doc/development/policies.md
@@ -88,10 +88,10 @@ An example debug output would look as follows:
Each line represents a rule that was evaluated. There are a few things to note:
1. The `-` or `+` symbol indicates whether the rule block was evaluated to be
-`false` or `true`, respectively.
+ `false` or `true`, respectively.
2. The number inside the brackets indicates the score.
3. The last part of the line (e.g. `@john : Issue/1`) shows the username
-and subject for that rule.
+ and subject for that rule.
Here you can see that the first four rules were evaluated `false` for
which user and subject. For example, you can see in the last line that
diff --git a/doc/development/polling.md b/doc/development/polling.md
index 3b34f985cd4..76bb5ae7819 100644
--- a/doc/development/polling.md
+++ b/doc/development/polling.md
@@ -51,6 +51,7 @@ request path. By doing this we avoid query parameter ordering problems and make
route matching easier.
For more information see:
+
- [`Poll-Interval` header](fe_guide/performance.md#realtime-components)
- [RFC 7232](https://tools.ietf.org/html/rfc7232)
- [ETag proposal](https://gitlab.com/gitlab-org/gitlab-ce/issues/26926)
diff --git a/doc/development/profiling.md b/doc/development/profiling.md
index f41d635de43..b2f3a105b23 100644
--- a/doc/development/profiling.md
+++ b/doc/development/profiling.md
@@ -10,6 +10,10 @@ There is a `Gitlab::Profiler.profile` method, and corresponding
`bin/profile-url` script, that enable profiling a GET or POST request to a
specific URL, either as an anonymous user (the default) or as a specific user.
+NOTE: **Note:** The first argument to the profiler is either a full URL
+(including the instance hostname) or an absolute path, including the
+leading slash.
+
When using the script, command-line documentation is available by passing no
arguments.
@@ -86,10 +90,8 @@ 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
profile and log output to S3.
-For GitLab.com, currently the latest profiling data has been [moved from
-Redash to Looker](https://gitlab.com/gitlab-com/Product/issues/5#note_121194467).
-We are [currently investigating how to make this data
-public](https://gitlab.com/meltano/looker/issues/294).
+For GitLab.com, you can find the latest results here:
+<http://redash.gitlab.com/dashboard/gitlab-profiler-statistics>
## Sherlock
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index 4cc500ed1b6..dcb32c89f65 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -73,6 +73,7 @@ To make sure that indices still fit. You could find great details in:
## Run tests
In order to run the test you can use the following commands:
+
- `rake spec` to run the rspec suite
- `rake karma` to run the karma test suite
- `rake gitlab:test` to run all the tests
diff --git a/doc/development/rolling_out_changes_using_feature_flags.md b/doc/development/rolling_out_changes_using_feature_flags.md
index ef1aba95712..8d35a4ecee2 100644
--- a/doc/development/rolling_out_changes_using_feature_flags.md
+++ b/doc/development/rolling_out_changes_using_feature_flags.md
@@ -105,6 +105,20 @@ Flags](feature_flags.md) guide) supports rolling out changes to a percentage of
users. This in turn can be controlled using [GitLab
chatops](https://docs.gitlab.com/ee/ci/chatops/).
+For an up to date list of feature flag commands please see [the source
+code](https://gitlab.com/gitlab-com/chatops/blob/master/lib/chatops/commands/feature.rb).
+Note that all the examples in that file must be preceded by
+`/chatops run`.
+
+If you get an error "Whoops! This action is not allowed. This incident
+will be reported." that means your Slack account is not allowed to
+change feature flags. To test if you are allowed to do anything at all,
+run:
+
+```
+/chatops run feature --help
+```
+
For example, to enable a feature for 25% of all users, run the following in
Slack:
@@ -135,6 +149,20 @@ want to wait several hours or even days. This is entirely up to you, just make
sure it is clearly communicated to your team, and the Production team if you
anticipate any potential problems.
+Feature gates can also be actor based, for example a feature could first be
+enabled for only the `gitlab-ce` project. The project is passed by supplying a
+`--project` flag:
+
+```
+/chatops run feature set --project=gitlab-org/gitlab-ce some_feature true
+```
+
+For groups the `--group` flag is available:
+
+```
+/chatops run feature set --group=gitlab-org some_feature true
+```
+
Once a change is deemed stable, submit a new merge request to remove the
feature flag. This ensures the change is available to all users and self-hosted
instances. Make sure to add the ~"feature flag" label to this merge request so
@@ -188,3 +216,12 @@ to be shipped. You can do this via ChatOps:
Note that you can do this at any time, even before the merge request using the
flag has been merged!
+
+### Cleaning up
+
+When a feature gate has been removed from the code base, the value still exists
+in the database. This can be removed through ChatOps:
+
+```
+/chatops run feature delete some_feature
+```
diff --git a/doc/development/testing.md b/doc/development/testing.md
index 45b1519ece8..79ef8e75432 100644
--- a/doc/development/testing.md
+++ b/doc/development/testing.md
@@ -1 +1,5 @@
-This document was moved to [testing_guide/index.md](testing_guide/index.md).
+---
+redirect_to: 'testing_guide/index.md'
+---
+
+This document was moved to [another location](testing_guide/index.md).
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index 4cc3812b0f0..cfe0e6f70fc 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -40,7 +40,7 @@ bundle exec rspec spec/[path]/[to]/[spec].rb
to separate phases.
- Use `Gitlab.config.gitlab.host` rather than hard coding `'localhost'`
- Don't assert against the absolute value of a sequence-generated attribute (see
- [Gotchas](../gotchas.md#dont-assert-against-the-absolute-value-of-a-sequence-generated-attribute)).
+ [Gotchas](../gotchas.md#do-not-assert-against-the-absolute-value-of-a-sequence-generated-attribute)).
- Don't supply the `:each` argument to hooks since it's the default.
- On `before` and `after` hooks, prefer it scoped to `:context` over `:all`
- When using `evaluate_script("$('.js-foo').testSomething()")` (or `execute_script`) which acts on a given element,
@@ -168,12 +168,13 @@ instead of 30+ seconds in case of a regular `spec_helper`.
### `let` variables
-GitLab's RSpec suite has made extensive use of `let` variables to reduce
-duplication. However, this sometimes [comes at the cost of clarity][lets-not],
+GitLab's RSpec suite has made extensive use of `let`(along with it strict, non-lazy
+version `let!`) variables to reduce duplication. However, this sometimes [comes at the cost of clarity][lets-not],
so we need to set some guidelines for their use going forward:
-- `let` variables are preferable to instance variables. Local variables are
- preferable to `let` variables.
+- `let!` variables are preferable to instance variables. `let` variables
+ are preferable to `let!` variables. Local variables are preferable to
+ `let` variables.
- Use `let` to reduce duplication throughout an entire spec file.
- Don't use `let` to define variables used by a single test; define them as
local variables inside the test's `it` block.
@@ -183,6 +184,9 @@ so we need to set some guidelines for their use going forward:
- Try to avoid overriding the definition of one `let` variable with another.
- Don't define a `let` variable that's only used by the definition of another.
Use a helper method instead.
+- `let!` variables should be used only in case if strict evaluation with defined
+ order is required, otherwise `let` will suffice. Remember that `let` is lazy and won't
+ be evaluated until it is referenced.
[lets-not]: https://robots.thoughtbot.com/lets-not
@@ -358,16 +362,11 @@ range of inputs, might look like this:
describe "#==" do
using RSpec::Parameterized::TableSyntax
- let(:project1) { create(:project) }
- let(:project2) { create(:project) }
where(:a, :b, :result) do
1 | 1 | true
1 | 2 | false
true | true | true
true | false | false
- project1 | project1 | true
- project2 | project2 | true
- project 1 | project2 | false
end
with_them do
@@ -380,6 +379,11 @@ describe "#==" do
end
```
+CAUTION: **Caution:**
+Only use simple values as input in the `where` block. Using procs, stateful
+objects, FactoryBot-created objects etc. can lead to
+[unexpected results](https://github.com/tomykaira/rspec-parameterized/issues/8).
+
### Prometheus tests
Prometheus metrics may be preserved from one test run to another. To ensure that metrics are
diff --git a/doc/development/testing_guide/ci.md b/doc/development/testing_guide/ci.md
index 5aa668290b4..7a7fca46534 100644
--- a/doc/development/testing_guide/ci.md
+++ b/doc/development/testing_guide/ci.md
@@ -31,7 +31,11 @@ After that, the next pipeline will use the up-to-date
The GitLab test suite is [monitored] for the `master` branch, and any branch
that includes `rspec-profile` in their name.
+A [public dashboard] is available for everyone to see. Feel free to look at the
+slowest test files and try to improve them.
+
[monitored]: ../performance.md#rspec-profiling
+[public dashboard]: https://redash.gitlab.com/public/dashboards/l1WhHXaxrCWM5Ai9D7YDqHKehq6OU3bx5gssaiWe?org_slug=default
## CI setup
diff --git a/doc/development/testing_guide/end_to_end_tests.md b/doc/development/testing_guide/end_to_end_tests.md
index e9f236c6b3a..9837ea515a3 100644
--- a/doc/development/testing_guide/end_to_end_tests.md
+++ b/doc/development/testing_guide/end_to_end_tests.md
@@ -17,18 +17,25 @@ a black-box testing framework for the API and the UI.
We run scheduled pipeline each night to test nightly builds created by Omnibus.
You can find these nightly pipelines at [gitlab-org/quality/nightly/pipelines][quality-nightly-pipelines].
+Results are reported in the `#qa-nightly` Slack channel.
### Testing staging
We run scheduled pipeline each night to test staging.
You can find these nightly pipelines at [gitlab-org/quality/staging/pipelines][quality-staging-pipelines].
+Results are reported in the `#qa-staging` Slack channel.
### Testing code in merge requests
-It is possible to run end-to-end tests (eventually being run within a
-[GitLab QA pipeline][gitlab-qa-pipelines]) for a merge request by triggering
-the `package-and-qa` manual action in the `test` stage, that should be present
-in a merge request widget (unless the merge request is from a fork).
+#### Using the `package-and-qa` job
+
+It is possible to run end-to-end tests for a merge request, eventually being run in
+a pipeline in the [`gitlab-qa`](https://gitlab.com/gitlab-org/gitlab-qa/) project,
+by triggering the `package-and-qa` manual action in the `test` stage (not
+available for forks).
+
+**This runs end-to-end tests against a custom Omnibus package built from your
+merge request's changes.**
Manual action that starts end-to-end tests is also available in merge requests
in [Omnibus GitLab][omnibus-gitlab].
@@ -40,27 +47,68 @@ Below you can read more about how to use it and how does it work.
Currently, we are using _multi-project pipeline_-like approach to run QA
pipelines.
+![QA on merge requests CI/CD architecture](img/qa_on_merge_requests_cicd_architecture.png)
+
+<details>
+<summary>Show mermaid source</summary>
+<pre>
+graph LR
+ A1 -.->|1. Triggers an omnibus-gitlab pipeline and wait for it to be done| A2
+ B2[<b>`Trigger-qa` stage</b><br />`Trigger:qa-test` job] -.->|2. Triggers a gitlab-qa pipeline and wait for it to be done| A3
+
+subgraph gitlab-ce/ee pipeline
+ A1[<b>`test` stage</b><br />`package-and-qa` job]
+ end
+
+subgraph omnibus-gitlab pipeline
+ A2[<b>`Trigger-docker` stage</b></b><br />`Trigger:gitlab-docker` job] -->|once done| B2
+ end
+
+subgraph gitlab-qa pipeline
+ A3>QA jobs run] -.->|3. Reports back the pipeline result to the `package-and-qa` job<br />and post the result on the original commit tested| A1
+ end
+</pre>
+</details>
+
1. Developer triggers a manual action, that can be found in CE / EE merge
-requests. This starts a chain of pipelines in multiple projects.
+ requests. This starts a chain of pipelines in multiple projects.
1. The script being executed triggers a pipeline in [Omnibus GitLab][omnibus-gitlab]
-and waits for the resulting status. We call this a _status attribution_.
+ and waits for the resulting status. We call this a _status attribution_.
1. GitLab packages are being built in the [Omnibus GitLab][omnibus-gitlab]
-pipeline. Packages are then pushed to its Container Registry.
+ pipeline. Packages are then pushed to its Container Registry.
1. When packages are ready, and available in the registry, a final step in the
-[Omnibus GitLab][omnibus-gitlab] pipeline, triggers a new
-[GitLab QA pipeline][gitlab-qa-pipelines]. It also waits for a resulting status.
+ [Omnibus GitLab][omnibus-gitlab] pipeline, triggers a new
+ [GitLab QA pipeline][gitlab-qa-pipelines]. It also waits for a resulting status.
1. GitLab QA pulls images from the registry, spins-up containers and runs tests
-against a test environment that has been just orchestrated by the `gitlab-qa`
-tool.
+ against a test environment that has been just orchestrated by the `gitlab-qa`
+ tool.
1. The result of the [GitLab QA pipeline][gitlab-qa-pipelines] is being
-propagated upstream, through Omnibus, back to the CE / EE merge request.
+ propagated upstream, through Omnibus, back to the CE / EE merge request.
+
+#### Using the `review-qa-all` jobs
-#### How do I write tests?
+On every pipeline during the `test` stage, the `review-qa-smoke` job is
+automatically started: it runs the QA smoke suite against the
+[Review App][review-apps].
+
+You can also manually start the `review-qa-all`: it runs the full QA suite
+against the [Review App][review-apps].
+
+**This runs end-to-end tests against a Review App based on [the official GitLab
+Helm chart][helm-chart], itself deployed with custom
+[Cloud Native components][cng] built from your merge request's changes.**
+
+See [Review Apps][review-apps] for more details about Review Apps.
+
+[helm-chart]: https://gitlab.com/charts/gitlab/
+[cng]: https://gitlab.com/gitlab-org/build/CNG
+
+## How do I write tests?
In order to write new tests, you first need to learn more about GitLab QA
architecture. See the [documentation about it][gitlab-qa-architecture].
@@ -80,10 +128,11 @@ you can find an issue you would like to work on in
[omnibus-gitlab]: https://gitlab.com/gitlab-org/omnibus-gitlab
[gitlab-qa]: https://gitlab.com/gitlab-org/gitlab-qa
-[gitlab-qa-readme]: https://gitlab.com/gitlab-org/gitlab-qa/tree/master/README.md
[gitlab-qa-pipelines]: https://gitlab.com/gitlab-org/gitlab-qa/pipelines
+[gitlab-qa-readme]: https://gitlab.com/gitlab-org/gitlab-qa/tree/master/README.md
[quality-nightly-pipelines]: https://gitlab.com/gitlab-org/quality/nightly/pipelines
[quality-staging-pipelines]: https://gitlab.com/gitlab-org/quality/staging/pipelines
+[review-apps]: ./review_apps.md
[gitlab-qa-architecture]: https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/architecture.md
[gitlab-qa-issues]: https://gitlab.com/gitlab-org/gitlab-qa/issues?label_name%5B%5D=new+scenario
[gitlab-ce-issues]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name[]=QA&label_name[]=test
diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md
index 6012f1080ab..71c9637e72c 100644
--- a/doc/development/testing_guide/frontend_testing.md
+++ b/doc/development/testing_guide/frontend_testing.md
@@ -13,15 +13,49 @@ in the future.
See the [Testing Standards and Style Guidelines](index.md) page for more
information on general testing practices at GitLab.
+## Jest
+
+GitLab has started to migrate tests to the [Jest](https://jestjs.io)
+testing framework. You can read a [detailed evaluation](https://gitlab.com/gitlab-org/gitlab-ce/issues/49171)
+of Jest compared to our use of Karma and Jasmine. In summary, it will allow us
+to improve the performance and consistency of our frontend tests.
+
+Jest tests can be found in `/spec/frontend` and `/ee/spec/frontend` in EE.
+
+It is not yet a requirement to use Jest. You can view the
+[epic](https://gitlab.com/groups/gitlab-org/-/epics/873) of issues
+we need to solve before being able to use Jest for all our needs.
+
+### Timeout error
+
+The default timeout for Jest is set in
+[`/spec/frontend/test_setup.js`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/frontend/test_setup.js).
+
+If your test exceeds that time, it will fail.
+
+If you cannot improve the performance of the tests, you can increase the timeout
+for a specific test using
+[`setTestTimeout`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/spec/frontend/helpers/timeout.js).
+
+```javascript
+import { setTestTimeout } from 'helpers/timeout';
+
+describe('Component', () => {
+ it('does something amazing', () => {
+ setTestTimeout(500);
+ // ...
+ });
+});
+```
+
+Remember that the performance of each test depends on the environment.
+
## Karma test suite
GitLab uses the [Karma][karma] test runner with [Jasmine] as its test
-framework for our JavaScript unit and integration tests. For integration tests,
-we generate HTML files using RSpec (see `spec/javascripts/fixtures/*.rb` for examples).
-Some fixtures are still HAML templates that are translated to HTML files using the same mechanism (see `static_fixtures.rb`).
-Adding these static fixtures should be avoided as they are harder to keep up to date with real views.
-The existing static fixtures will be migrated over time.
-Please see [gitlab-org/gitlab-ce#24753](https://gitlab.com/gitlab-org/gitlab-ce/issues/24753) to track our progress.
+framework for our JavaScript unit and integration tests.
+We generate HTML and JSON fixtures from backend views and controllers
+using RSpec (see `spec/javascripts/fixtures/*.rb` for examples).
Fixtures are served during testing by the [jasmine-jquery][jasmine-jquery] plugin.
JavaScript tests live in `spec/javascripts/`, matching the folder structure
@@ -134,21 +168,22 @@ placeholders, and recalling when they are called and the arguments that are
passed to them. These tools should be used liberally, to test for expected
behavior, to mock responses, and to block unwanted side effects (such as a
method that would generate a network request or alter `window.location`). The
-documentation for these methods can be found in the [jasmine introduction page](https://jasmine.github.io/2.0/introduction.html#section-Spies).
+documentation for these methods can be found in the [Jasmine introduction page](https://jasmine.github.io/2.0/introduction.html#section-Spies).
Sometimes you may need to spy on a method that is directly imported by another
module. GitLab has a custom `spyOnDependency` method which utilizes
[babel-plugin-rewire](https://github.com/speedskater/babel-plugin-rewire) to
achieve this. It can be used like so:
-```javascript
+```js
// my_module.js
import { visitUrl } from '~/lib/utils/url_utility';
export default function doSomething() {
visitUrl('/foo/bar');
}
-
+```
+```js
// my_module_spec.js
import doSomething from '~/my_module';
@@ -167,7 +202,7 @@ export of a module who's import you want to stub, rather than an object which
contains a method you wish to stub (if the module does not have a default
export, one is be generated by the babel plugin). The second parameter is the
name of the import you wish to change. The result of the function is a Spy
-object which can be treated like any other jasmine spy object.
+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).
@@ -176,38 +211,44 @@ Further documentation on the babel rewire pluign API can be found on
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).
+#### Migrating flaky Karma tests to Jest
+
+Some of our Karma tests are flaky because they access the properties of a shared scope.
+This also means that they are not easily parallelized.
+
+Migrating flaky Karma tests to Jest will help significantly as each test is executed
+in an isolated scope, improving performance and predictability.
+
### Vue.js unit tests
See this [section][vue-test].
### Running frontend tests
-`rake karma` runs the frontend-only (JavaScript) tests.
-It consists of two subtasks:
+For running the frontend tests, you need the following commands:
-- `rake karma:fixtures` (re-)generates fixtures
-- `rake karma:tests` actually executes the tests
+- `rake karma:fixtures` (re-)generates fixtures.
+- `yarn test` executes the tests.
-As long as the fixtures don't change, `rake karma:tests` (or `yarn karma`)
-is sufficient (and saves you some time).
+As long as the fixtures don't change, `yarn test` is sufficient (and saves you some time).
### Live testing and focused testing
-While developing locally, it may be helpful to keep karma running so that you
+While developing locally, it may be helpful to keep Karma running so that you
can get instant feedback on as you write tests and modify code. To do this
-you can start karma with `yarn run karma-start`. It will compile the javascript
+you can start Karma with `yarn run karma-start`. It will compile the javascript
assets and run a server at `http://localhost:9876/` where it will automatically
run the tests on any browser which connects to it. You can enter that url on
multiple browsers at once to have it run the tests on each in parallel.
-While karma is running, any changes you make will instantly trigger a recompile
+While Karma is running, any changes you make will instantly trigger a recompile
and retest of the entire test suite, so you can see instantly if you've broken
-a test with your changes. You can use [jasmine focused][jasmine-focus] or
-excluded tests (with `fdescribe` or `xdescribe`) to get karma to run only the
+a test with your changes. You can use [Jasmine focused][jasmine-focus] or
+excluded tests (with `fdescribe` or `xdescribe`) to get Karma to run only the
tests you want while you're working on a specific feature, but make sure to
remove these directives when you commit your code.
-It is also possible to only run karma on specific folders or files by filtering
+It is also possible to only run Karma on specific folders or files by filtering
the run tests via the argument `--filter-spec` or short `-f`:
```bash
@@ -236,25 +277,6 @@ Information on setting up and running RSpec integration tests with
## Gotchas
-### Errors due to use of unsupported JavaScript features
-
-Similar errors will be thrown if you're using JavaScript features not yet
-supported by the PhantomJS test runner which is used for both Karma and RSpec
-tests. We polyfill some JavaScript objects for older browsers, but some
-features are still unavailable:
-
-- Array.from
-- Array.first
-- Async functions
-- Generators
-- Array destructuring
-- For..Of
-- Symbol/Symbol.iterator
-- Spread
-
-Until these are polyfilled appropriately, they should not be used. Please
-update this list with additional unsupported features.
-
### RSpec errors due to JavaScript
By default RSpec unit tests will not run JavaScript in the headless browser
diff --git a/doc/development/testing_guide/img/qa_on_merge_requests_cicd_architecture.png b/doc/development/testing_guide/img/qa_on_merge_requests_cicd_architecture.png
new file mode 100644
index 00000000000..5b93a05db96
--- /dev/null
+++ b/doc/development/testing_guide/img/qa_on_merge_requests_cicd_architecture.png
Binary files differ
diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md
index 85b47f88cc4..ffc71051377 100644
--- a/doc/development/testing_guide/review_apps.md
+++ b/doc/development/testing_guide/review_apps.md
@@ -70,9 +70,8 @@ subgraph GCP `gitlab-review-apps` project
you get a dedicated environment for your branch that's very close to what
it would look in production.
1. Once the [`review-deploy`][review-deploy] job succeeds, you should be able to
- use your Review App thanks to the direct link to it from the MR widget. The
- default username is `root` and its password can be found in the 1Password
- secure note named **gitlab-{ce,ee} Review App's root password**.
+ use your Review App thanks to the direct link to it from the MR widget. To log
+ into the Review App, see "Log into my Review App?" below.
**Additional notes:**
@@ -91,13 +90,32 @@ subgraph GCP `gitlab-review-apps` project
## QA runs
On every [pipeline][gitlab-pipeline] during the `test` stage, the
-`review-qa-smoke` job is automatically started: it runs the smoke QA suite.
-You can also manually start the `review-qa-all`: it runs the full QA suite.
+`review-qa-smoke` job is automatically started: it runs the QA smoke suite.
+You can also manually start the `review-qa-all`: it runs the QA full suite.
Note that both jobs first wait for the `review-deploy` job to be finished.
+## Performance Metrics
+
+On every [pipeline][gitlab-pipeline] during the `test` stage, the
+`review-performance` job is automatically started: this job does basic
+browser performance testing using [Sitespeed.io Container](https://docs.gitlab.com/ee/user/project/merge_requests/browser_performance_testing.html) .
+
+This job waits for the `review-deploy` job to be finished.
+
## How to?
+### Log into my Review App?
+
+The default username is `root` and its password can be found in the 1Password
+secure note named **gitlab-{ce,ee} Review App's root password**.
+
+### Enable a feature flag for my Review App?
+
+1. Open your Review App and log in as documented above.
+1. Create a personal access token.
+1. Enable the feature flag using the [Feature flag API](../../api/features.md).
+
### Find my Review App slug?
1. Open the `review-deploy` job.
diff --git a/doc/development/testing_guide/smoke.md b/doc/development/testing_guide/smoke.md
index 3360031c220..30d861d7d68 100644
--- a/doc/development/testing_guide/smoke.md
+++ b/doc/development/testing_guide/smoke.md
@@ -7,13 +7,19 @@ functionality is working.
Currently, our suite consists of this basic functionality coverage:
-- User Login (Standard Auth)
-- Project Creation
-- Issue Creation
-- Merge Request Creation
+- User standard authentication
+- SSH Key creation and addition to a user
+- Project simple creation
+- Project creation with Auto-DevOps enabled
+- Issue creation
+- Merge Request creation
+- Snippet creation
Smoke tests have the `:smoke` RSpec metadata.
+See [End-to-end Testing](./end_to_end_tests.md) for more details about
+end-to-end tests.
+
---
[Return to Testing documentation](index.md)
diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md
index 070b6477a7a..352651fe91b 100644
--- a/doc/development/testing_guide/testing_levels.md
+++ b/doc/development/testing_guide/testing_levels.md
@@ -46,7 +46,7 @@ They're useful to test permissions, redirections, what view is rendered etc.
| `app/mailers/` | `spec/mailers/` | RSpec | |
| `lib/api/` | `spec/requests/api/` | RSpec | |
| `lib/ci/api/` | `spec/requests/ci/api/` | RSpec | |
-| `app/assets/javascripts/` | `spec/javascripts/` | Karma | More details in the [JavaScript](#javascript) section. |
+| `app/assets/javascripts/` | `spec/javascripts/` | Karma | More details in the [Karma JavaScript test suite](frontend_testing.md#karma-test-suite) section. |
### About controller tests
@@ -159,6 +159,10 @@ Every new feature should come with a [test plan].
> See [end-to-end tests](end_to_end_tests.md) for more information.
+Note that `qa/spec` contains unit tests of the QA framework itself, not to be
+confused with the application's [unit tests](#unit-tests) or
+[end-to-end tests](#black-box-tests-at-the-system-level-aka-end-to-end-tests).
+
[multiple pieces]: ../architecture.md#components
[GitLab Shell]: https://gitlab.com/gitlab-org/gitlab-shell
[GitLab Workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse
@@ -206,7 +210,7 @@ trade-off:
- Integration tests are a bit more expensive, but don't abuse them. A system test
is often better than an integration test that is stubbing a lot of internals.
- System tests are expensive (compared to unit tests), even more if they require
- a JavaScript driver. Make sure to follow the guidelines in the [Speed](#test-speed)
+ a JavaScript driver. Make sure to follow the guidelines in the [Speed](best_practices.md#test-speed)
section.
Another way to see it is to think about the "cost of tests", this is well
@@ -230,6 +234,8 @@ you should write an integration test using Jasmine.
[big]: https://twitter.com/timbray/status/822470746773409794
[picture]: https://twitter.com/withzombies/status/829716565834752000
[tests-cost]: https://medium.com/table-xi/high-cost-tests-and-high-value-tests-a86e27a54df#.2ulyh3a4e
+[RSpec]: https://github.com/rspec/rspec-rails#feature-specs
+[Capybara]: https://github.com/teamcapybara/capybara
---
diff --git a/doc/development/ux_guide/users.md b/doc/development/ux_guide/users.md
index 11bac11257c..3aecbbffd73 100644
--- a/doc/development/ux_guide/users.md
+++ b/doc/development/ux_guide/users.md
@@ -1,5 +1,5 @@
---
-redirect_to: 'https://design.gitlab.com/getting-started/personas/'
+redirect_to: 'https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/'
---
-The content of this document was moved into the [GitLab Design System](https://design.gitlab.com/).
+This document was moved to [another location](https://about.gitlab.com/handbook/marketing/product-marketing/roles-personas/).
diff --git a/doc/gitlab-basics/add-merge-request.md b/doc/gitlab-basics/add-merge-request.md
index 5cc014419ad..7bb2e014738 100644
--- a/doc/gitlab-basics/add-merge-request.md
+++ b/doc/gitlab-basics/add-merge-request.md
@@ -8,7 +8,7 @@ request. For more information, check the
---
1. Before you start, you should have already [created a branch](create-branch.md)
- and [pushed your changes](basic-git-commands.md) to GitLab.
+ and [pushed your changes](start-using-git.md#send-changes-to-gitlabcom) to GitLab.
1. Go to the project where you'd like to merge your changes and click on the
**Merge requests** tab.
1. Click on **New merge request** on the right side of the screen.
diff --git a/doc/gitlab-basics/create-group.md b/doc/gitlab-basics/create-group.md
index 985a52d88f5..8c0d3561882 100644
--- a/doc/gitlab-basics/create-group.md
+++ b/doc/gitlab-basics/create-group.md
@@ -1,2 +1,5 @@
+---
+redirect_to: '../user/group/index.md#create-a-new-group'
+---
This document was moved to [another location](../user/group/index.md#create-a-new-group).
diff --git a/doc/gitlab-basics/create-issue.md b/doc/gitlab-basics/create-issue.md
index abb163dbf18..818a03a6f02 100644
--- a/doc/gitlab-basics/create-issue.md
+++ b/doc/gitlab-basics/create-issue.md
@@ -1,2 +1,5 @@
+---
+redirect_to: '../user/project/issues/index.md#new-issue'
+---
This document was moved to [another location](../user/project/issues/index.md#new-issue).
diff --git a/doc/gitlab-basics/create-project.md b/doc/gitlab-basics/create-project.md
index 978ffde84ad..3e99496d531 100644
--- a/doc/gitlab-basics/create-project.md
+++ b/doc/gitlab-basics/create-project.md
@@ -42,7 +42,9 @@ Project templates can pre-populate your project with necessary files to get you
There are two types of project templates:
-- [Built-in templates](#builtin-templates), sourced from the [`project-templates`](https://gitlab.com/gitlab-org/project-templates) group.
+- [Built-in templates](#built-in-templates), sourced from the following groups:
+ - [`project-templates`](https://gitlab.com/gitlab-org/project-templates)
+ - [`pages`](https://gitlab.com/pages)
- [Custom project templates](#custom-project-templates-premium-only), for custom templates configured by GitLab administrators and users.
### Built-in templates
@@ -50,7 +52,7 @@ There are two types of project templates:
Built-in templates are project templates that are:
- Developed and maintained in the
- [`project-templates`](https://gitlab.com/gitlab-org/project-templates) group.
+ [`project-templates`](https://gitlab.com/gitlab-org/project-templates) and [`pages`](https://gitlab.com/pages) groups.
- Released with GitLab.
To use a built-in template on the **New project** page:
@@ -64,7 +66,7 @@ To use a built-in template on the **New project** page:
TIP: **Tip:**
You can improve the existing built-in templates or contribute new ones on the
-[`project-templates`](https://gitlab.com/gitlab-org/project-templates) group.
+[`project-templates`](https://gitlab.com/gitlab-org/project-templates) and [`pages`](https://gitlab.com/pages) groups.
### Custom project templates **[PREMIUM ONLY]**
diff --git a/doc/gitlab-basics/create-your-ssh-keys.md b/doc/gitlab-basics/create-your-ssh-keys.md
index 8c2f48fb1e2..8fecdc6948e 100644
--- a/doc/gitlab-basics/create-your-ssh-keys.md
+++ b/doc/gitlab-basics/create-your-ssh-keys.md
@@ -1,36 +1,22 @@
-# How to create your SSH Keys
+# How to create your SSH keys
-1. The first thing you need to do is go to your [command line](start-using-git.md)
- and follow the [instructions](../ssh/README.md) to generate your SSH key pair.
+This topic describes how to create SSH keys. You do this to use Git over SSH instead of Git over HTTP.
-1. Once you do that, login to GitLab with your credentials.
-1. On the upper right corner, click on your avatar and go to your **Profile settings**.
-
- ![Profile settings dropdown](img/profile_settings.png)
-
-1. Navigate to the **SSH keys** tab.
-
- ![SSH Keys](img/profile_settings_ssh_keys.png)
-
-1. Paste your **public** key that you generated in the first step in the 'Key'
- box.
-
- ![Paste SSH public key](img/profile_settings_ssh_keys_paste_pub.png)
+## Creating your SSH keys
+1. Go to your [command line](start-using-git.md) and follow the [instructions](../ssh/README.md) to generate your SSH key pair.
+1. Log in to GitLab.
+1. In the upper-right corner, click your avatar and select **Settings**.
+1. On the **User Settings** menu, select **SSH keys**.
+1. Paste the **public** key generated in the first step in the **Key**
+ text field.
1. Optionally, give it a descriptive title so that you can recognize it in the
event you add multiple keys.
-
- ![SSH key title](img/profile_settings_ssh_keys_title.png)
-
-1. Finally, click on **Add key** to add it to GitLab. You will be able to see
- its fingerprint, its title and creation date.
+1. Finally, click the **Add key** button to add it to GitLab. You will be able to see
+ its fingerprint, title, and creation date.
![SSH key single page](img/profile_settings_ssh_keys_single_key.png)
->**Note:**
-Once you add a key, you cannot edit it, only remove it. In case the paste
-didn't work, you will have to remove the offending key and re-add it.
-
----
-
-Congratulations! You are now ready to use Git over SSH, instead of Git over HTTP!
+NOTE: **Note:**
+Once you add a key, you cannot edit it. If the paste
+didn't work, you need to remove the offending key and re-add it.
diff --git a/doc/gitlab-basics/img/profile_settings.png b/doc/gitlab-basics/img/profile_settings.png
deleted file mode 100644
index b91b698fb18..00000000000
--- a/doc/gitlab-basics/img/profile_settings.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys.png b/doc/gitlab-basics/img/profile_settings_ssh_keys.png
deleted file mode 100644
index 8ac603a2af9..00000000000
--- a/doc/gitlab-basics/img/profile_settings_ssh_keys.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png
deleted file mode 100644
index 0b1c64a72f3..00000000000
--- a/doc/gitlab-basics/img/profile_settings_ssh_keys_paste_pub.png
+++ /dev/null
Binary files differ
diff --git a/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png b/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png
deleted file mode 100644
index 02ca0bf7478..00000000000
--- a/doc/gitlab-basics/img/profile_settings_ssh_keys_title.png
+++ /dev/null
Binary files differ
diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md
index 1d5e5dd6e15..fb939ff8aac 100644
--- a/doc/hooks/custom_hooks.md
+++ b/doc/hooks/custom_hooks.md
@@ -1,3 +1,7 @@
+---
+redirect_to: '../administration/custom_hooks.md'
+---
+
# Custom Git Hooks
This document was moved to [administration/custom_hooks.md](../administration/custom_hooks.md).
diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md
index db0f03f2c98..9544983974f 100644
--- a/doc/incoming_email/README.md
+++ b/doc/incoming_email/README.md
@@ -1 +1,5 @@
+---
+redirect_to: '../administration/reply_by_email.md'
+---
+
This document was moved to [administration/reply_by_email](../administration/reply_by_email.md).
diff --git a/doc/incoming_email/postfix.md b/doc/incoming_email/postfix.md
index 90833238ac5..a7192325229 100644
--- a/doc/incoming_email/postfix.md
+++ b/doc/incoming_email/postfix.md
@@ -1 +1,5 @@
+---
+redirect_to: '../administration/reply_by_email_postfix_setup.md'
+---
+
This document was moved to [administration/reply_by_email_postfix_setup](../administration/reply_by_email_postfix_setup.md).
diff --git a/doc/install/aws/index.md b/doc/install/aws/index.md
index 51d2a232dc0..0000e03f1d7 100644
--- a/doc/install/aws/index.md
+++ b/doc/install/aws/index.md
@@ -1,11 +1,11 @@
-# Installing GitLab on Amazon Web Services (AWS)
-
-To install GitLab on AWS, you can use the Amazon Machine Images (AMIs) that GitLab
-provides with [each release](https://about.gitlab.com/releases/).
+# Installing GitLab HA on Amazon Web Services (AWS)
This page offers a walkthrough of a common HA (Highly Available) configuration
for GitLab on AWS. You should customize it to accommodate your needs.
+NOTE: **Note**
+For organizations with 300 users or less, the recommended AWS installation method is to launch an EC2 single box [Omnibus Installation](https://about.gitlab.com/install/) and implement a snapshot strategy for backing up the data.
+
## Introduction
GitLab on AWS can leverage many of the services that are already
@@ -55,6 +55,8 @@ Here's a list of the AWS services we will use, with links to pricing information
- **ElastiCache**: An in-memory cache environment will be used to provide a
High Availability Redis configuration. See the
[Amazon ElastiCache pricing](https://aws.amazon.com/elasticache/pricing/).
+
+NOTE: **Note:** Please note that while we will be using EBS for storage, we do not recommend using EFS as it may negatively impact GitLab's performance. You can review the [relevant documentation](../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
## Creating an IAM EC2 instance role and profile
To minimize the permissions of the user, we'll create a new [IAM](https://docs.aws.amazon.com/IAM/latest/UserGuide/introduction.html)
@@ -379,6 +381,10 @@ size depends on your needs and you can always migrate to a bigger volume later.
You will be able to [set up that volume](#setting-up-the-ebs-volume)
after the instance is created.
+CAUTION: **Caution:**
+We **do not** recommend using the AWS Elastic File System (EFS), as it can result
+in [significantly degraded performance](../../administration/high_availability/nfs.html#avoid-using-awss-elastic-file-system-efs).
+
### Configure security group
As a last step, configure the security group:
@@ -606,7 +612,7 @@ To back up GitLab:
To restore GitLab, first review the [restore documentation](../../raketasks/backup_restore.md#restore),
and primarily the restore prerequisites. Then, follow the steps under the
-[Omnibus installations section](../../raketasks/backup_restore.md#restore-for-omnibus-installations).
+[Omnibus installations section](../../raketasks/backup_restore.md#restore-for-omnibus-gitlab-installations).
## Updating GitLab
diff --git a/doc/install/azure/index.md b/doc/install/azure/index.md
index be14858f4c8..fa5be1d30f9 100644
--- a/doc/install/azure/index.md
+++ b/doc/install/azure/index.md
@@ -21,14 +21,14 @@ First, you'll need an account on Azure. There are three ways to do this:
- If your company (or you) already has an account, then you are ready to go!
- You can also open your own Azure account for free. _At time of writing_, you get $200
-of credit to spend on Azure services for 30 days. You can use this credit to try out paid Azure
-services, exploring Microsoft's cloud for free. Even after the first 30 days, you never have to pay
-anything unless you decide to transition to paid services with a Pay-As-You-Go Azure subscription.
-This is a great way to try out Azure and cloud computing, and you can
-[read more in their comprehensive FAQ][Azure-Free-Account-FAQ].
+ of credit to spend on Azure services for 30 days. You can use this credit to try out paid Azure
+ services, exploring Microsoft's cloud for free. Even after the first 30 days, you never have to pay
+ anything unless you decide to transition to paid services with a Pay-As-You-Go Azure subscription.
+ This is a great way to try out Azure and cloud computing, and you can
+ [read more in their comprehensive FAQ][Azure-Free-Account-FAQ].
- If you have an MSDN subscription, you can activate your Azure subscriber benefits. Your MSDN
-subscription gives you recurring Azure credits every month, so why not put those credits to use and
-try out GitLab right now?
+ subscription gives you recurring Azure credits every month, so why not put those credits to use and
+ try out GitLab right now?
## Working with Azure
@@ -216,10 +216,10 @@ Like all servers, our VM will be running many services. However, we want to open
ports to enable public internet access to two services in particular:
1. **HTTP** (port 80) - opening port 80 will enable our VM to respond to HTTP requests, allowing
-public access to the instance of GitLab running on our VM.
+ public access to the instance of GitLab running on our VM.
1. **SSH** (port 22) - opening port 22 will enable our VM to respond to SSH connection requests,
-allowing public access (with authentication) to remote terminal sessions
-_(you'll see why we need [SSH] access to our VM [later on in this tutorial](#maintaining-your-gitlab-instance))_
+ allowing public access (with authentication) to remote terminal sessions
+ _(you'll see why we need [SSH] access to our VM [later on in this tutorial](#maintaining-your-gitlab-instance))_
### Open HTTP on Port 80
diff --git a/doc/install/docker.md b/doc/install/docker.md
index d0129f0f5c4..4568a949b0a 100644
--- a/doc/install/docker.md
+++ b/doc/install/docker.md
@@ -2,7 +2,7 @@
[Docker](https://www.docker.com) and container technology have been revolutionizing the software world for the past few years. They combine the performance and efficiency of native execution with the abstraction, security, and immutability of virtualization.
-GitLab provides official Docker images to allowing you to easily take advantage of the benefits of containerization while operating your GitLab instance.
+GitLab provides official Docker images allowing you to easily take advantage of the benefits of containerization while operating your GitLab instance.
## Omnibus GitLab based images
diff --git a/doc/install/installation.md b/doc/install/installation.md
index fb24d4fa0ef..61f544deabe 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -9,7 +9,8 @@ On heavily used GitLab instances the memory usage of the Sidekiq background work
Omnibus packages solve this by [letting the Sidekiq terminate gracefully](../administration/operations/sidekiq_memory_killer.md) if it uses too much memory.
After this termination Runit will detect Sidekiq is not running and will start it.
-Since installations from source don't have Runit, Sidekiq can't be terminated and its memory usage will grow over time.
+Since installations from source don't use Runit for process supervision, Sidekiq
+can't be terminated and its memory usage will grow over time.
## Select Version to Install
@@ -72,7 +73,8 @@ Install the required packages (needed to compile Ruby and native extensions to R
```sh
sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libre2-dev \
libreadline-dev libncurses5-dev libffi-dev curl openssh-server checkinstall libxml2-dev \
- libxslt-dev libcurl4-openssl-dev libicu-dev logrotate rsync python-docutils pkg-config cmake
+ libxslt-dev libcurl4-openssl-dev libicu-dev logrotate rsync python-docutils pkg-config cmake \
+ runit
```
Ubuntu 14.04 (Trusty Tahr) doesn't have the `libre2-dev` package available, but
diff --git a/doc/install/kubernetes/preparation/eks.md b/doc/install/kubernetes/preparation/eks.md
index 647b856d54e..ea3b075dd82 100644
--- a/doc/install/kubernetes/preparation/eks.md
+++ b/doc/install/kubernetes/preparation/eks.md
@@ -5,6 +5,7 @@ There are a few nuances to Amazon EKS which are important to be aware of, when d
## Persistent volume management
There are two methods to manage volume claims on Kubernetes:
+
1. Manually creating each persistent volume (recommended on EKS)
1. Utilizing dynamic provisioning to automatically create the persistent volumes
diff --git a/doc/install/kubernetes/preparation/tiller.md b/doc/install/kubernetes/preparation/tiller.md
index 107df074b3b..684df14ac2c 100644
--- a/doc/install/kubernetes/preparation/tiller.md
+++ b/doc/install/kubernetes/preparation/tiller.md
@@ -7,7 +7,7 @@ Helm consists of two parts, the `helm` client and a `tiller` server inside Kuber
NOTE: **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)
+is possible to use [Tiller locally](https://docs.gitlab.com/charts/installation/tools.html#local-tiller)
and avoid deploying it into the cluster. This should only be used when Tiller
cannot be normally deployed.
@@ -15,7 +15,7 @@ cannot be normally deployed.
Tiller is deployed into the cluster and interacts with the Kubernetes API to deploy your applications. If role based access control (RBAC) is enabled, Tiller will need to be [granted permissions](#preparing-for-helm-with-rbac) to allow it to talk to the Kubernetes API.
-If RBAC is not enabled, skip to [initalizing Helm](#initialize-helm).
+If RBAC is not enabled, skip to [initializing Helm](#initialize-helm).
If you are not sure whether RBAC is enabled in your cluster, or to learn more, read through our [RBAC documentation](rbac.md).
@@ -54,19 +54,25 @@ Some clusters require authentication to use `kubectl` to create the Tiller roles
#### Upload the RBAC config as an admin user (GKE)
-For GKE, you need to grab the admin credentials:
+For GKE, you need to obtain the admin credentials. This command will output the admin password:
```
gcloud container clusters describe <cluster-name> --zone <zone> --project <project-id> --format='value(masterAuth.password)'
```
-This command will output the admin password. We need the password to authenticate with `kubectl` and create the role.
+Use the admin password to set the admin credentials. Replace the password value below with the output value from the above step:
```
-kubectl --username=admin --password=xxxxxxxxxxxxxx create -f rbac-config.yaml
+kubectl config set-credentials admin --username=admin --password=xxxxxxxxxxxxxx
```
-#### Upload the RBAC config (Other clusters)
+Once credentials have been set, create the role:
+
+```
+kubectl --user=admin create -f rbac-config.yaml
+```
+
+#### Upload the RBAC config (Non-GKE clusters)
For other clusters like Amazon EKS, you can directly upload the RBAC configuration.
diff --git a/doc/install/openshift_and_gitlab/index.md b/doc/install/openshift_and_gitlab/index.md
index 509020d1975..77bd9e9f7a9 100644
--- a/doc/install/openshift_and_gitlab/index.md
+++ b/doc/install/openshift_and_gitlab/index.md
@@ -11,7 +11,7 @@ date: 2016-06-28
CAUTION: **Deprecated:**
This article is deprecated. Use the official Kubernetes Helm charts for
installing GitLab to OpenShift. Check out the
-[official installation docs](https://gitlab.com/charts/gitlab/blob/master/doc/cloud/openshift.md)
+[official installation docs](https://docs.gitlab.com/charts/installation/cloud/openshift.html)
for details.
## Introduction
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 1b7e0d1d0ab..9a6c2ce1976 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -33,7 +33,7 @@ Please consider using a virtual machine to run GitLab.
## Ruby versions
-GitLab requires Ruby (MRI) 2.3. Support for Ruby versions below 2.3 (2.1, 2.2) will stop with GitLab 8.13.
+GitLab requires Ruby (MRI) 2.5. Support for Ruby versions below 2.5 (2.3, 2.4) will stop with GitLab 11.6.
You will have to use the standard MRI implementation of Ruby.
We love [JRuby](https://www.jruby.org/) and [Rubinius](https://rubinius.com) but GitLab
@@ -51,6 +51,8 @@ Apart from a local hard drive you can also mount a volume that supports the netw
If you have enough RAM memory and a recent CPU the speed of GitLab is mainly limited by hard drive seek times. Having a fast drive (7200 RPM and up) or a solid state drive (SSD) will improve the responsiveness of GitLab.
+NOTE: **Note:** Since file system performance may affect GitLab's overall performance, we do not recommend using EFS for storage. See the [relevant documentation](../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
+
### CPU
- 1 core supports up to 100 users but the application can be a bit slower due to having all workers and background jobs running on the same core
@@ -205,6 +207,9 @@ We support the current and the previous major release of:
- Microsoft Edge
- Internet Explorer 11
+The browser vendors release regular minor version updates with important bug fixes and security updates.
+Support is only provided for the current minor version of the major version you are running.
+
Each time a new browser version is released, we begin supporting that version and stop supporting the third most recent version.
Note: We do not support running GitLab with JavaScript disabled in the browser and have no plans of supporting that
diff --git a/doc/integration/README.md b/doc/integration/README.md
index 7941cb42667..f5bc0693b84 100644
--- a/doc/integration/README.md
+++ b/doc/integration/README.md
@@ -11,8 +11,7 @@ See the documentation below for details on how to configure these services.
- [Akismet](akismet.md) Configure Akismet to stop spam
- [Auth0 OmniAuth](auth0.md) Enable the Auth0 OmniAuth provider
-- [Bitbucket](bitbucket.md) Import projects from Bitbucket.org and login to your GitLab instance with your
-Bitbucket.org account
+- [Bitbucket](bitbucket.md) Import projects from Bitbucket.org and login to your GitLab instance with your Bitbucket.org account
- [CAS](cas.md) Configure GitLab to sign in using CAS
- [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc.
- [Gmail actions buttons](gmail_action_buttons_for_gitlab.md) Adds GitLab actions to messages
diff --git a/doc/integration/auth0.md b/doc/integration/auth0.md
index e2ed7a4b1ab..c67375ede50 100644
--- a/doc/integration/auth0.md
+++ b/doc/integration/auth0.md
@@ -4,18 +4,18 @@ To enable the Auth0 OmniAuth provider, you must create an Auth0 account, and an
application.
1. Sign in to the [Auth0 Console](https://auth0.com/auth/login). If you need to
-create an account, you can do so at the same link.
+ create an account, you can do so at the same link.
1. Select "New App/API".
1. Provide the Application Name ('GitLab' works fine).
1. Once created, you should see the Quick Start options. Disregard them and
-select 'Settings' above the Quick Start options.
+ select 'Settings' above the Quick Start options.
1. At the top of the Settings screen, you should see your Domain, Client ID and
-Client Secret. Take note of these as you'll need to put them in the
-configuration file. For example:
+ Client Secret. Take note of these as you'll need to put them in the
+ configuration file. For example:
- Domain: `test1234.auth0.com`
- Client ID: `t6X8L2465bNePWLOvt9yi41i`
- Client Secret: `KbveM3nqfjwCbrhaUy_gDu2dss8TIlHIdzlyf33pB7dEK5u_NyQdp65O_o02hXs2`
@@ -44,7 +44,7 @@ configuration file. For example:
```
1. See [Initial OmniAuth Configuration](omniauth.md#initial-omniauth-configuration)
-for initial settings.
+ for initial settings.
1. Add the provider configuration:
@@ -76,10 +76,10 @@ for initial settings.
```
1. Change `YOUR_AUTH0_CLIENT_ID` to the client ID from the Auth0 Console page
-from step 5.
+ from step 5.
1. Change `YOUR_AUTH0_CLIENT_SECRET` to the client secret from the Auth0 Console
-page from step 5.
+ page from step 5.
1. [Reconfigure][] or [restart GitLab][] for the changes to take effect if you
installed GitLab via Omnibus or from source respectively.
diff --git a/doc/integration/chat_commands.md b/doc/integration/chat_commands.md
index 2856992ee25..1a4fb46046d 100644
--- a/doc/integration/chat_commands.md
+++ b/doc/integration/chat_commands.md
@@ -1 +1,5 @@
+---
+redirect_to: 'slash_commands.md'
+---
+
This document was moved to [integration/slash_commands.md](slash_commands.md).
diff --git a/doc/integration/crowd.md b/doc/integration/crowd.md
index 2bc526dc3db..30e3e888b29 100644
--- a/doc/integration/crowd.md
+++ b/doc/integration/crowd.md
@@ -1 +1,5 @@
+---
+redirect_to: '../administration/auth/crowd.md'
+---
+
This document was moved to [`administration/auth/crowd`](../administration/auth/crowd.md).
diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md
index 075feaeead9..c3328e01081 100644
--- a/doc/integration/external-issue-tracker.md
+++ b/doc/integration/external-issue-tracker.md
@@ -1,14 +1,17 @@
# External issue tracker
-GitLab has a great issue tracker but you can also use an external one such as
-Jira, Redmine, or Bugzilla. Issue trackers are configurable per GitLab project and allow
-you to do the following:
+GitLab has a great [issue tracker](../user/project/issues/index.md) but you can also use an external one
+such as Jira, Redmine, YouTrack, or Bugzilla. External issue trackers are configurable per GitLab project.
-- you can reference these external issues inside GitLab interface
- (merge requests, commits, comments) and they will be automatically converted
- into links
+Once configured, you can reference external issues using the format `CODE-123`, where:
-You can have enabled both external and internal GitLab issue trackers in parallel. The **Issues** link always opens the internal issue tracker and in case the internal issue tracker is disabled the link is not visible in the menu.
+- `CODE` is a unique code for the tracker.
+- `123` is the issue number in the tracker.
+
+These references in GitLab merge requests, commits, or comments are automatically converted to links to the issues.
+
+You can keep GitLab's issue tracker enabled in parallel or disable it. When enabled, the **Issues** link in the
+GitLab menu always opens the internal issue tracker. When disabled, the link is not visible in the menu.
## Configuration
@@ -20,6 +23,7 @@ To enable an external issue tracker you must configure the appropriate **Service
Visit the links below for details:
- [Redmine](../user/project/integrations/redmine.md)
+- [YouTrack](../user/project/integrations/youtrack.md)
- [Jira](../user/project/integrations/jira.md)
- [Bugzilla](../user/project/integrations/bugzilla.md)
- [Custom Issue Tracker](../user/project/integrations/custom_issue_tracker.md)
diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md
index a67de23b17b..fe789a80eed 100644
--- a/doc/integration/facebook.md
+++ b/doc/integration/facebook.md
@@ -9,7 +9,7 @@ To enable the Facebook OmniAuth provider you must register your application with
1. Select the type "Website"
1. Enter a name for your app. This can be anything. Consider something like "&lt;Organization&gt;'s GitLab" or "&lt;Your Name&gt;'s GitLab" or
-something else descriptive.
+ something else descriptive.
1. Choose "Create New Facebook App ID"
diff --git a/doc/integration/jira.md b/doc/integration/jira.md
index b6923f74e28..c09fde08326 100644
--- a/doc/integration/jira.md
+++ b/doc/integration/jira.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/jira.md'
+---
+
This document was moved to [integrations/jira](../user/project/integrations/jira.md).
diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md
index 242890af981..76c124d2ce9 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -1 +1,5 @@
+---
+redirect_to: '../administration/auth/ldap.md'
+---
+
This document was moved to [`administration/auth/ldap`](../administration/auth/ldap.md).
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index 4db986197f3..2932c884d04 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -199,7 +199,7 @@ from the Omniauth provider's documentation.
sudo -u git -H bundle install --without development test mysql --path vendor/bundle --no-deployment
- > These are the same commands you used in the [Install Gems section](#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`.
+ > These are the same commands you used during initial installation in the [Install Gems section](../install/installation.md#install-gems) with `--path vendor/bundle --no-deployment` instead of `--deployment`.
- Start GitLab:
@@ -256,7 +256,7 @@ gitlab_rails['omniauth_enabled'] = false
You can enable profile syncing from selected OmniAuth providers and for all or for specific user information.
-When authenticating using LDAP, the user's email is always synced.
+When authenticating using LDAP, the user's name and email are always synced.
```ruby
gitlab_rails['sync_profile_from_provider'] = ['twitter', 'google_oauth2']
diff --git a/doc/integration/slack.md b/doc/integration/slack.md
index 8cd151fbf95..f84ab769218 100644
--- a/doc/integration/slack.md
+++ b/doc/integration/slack.md
@@ -1 +1,5 @@
+---
+redirect_to: '../project_services/slack.md'
+---
+
This document was moved to [project_services/slack.md](../project_services/slack.md).
diff --git a/doc/integration/slash_commands.md b/doc/integration/slash_commands.md
index 82ba1d7d984..cd755089be8 100644
--- a/doc/integration/slash_commands.md
+++ b/doc/integration/slash_commands.md
@@ -1,5 +1,7 @@
# Slash Commands
+> The `run` command was [introduced](https://gitlab.com/gitlab-org/gitlab-ee/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. [Moved](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9.
+
Slash commands in Mattermost and Slack allow you to control GitLab and view GitLab content right inside your chat client, without having to leave it. For Slack, this requires a [project service configuration](../user/project/integrations/slack_slash_commands.md). Simply type the command as a message in your chat client to activate it.
Commands are scoped to a project, with a trigger term that is specified during configuration.
@@ -16,6 +18,7 @@ Taking the trigger term as `project-name`, the commands are:
| `/project-name issue search <query>` | Shows up to 5 issues matching `<query>` |
| `/project-name issue move <id> to <project>` | Moves issue ID `<id>` to `<project>` |
| `/project-name deploy <from> to <to>` | Deploy from the `<from>` environment to the `<to>` environment |
+| `/project-name run <job name> <arguments>` | Execute [ChatOps](../ci/chatops/README.md) job `<job name>` on `master` |
Note that if you are using the [GitLab Slack application](https://docs.gitlab.com/ee/user/project/integrations/gitlab_slack_application.html) for
your GitLab.com projects, you need to [add the `gitlab` keyword at the beginning of the command](https://docs.gitlab.com/ee/user/project/integrations/gitlab_slack_application.html#usage).
diff --git a/doc/logs/logs.md b/doc/logs/logs.md
index a2eca62d691..0cb092c85fd 100644
--- a/doc/logs/logs.md
+++ b/doc/logs/logs.md
@@ -1 +1,5 @@
+---
+redirect_to: '../administration/logs.md'
+---
+
This document was moved to [administration/logs.md](../administration/logs.md).
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index 4ac81ab3ee7..29cb6ac9164 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -1 +1,5 @@
-This document was moved to [user/markdown.md](../user/markdown.md).
+---
+redirect_to: '../user/markdown.md'
+---
+
+This document was moved to [another location](../user/markdown.md).
diff --git a/doc/monitoring/health_check.md b/doc/monitoring/health_check.md
index 6cf93c33ec2..b611fa388b4 100644
--- a/doc/monitoring/health_check.md
+++ b/doc/monitoring/health_check.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/admin_area/monitoring/health_check.md'
+---
+
This document was moved to [user/admin_area/monitoring/health_check](../user/admin_area/monitoring/health_check.md).
diff --git a/doc/monitoring/performance/gitlab_configuration.md b/doc/monitoring/performance/gitlab_configuration.md
index 19d46135930..233a12ebd6f 100644
--- a/doc/monitoring/performance/gitlab_configuration.md
+++ b/doc/monitoring/performance/gitlab_configuration.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../administration/monitoring/performance/gitlab_configuration.md'
+---
+
This document was moved to [administration/monitoring/performance/gitlab_configuration](../../administration/monitoring/performance/gitlab_configuration.md).
diff --git a/doc/monitoring/performance/grafana_configuration.md b/doc/monitoring/performance/grafana_configuration.md
index 0d4be02ff5f..f4e3561a19f 100644
--- a/doc/monitoring/performance/grafana_configuration.md
+++ b/doc/monitoring/performance/grafana_configuration.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../administration/monitoring/performance/grafana_configuration.md'
+---
+
This document was moved to [administration/monitoring/performance/grafana_configuration](../../administration/monitoring/performance/grafana_configuration.md).
diff --git a/doc/monitoring/performance/influxdb_configuration.md b/doc/monitoring/performance/influxdb_configuration.md
index 15fd275e916..ae5f4c7e9df 100644
--- a/doc/monitoring/performance/influxdb_configuration.md
+++ b/doc/monitoring/performance/influxdb_configuration.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../administration/monitoring/performance/influxdb_configuration.md'
+---
+
This document was moved to [administration/monitoring/performance/influxdb_configuration](../../administration/monitoring/performance/influxdb_configuration.md).
diff --git a/doc/monitoring/performance/influxdb_schema.md b/doc/monitoring/performance/influxdb_schema.md
index e53f9701dc3..57fb74cb6cd 100644
--- a/doc/monitoring/performance/influxdb_schema.md
+++ b/doc/monitoring/performance/influxdb_schema.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../administration/monitoring/performance/influxdb_schema.md'
+---
+
This document was moved to [administration/monitoring/performance/influxdb_schema](../../administration/monitoring/performance/influxdb_schema.md).
diff --git a/doc/monitoring/performance/introduction.md b/doc/monitoring/performance/introduction.md
index 4d6f02b6547..e23eabd5f40 100644
--- a/doc/monitoring/performance/introduction.md
+++ b/doc/monitoring/performance/introduction.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../administration/monitoring/performance/index.md'
+---
+
This document was moved to [administration/monitoring/performance/introduction](../../administration/monitoring/performance/index.md).
diff --git a/doc/operations/README.md b/doc/operations/README.md
index d7a83948b87..319e5f48d38 100644
--- a/doc/operations/README.md
+++ b/doc/operations/README.md
@@ -1 +1,5 @@
+---
+redirect_to: '../administration/operations/index.md'
+---
+
This document was moved to [another location](../administration/operations/index.md).
diff --git a/doc/operations/cleaning_up_redis_sessions.md b/doc/operations/cleaning_up_redis_sessions.md
index 2a1d0a8c8eb..bde7fffd090 100644
--- a/doc/operations/cleaning_up_redis_sessions.md
+++ b/doc/operations/cleaning_up_redis_sessions.md
@@ -1 +1,5 @@
-This document was moved to [administration/operations/cleaning_up_redis_sessions](../administration/operations/cleaning_up_redis_sessions.md).
+---
+redirect_to: '../administration/operations/cleaning_up_redis_sessions.md'
+---
+
+This document was moved to [another location](../administration/operations/cleaning_up_redis_sessions.md).
diff --git a/doc/operations/moving_repositories.md b/doc/operations/moving_repositories.md
index c54bca324a5..57d47e3e9ea 100644
--- a/doc/operations/moving_repositories.md
+++ b/doc/operations/moving_repositories.md
@@ -1 +1,5 @@
-This document was moved to [administration/operations/moving_repositories](../administration/operations/moving_repositories.md).
+---
+redirect_to: '../administration/operations/moving_repositories.md'
+---
+
+This document was moved to [another location](../administration/operations/moving_repositories.md).
diff --git a/doc/operations/sidekiq_memory_killer.md b/doc/operations/sidekiq_memory_killer.md
index cf7c3b2e2ed..2df4a6e5648 100644
--- a/doc/operations/sidekiq_memory_killer.md
+++ b/doc/operations/sidekiq_memory_killer.md
@@ -1 +1,5 @@
-This document was moved to [administration/operations/sidekiq_memory_killer](../administration/operations/sidekiq_memory_killer.md).
+---
+redirect_to: '../administration/operations/sidekiq_memory_killer.md'
+---
+
+This document was moved to [another location](../administration/operations/sidekiq_memory_killer.md).
diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md
index fbc9697b755..949f4a66c9d 100644
--- a/doc/operations/unicorn.md
+++ b/doc/operations/unicorn.md
@@ -1 +1,5 @@
-This document was moved to [administration/operations/unicorn](../administration/operations/unicorn.md).
+---
+redirect_to: '../administration/operations/unicorn.md'
+---
+
+This document was moved to [another location](../administration/operations/unicorn.md).
diff --git a/doc/pages/README.md b/doc/pages/README.md
index 7878bce3f10..c67847f1a83 100644
--- a/doc/pages/README.md
+++ b/doc/pages/README.md
@@ -1 +1,5 @@
-This document was moved to [pages/index.md](../user/project/pages/index.md).
+---
+redirect_to: '../user/project/pages/index.md'
+---
+
+This document was moved to [another location](../user/project/pages/index.md).
diff --git a/doc/pages/administration.md b/doc/pages/administration.md
index 4eb3bb32c77..015dd54ec7f 100644
--- a/doc/pages/administration.md
+++ b/doc/pages/administration.md
@@ -1 +1,5 @@
-This document was moved to [administration/pages](../administration/pages/index.md).
+---
+redirect_to: '../administration/pages/index.md'
+---
+
+This document was moved to [another location](../administration/pages/index.md).
diff --git a/doc/pages/getting_started_part_one.md b/doc/pages/getting_started_part_one.md
index 1d63ccb4d2f..a0feed0b477 100644
--- a/doc/pages/getting_started_part_one.md
+++ b/doc/pages/getting_started_part_one.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/pages/getting_started_part_one.md'
+---
+
This document was moved to [another location](../user/project/pages/getting_started_part_one.md).
diff --git a/doc/pages/getting_started_part_three.md b/doc/pages/getting_started_part_three.md
index 1697b5cd6b4..b65247ff7b7 100644
--- a/doc/pages/getting_started_part_three.md
+++ b/doc/pages/getting_started_part_three.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/pages/getting_started_part_three.md'
+---
+
This document was moved to [another location](../user/project/pages/getting_started_part_three.md).
diff --git a/doc/pages/getting_started_part_two.md b/doc/pages/getting_started_part_two.md
index a58affec73d..05353c171fc 100644
--- a/doc/pages/getting_started_part_two.md
+++ b/doc/pages/getting_started_part_two.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/pages/getting_started_part_two.md'
+---
+
This document was moved to [another location](../user/project/pages/getting_started_part_two.md).
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index 78d67aeec78..85923a40b2c 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -1,3 +1,7 @@
+---
+redirect_to: '../user/permissions.md'
+---
+
# Permissions
This document was moved to [user/permissions.md](../user/permissions.md).
diff --git a/doc/profile/README.md b/doc/profile/README.md
index fda6d85a84c..4932cf33b87 100644
--- a/doc/profile/README.md
+++ b/doc/profile/README.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/profile/index.md'
+---
+
This document was moved to [user/profile/account](../user/profile/index.md).
diff --git a/doc/profile/preferences.md b/doc/profile/preferences.md
index cc16f3afe41..cf99bd61f5d 100644
--- a/doc/profile/preferences.md
+++ b/doc/profile/preferences.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/profile/preferences.md'
+---
+
This document was moved to [another location](../user/profile/preferences.md).
diff --git a/doc/profile/two_factor_authentication.md b/doc/profile/two_factor_authentication.md
index 60918a0339c..453ac833f59 100644
--- a/doc/profile/two_factor_authentication.md
+++ b/doc/profile/two_factor_authentication.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/profile/account/two_factor_authentication.md'
+---
+
This document was moved to [user/profile/account](../user/profile/account/two_factor_authentication.md).
diff --git a/doc/project_services/bamboo.md b/doc/project_services/bamboo.md
index 5b171080c72..a1ff8909f87 100644
--- a/doc/project_services/bamboo.md
+++ b/doc/project_services/bamboo.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/bamboo.md'
+---
+
This document was moved to [user/project/integrations/bamboo.md](../user/project/integrations/bamboo.md).
diff --git a/doc/project_services/bugzilla.md b/doc/project_services/bugzilla.md
index e67055d5616..1abf1b28939 100644
--- a/doc/project_services/bugzilla.md
+++ b/doc/project_services/bugzilla.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/bugzilla.md'
+---
+
This document was moved to [user/project/integrations/bugzilla.md](../user/project/integrations/bugzilla.md).
diff --git a/doc/project_services/emails_on_push.md b/doc/project_services/emails_on_push.md
index a2e831ada34..c5ab8aa5c70 100644
--- a/doc/project_services/emails_on_push.md
+++ b/doc/project_services/emails_on_push.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/emails_on_push.md'
+---
+
This document was moved to [user/project/integrations/emails_on_push.md](../user/project/integrations/emails_on_push.md).
diff --git a/doc/project_services/irker.md b/doc/project_services/irker.md
index 7f0850dcc24..af47abab117 100644
--- a/doc/project_services/irker.md
+++ b/doc/project_services/irker.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/irker.md'
+---
+
This document was moved to [user/project/integrations/irker.md](../user/project/integrations/irker.md).
diff --git a/doc/project_services/jira.md b/doc/project_services/jira.md
index 63614feba82..6a0108f400f 100644
--- a/doc/project_services/jira.md
+++ b/doc/project_services/jira.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/jira.md'
+---
+
This document was moved to [user/project/integrations/jira.md](../user/project/integrations/jira.md).
diff --git a/doc/project_services/kubernetes.md b/doc/project_services/kubernetes.md
index 0497a13c2b7..cfe36fcd1b2 100644
--- a/doc/project_services/kubernetes.md
+++ b/doc/project_services/kubernetes.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/kubernetes.md'
+---
+
This document was moved to [user/project/integrations/kubernetes.md](../user/project/integrations/kubernetes.md).
diff --git a/doc/project_services/mattermost.md b/doc/project_services/mattermost.md
index 554a028853e..de9f4d14cf7 100644
--- a/doc/project_services/mattermost.md
+++ b/doc/project_services/mattermost.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/mattermost.md'
+---
+
This document was moved to [user/project/integrations/mattermost.md](../user/project/integrations/mattermost.md).
diff --git a/doc/project_services/mattermost_slash_commands.md b/doc/project_services/mattermost_slash_commands.md
index 7c238b5dc37..82ec34739c1 100644
--- a/doc/project_services/mattermost_slash_commands.md
+++ b/doc/project_services/mattermost_slash_commands.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/mattermost_slash_commands.md'
+---
+
This document was moved to [user/project/integrations/mattermost_slash_commands.md](../user/project/integrations/mattermost_slash_commands.md).
diff --git a/doc/project_services/project_services.md b/doc/project_services/project_services.md
index 2c555c4edae..a355851a273 100644
--- a/doc/project_services/project_services.md
+++ b/doc/project_services/project_services.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/project_services.md'
+---
+
This document was moved to [user/project/integrations/project_services.md](../user/project/integrations/project_services.md).
diff --git a/doc/project_services/redmine.md b/doc/project_services/redmine.md
index 6010aa4dc75..05f28f00adc 100644
--- a/doc/project_services/redmine.md
+++ b/doc/project_services/redmine.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/redmine.md'
+---
+
This document was moved to [user/project/integrations/redmine.md](../user/project/integrations/redmine.md).
diff --git a/doc/project_services/services_templates.md b/doc/project_services/services_templates.md
index 8905d667c5a..ac6b85cc801 100644
--- a/doc/project_services/services_templates.md
+++ b/doc/project_services/services_templates.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/services_templates.md'
+---
+
This document was moved to [user/project/integrations/services_templates.md](../user/project/integrations/services_templates.md).
diff --git a/doc/project_services/slack.md b/doc/project_services/slack.md
index 1d3f98705e3..4c89ce92002 100644
--- a/doc/project_services/slack.md
+++ b/doc/project_services/slack.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/slack.md'
+---
+
This document was moved to [user/project/integrations/slack.md](../user/project/integrations/slack.md).
diff --git a/doc/project_services/slack_slash_commands.md b/doc/project_services/slack_slash_commands.md
index 9554c8decc8..ca0034256f1 100644
--- a/doc/project_services/slack_slash_commands.md
+++ b/doc/project_services/slack_slash_commands.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/slack_slash_commands.md'
+---
+
This document was moved to [user/project/integrations/slack_slash_commands.md](../user/project/integrations/slack_slash_commands.md).
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index 81ee7338e4e..8601551e3bd 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -1,6 +1,6 @@
# Public access
-GitLab allows you to change your projects' visibility in order be accessed
+GitLab allows [Owners](../user/permissions.md) to change a projects' visibility in order to be accessed
**publicly** or **internally**.
Projects with either of these visibility levels will be listed in the
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 2514ca94775..574ba961cb0 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -100,6 +100,13 @@ the gitlab task runner pod via `kubectl`. Refer to [backing up a GitLab installa
kubectl exec -it <gitlab task-runner pod> backup-utility
```
+Similarly to the Kubernetes case, if you have scaled out your GitLab
+cluster to use multiple application servers, you should pick a
+designated node (that won't be auto-scaled away) for running the
+backup rake task. Because the backup rake task is tightly coupled to
+the main Rails application, this is typically a node on which you're
+also running Unicorn/Puma and/or Sidekiq.
+
Example output:
```
@@ -195,6 +202,26 @@ To use the `copy` strategy instead of the default streaming strategy, specify
sudo gitlab-rake gitlab:backup:create STRATEGY=copy
```
+### Backup filename
+
+By default a backup file is created according to the specification in [the Backup timestamp](#backup-timestamp) section above. You can however override the `[TIMESTAMP]` part of the filename by setting the `BACKUP` environment variable. For example:
+
+```sh
+sudo gitlab-rake gitlab:backup:create BACKUP=dump
+```
+
+The resulting file will then be `dump_gitlab_backup.tar`. This is useful for systems that make use of rsync and incremental backups, and will result in considerably faster transfer speeds.
+
+### Rsyncable
+
+To make sure the generated archive is intelligently transferable by rsync, the `GZIP_RSYNCABLE=yes` option can be set. This will set the `--rsyncable` option to `gzip`. This is only useful in combination with setting [the Backup filename option](#backup-filename).
+
+Note that the `--rsyncable` option in `gzip` is not guaranteed to be available on all distributions. To verify that it is available in your distribution you can run `gzip --help` or consult the man pages.
+
+```sh
+sudo gitlab-rake gitlab:backup:create BACKUP=dump GZIP_RSYNCABLE=yes
+```
+
### Excluding specific directories from the backup
You can choose what should be exempt from the backup up by adding the environment variable `SKIP`.
@@ -382,6 +409,8 @@ an access key from the Google console first:
1. Select "Interoperability" and create an access key
1. Make note of the "Access Key" and "Secret" and replace them in the
configurations below
+1. In the buckets advanced settings ensure the Access Control option "Set object-level
+ and bucket-level permissions" is selected
1. Make sure you already have a bucket created
For Omnibus GitLab packages:
@@ -442,6 +471,8 @@ backups will be copied to, and will be created if it does not exist. If the
directory that you want to copy the tarballs to is the root of your mounted
directory, just use `.` instead.
+NOTE: **Note:** Since file system performance may affect GitLab's overall performance, we do not recommend using EFS for storage. See the [relevant documentation](../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs) for more details.
+
For Omnibus GitLab packages:
1. Edit `/etc/gitlab/gitlab.rb`:
diff --git a/doc/raketasks/check.md b/doc/raketasks/check.md
index f7f6a40cd04..ceb089e80c0 100644
--- a/doc/raketasks/check.md
+++ b/doc/raketasks/check.md
@@ -1,3 +1,5 @@
-# Check Rake Tasks
+---
+redirect_to: '../administration/raketasks/check.md'
+---
-This document was moved to [administration/raketasks/check](../administration/raketasks/check.md).
+This document was moved to [another location](../administration/raketasks/check.md).
diff --git a/doc/raketasks/maintenance.md b/doc/raketasks/maintenance.md
index 266aeb7d60e..f554a09d94d 100644
--- a/doc/raketasks/maintenance.md
+++ b/doc/raketasks/maintenance.md
@@ -1,3 +1,5 @@
-# Maintenance Rake Tasks
+---
+redirect_to: '../administration/raketasks/maintenance.md'
+---
-This document was moved to [administration/raketasks/maintenance](../administration/raketasks/maintenance.md).
+This document was moved to [another location](../administration/raketasks/maintenance.md).
diff --git a/doc/security/img/ssh_keys_restricted_key_icon.png b/doc/security/img/ssh_keys_restricted_key_icon.png
new file mode 100644
index 00000000000..ad3749e8233
--- /dev/null
+++ b/doc/security/img/ssh_keys_restricted_key_icon.png
Binary files differ
diff --git a/doc/security/ssh_keys_restrictions.md b/doc/security/ssh_keys_restrictions.md
index 213fa5bfef5..6b6a8a06cc9 100644
--- a/doc/security/ssh_keys_restrictions.md
+++ b/doc/security/ssh_keys_restrictions.md
@@ -17,3 +17,11 @@ In the Admin area under **Settings** (`/admin/application_settings`), look for
the "Visibility and Access Controls" area:
![SSH keys restriction admin settings](img/ssh_keys_restrictions_settings.png)
+
+If a restriction is imposed on any key type, users will be unable to upload new SSH keys that don't meet the requirement. Any existing keys that don't meet it will be disabled but not removed and users will be unable to pull or push code using them.
+
+An icon will be visible to the user of a restricted key in the SSH keys section of their profile:
+
+![Restricted SSH key icon](img/ssh_keys_restricted_key_icon.png)
+
+Hovering over this icon will tell you why the key is restricted.
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index 55b678d6af5..a3698d60f6d 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -147,7 +147,7 @@ Please refer to `group_rename` and `user_rename` for that case.
"user_name": "John Smith",
"user_username": "johnsmith",
"user_id": 41,
- "project_visibility": "private"
+ "project_visibility": "visibilitylevel|private"
}
```
@@ -158,7 +158,7 @@ Please refer to `group_rename` and `user_rename` for that case.
"created_at": "2012-07-21T07:30:56Z",
"updated_at": "2012-07-21T07:38:22Z",
"event_name": "user_remove_from_team",
- "project_access": "Maintainer",
+ "access_level": "Maintainer",
"project_id": 74,
"project_name": "StoreCloud",
"project_path": "storecloud",
@@ -167,7 +167,7 @@ Please refer to `group_rename` and `user_rename` for that case.
"user_name": "John Smith",
"user_username": "johnsmith",
"user_id": 41,
- "project_visibility": "private"
+ "project_visibility": "visibilitylevel|private"
}
```
diff --git a/doc/topics/authentication/index.md b/doc/topics/authentication/index.md
index df6897002c9..0fc7f57feab 100644
--- a/doc/topics/authentication/index.md
+++ b/doc/topics/authentication/index.md
@@ -21,7 +21,7 @@ This page gathers all the resources for the topic **Authentication** within GitL
- [Enforce Two-factor Authentication (2FA)](../../security/two_factor_authentication.md#enforce-two-factor-authentication-2fa)
- **Articles:**
- [How to Configure LDAP with GitLab CE](../../administration/auth/how_to_configure_ldap_gitlab_ce/index.md)
- - [How to Configure LDAP with GitLab EE](https://docs.gitlab.com/ee/administration/auth/how_to_configure_ldap_gitlab_ee/index.md)
+ - [How to Configure LDAP with GitLab EE](https://docs.gitlab.com/ee/administration/auth/how_to_configure_ldap_gitlab_ee/index.html)
- [Feature Highlight: LDAP Integration](https://about.gitlab.com/2014/07/10/feature-highlight-ldap-sync/)
- [Debugging LDAP](https://about.gitlab.com/handbook/support/workflows/support-engineering/ldap/debugging_ldap.html)
- **Integrations:**
@@ -35,7 +35,7 @@ This page gathers all the resources for the topic **Authentication** within GitL
## API
-- [OAuth 2 Tokens](../../api/README.md#oauth-2-tokens)
+- [OAuth 2 Tokens](../../api/README.md#oauth2-tokens)
- [Personal access tokens](../../api/README.md#personal-access-tokens)
- [Impersonation tokens](../../api/README.md#impersonation-tokens)
- [GitLab as an OAuth2 provider](../../api/oauth2.md#gitlab-as-an-oauth2-provider)
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 04d3b5cb277..11f24b4b701 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -13,7 +13,7 @@ Starting with GitLab 11.3, the Auto DevOps pipeline is enabled by default for al
projects. If it has not been explicitly enabled for the project, Auto DevOps will be automatically
disabled on the first pipeline failure. Your project will continue to use an alternative
[CI/CD configuration file](../../ci/yaml/README.md) if one is found. A GitLab
-administrator can [change this setting](../../user/admin_area/settings/continuous_integration.html#auto-devops)
+administrator can [change this setting](../../user/admin_area/settings/continuous_integration.html#auto-devops-core-only)
in the admin area.
With Auto DevOps, the software development process becomes easier to set up
@@ -114,7 +114,7 @@ To make full use of Auto DevOps, you will need:
will need Prometheus installed somewhere (inside or outside your cluster) and
configured to scrape your Kubernetes cluster. To get response metrics
(in addition to system metrics), you need to
- [configure Prometheus to monitor NGINX](../../user/project/integrations/prometheus_library/nginx_ingress.md#configuring-prometheus-to-monitor-for-nginx-ingress-metrics).
+ [configure Prometheus to monitor NGINX](../../user/project/integrations/prometheus_library/nginx_ingress.md#configuring-nginx-ingress-monitoring).
The [Prometheus service](../../user/project/integrations/prometheus.md)
integration needs to be enabled for the project, or enabled as a
[default service template](../../user/project/integrations/services_templates.md)
@@ -166,7 +166,7 @@ them to the Kubernetes pods that run your application(s).
When using Auto DevOps, you may want to deploy different environments to
different Kubernetes clusters. This is possible due to the 1:1 connection that
-[exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters).
+[exists between them](../../user/project/clusters/index.md#multiple-kubernetes-clusters-premium).
In the [Auto DevOps template](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml)
(used behind the scenes by Auto DevOps), there are currently 3 defined environment names that you need to know:
@@ -179,7 +179,7 @@ Those environments are tied to jobs that use [Auto Deploy](#auto-deploy), so
except for the environment scope, they would also need to have a different
domain they would be deployed to. This is why you need to define a separate
`KUBE_INGRESS_BASE_DOMAIN` variable for all the above
-[based on the environment](../../ci/variables/README.md#limiting-environment-scopes-of-variables).
+[based on the environment](https://docs.gitlab.com/ee/ci/variables/index.html#limiting-environment-scopes-of-variables-premium).
The following table is an example of how the three different clusters would
be configured.
@@ -188,7 +188,7 @@ be configured.
| ------------ | -------------- | ----------------------------- | ------------- | ------ |
| review | `review/*` | `review.example.com` | `review/*` | The review cluster which will run all [Review Apps](../../ci/review_apps/index.md). `*` is a wildcard, which means it will be used by every environment name starting with `review/`. |
| staging | `staging` | `staging.example.com` | `staging` | (Optional) The staging cluster which will run the deployments of the staging environments. You need to [enable it first](#deploy-policy-for-staging-and-production-environments). |
-| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production). |
+| production | `production` | `example.com` | `production` | The production cluster which will run the deployments of the production environment. You can use [incremental rollouts](#incremental-rollout-to-production-premium). |
To add a different cluster for each environment:
@@ -230,8 +230,25 @@ can enable/disable Auto DevOps at either the project-level or instance-level.
1. Click **Save changes** for the changes to take effect.
NOTE: **Note:**
-Even when disabled at the instance level, project maintainers are still able to enable
-Auto DevOps at the project level.
+Even when disabled at the instance level, group owners and project maintainers are still able to enable
+Auto DevOps at group-level and project-level, respectively.
+
+### Enabling/disabling Auto DevOps at the group-level
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/52447) in GitLab 11.10.
+
+To enable or disable Auto DevOps at the group-level:
+
+1. Go to group's **Settings > CI/CD > Auto DevOps** page.
+1. Toggle the **Default to Auto DevOps pipeline** checkbox (checked to enable, unchecked to disable).
+1. Click **Save changes** button for the changes to take effect.
+
+When enabling or disabling Auto DevOps at group-level, group configuration will be implicitly used for
+the subgroups and projects inside that group, unless Auto DevOps is specifically enabled or disabled on
+the subgroup or project.
+
+NOTE: **Note**
+Only administrators and group owners are allowed to enable or disable Auto DevOps at group-level.
### Enabling/disabling Auto DevOps at the project-level
@@ -269,12 +286,12 @@ The available options are:
- **Continuous deployment to production**: Enables [Auto Deploy](#auto-deploy)
with `master` branch directly deployed to production.
- **Continuous deployment to production using timed incremental rollout**: Sets the
- [`INCREMENTAL_ROLLOUT_MODE`](#timed-incremental-rollout-to-production) variable
+ [`INCREMENTAL_ROLLOUT_MODE`](#timed-incremental-rollout-to-production-premium) variable
to `timed`, and production deployment will be executed with a 5 minute delay between
each increment in rollout.
- **Automatic deployment to staging, manual deployment to production**: Sets the
[`STAGING_ENABLED`](#deploy-policy-for-staging-and-production-environments) and
- [`INCREMENTAL_ROLLOUT_MODE`](#incremental-rollout-to-production) variables
+ [`INCREMENTAL_ROLLOUT_MODE`](#incremental-rollout-to-production-premium) variables
to `1` and `manual`. This means:
- `master` branch is directly deployed to staging.
@@ -291,7 +308,7 @@ Auto Build creates a build of the application using an existing `Dockerfile` or
Heroku buildpacks.
Either way, the resulting Docker image is automatically pushed to the
-[Container Registry][container-registry] and tagged with the commit SHA.
+[Container Registry][container-registry] and tagged with the commit SHA or tag.
#### Auto Build using a Dockerfile
@@ -442,10 +459,10 @@ process. Auto Review Apps create a Review App for each branch.
Auto Review Apps will deploy your app to your Kubernetes cluster only. When no cluster
is available, no deployment will occur.
-The Review App will have a unique URL based on the project name, the branch
+The Review App will have a unique URL based on the project ID, the branch or tag
name, and a unique number, combined with the Auto DevOps base domain. For
-example, `user-project-branch-1234.example.com`. A link to the Review App shows
-up in the merge request widget for easy discovery. When the branch is deleted,
+example, `13083-review-project-branch-123456.example.com`. A link to the Review App shows
+up in the merge request widget for easy discovery. When the branch or tag is deleted,
for example after the merge request is merged, the Review App will automatically
be deleted.
@@ -503,20 +520,25 @@ Auto Deploy doesn't include deployments to staging or canary by default, but the
[Auto DevOps template] contains job definitions for these tasks if you want to
enable them.
-You can make use of [environment variables](#helm-chart-variables) to automatically
+You can make use of [environment variables](#environment-variables) to automatically
scale your pod replicas.
-It's important to note that when a project is deployed to a Kubernetes cluster,
-it relies on a Docker image that has been pushed to the
-[GitLab Container Registry](../../user/project/container_registry.md). Kubernetes
-fetches this image and uses it to run the application. If the project is public,
-the image can be accessed by Kubernetes without any authentication, allowing us
-to have deployments more usable. If the project is private/internal, the
-Registry requires credentials to pull the image. Currently, this is addressed
-by providing `CI_JOB_TOKEN` as the password that can be used, but this token will
-no longer be valid as soon as the deployment job finishes. This means that
-Kubernetes can run the application, but in case it should be restarted or
-executed somewhere else, it cannot be accessed again.
+> [Introduced][ce-19507] in GitLab 11.0.
+
+For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token)
+will be automatically created, when Auto DevOps is enabled and the Auto DevOps settings are saved. This Deploy Token
+can be used for permanent access to the registry.
+
+If the GitLab Deploy Token cannot be found, `CI_REGISTRY_PASSWORD` is
+used. Note that `CI_REGISTRY_PASSWORD` is only valid during deployment.
+This means that Kubernetes will be able to successfully pull the
+container image during deployment but in cases where the image needs to
+be pulled again, e.g. after pod eviction, Kubernetes will fail to do so
+as it will be attempting to fetch the image using
+`CI_REGISTRY_PASSWORD`.
+
+NOTE: **Note:**
+When the GitLab Deploy Token has been manually revoked, it won't be automatically created.
#### Migrations
@@ -551,15 +573,6 @@ The `/app` path is the directory of your project inside the docker image
as [configured by
Herokuish](https://github.com/gliderlabs/herokuish#paths)
-> [Introduced][ce-19507] in GitLab 11.0.
-
-For internal and private projects a [GitLab Deploy Token](../../user/project/deploy_tokens/index.md#gitlab-deploy-token)
-will be automatically created, when Auto DevOps is enabled and the Auto DevOps settings are saved. This Deploy Token
-can be used for permanent access to the registry.
-
-Note: **Note**
-When the GitLab Deploy Token has been manually revoked, it won't be automatically created.
-
### Auto Monitoring
NOTE: **Note:**
@@ -581,7 +594,7 @@ The metrics include:
In order to make use of monitoring you need to:
-1. [Deploy Prometheus](../../user/project/integrations/prometheus.md#configuring-your-own-prometheus-server-within-kubernetes) into your Kubernetes cluster
+1. [Deploy Prometheus](../../user/project/integrations/prometheus.md) into your Kubernetes cluster
1. If you would like response metrics, ensure you are running at least version
0.9.0 of NGINX Ingress and
[enable Prometheus metrics](https://github.com/kubernetes/ingress-nginx/blob/master/docs/examples/customization/custom-vts-metrics-prometheus/nginx-vts-metrics-conf.yaml).
@@ -684,7 +697,7 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| **Variable** | **Description** |
| ------------ | --------------- |
-| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-domain). By default, set automatically by the [Auto DevOps setting](#enabling-auto-devops). This variable is deprecated and [is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959). Use `KUBE_INGRESS_BASE_DOMAIN` instead. |
+| `AUTO_DEVOPS_DOMAIN` | The [Auto DevOps domain](#auto-devops-base-domain). By default, set automatically by the [Auto DevOps setting](#enablingdisabling-auto-devops). This variable is deprecated and [is scheduled to be removed](https://gitlab.com/gitlab-org/gitlab-ce/issues/56959). Use `KUBE_INGRESS_BASE_DOMAIN` instead. |
| `AUTO_DEVOPS_CHART` | The Helm Chart used to deploy your apps; defaults to the one [provided by GitLab](https://gitlab.com/charts/auto-deploy-app). |
| `AUTO_DEVOPS_CHART_REPOSITORY` | The Helm Chart repository used to search for charts; defaults to `https://charts.gitlab.io`. |
| `REPLICAS` | The number of replicas to deploy; defaults to 1. |
@@ -697,16 +710,18 @@ also be customized, and you can easily use a [custom buildpack](#custom-buildpac
| `POSTGRES_USER` | The PostgreSQL user; defaults to `user`. Set it to use a custom username. |
| `POSTGRES_PASSWORD` | The PostgreSQL password; defaults to `testing-password`. Set it to use a custom password. |
| `POSTGRES_DB` | The PostgreSQL database name; defaults to the value of [`$CI_ENVIRONMENT_SLUG`](../../ci/variables/README.md#predefined-environment-variables). Set it to use a custom database name. |
+| `POSTGRES_VERSION` | Tag for the [`postgres` Docker image](https://hub.docker.com/_/postgres) to use. Defaults to `9.6.2`. |
| `BUILDPACK_URL` | The buildpack's full URL. It can point to either Git repositories or a tarball URL. For Git repositories, it is possible to point to a specific `ref`, for example `https://github.com/heroku/heroku-buildpack-ruby.git#v142` |
| `SAST_CONFIDENCE_LEVEL` | The minimum confidence level of security issues you want to be reported; `1` for Low, `2` for Medium, `3` for High; defaults to `3`.|
| `DEP_SCAN_DISABLE_REMOTE_CHECKS` | Whether remote Dependency Scanning checks are disabled; defaults to `"false"`. Set to `"true"` to disable checks that send data to GitLab central servers. [Read more about remote checks](https://gitlab.com/gitlab-org/security-products/dependency-scanning#remote-checks).|
| `DB_INITIALIZE` | From GitLab 11.4, this variable can be used to specify the command to run to initialize the application's PostgreSQL database. It runs inside the application pod. |
| `DB_MIGRATE` | From GitLab 11.4, this variable can be used to specify the command to run to migrate the application's PostgreSQL database. It runs inside the application pod. |
| `STAGING_ENABLED` | From GitLab 10.8, this variable can be used to define a [deploy policy for staging and production environments](#deploy-policy-for-staging-and-production-environments). |
-| `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_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> |
+| `CANARY_ENABLED` | From GitLab 11.0, this variable can be used to define a [deploy policy for canary environments](#deploy-policy-for-canary-environments-premium). |
+| `INCREMENTAL_ROLLOUT_MODE`| From GitLab 11.4, this variable, if present, can be used to enable an [incremental rollout](#incremental-rollout-to-production-premium) of your application for the production environment.<br/>Set to: <ul><li>`manual`, for manual deployment jobs.</li><li>`timed`, for automatic rollout deployments with a 5 minute delay each one.</li></ul> |
| `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. |
| `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. |
+| `LICENSE_MANAGEMENT_DISABLED` | From GitLab 11.0, this variable can be used to disable the `license_management` 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. |
@@ -886,7 +901,7 @@ increasing the rollout up to 100%.
If `INCREMENTAL_ROLLOUT_MODE` is set to `manual` in your project, then instead
of the standard `production` job, 4 different
-[manual jobs](../../ci/pipelines.md#manual-actions-from-the-pipeline-graph)
+[manual jobs](../../ci/pipelines.md#manual-actions-from-pipeline-graphs)
will be created:
1. `rollout 10%`
@@ -938,7 +953,7 @@ TIP: **Tip:**
You can also set this inside your [project's settings](#deployment-strategy).
This configuration based on
-[incremental rollout to production](#incremental-rollout-to-production).
+[incremental rollout to production](#incremental-rollout-to-production-premium).
Everything behaves the same way, except:
@@ -1007,6 +1022,10 @@ planned for a subsequent release.
buildpack](#custom-buildpacks).
- Auto Test may fail because of a mismatch between testing frameworks. In this
case, you may need to customize your `.gitlab-ci.yml` with your test commands.
+- Auto Deploy may fail if it is unable to create a Kubernetes namespace and
+ service account for your project. See the
+ [troubleshooting failed deployments](../../user/project/clusters/index.md#troubleshooting-failed-deployment-jobs)
+ section to debug why these resources were not created.
### Disable the banner instance wide
diff --git a/doc/topics/autodevops/quick_start_guide.md b/doc/topics/autodevops/quick_start_guide.md
index 9749bd63f2b..367e192b85a 100644
--- a/doc/topics/autodevops/quick_start_guide.md
+++ b/doc/topics/autodevops/quick_start_guide.md
@@ -159,15 +159,15 @@ In the **test** stage, GitLab runs various checks on the application:
- The `test` job runs unit and integration tests by detecting the language and
framework ([Auto Test](index.md#auto-test))
- The `code_quality` job checks the code quality and is allowed to fail
- ([Auto Code Quality](index.md#auto-code-quality)) **[STARTER]**
+ ([Auto Code Quality](index.md#auto-code-quality-starter)) **[STARTER]**
- The `container_scanning` job checks the Docker container if it has any
vulnerabilities and is allowed to fail ([Auto Container Scanning](index.md#auto-container-scanning))
- The `dependency_scanning` job checks if the application has any dependencies
- susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning)) **[ULTIMATE]**
+ susceptible to vulnerabilities and is allowed to fail ([Auto Dependency Scanning](index.md#auto-dependency-scanning-ultimate)) **[ULTIMATE]**
- The `sast` job runs static analysis on the current code to check for potential
- security issues and is allowed to fail([Auto SAST](index.md#auto-sast)) **[ULTIMATE]**
+ security issues and is allowed to fail([Auto SAST](index.md#auto-sast-ultimate)) **[ULTIMATE]**
- The `license_management` job searches the application's dependencies to determine each of their
- licenses and is allowed to fail ([Auto License Management](index.md#auto-license-management)) **[ULTIMATE]**
+ licenses and is allowed to fail ([Auto License Management](index.md#auto-license-management-ultimate)) **[ULTIMATE]**
NOTE: **Note:**
As you might have noticed, all jobs except `test` are allowed to fail in the
@@ -178,7 +178,7 @@ deploys the application in Kubernetes ([Auto Deploy](index.md#auto-deploy)).
Lastly, in the **performance** stage, some performance tests will run
on the deployed application
-([Auto Browser Performance Testing](index.md#auto-browser-performance-testing)). **[PREMIUM]**
+([Auto Browser Performance Testing](index.md#auto-browser-performance-testing-premium)). **[PREMIUM]**
---
@@ -285,8 +285,8 @@ all within GitLab. Despite its automatic nature, Auto DevOps can also be configu
and customized to fit your workflow. Here are some helpful resources for further reading:
1. [Auto DevOps](index.md)
-1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters) **[PREMIUM]**
-1. [Incremental rollout to production](index.md#incremental-rollout-to-production) **[PREMIUM]**
+1. [Multiple Kubernetes clusters](index.md#using-multiple-kubernetes-clusters-premium) **[PREMIUM]**
+1. [Incremental rollout to production](index.md#incremental-rollout-to-production-premium) **[PREMIUM]**
1. [Disable jobs you don't need with environment variables](index.md#environment-variables)
1. [Use a static IP for your cluster](../../user/project/clusters/index.md#using-a-static-ip)
1. [Use your own buildpacks to build your application](index.md#custom-buildpacks)
diff --git a/doc/university/README.md b/doc/university/README.md
index 3e7d02770e4..cf13246067f 100644
--- a/doc/university/README.md
+++ b/doc/university/README.md
@@ -4,12 +4,9 @@ comments: false
# GitLab University
-GitLab University is the best place to learn about **Version Control with Git and GitLab**.
+GitLab University is a great place to start when learning about version control with Git and GitLab, as well as other GitLab features.
-It doesn't replace, but accompanies our great [Documentation](https://docs.gitlab.com)
-and [Blog Articles](https://about.gitlab.com/blog/).
-
-Would you like to contribute to GitLab University? Then please take a look at our contribution [process](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/PROCESS.md) for more information.
+If you're looking for a GitLab subscription for _your university_, see our [Education](https://about.gitlab.com/solutions/education/) page.
## GitLab University Curriculum
diff --git a/doc/university/high-availability/aws/README.md b/doc/university/high-availability/aws/README.md
index b4ba5b7a24e..1906655dfa5 100644
--- a/doc/university/high-availability/aws/README.md
+++ b/doc/university/high-availability/aws/README.md
@@ -3,7 +3,7 @@ comments: false
---
> **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).
+in [significantly degraded performance](../../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs).
# High Availability on AWS
@@ -125,10 +125,10 @@ image below we have the settings for this article but note the
following two options which are of particular interest for HA:
1. Multi-AZ-Deployment is recommended as redundancy. Read more at
-[High Availability (Multi-AZ)](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html)
+ [High Availability (Multi-AZ)](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/Concepts.MultiAZ.html)
1. While we chose a General Purpose (SSD) for this article a Provisioned
-IOPS (SSD) is best suited for HA. Read more about it at
-[Storage for Amazon RDS](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html)
+ IOPS (SSD) is best suited for HA. Read more about it at
+ [Storage for Amazon RDS](http://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_Storage.html)
![RDS Instance Specs](img/instance_specs.png)
@@ -182,7 +182,7 @@ Another option is to build a simple NFS server using a vanilla Linux server back
by AWS Elastic Block Storage (EBS).
> **Note:** GitLab does not recommend using AWS Elastic File System (EFS). See
- details in [High Availability NFS documentation](../../../administration/high_availability/nfs.md#aws-elastic-file-system)
+ details in [High Availability NFS documentation](../../../administration/high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs)
***
diff --git a/doc/university/training/end-user/README.md b/doc/university/training/end-user/README.md
index 60c0eadc572..99fb5e83387 100644
--- a/doc/university/training/end-user/README.md
+++ b/doc/university/training/end-user/README.md
@@ -86,6 +86,7 @@ git config --global user.email you@example.com
```bash
git config --global --list
```
+
- You might want or be required to use an SSH key.
- Instructions: [SSH](http://doc.gitlab.com/ce/ssh/README.html)
@@ -413,6 +414,7 @@ Revert is safer considering we can revert a revert
---
### Version Control
+
- Local VCS was used with a filesystem or a simple db.
- Centralized VCS such as Subversion includes collaboration but
still is prone to data loss as the main server is the single point of
diff --git a/doc/university/training/topics/stash.md b/doc/university/training/topics/stash.md
index f1c91fb1b37..dfd28fbcbc9 100644
--- a/doc/university/training/topics/stash.md
+++ b/doc/university/training/topics/stash.md
@@ -30,7 +30,7 @@ and we need to change to a different branch.
----------
- Every time we save a stash it gets stacked so by using list we can see all our
-stashes.
+ stashes.
```
git stash list
diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md
index 1f4b5fbffa7..350072186ee 100644
--- a/doc/update/mysql_to_postgresql.md
+++ b/doc/update/mysql_to_postgresql.md
@@ -95,7 +95,7 @@ Now, you can use pgloader to migrate the data from MySQL to PostgreSQL:
```
1. Once the migration finishes, you should see a summary table that looks like
-the following:
+ the following:
```
table name read imported errors total time
@@ -242,7 +242,7 @@ Now, you can use pgloader to migrate the data from MySQL to PostgreSQL:
```
1. Once the migration finishes, you should see a summary table that looks like
-the following:
+ the following:
```
table name read imported errors total time
diff --git a/doc/update/restore_after_failure.md b/doc/update/restore_after_failure.md
index faa3a6e65cb..964a3c76c04 100644
--- a/doc/update/restore_after_failure.md
+++ b/doc/update/restore_after_failure.md
@@ -12,7 +12,7 @@ First, roll back the code or package. For source installations this involves
checking out the older version (branch or tag). For Omnibus installations this
means installing the older .deb or .rpm package. Then, restore from a backup.
Follow the instructions in the
-[Backup and Restore](../raketasks/backup_restore.md#restore-a-previously-created-backup)
+[Backup and Restore](../raketasks/backup_restore.md#restore)
documentation.
## Potential problems on the next upgrade
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index 20d8ebecc0a..b0c9aaa1eed 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -40,8 +40,8 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
### 3. Update Ruby
-NOTE: Beginning in GitLab 11.0, we only support Ruby 2.4 or higher, and dropped
-support for Ruby 2.3. Be sure to upgrade if necessary.
+NOTE: Beginning in GitLab 11.6, we only support Ruby 2.5 or higher, and dropped
+support for Ruby 2.4. Be sure to upgrade if necessary.
You can check which version you are running with `ruby -v`.
diff --git a/doc/user/account/security.md b/doc/user/account/security.md
index f4078876fab..8a8edc23529 100644
--- a/doc/user/account/security.md
+++ b/doc/user/account/security.md
@@ -1 +1,5 @@
+---
+redirect_to: '../profile/account/index.md'
+---
+
This document was moved to [profile](../profile/account/index.md).
diff --git a/doc/user/account/two_factor_authentication.md b/doc/user/account/two_factor_authentication.md
index ea2c8307860..42a66becc50 100644
--- a/doc/user/account/two_factor_authentication.md
+++ b/doc/user/account/two_factor_authentication.md
@@ -1 +1,5 @@
+---
+redirect_to: '../profile/account/two_factor_authentication.md'
+---
+
This document was moved to [profile/account/two_factor_authentication](../profile/account/two_factor_authentication.md).
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
new file mode 100644
index 00000000000..00cea22e4e1
--- /dev/null
+++ b/doc/user/admin_area/index.md
@@ -0,0 +1,29 @@
+# GitLab Admin Area **[CORE ONLY]**
+
+The Admin Area provides a web UI for administering some features of GitLab self-managed instances.
+
+To access the Admin Area, either:
+
+- Click the Admin Area icon (the spanner or wrench icon).
+- Visit `/admin` on your self-managed instance.
+
+NOTE: **Note:**
+Only admin users can access the Admin Area.
+
+## Admin Area sections
+
+The Admin Area is made up of the following sections:
+
+| Section | Description |
+|:------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Overview | View your GitLab Dashboard, and maintain projects, users, groups, jobs, runners, and Gitaly servers. |
+| Monitoring | View GitLab system information, and information on background jobs, logs, [health checks](monitoring/health_check.md), request profiles, and audit logs. |
+| Messages | Send and manage [broadcast messages](broadcast_messages.md) for your users. |
+| System Hooks | Configure [system hooks](../../system_hooks/system_hooks.md) for many events. |
+| Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. |
+| Abuse Reports | Manage [abuse reports](abuse_reports.md) submitted by your users. |
+| Deploy Keys | Create instance-wide [SSH deploy keys](../../ssh/README.md#deploy-keys). |
+| Service Templates | Create [service templates](../project/integrations/services_templates.md) for projects. |
+| Labels | Create and maintain [labels](labels.md) for your GitLab instance. |
+| Appearance | Customize [GitLab's appearance](../../customization/index.md). |
+| Settings | Modify the [settings](settings/index.md) for your GitLab instance. |
diff --git a/doc/user/admin_area/monitoring/health_check.md b/doc/user/admin_area/monitoring/health_check.md
index c22982ac190..e183898dfb1 100644
--- a/doc/user/admin_area/monitoring/health_check.md
+++ b/doc/user/admin_area/monitoring/health_check.md
@@ -1,11 +1,11 @@
# Health Check
> **Notes:**
+
> - Liveness and readiness probes were [introduced][ce-10416] in GitLab 9.1.
-> - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and will
-> be deprecated in GitLab 9.1. Read more in the [old behavior](#old-behavior)
-> section.
-> - [Access token](#access-token) has been deprecated in GitLab 9.4
+> - The `health_check` endpoint was [introduced][ce-3888] in GitLab 8.8 and was
+> be deprecated in GitLab 9.1.
+> - [Access token](#access-token-deprecated) has been deprecated in GitLab 9.4
> in favor of [IP whitelist](#ip-whitelist)
GitLab provides liveness and readiness probes to indicate service health and
@@ -16,21 +16,19 @@ traffic until the system is ready or restart the container as needed.
## IP whitelist
-To access monitoring resources, the client IP needs to be included in a whitelist.
+To access monitoring resources, the requesting client IP needs to be included in a whitelist.
[Read how to add IPs to a whitelist for the monitoring endpoints][admin].
## Using the endpoints
-With default whitelist settings, the probes can be accessed from localhost:
+With default whitelist settings, the probes can be accessed from localhost using the following URLs:
- `http://localhost/-/health`
- `http://localhost/-/readiness`
- `http://localhost/-/liveness`
-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:
+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
@@ -38,9 +36,9 @@ GitLab OK
The readiness and liveness probes will provide a report of system health in JSON format.
-Readiness example output:
+`readiness` probe example output:
-```
+```json
{
"queues_check" : {
"status" : "ok"
@@ -60,9 +58,9 @@ Readiness example output:
}
```
-Liveness example output:
+`liveness` probe example output:
-```
+```json
{
"cache_check" : {
"status" : "ok"
diff --git a/doc/user/admin_area/settings/continuous_integration.md b/doc/user/admin_area/settings/continuous_integration.md
index d4853a5842e..22011cc6967 100644
--- a/doc/user/admin_area/settings/continuous_integration.md
+++ b/doc/user/admin_area/settings/continuous_integration.md
@@ -20,13 +20,13 @@ From now on, every existing project and newly created ones that don't have a
`.gitlab-ci.yml`, will use the Auto DevOps pipelines.
If you want to disable it for a specific project, you can do so in
-[its settings](../../../topics/autodevops/index.md#enabling-auto-devops).
+[its settings](../../../topics/autodevops/index.md#enablingdisabling-auto-devops).
## Maximum artifacts size **[CORE ONLY]**
The maximum size of the [job artifacts][art-yml] can be set in the Admin area
of your GitLab instance. The value is in *MB* and the default is 100MB per job;
-on GitLab.com it's [set to 1G](../../gitlab_com/index.md#gitlab-ci-cd).
+on GitLab.com it's [set to 1G](../../gitlab_com/index.md#gitlab-cicd).
To change it:
@@ -38,16 +38,16 @@ To change it:
The default expiration time of the [job artifacts](../../../administration/job_artifacts.md)
can be set in the Admin area of your GitLab instance. The syntax of duration is
-described in [`artifacts:expire_in`](../../../ci/yaml/README.md#artifacts-expire_in)
+described in [`artifacts:expire_in`](../../../ci/yaml/README.md#artifactsexpire_in)
and the default value is `30 days`. On GitLab.com they
-[never expire](../../gitlab_com/index.md#gitlab-ci-cd).
+[never expire](../../gitlab_com/index.md#gitlab-cicd).
1. Go to **Admin area > Settings > Continuous Integration and Deployment**.
1. Change the value of default expiration time.
1. Hit **Save changes** for the changes to take effect.
This setting is set per job and can be overridden in
-[`.gitlab-ci.yml`](../../../ci/yaml/README.md#artifacts-expire_in).
+[`.gitlab-ci.yml`](../../../ci/yaml/README.md#artifactsexpire_in).
To disable the expiration, set it to `0`. The default unit is in seconds.
## Archive jobs **[CORE ONLY]**
diff --git a/doc/user/discussions/img/reply_to_comment.gif b/doc/user/discussions/img/reply_to_comment.gif
new file mode 100644
index 00000000000..c62f7fdb5fe
--- /dev/null
+++ b/doc/user/discussions/img/reply_to_comment.gif
Binary files differ
diff --git a/doc/user/discussions/img/reply_to_comment_button.png b/doc/user/discussions/img/reply_to_comment_button.png
new file mode 100644
index 00000000000..d362b19785c
--- /dev/null
+++ b/doc/user/discussions/img/reply_to_comment_button.png
Binary files differ
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index d202b1260ad..23b9604a456 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -10,31 +10,36 @@ You can leave a comment in the following places:
- commits
- commit diffs
-The comment area supports [Markdown] and [quick actions]. One can edit their
-own comment at any time, and anyone with [Maintainer access level][permissions] or
-higher can also edit a comment made by someone else.
+There are standard comments, and you also have the option to create a comment
+in the form of a threaded discussion. A comment can also be [turned into a discussion](#start-a-discussion-by-replying-to-a-standard-comment)
+when it receives a reply.
-You could also reply to the notification email in order to reply to a comment,
-provided that [Reply by email] is configured by your GitLab admin. This also
-supports [Markdown] and [quick actions] as if replied from the web.
+The comment area supports [Markdown] and [quick actions]. You can edit your own
+comment at any time, and anyone with [Maintainer access level][permissions] or
+higher can also edit a comment made by someone else.
-Apart from the standard comments, you also have the option to create a comment
-in the form of a resolvable or threaded discussion.
+You can also reply to a comment notification email to reply to the comment if
+[Reply by email] is configured for your GitLab instance. Replying to a standard comment
+creates another standard comment. Replying to a discussion comment creates a reply in the
+discussion thread. Email replies support [Markdown] and [quick actions], just as if you replied from the web.
-## Resolvable discussions
+## Resolvable comments and discussions
> **Notes:**
+>
> - The main feature was [introduced][ce-5022] in GitLab 8.11.
> - Resolvable discussions can be added only to merge request diffs.
Discussion resolution helps keep track of progress during planning or code review.
-Resolving comments prevents you from forgetting to address feedback and lets you
-hide discussions that are no longer relevant.
-!["A discussion between two people on a piece of code"][discussion-view]
+Every standard comment or discussion thread in merge requests, commits, commit diffs, and
+snippets is initially displayed as unresolved. They can then be individually resolved by anyone
+with at least Developer access to the project or by the author of the change being reviewed.
+
+The need to resolve all standard comments or discussions prevents you from forgetting
+to address feedback and lets you hide discussions that are no longer relevant.
-Comments and discussions can be resolved by anyone with at least Developer
-access to the project or the author of the merge request.
+!["A discussion between two people on a piece of code"][discussion-view]
### Commit discussions in the context of a merge request
@@ -248,10 +253,10 @@ For large projects with many contributors, it may be useful to stop discussions
in issues or merge requests in these scenarios:
- The project maintainer has already resolved the discussion and it is not helpful
-for continued feedback. The project maintainer has already directed new conversation
-to newer issues or merge requests.
+ for continued feedback. The project maintainer has already directed new conversation
+ to newer issues or merge requests.
- The people participating in the discussion are trolling, abusive, or otherwise
-being unproductive.
+ being unproductive.
In these cases, a user with Developer permissions or higher in the project can lock (and unlock)
an issue or a merge request, using the "Lock" section in the sidebar. For issues,
@@ -272,21 +277,21 @@ edit existing comments. Non-team members are restricted from adding or editing c
| :-----------: | :----------: |
| ![Comment form member](img/lock_form_member.png) | ![Comment form non-member](img/lock_form_non_member.png) |
-Additionally locked issues can not be reopened.
+Additionally, locked issues and merge requests can not be reopened.
## Filtering notes
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/26723) in GitLab 11.5.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/26723) in GitLab 11.5.
-For issues with many comments like activity notes and user comments, sometimes
-finding useful information can be hard. There is a way to filter comments from single notes and discussions for merge requests and issues.
+For issues with many comments like activity notes and user comments, sometimes
+finding useful information can be hard. There is a way to filter comments from single notes and discussions for merge requests and issues.
From a merge request's **Discussion** tab, or from an epic/issue overview, find the filter's dropdown menu on the right side of the page, from which you can choose one of the following options:
- **Show all activity**: displays all user comments and system notes
-(issue updates, mentions from other issues, changes to the description, etc).
+ (issue updates, mentions from other issues, changes to the description, etc).
- **Show comments only**: only displays user comments in the list.
-- **Show history only**: only displays activity notes.
+- **Show history only**: only displays activity notes.
![Notes filters dropdown options](img/index_notes_filters.png)
@@ -298,21 +303,21 @@ from any device you're logged into.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/18008) in GitLab 11.6.
-As a reviewer, you're able to suggest code changes with a simple
+As a reviewer, you're able to suggest code changes with a simple
markdown syntax in Merge Request Diff discussions. Then, the
-Merge Request author (or other users with appropriate
+Merge Request author (or other users with appropriate
[permission](../permissions.md)) is able to apply these
-suggestions with a click, which will generate a commit in
+suggestions with a click, which will generate a commit in
the Merge Request authored by the user that applied them.
1. Choose a line of code to be changed, add a new comment, then click
-on the **Insert suggestion** icon in the toolbar:
+ on the **Insert suggestion** icon in the toolbar:
![Add a new comment](img/insert_suggestion.png)
-
+
> **Note:**
The suggestion will only affect the commented line. Multi-line
- suggestions are currently not supported. Will be introduced by
+ suggestions are currently not supported. Will be introduced by
[#53310](https://gitlab.com/gitlab-org/gitlab-ce/issues/53310).
1. In the comment, add your suggestion to the pre-populated code block:
@@ -327,9 +332,9 @@ on the **Insert suggestion** icon in the toolbar:
![Apply suggestions](img/suggestion.png)
> **Note:**
- Discussions are _not_ automatically resolved. Will be introduced by
+ Discussions are _not_ automatically resolved. Will be introduced by
[#54405](https://gitlab.com/gitlab-org/gitlab-ce/issues/54405).
-
+
Once the author applies a suggestion, it will be marked with the **Applied** label,
and GitLab will create a new commit with the message `Apply suggestion to <file-name>`
and push the suggested change directly into the codebase in the merge request's branch.
@@ -339,6 +344,26 @@ and push the suggested change directly into the codebase in the merge request's
Custom commit messages will be introduced by
[#54404](https://gitlab.com/gitlab-org/gitlab-ce/issues/54404).
+## Start a discussion by replying to a standard comment
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/30299) in GitLab 11.9
+
+To reply to a standard (non-discussion) comment, you can use the **Reply to comment** button.
+
+![Reply to comment button](img/reply_to_comment_button.png)
+
+The **Reply to comment** button is only displayed if you have permissions to reply to an existing discussion, or start a discussion from a standard comment.
+
+Clicking on the **Reply to comment** button will bring the reply area into focus and you can type your reply.
+
+![Reply to comment feature](img/reply_to_comment.gif)
+
+Relying to a non-discussion comment will convert the non-discussion comment to a
+threaded discussion once the reply is submitted. This conversion is considered an edit
+to the original comment, so a note about when it was last edited will appear underneath it.
+
+This feature only exists for Issues, Merge requests, and Epics. Commits, Snippets and Merge request diff discussions are not supported yet.
+
[ce-5022]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5022
[ce-7125]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7125
[ce-7527]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7527
diff --git a/doc/user/gitlab_com/index.md b/doc/user/gitlab_com/index.md
index c1c9b8bf43c..2fb6cec55fa 100644
--- a/doc/user/gitlab_com/index.md
+++ b/doc/user/gitlab_com/index.md
@@ -51,7 +51,7 @@ Below are the settings for [GitLab Pages].
| TLS certificates support| yes | no |
The maximum size of your Pages site is regulated by the artifacts maximum size
-which is part of [GitLab CI/CD](#gitlab-ci-cd).
+which is part of [GitLab CI/CD](#gitlab-cicd).
## GitLab CI/CD
@@ -60,7 +60,7 @@ Below are the current settings regarding [GitLab CI/CD](../../ci/README.md).
| Setting | GitLab.com | Default |
| ----------- | ----------------- | ------------- |
| Artifacts maximum size | 1G | 100M |
-| Artifacts [expiry time](../../ci/yaml/README.md#artifacts-expire_in) | kept forever | deleted after 30 days unless otherwise specified |
+| Artifacts [expiry time](../../ci/yaml/README.md#artifactsexpire_in) | kept forever | deleted after 30 days unless otherwise specified |
## Repository size limit
@@ -71,6 +71,14 @@ or over the size limit, you can [reduce your repository size with Git](../projec
| ----------- | ----------------- | ------------- |
| Repository size including LFS | 10G | Unlimited |
+## IP range
+
+GitLab.com, CI/CD, and related services are deployed into Google Cloud Platform (GCP). Any
+IP based firewall can be configured by looking up all
+[IP address ranges or CIDR blocks for GCP](https://cloud.google.com/compute/docs/faq#where_can_i_find_product_name_short_ip_ranges).
+
+[Static endpoints](https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues/5071) are being considered.
+
## Shared Runners
Shared Runners on GitLab.com run in [autoscale mode] and powered by
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index 8d223d1c5f0..984881ef26c 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -25,8 +25,21 @@ deployments.
| Application | GitLab version | Description | Helm Chart |
| ----------- | -------------- | ----------- | ---------- |
-| [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. | n/a |
-| [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](../../../topics/autodevops/index.md) or deploy your own web apps. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) |
+| [Helm Tiller](https://docs.helm.sh) | 11.6+ | 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. | n/a |
+| [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress) | 11.6+ | 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](../../../topics/autodevops/index.md) or deploy your own web apps. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) |
+| [Cert-Manager](https://docs.cert-manager.io/en/latest/) | 11.6+ | Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert-Manager on your cluster will issue a certificate by [Let's Encrypt](https://letsencrypt.org/) and ensure that certificates are valid and up-to-date. | [stable/cert-manager](https://github.com/helm/charts/tree/master/stable/cert-manager) |
+| [GitLab Runner](https://docs.gitlab.com/runner/) | 11.10+ | 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](../../../ci/README.md), 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](../../project/clusters/index.md#security-implications) before doing so. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
+
+NOTE: **Note:**
+Some [cluster
+applications](../../project/clusters/index.md#installing-applications)
+are installable only for a project-level cluster. Support for installing these
+applications in a group-level cluster is planned for future releases. For updates, see:
+
+- Support installing [JupyterHub in group-level
+ clusters](https://gitlab.com/gitlab-org/gitlab-ce/issues/51989)
+- Support installing [Prometheus in group-level
+ clusters](https://gitlab.com/gitlab-org/gitlab-ce/issues/51963)
## RBAC compatibility
@@ -56,7 +69,7 @@ group. That way you can have different clusters for different environments,
like dev, staging, production, etc.
Add another cluster similar to the first one and make sure to
-[set an environment scope](#environment-scopes) that will
+[set an environment scope](#environment-scopes-premium) that will
differentiate the new cluster from the rest.
## Base domain
@@ -72,11 +85,10 @@ The domain should have a wildcard DNS configured to the Ingress IP address.
## Environment scopes **[PREMIUM]**
-When adding more than one Kubernetes cluster to your project, you need
-to differentiate them with an environment scope. The environment scope
-associates clusters with [environments](../../../ci/environments.md)
-similar to how the [environment-specific
-variables](../../../ci/variables/README.md#limiting-environment-scopes-of-variables)
+When adding more than one Kubernetes cluster to your project, you need to differentiate
+them with an environment scope. The environment scope associates clusters with
+[environments](../../../ci/environments.md) similar to how the
+[environment-specific variables](https://docs.gitlab.com/ee/ci/variables/README.html#limiting-environment-scopes-of-variables-premium)
work.
While evaluating which environment matches the environment scope of a
@@ -133,4 +145,4 @@ The following features are not currently available for group-level clusters:
1. Terminals (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55487)).
1. Pod logs (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55488)).
-1. Deployment boards (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55488)).
+1. Deployment boards (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55489)).
diff --git a/doc/user/group/index.md b/doc/user/group/index.md
index 5fea683a7fd..3bcfd30079d 100644
--- a/doc/user/group/index.md
+++ b/doc/user/group/index.md
@@ -18,9 +18,9 @@ a link to the group settings. By clicking the last button you can leave that gro
You can create groups for numerous reasons. To name a few:
- Organize related projects under the same [namespace](#namespaces), add members to that
-group and grant access to all their projects at once
+ group and grant access to all their projects at once
- Create a group, include members of your team, and make it easier to
-`@mention` all the team at once in issues and merge requests
+ `@mention` all the team at once in issues and merge requests
- Create a group for your company members, and create [subgroups](subgroups/index.md)
for each individual team. Let's say you create a group called `company-team`, and among others,
you created subgroups in this group for each individual team `backend-team`,
@@ -43,11 +43,11 @@ In GitLab, a namespace is a unique name to be used as a user name, a group name,
For example, consider a user named Alex:
1. Alex creates an account on GitLab.com with the username `alex`;
-their profile will be accessed under `https://gitlab.example.com/alex`
+ their profile will be accessed under `https://gitlab.example.com/alex`
1. Alex creates a group for their team with the groupname `alex-team`;
-the group and its projects will be accessed under `https://gitlab.example.com/alex-team`
+ the group and its projects will be accessed under `https://gitlab.example.com/alex-team`
1. Alex creates a subgroup of `alex-team` with the subgroup name `marketing`;
-this subgroup and its projects will be accessed under `https://gitlab.example.com/alex-team/marketing`
+ this subgroup and its projects will be accessed under `https://gitlab.example.com/alex-team/marketing`
By doing so:
@@ -63,9 +63,8 @@ together in a single list view.
## Create a new group
-> **Notes:**
-> - For a list of words that are not allowed to be used as group names see the
-> [reserved names](../reserved_names.md).
+> For a list of words that are not allowed to be used as group names see the
+> [reserved names](../reserved_names.md).
You can create a group in GitLab from:
@@ -107,7 +106,7 @@ Consider we have a group with two projects:
- On the **Group Members** page we can now add a new user to the group.
- Now because this user is a **Developer** member of the group, he automatically
-gets **Developer** access to **all projects** within that group.
+ gets **Developer** access to **all projects** within that group.
If necessary, you can increase the access level of an individual user for a specific project,
by adding them again as a new member to the project with the new permission levels.
@@ -154,7 +153,7 @@ There are two different ways to add a new project to a group:
## Transfer projects into groups
-Learn how to [transfer a project into a group](../project/index.md#transfer-an-existing-project-into-a-group).
+Learn how to [transfer a project into a group](../project/settings/index.md#transferring-an-existing-project-into-another-namespace).
## Sharing a project with a group
@@ -168,19 +167,21 @@ Alternatively, you can [lock the sharing with group feature](#share-with-group-l
In GitLab Enterprise Edition it is possible to manage GitLab group memberships using LDAP groups.
See [the GitLab Enterprise Edition documentation](../../integration/ldap.md) for more information.
-## Transfer groups to another group
+## Transferring groups
-From 10.5 there are two different ways to transfer a group:
+From GitLab 10.5, groups can be transferred in the following ways:
-- Either by transferring a group into another group (making it a subgroup of that group).
-- Or by converting a subgroup into a root group (a group with no parent).
+- Top-level groups can be transferred to a group, converting them into subgroups.
+- Subgroups can be transferred to a new parent group.
+- Subgroups can be transferred out from a parent group, converting them into top-level groups.
-Please make sure to understand that:
+When transferring groups, note:
-- Changing a group's parent can have unintended side effects. See [Redirects when changing repository paths](https://docs.gitlab.com/ce/user/project/index.html#redirects-when-changing-repository-paths)
-- You can only transfer the group to a group you manage.
+- Changing a group's parent can have unintended side effects. See [Redirects when changing repository paths](../project/index.md#redirects-when-changing-repository-paths).
+- You can only transfer groups to groups you manage.
- You will need to update your local repositories to point to the new location.
-- If the parent group's visibility is lower than the group current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.
+- If the parent group's visibility is lower than the group's current visibility, visibility levels for subgroups and projects will be changed to match the new parent group's visibility.
+- Only explicit group membership is transferred, not inherited membership. If the group's owners have only inherited membership, this would leave the group without an owner. In this case, the user transferring the group becomes the group's owner.
## Group settings
@@ -267,9 +268,9 @@ Define project templates at a group-level by setting a group as a template sourc
### Advanced settings
- **Projects**: view all projects within that group, add members to each project,
-access each project's settings, and remove any project from the same screen.
+ access each project's settings, and remove any project from the same screen.
- **Webhooks**: configure [webhooks](../project/integrations/webhooks.md) to your group.
- **Kubernetes cluster integration**: connect your GitLab group with [Kubernetes clusters](clusters/index.md).
- **Audit Events**: view [Audit Events](https://docs.gitlab.com/ee/administration/audit_events.html#audit-events)
-for the group. **[STARTER ONLY]**
+ for the group. **[STARTER ONLY]**
- **Pipelines quota**: keep track of the [pipeline quota](../admin_area/settings/continuous_integration.md) for the group.
diff --git a/doc/user/group/subgroups/index.md b/doc/user/group/subgroups/index.md
index b6f8f55978b..3cecefe11f5 100644
--- a/doc/user/group/subgroups/index.md
+++ b/doc/user/group/subgroups/index.md
@@ -1,8 +1,8 @@
# Subgroups
NOTE: **Note:**
-[Introduced][ce-2772] in GitLab 9.0. Not available when using MySQL as external
-database (support removed in GitLab 9.3 [due to performance reasons][issue]).
+[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2772) in GitLab 9.0. Not available when using MySQL as external
+database (support removed in GitLab 9.3 [due to performance reasons](https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600)).
With subgroups (aka nested groups or hierarchical groups) you can have
up to 20 levels of nested groups, which among other things can help you to:
@@ -13,7 +13,7 @@ up to 20 levels of nested groups, which among other things can help you to:
- **Organize large projects.** For large projects, subgroups makes it
potentially easier to separate permissions on parts of the source code.
- **Make it easier to manage people and control visibility.** Give people
- different [permissions][] depending on their group [membership](#membership).
+ different [permissions](../../permissions.md#group-members-permissions) depending on their group [membership](#membership).
## Database Requirements
@@ -80,9 +80,9 @@ structure.
NOTE: **Note:**
You need to be an Owner of a group in order to be able to create a subgroup. For
-more information check the [permissions table][permissions].
+more information check the [permissions table](../../permissions.md#group-members-permissions).
For a list of words that are not allowed to be used as group names see the
-[reserved names][reserved].
+[reserved names](../../reserved_names.md).
Users can always create subgroups if they are explicitly added as an Owner to
a parent group even if group creation is disabled by an administrator in their
settings.
@@ -176,6 +176,6 @@ Here's a list of what you can't do with subgroups:
`group/subgroup01/subgroup03`.
[ce-2772]: https://gitlab.com/gitlab-org/gitlab-ce/issues/2772
-[permissions]: ../../permissions.md#group
+[permissions]: ../../permissions.md#group-members-permissions
[reserved]: ../../reserved_names.md
[issue]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30472#note_27747600
diff --git a/doc/user/index.md b/doc/user/index.md
index 22506b30498..d408504249e 100644
--- a/doc/user/index.md
+++ b/doc/user/index.md
@@ -8,13 +8,13 @@ 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/pricing/)
-includes, except [GitLab administrator](../README.md#administrator-documentation)
+includes, except [GitLab administrator](../administration/index.md)
settings, unless you have admin privileges to install, configure,
and upgrade your GitLab instance.
Admin privileges for [GitLab.com](https://gitlab.com/) are restricted to the GitLab team.
-For more information on configuring GitLab self-managed instances, see [Administrator documentation](../README.md#administrator-documentation).
+For more information on configuring GitLab self-managed instances, see [Administrator documentation](../administration/index.md).
## Overview
@@ -36,34 +36,34 @@ To get familiar with the concepts needed to develop code on GitLab, read the fol
GitLab is a Git-based platform that integrates a great number of essential tools for software development and deployment, and project management:
-- Hosting code in repositories with version control
+- Hosting code in repositories with version control.
- Tracking proposals for new implementations, bug reports, and feedback with a
-fully featured [Issue Tracker](project/issues/index.md#issue-tracker)
-- Organizing and prioritizing with [Issue Boards](project/issues/index.md#issue-boards)
+ fully featured [Issue Tracker](project/issues/index.md#issue-tracker).
+- Organizing and prioritizing with [Issue Boards](project/issues/index.md#issue-board).
- Reviewing code in [Merge Requests](project/merge_requests/index.md) with live-preview changes per
-branch with [Review Apps](../ci/review_apps/index.md)
-- Building, testing and deploying with built-in [Continuous Integration](../ci/README.md)
-- Deploying personal and professional static websites with [GitLab Pages](project/pages/index.md)
-- Integrating with Docker by using [GitLab Container Registry](project/container_registry.md)
-- Tracking the development lifecycle by usingn [GitLab Cycle Analytics](project/cycle_analytics.md)
+ branch with [Review Apps](../ci/review_apps/index.md).
+- Building, testing, and deploying with built-in [Continuous Integration](../ci/README.md).
+- Deploying personal and professional static websites with [GitLab Pages](project/pages/index.md).
+- Integrating with Docker by using [GitLab Container Registry](project/container_registry.md).
+- Tracking the development lifecycle by using [GitLab Cycle Analytics](project/cycle_analytics.md).
With GitLab Enterprise Edition, you can also:
-- Provide support with [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html)
+- Provide support with [Service Desk](https://docs.gitlab.com/ee/user/project/service_desk.html).
- Improve collaboration with
-[Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/index.html#merge-request-approvals),
-[Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html),
-and [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards)
-- Create formal relationships between issues with [Related Issues](https://docs.gitlab.com/ee/user/project/issues/related_issues.html)
+ [Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/index.html#merge-request-approvals),
+ [Multiple Assignees for Issues](https://docs.gitlab.com/ee/user/project/issues/multiple_assignees_for_issues.html),
+ and [Multiple Issue Boards](project/issue_board.md#multiple-issue-boards-starter).
+- Create formal relationships between issues with [Related Issues](https://docs.gitlab.com/ee/user/project/issues/related_issues.html).
- Use [Burndown Charts](https://docs.gitlab.com/ee/user/project/milestones/burndown_charts.html) to track progress during a sprint or while working on a new version of their software.
-- Leverage [Elasticsearch](https://docs.gitlab.com/ee/integration/elasticsearch.html) with [Advanced Global Search](https://docs.gitlab.com/ee/user/search/advanced_global_search.html) and [Advanced Syntax Search](https://docs.gitlab.com/ee/user/search/advanced_search_syntax.html) for faster, more advanced code search across your entire GitLab instance
-- [Authenticate users with Kerberos](https://docs.gitlab.com/ee/integration/kerberos.html)
+- Leverage [Elasticsearch](https://docs.gitlab.com/ee/integration/elasticsearch.html) with [Advanced Global Search](https://docs.gitlab.com/ee/user/search/advanced_global_search.html) and [Advanced Syntax Search](https://docs.gitlab.com/ee/user/search/advanced_search_syntax.html) for faster, more advanced code search across your entire GitLab instance.
+- [Authenticate users with Kerberos](https://docs.gitlab.com/ee/integration/kerberos.html).
- [Mirror a repository](https://docs.gitlab.com/ee/workflow/repository_mirroring.html) from elsewhere on your local server.
-- [Export issues as CSV](https://docs.gitlab.com/ee/user/project/issues/csv_export.html)
-- View your entire CI/CD pipeline involving more than one project with [Multiple-Project Pipeline Graphs](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html)
-- [Lock files](https://docs.gitlab.com/ee/user/project/file_lock.html) to prevent conflicts
-- View the current health and status of each CI environment running on Kubernetes with [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html)
-- Leverage continuous delivery method with [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html)
+- [Export issues as CSV](https://docs.gitlab.com/ee/user/project/issues/csv_export.html).
+- View your entire CI/CD pipeline involving more than one project with [Multiple-Project Pipelines](https://docs.gitlab.com/ee/ci/multi_project_pipeline_graphs.html).
+- [Lock files](https://docs.gitlab.com/ee/user/project/file_lock.html) to prevent conflicts.
+- View the current health and status of each CI environment running on Kubernetes with [Deploy Boards](https://docs.gitlab.com/ee/user/project/deploy_boards.html).
+- Leverage continuous delivery method with [Canary Deployments](https://docs.gitlab.com/ee/user/project/canary_deployments.html).
You can also [integrate](project/integrations/project_services.md) GitLab with
numerous third-party applications, such as Mattermost, Microsoft Teams, Trello,
@@ -77,12 +77,12 @@ build, test, and deploy your app with built-in GitLab CI/CD. Or, you can do
it all at once, from one single project.
- [Repositories](project/repository/index.md): Host your codebase in
-repositories with version control and as part of a fully integrated platform.
+ repositories with version control and as part of a fully integrated platform.
- [Issues](project/issues/index.md): Explore the best of GitLab Issues' features.
- [Merge Requests](project/merge_requests/index.md): Collaborate on code,
-reviews, live preview changes per branch, and request approvals with Merge Requests.
+ reviews, live preview changes per branch, and request approvals with Merge Requests.
- [Milestones](project/milestones/index.md): Work on multiple issues and merge
-requests towards the same target date with Milestones.
+ requests towards the same target date with Milestones.
## GitLab CI/CD
@@ -92,9 +92,9 @@ directly from GitLab. No third-party integrations needed.
- [GitLab Auto Deploy](../ci/autodeploy/index.md): Deploy your application out-of-the-box with GitLab Auto Deploy.
- [Review Apps](../ci/review_apps/index.md): Live-preview the changes introduced by a merge request with Review Apps.
- [GitLab Pages](project/pages/index.md): Publish your static site directly from
-GitLab with GitLab Pages. You can build, test, and deploy any Static Site Generator with Pages.
+ GitLab with GitLab Pages. You can build, test, and deploy any Static Site Generator with Pages.
- [GitLab Container Registry](project/container_registry.md): Build and deploy Docker
-images with Container Registry.
+ images with Container Registry.
## Account
@@ -102,13 +102,13 @@ There is a lot you can customize and configure
to enjoy the best of GitLab.
- [Settings](profile/index.md): Manage your user settings to change your personal info,
-personal access tokens, authorized applications, etc.
+ personal access tokens, authorized applications, etc.
- [Authentication](../topics/authentication/index.md): Read through the authentication
-methods available in GitLab.
+ methods available in GitLab.
- [Permissions](permissions.md): Learn the different set of permissions levels for each
-user type (guest, reporter, developer, maintainer, owner).
+ user type (guest, reporter, developer, maintainer, owner).
- [Feature highlight](feature_highlight.md): Learn more about the little blue dots
-around the app that explain certain features
+ around the app that explain certain features
- [Abuse reports](abuse_reports.md): Report abuse from users to GitLab administrators
## Groups
@@ -125,7 +125,7 @@ merge requests, code snippets, and commits.
When performing inline reviews to implementations
to your codebase through merge requests you can
-gather feedback through [resolvable discussions](discussions/index.md#resolvable-discussions).
+gather feedback through [resolvable discussions](discussions/index.md#resolvable-comments-and-discussions).
### GitLab Flavored Markdown (GFM)
diff --git a/doc/user/instance_statistics/convdev.md b/doc/user/instance_statistics/convdev.md
index 52b99b69a02..2c9e0ecbf65 100644
--- a/doc/user/instance_statistics/convdev.md
+++ b/doc/user/instance_statistics/convdev.md
@@ -1,27 +1,26 @@
# Conversational Development Index
-> [Introduced][ce-30469] in GitLab 9.3.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/30469) in GitLab 9.3.
+
+NOTE: **Note:**
+Your GitLab instance's [usage ping](../admin_area/settings/usage_statistics.md#usage-ping-core-only) must be activated in order to use this feature.
The Conversational Development Index (ConvDev Index) gives you an overview of your entire
instance's adoption of [Concurrent DevOps](https://about.gitlab.com/concurrent-devops/)
-from planning to monitoring. It displays the usage of these GitLab features over
+from planning to monitoring.
+
+This displays the usage of these GitLab features over
the last 30 days, averaged over the number of active users in that time period. It also
provides a Lead score per feature, which is calculated based on GitLab's analysis
-of top-performing instances based on [usage ping data][ping] that GitLab has
-collected. Your score is compared to the lead score, expressed as a percentage.
-Your overall index score is an average of all your feature score percentages.
+of top-performing instances based on [usage ping data](../admin_area/settings/usage_statistics.md#usage-ping-core-only) that GitLab has
+collected. Your score is compared to the lead score of each feature and then expressed as a percentage at the bottom of said feature.
+Your overall index score is an average of all your feature score percentages - this percentage value is presented above all the of features on the page.
![ConvDev index](img/convdev_index.png)
The page also provides helpful links to articles and GitLab docs, to help you
improve your scores.
-Your GitLab instance's [usage ping][ping] must be activated in order to use this feature.
Usage ping data is aggregated on GitLab's servers for analysis. Your usage
-information is **not sent** to any other GitLab instances.
-
-If you have just started using GitLab, it may take a few weeks for data to be
+information is **not sent** to any other GitLab instances. If you have just started using GitLab, it may take a few weeks for data to be
collected before this feature is available.
-
-[ce-30469]: https://gitlab.com/gitlab-org/gitlab-ce/issues/30469
-[ping]: ../admin_area/settings/usage_statistics.md#usage-ping
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index a7a87773eec..8239742969a 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -267,10 +267,7 @@ However the wrapping tags cannot be mixed as such:
### Emoji
-> If this is not rendered correctly, see
-https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#emoji
-
-```
+```md
Sometimes you want to :monkey: around a bit and add some :star2: to your :speech_balloon:. Well we have a gift for you:
:zap: You can use emoji anywhere GFM is supported. :v:
@@ -288,15 +285,15 @@ On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/he
Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
```
-Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/monkey.png" width="20px" height="20px"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/star2.png" width="20px" height="20px"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speech_balloon.png" width="20px" height="20px">. Well we have a gift for you:
+Sometimes you want to <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/monkey.png" width="20px" height="20px" style="display:inline;margin:0"> around a bit and add some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/star2.png" width="20px" height="20px" style="display:inline;margin:0"> to your <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/speech_balloon.png" width="20px" height="20px" style="display:inline;margin:0">. Well we have a gift for you:
-<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/zap.png" width="20px" height="20px">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/v.png" width="20px" height="20px">
+<img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/zap.png" width="20px" height="20px" style="display:inline;margin:0">You can use emoji anywhere GFM is supported. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/v.png" width="20px" height="20px" style="display:inline;margin:0">
-You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/bug.png" width="20px" height="20px"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/speak_no_evil.png" width="20px" height="20px"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/snail.png" width="20px" height="20px"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/birthday.png" width="20px" height="20px">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/heart.png" width="20px" height="20px"> you for that.
+You can use it to point out a <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/bug.png" width="20px" height="20px" style="display:inline;margin:0"> or warn about <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/speak_no_evil.png" width="20px" height="20px" style="display:inline;margin:0"> patches. And if someone improves your really <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/snail.png" width="20px" height="20px" style="display:inline;margin:0"> code, send them some <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/birthday.png" width="20px" height="20px" style="display:inline;margin:0">. People will <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/heart.png" width="20px" height="20px" style="display:inline;margin:0"> you for that.
-If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/fearful.png" width="20px" height="20px">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/family.png" width="20px" height="20px">. All you need to do is to look up one of the supported codes.
+If you are new to this, don't be <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/fearful.png" width="20px" height="20px" style="display:inline;margin:0">. You can easily join the emoji <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/family.png" width="20px" height="20px" style="display:inline;margin:0">. All you need to do is to look up one of the supported codes.
-Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/app/assets/images/emoji/thumbsup.png" width="20px" height="20px">
+Consult the [Emoji Cheat Sheet](https://www.webfx.com/tools/emoji-cheat-sheet/) for a list of all supported emoji codes. <img src="https://gitlab.com/gitlab-org/gitlab-ce/raw/master/public/-/emojis/1/thumbsup.png" width="20px" height="20px" style="display:inline;margin:0">
Most emoji are natively supported on macOS, Windows, iOS, Android and will fallback to image-based emoji where there is lack of support.
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index 74a966f3a17..a340dd063e4 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -14,7 +14,7 @@ be able to create issues, leave comments, and clone or download the project code
When a member leaves the team all the assigned [Issues](project/issues/index.md) and [Merge Requests](project/merge_requests/index.md)
will be unassigned automatically.
-GitLab [administrators](../README.md#administrator-documentation) receive all permissions.
+GitLab [administrators](../administration/index.md) receive all permissions.
To add or import a user, you can follow the
[project members documentation](../user/project/members/index.md).
@@ -23,6 +23,12 @@ To add or import a user, you can follow the
See our [product handbook on permissions](https://about.gitlab.com/handbook/product#permissions-in-gitlab)
+## Instance-wide user permissions
+
+By default, users can create top-level groups and change their
+usernames. A GitLab administrator can configure the GitLab instance to
+[modify this behavior](../administration/user_settings.md).
+
## Project members permissions
NOTE: **Note:**
@@ -42,6 +48,8 @@ The following table depicts the various user permission levels in a project.
| See a job log | ✓ [^3] | ✓ | ✓ | ✓ | ✓ |
| Download and browse job artifacts | ✓ [^3] | ✓ | ✓ | ✓ | ✓ |
| View wiki pages | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
+| Create and edit wiki pages | | | ✓ | ✓ | ✓ |
+| Delete wiki pages | | | | ✓ | ✓ |
| View license management reports **[ULTIMATE]** | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| View Security reports **[ULTIMATE]** | ✓ [^1] | ✓ | ✓ | ✓ | ✓ |
| View project code | [^1] | ✓ | ✓ | ✓ | ✓ |
@@ -62,6 +70,8 @@ The following table depicts the various user permission levels in a project.
| Lock issue discussions | | ✓ | ✓ | ✓ | ✓ |
| Create issue from vulnerability **[ULTIMATE]** | | ✓ | ✓ | ✓ | ✓ |
| View Error Tracking list | | ✓ | ✓ | ✓ | ✓ |
+| Pull from [Maven repository](https://docs.gitlab.com/ee/user/project/packages/maven_repository.html) or [NPM registry](https://docs.gitlab.com/ee/user/project/packages/npm_registry.html) **[PREMIUM]** | | ✓ | ✓ | ✓ | ✓ |
+| Publish to [Maven repository](https://docs.gitlab.com/ee/user/project/packages/maven_repository.html) or [NPM registry](https://docs.gitlab.com/ee/user/project/packages/npm_registry.html) **[PREMIUM]** | | | ✓ | ✓ | ✓ |
| Lock merge request discussions | | | ✓ | ✓ | ✓ |
| Create new environments | | | ✓ | ✓ | ✓ |
| Stop environments | | | ✓ | ✓ | ✓ |
@@ -72,7 +82,6 @@ The following table depicts the various user permission levels in a project.
| Force push to non-protected branches | | | ✓ | ✓ | ✓ |
| Remove non-protected branches | | | ✓ | ✓ | ✓ |
| Add tags | | | ✓ | ✓ | ✓ |
-| Write a wiki | | | ✓ | ✓ | ✓ |
| Cancel and retry jobs | | | ✓ | ✓ | ✓ |
| Create or update commit status | | | ✓ | ✓ | ✓ |
| Update a container registry | | | ✓ | ✓ | ✓ |
@@ -97,7 +106,7 @@ The following table depicts the various user permission levels in a project.
| Manage variables | | | | ✓ | ✓ |
| Manage GitLab Pages | | | | ✓ | ✓ |
| Manage GitLab Pages domains and certificates | | | | ✓ | ✓ |
-| Remove GitLab Pages | | | | | ✓ |
+| Remove GitLab Pages | | | | ✓ | ✓ |
| View GitLab Pages protected by [access control](project/pages/introduction.md#gitlab-pages-access-control-core-only) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Manage clusters | | | | ✓ | ✓ |
| Manage license policy **[ULTIMATE]** | | | | ✓ | ✓ |
@@ -107,10 +116,10 @@ The following table depicts the various user permission levels in a project.
| Transfer project to another namespace | | | | | ✓ |
| Remove project | | | | | ✓ |
| Delete issues | | | | | ✓ |
-| Remove pages | | | | | ✓ |
| Force push to protected branches [^4] | | | | | |
| Remove protected branches [^4] | | | | | |
| View project Audit Events | | | | ✓ | ✓ |
+| View project statistics | | ✓ | ✓ | ✓ | ✓ |
## Project features permissions
@@ -235,7 +244,7 @@ The regex pattern format is Ruby, but it needs to be convertible to JavaScript,
Here are some examples:
-- Use `\.internal@domain\.com` to mark email addresses containing ".internal@domain.com" internal.
+- Use `\.internal@domain\.com$` to mark email addresses ending with ".internal@domain.com" internal.
- Use `^(?:(?!\.ext@domain\.com).)*$\r?` to mark users with email addresses NOT including .ext@domain.com internal.
Please be aware that this regex could lead to a DOS attack, [see](https://en.wikipedia.org/wiki/ReDoS?) ReDos on Wikipedia.
diff --git a/doc/user/profile/account/index.md b/doc/user/profile/account/index.md
index 06667bfc5f1..56b6498e16c 100644
--- a/doc/user/profile/account/index.md
+++ b/doc/user/profile/account/index.md
@@ -1,2 +1,5 @@
+---
+redirect_to: '../index.md#profile-settings'
+---
This document was moved to [../index.md#profile-settings](../index.md#profile-settings).
diff --git a/doc/user/profile/account/two_factor_authentication.md b/doc/user/profile/account/two_factor_authentication.md
index 0a7c5832a2d..b74bd81d467 100644
--- a/doc/user/profile/account/two_factor_authentication.md
+++ b/doc/user/profile/account/two_factor_authentication.md
@@ -41,7 +41,7 @@ or a U2F device.
**On your phone:**
1. Install a compatible application. We recommend [Google Authenticator]
-\(proprietary\) or [FreeOTP] \(open source\).
+ \(proprietary\) or [FreeOTP] \(open source\).
1. In the application, add a new entry in one of two ways:
- Scan the code with your phone's camera to add the entry automatically.
- Enter the details provided to add the entry manually.
@@ -59,6 +59,7 @@ of recovery codes.
### Enable 2FA via U2F device
> **Notes:**
+>
> - GitLab officially only supports [Yubikey] U2F devices.
> - Support for U2F devices was added in GitLab 8.8.
diff --git a/doc/user/profile/active_sessions.md b/doc/user/profile/active_sessions.md
index 5119c0e30d0..28e3f4904a9 100644
--- a/doc/user/profile/active_sessions.md
+++ b/doc/user/profile/active_sessions.md
@@ -4,7 +4,7 @@
> in GitLab 10.8.
GitLab lists all devices that have logged into your account. This allows you to
-review the sessions and revoke any of it that you don't recognize.
+review the sessions.
## Listing all active sessions
@@ -12,9 +12,3 @@ review the sessions and revoke any of it that you don't recognize.
1. Navigate to the **Active Sessions** tab.
![Active sessions list](img/active_sessions_list.png)
-
-## Revoking a session
-
-1. Navigate to your [profile's](#profile-settings) **Settings > Active Sessions**.
-1. Click on **Revoke** besides a session. The current session cannot be
- revoked, as this would sign you out of GitLab.
diff --git a/doc/user/profile/img/active_sessions_list.png b/doc/user/profile/img/active_sessions_list.png
index 5d94dca69cc..1e242ac4710 100644
--- a/doc/user/profile/img/active_sessions_list.png
+++ b/doc/user/profile/img/active_sessions_list.png
Binary files differ
diff --git a/doc/user/profile/img/personal_access_tokens.png b/doc/user/profile/img/personal_access_tokens.png
deleted file mode 100644
index d29f4cb0a20..00000000000
--- a/doc/user/profile/img/personal_access_tokens.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md
index ba756c4b793..b216b9f255c 100644
--- a/doc/user/profile/index.md
+++ b/doc/user/profile/index.md
@@ -35,13 +35,13 @@ From there, you can:
- Manage [2FA](account/two_factor_authentication.md)
- Change your username and [delete your account](account/delete_account.md)
- Manage applications that can
-[use GitLab as an OAuth provider](../../integration/oauth_provider.md#introduction-to-oauth)
+ [use GitLab as an OAuth provider](../../integration/oauth_provider.md#introduction-to-oauth)
- Manage [personal access tokens](personal_access_tokens.md) to access your account via API and authorized applications
- Add and delete emails linked to your account
- Choose which email to use for notifications, web-based commits, and display on your public profile
-- Manage [SSH keys](../../ssh/README.md#ssh) to access your account via SSH
+- Manage [SSH keys](../../ssh/README.md) to access your account via SSH
- Manage your [preferences](preferences.md#syntax-highlighting-theme)
-to customize your own GitLab experience
+ to customize your own GitLab experience
- [View your active sessions](active_sessions.md) and revoke any of them if necessary
- Access your audit log, a security log of important events involving your account
diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md
index 7d55048c994..3a4d09c35d9 100644
--- a/doc/user/profile/personal_access_tokens.md
+++ b/doc/user/profile/personal_access_tokens.md
@@ -20,21 +20,19 @@ at midnight UTC.
You can create as many personal access tokens as you like from your GitLab
profile.
-1. Log in to your GitLab account.
-1. Go to your **Profile settings**.
-1. Go to **Access tokens**.
-1. Choose a name and optionally an expiry date for the token.
+1. Log in to GitLab.
+1. In the upper-right corner, click your avatar and select **Settings**.
+1. On the **User Settings** menu, select **Access Tokens**.
+1. Choose a name and optional expiry date for the token.
1. Choose the [desired scopes](#limiting-scopes-of-a-personal-access-token).
-1. Click on **Create personal access token**.
+1. Click the **Create personal access token** button.
1. Save the personal access token somewhere safe. Once you leave or refresh
the page, you won't be able to access it again.
-![Personal access tokens page](img/personal_access_tokens.png)
+### Revoking a personal access token
-## Revoking a personal access token
-
-At any time, you can revoke any personal access token by just clicking the
-respective **Revoke** button under the 'Active personal access tokens' area.
+At any time, you can revoke any personal access token by clicking the
+respective **Revoke** button under the **Active Personal Access Token** area.
## Limiting scopes of a personal access token
diff --git a/doc/user/profile/preferences.md b/doc/user/profile/preferences.md
index 7387d1810ca..f399dc40164 100644
--- a/doc/user/profile/preferences.md
+++ b/doc/user/profile/preferences.md
@@ -93,8 +93,20 @@ You can choose between 3 options:
## Localization
+### Language
+
+Select your preferred language from a list of supported languages.
+
+*This feature is experimental and translations are not complete yet.*
+
### First day of the week
The first day of the week can be customised for calendar views and date pickers.
-You can choose **Sunday** or **Monday** as the first day of the week. If you select **System Default**, the system-wide default setting will be used.
+You can choose one of the following options as the first day of the week:
+
+- Saturday
+- Sunday
+- Monday
+
+If you select **System Default**, the system-wide default setting will be used.
diff --git a/doc/user/project/badges.md b/doc/user/project/badges.md
index 19eb95099ce..8849dd2d684 100644
--- a/doc/user/project/badges.md
+++ b/doc/user/project/badges.md
@@ -63,6 +63,12 @@ are available:
- `%{commit_sha}`: ID of the most recent commit to the default branch of a
project's repository
+NOTE: **Note:**
+Placeholders allow badges to expose otherwise-private information, such as the
+default branch or commit SHA when the project is configured to have a private
+repository. This is by design, as badges are intended to be used publicly. Avoid
+using these placeholders if the information is sensitive.
+
## API
You can also configure badges via the GitLab API. As in the settings, there is
diff --git a/doc/user/project/builds/artifacts.md b/doc/user/project/builds/artifacts.md
index 514c729b37d..1b0f3f61394 100644
--- a/doc/user/project/builds/artifacts.md
+++ b/doc/user/project/builds/artifacts.md
@@ -1 +1,5 @@
+---
+redirect_to: '../pipelines/job_artifacts.md'
+---
+
This document was moved to [pipelines/job_artifacts](../pipelines/job_artifacts.md).
diff --git a/doc/user/project/bulk_editing.md b/doc/user/project/bulk_editing.md
index fead99c5e88..d0c7daf4692 100644
--- a/doc/user/project/bulk_editing.md
+++ b/doc/user/project/bulk_editing.md
@@ -1,6 +1,7 @@
# Bulk editing issues and merge requests
> **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
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 85a4af24dc5..ed883e6dcdc 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -41,7 +41,7 @@ integration, make sure the following requirements are met:
- A [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
is set up and you have permissions to access it.
-- The Kubernetes Engine API is enabled. Follow the steps as outlined in the
+- The Kubernetes Engine API and related service are enabled. It should work immediately but may take up to 10 minutes after you create a project. For more information see the
["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin).
### Creating the cluster
@@ -69,11 +69,20 @@ new Kubernetes cluster to your project:
- **Number of nodes** - Enter the number of nodes you wish the cluster to have.
- **Machine type** - The [machine type](https://cloud.google.com/compute/docs/machine-types)
of the Virtual Machine instance that the cluster will be based on.
+ - **RBAC-enabled cluster** - Leave this checked if using default GKE creation options, see the [RBAC section](#role-based-access-control-rbac) for more information.
1. Finally, click the **Create Kubernetes cluster** button.
After a couple of minutes, your cluster will be ready to go. You can now proceed
to install some [pre-defined applications](#installing-applications).
+NOTE: **Note:**
+GitLab requires basic authentication enabled and a client certificate issued for
+the cluster in order to setup an [initial service
+account](#access-controls). Starting from [GitLab
+11.10](https://gitlab.com/gitlab-org/gitlab-ce/issues/58208), the cluster
+creation process will explicitly request that basic authentication and
+client certificate is enabled.
+
## Adding an existing Kubernetes cluster
To add an existing Kubernetes cluster to your project:
@@ -86,15 +95,26 @@ To add an existing Kubernetes cluster to your project:
1. Click **Add Kubernetes cluster**.
1. Click **Add an existing Kubernetes cluster** and fill in the details:
- **Kubernetes cluster name** (required) - The name you wish to give the cluster.
- - **Environment scope** (required)- The
- [associated environment](#setting-the-environment-scope) to this cluster.
+ - **Environment scope** (required) - The
+ [associated environment](#setting-the-environment-scope-premium) to this cluster.
- **API URL** (required) -
It's the URL that GitLab uses to access the Kubernetes API. Kubernetes
exposes several APIs, we want the "base" URL that is common to all of them,
e.g., `https://kubernetes.example.com` rather than `https://kubernetes.example.com/api/v1`.
- - **CA certificate** (optional) -
- If the API is using a self-signed TLS certificate, you'll also need to include
- the `ca.crt` contents here.
+
+ Get the API URL by running this command:
+
+ ```sh
+ kubectl cluster-info | grep 'Kubernetes master' | awk '/http/ {print $NF}'
+ ```
+ - **CA certificate** (required) - A valid Kubernetes certificate is needed to authenticate to the EKS cluster. We will use the certificate created by default.
+ - List the secrets with `kubectl get secrets`, and one should named similar to
+ `default-token-xxxxx`. Copy that token name for use below.
+ - Get the certificate by running this command:
+
+ ```sh
+ kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 --decode
+ ```
- **Token** -
GitLab authenticates against Kubernetes using service tokens, which are
scoped to a particular `namespace`.
@@ -102,36 +122,66 @@ To add an existing Kubernetes cluster to your project:
[`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)
privileges.** To create this service account:
- 1. Create a `gitlab` service account in the `default` namespace:
-
- ```bash
- kubectl create -f - <<EOF
- apiVersion: v1
- kind: ServiceAccount
- metadata:
- name: gitlab
- namespace: default
- EOF
- ```
- 1. Create a cluster role binding to give the `gitlab` service account
- `cluster-admin` privileges:
+ 1. Create a file called `gitlab-admin-service-account.yaml` with contents:
- ```bash
- kubectl create -f - <<EOF
+ ```yaml
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: gitlab-admin
+ namespace: kube-system
+ ---
+ apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
- apiVersion: rbac.authorization.k8s.io/v1
metadata:
- name: gitlab-cluster-admin
- subjects:
- - kind: ServiceAccount
- name: gitlab
- namespace: default
+ name: gitlab-admin
roleRef:
+ apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
- apiGroup: rbac.authorization.k8s.io
- EOF
+ subjects:
+ - kind: ServiceAccount
+ name: gitlab-admin
+ namespace: kube-system
+ ```
+
+ 1. Apply the service account and cluster role binding to your cluster:
+
+ ```bash
+ kubectl apply -f gitlab-admin-service-account.yaml
+ ```
+
+ Output:
+
+ ```bash
+ serviceaccount "gitlab-admin" created
+ clusterrolebinding "gitlab-admin" created
```
+
+ 1. Retrieve the token for the `gitlab-admin` service account:
+
+ ```bash
+ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep gitlab-admin | awk '{print $1}')
+ ```
+
+ Copy the `<authentication_token>` value from the output:
+
+ ```yaml
+ Name: gitlab-admin-token-b5zv4
+ Namespace: kube-system
+ Labels: <none>
+ Annotations: kubernetes.io/service-account.name=gitlab-admin
+ kubernetes.io/service-account.uid=bcfe66ac-39be-11e8-97e8-026dce96b6e8
+
+ Type: kubernetes.io/service-account-token
+
+ Data
+ ====
+ ca.crt: 1025 bytes
+ namespace: 11 bytes
+ token: <authentication_token>
+ ```
+
NOTE: **Note:**
For GKE clusters, you will need the
`container.clusterRoleBindings.create` permission to create a cluster
@@ -153,14 +203,6 @@ To add an existing Kubernetes cluster to your project:
After a couple of minutes, your cluster will be ready to go. You can now proceed
to install some [pre-defined applications](#installing-applications).
-To determine the:
-
-- API URL, run `kubectl cluster-info | grep 'Kubernetes master' | awk '/http/ {print $NF}'`.
-- Token:
- 1. List the secrets by running: `kubectl get secrets`. Note the name of the secret you need the token for.
- 1. Get the token for the appropriate secret by running: `kubectl get secret <SECRET_NAME> -o jsonpath="{['data']['token']}" | base64 --decode`.
-- CA certificate, run `kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 --decode`.
-
## Security implications
CAUTION: **Important:**
@@ -176,12 +218,19 @@ applications running on the cluster.
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24580) in GitLab 11.8.
-Domains at the cluster level permit support for multiple domains
-per [multiple Kubernetes clusters](#multiple-kubernetes-clusters-premium). When specifying a domain,
-this will be automatically set as an environment variable (`KUBE_INGRESS_BASE_DOMAIN`) during
-the [Auto DevOps](../../../topics/autodevops/index.md) stages.
+NOTE: **Note:**
+You do not need to specify a base domain on cluster settings when using GitLab Serverless. The domain in that case
+will be specified as part of the Knative installation. See [Installing Applications](#installing-applications).
+
+Specifying a base domain will automatically set `KUBE_INGRESS_BASE_DOMAIN` as an environment variable.
+If you are using [Auto DevOps](../../../topics/autodevops/index.md), this domain will be used for the different
+stages. For example, Auto Review Apps and Auto Deploy.
-The domain should have a wildcard DNS configured to the Ingress IP address.
+The domain should have a wildcard DNS configured to the Ingress IP address. After ingress has been installed (see [Installing Applications](#installing-applications)),
+you can either:
+
+- Create an `A` record that points to the Ingress IP address with your domain provider.
+- Enter a wildcard DNS address using a service such as nip.io or xip.io. For example, `192.168.1.1.xip.io`.
## Access controls
@@ -268,7 +317,7 @@ install it manually.
NOTE: **Note:**
Before starting the installation of applications, make sure that time is synchronized
between your GitLab server and your Kubernetes cluster. Otherwise, installation could fail
-and you may get errors like `Error: remote error: tls: bad certificate`
+and you may get errors like `Error: remote error: tls: bad certificate`
in the `stdout` of pods created by GitLab in your Kubernetes cluster.
GitLab provides a one-click install for various applications which can
@@ -292,10 +341,10 @@ by GitLab before installing any of the applications.
| ----------- | :------------: | ----------- | --------------- |
| [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. | n/a |
| [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. | [stable/nginx-ingress](https://github.com/helm/charts/tree/master/stable/nginx-ingress) |
-| [Cert Manager](http://docs.cert-manager.io/en/latest/) | 11.6+ | Cert Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert Manager on your cluster will issue a certificate by [Let's Encrypt](https://letsencrypt.org/) and ensure that certificates are valid and up-to-date. | [stable/cert-manager](https://github.com/helm/charts/tree/master/stable/cert-manager) |
+| [Cert-Manager](https://docs.cert-manager.io/en/latest/) | 11.6+ | Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert-Manager on your cluster will issue a certificate by [Let's Encrypt](https://letsencrypt.org/) and ensure that certificates are valid and up-to-date. | [stable/cert-manager](https://github.com/helm/charts/tree/master/stable/cert-manager) |
| [Prometheus](https://prometheus.io/docs/introduction/overview/) | 10.4+ | Prometheus is an open-source monitoring and alerting system useful to supervise your deployed applications. | [stable/prometheus](https://github.com/helm/charts/tree/master/stable/prometheus) |
-| [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. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
-| [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 a [custom Jupyter image](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) that installs additional useful packages on top of the base Jupyter. You will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix). More information on creating executable runbooks can be found in [our Nurtch documentation](runbooks/index.md#nurtch-executable-runbooks). **Note**: Authentication will be enabled for any user of the GitLab server via OAuth2. HTTPS will be supported in a future release. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
+| [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](../../../ci/README.md), 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. | [runner/gitlab-runner](https://gitlab.com/charts/gitlab-runner) |
+| [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 a [custom Jupyter image](https://gitlab.com/gitlab-org/jupyterhub-user-image/blob/master/Dockerfile) that installs additional useful packages on top of the base Jupyter. Authentication will be enabled only for [project members](../members/index.md) with [Developer or higher](../../permissions.md) access to the project. You will also see ready-to-use DevOps Runbooks built with Nurtch's [Rubix library](https://github.com/amit1rrr/rubix). More information on creating executable runbooks can be found in [our Nurtch documentation](runbooks/index.md#nurtch-executable-runbooks). Note that Ingress must be installed and have an IP address assigned before JupyterHub can be installed. | [jupyter/jupyterhub](https://jupyterhub.github.io/helm-chart/) |
| [Knative](https://cloud.google.com/knative) | 11.5+ | Knative provides a platform to create, deploy, and manage serverless workloads from a Kubernetes cluster. It is used in conjunction with, and includes [Istio](https://istio.io) to provide an external IP address for all programs hosted by Knative. You will be prompted to enter a wildcard domain where your applications will be exposed. Configure your DNS server to use the external IP address for that domain. For any application created and installed, they will be accessible as `<program_name>.<kubernetes_namespace>.<domain_name>`. This will require your kubernetes cluster to have [RBAC enabled](#role-based-access-control-rbac). | [knative/knative](https://storage.googleapis.com/triggermesh-charts)
With the exception of Knative, the applications will be installed in a dedicated
@@ -307,27 +356,55 @@ you should be careful as GitLab cannot detect it. In this case, installing
Tiller via the applications will result in the cluster having it twice, which
can lead to confusion during deployments.
-## Getting the external IP address
+### Upgrading applications
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/24789)
+in GitLab 11.8.
+
+Users can perform a one-click upgrade for the GitLab Runner application,
+when there is an upgrade available.
+
+To upgrade the GitLab Runner application:
+
+1. Navigate to your project's **Operations > Kubernetes**.
+1. Select your cluster.
+1. Click the **Upgrade** button for the Runnner application.
+
+The **Upgrade** button will not be shown if there is no upgrade
+available.
+
+NOTE: **Note:**
+Upgrades will reset values back to the values built into the `runner`
+chart plus the values set by
+[`values.yaml`](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/vendor/runner/values.yaml)
+
+## Getting the external endpoint
NOTE: **Note:**
With the following procedure, a load balancer must be installed in your cluster
-to obtain the external IP address. You can use either
+to obtain the endpoint. You can use either
[Ingress](#installing-applications), or Knative's own load balancer
([Istio](https://istio.io)) if using [Knative](#installing-applications).
-In order to publish your web application, you first need to find the external IP
-address associated to your load balancer.
+In order to publish your web application, you first need to find the endpoint which will be either an IP
+address or a hostname associated with your load balancer.
-### Let GitLab fetch the IP address
+### Automatically determining the external endpoint
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17052) in GitLab 10.6.
-If you [installed Ingress or Knative](#installing-applications),
-you should see the Ingress IP address on this same page within a few minutes.
-If you don't see this, GitLab might not be able to determine the IP address of
-your ingress application in which case you should manually determine it.
+After you install [Ingress or Knative](#installing-applications), Gitlab attempts to determine the external endpoint
+and it should be available within a few minutes. If the endpoint doesn't appear
+and your cluster runs on Google Kubernetes Engine:
-### Manually determining the IP address
+1. Check your [Kubernetes cluster on Google Kubernetes Engine](https://console.cloud.google.com/kubernetes) to ensure there are no errors on its nodes.
+1. Ensure you have enough [Quotas](https://console.cloud.google.com/iam-admin/quotas) on Google Kubernetes Engine. For more information, see [Resource Quotas](https://cloud.google.com/compute/quotas).
+1. Check [Google Cloud's Status](https://status.cloud.google.com/) to ensure they are not having any disruptions.
+
+If GitLab is still unable to determine the endpoint of your Ingress or Knative application, you can
+manually determine it by following the steps below.
+
+### Manually determining the external endpoint
If the cluster is on GKE, click the **Google Kubernetes Engine** link in the
**Advanced settings**, or go directly to the
@@ -337,7 +414,7 @@ the `gcloud` command in a local terminal or using the **Cloud Shell**.
If the cluster is not on GKE, follow the specific instructions for your
Kubernetes provider to configure `kubectl` with the right credentials.
-The output of the following examples will show the external IP address of your
+The output of the following examples will show the external endpoint of your
cluster. This information can then be used to set up DNS entries and forwarding
rules that allow external access to your deployed applications.
@@ -345,19 +422,19 @@ If you installed the Ingress [via the **Applications**](#installing-applications
run the following command:
```bash
-kubectl get svc --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
+kubectl get service --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].ip}'
```
-For Istio/Knative, the command will be different:
+Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
```bash
-kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
+kubectl get service --namespace=gitlab-managed-apps ingress-nginx-ingress-controller -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'
```
-Some Kubernetes clusters return a hostname instead, like [Amazon EKS](https://aws.amazon.com/eks/). For these platforms, run:
+For Istio/Knative, the command will be different:
```bash
-kubectl get service ingress-nginx-ingress-controller -n gitlab-managed-apps -o jsonpath="{.status.loadBalancer.ingress[0].hostname}".
+kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
```
Otherwise, you can list the IP addresses of all load balancers:
@@ -376,13 +453,12 @@ reserved IP.
Read how to [promote an ephemeral external IP address in GKE](https://cloud.google.com/compute/docs/ip-addresses/reserve-static-external-ip-address#promote_ephemeral_ip).
-### Pointing your DNS at the cluster IP
+### Pointing your DNS at the external endpoint
-Once you've set up the static IP, you should associate it to a [wildcard DNS
-record](https://en.wikipedia.org/wiki/Wildcard_DNS_record), in order to be able
-to reach your apps. This heavily depends on your domain provider, but in case
-you aren't sure, just create an A record with a wildcard host like
-`*.example.com.`.
+Once you've set up the external endpoint, you should associate it with a [wildcard DNS
+record](https://en.wikipedia.org/wiki/Wildcard_DNS_record) such as `*.example.com.`
+in order to be able to reach your apps. If your external endpoint is an IP address,
+use an A record. If your external endpoint is a hostname, use a CNAME record.
## Multiple Kubernetes clusters **[PREMIUM]**
@@ -393,17 +469,14 @@ project. That way you can have different clusters for different environments,
like dev, staging, production, etc.
Simply add another cluster, like you did the first time, and make sure to
-[set an environment scope](#setting-the-environment-scope) that will
+[set an environment scope](#setting-the-environment-scope-premium) that will
differentiate the new cluster with the rest.
## Setting the environment scope **[PREMIUM]**
-When adding more than one Kubernetes clusters to your project, you need
-to differentiate them with an environment scope. The environment scope
-associates clusters with [environments](../../../ci/environments.md)
-similar to how the [environment-specific
-variables](../../../ci/variables/README.md#limiting-environment-scopes-of-variables)
-work.
+When adding more than one Kubernetes cluster to your project, you need to differentiate
+them with an environment scope. The environment scope associates clusters with [environments](../../../ci/environments.md) similar to how the
+[environment-specific variables](https://docs.gitlab.com/ee/ci/variables/README.html#limiting-environment-scopes-of-variables-premium) work.
The default environment scope is `*`, which means all jobs, regardless of their
environment, will use that cluster. Each scope can only be used by a single
@@ -466,32 +539,33 @@ GitLab CI/CD build environment.
| `KUBE_CA_PEM_FILE` | Path to a file containing PEM data. Only present if a custom CA bundle was specified. |
| `KUBE_CA_PEM` | (**deprecated**) Raw PEM data. Only if a custom CA bundle was specified. |
| `KUBECONFIG` | Path to a file containing `kubeconfig` for this deployment. CA bundle would be embedded if specified. This config also embeds the same token defined in `KUBE_TOKEN` so you likely will only need this variable. This variable name is also automatically picked up by `kubectl` so you won't actually need to reference it explicitly if using `kubectl`. |
-| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](#base-domain) for more information. | 
+| `KUBE_INGRESS_BASE_DOMAIN` | From GitLab 11.8, this variable can be used to set a domain per cluster. See [cluster domains](#base-domain) for more information. |
NOTE: **NOTE:**
Prior to GitLab 11.5, `KUBE_TOKEN` was the Kubernetes token of the main
service account of the cluster integration.
-### Troubleshooting missing `KUBECONFIG` or `KUBE_TOKEN`
+### Troubleshooting failed deployment jobs
+
+GitLab will create a namespace and service account specifically for your
+deployment jobs. These resources are created just before the deployment
+job starts. Sometimes there may be errors that cause their creation to fail.
+
+In such instances, your job will fail with the message:
-GitLab will create a new service account specifically for your CI builds. The
-new service account is created when the cluster is added to the project.
-Sometimes there may be errors that cause the service account creation to fail.
+```The job failed to complete prerequisite tasks```
-In such instances, your build will not be passed the `KUBECONFIG` or
-`KUBE_TOKEN` variables and, if you are using Auto DevOps, your Auto DevOps
-pipelines will no longer trigger a `production` deploy build. You will need to
-check the [logs](../../../administration/logs.md) to debug why the service
-account creation failed.
+You will need to check the [logs](../../../administration/logs.md) to debug
+why the namespace and service account creation failed.
A common reason for failure is that the token you gave GitLab did not have
[`cluster-admin`](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#user-facing-roles)
privileges as GitLab expects.
-Another common problem for why these variables are not being passed to your
-builds is that they must have a matching
+Another common problem is caused by a missing `KUBECONFIG` or `KUBE_TOKEN`.
+To be passed to your job, it must have a matching
[`environment:name`](../../../ci/environments.md#defining-environments). If
-your build has no `environment:name` set, it will not be passed the Kubernetes
+your job has no `environment:name` set, it will not be passed the Kubernetes
credentials.
## Monitoring your Kubernetes cluster **[ULTIMATE]**
diff --git a/doc/user/project/clusters/runbooks/index.md b/doc/user/project/clusters/runbooks/index.md
index e1b8dc07b50..54c475a1762 100644
--- a/doc/user/project/clusters/runbooks/index.md
+++ b/doc/user/project/clusters/runbooks/index.md
@@ -4,6 +4,10 @@ Runbooks are a collection of documented procedures that explain how to
carry out a particular process, be it starting, stopping, debugging,
or troubleshooting a particular system.
+Using [Jupyter Notebooks](https://jupyter.org/) and the [Rubix library](https://github.com/Nurtch/rubix),
+users can get started writing their own executable runbooks.
+
+
## Overview
Historically, runbooks took the form of a decision tree or a detailed
@@ -23,7 +27,7 @@ runbooks. A sample runbook is provided, showcasing common operations.
**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
Watch this [video](https://www.youtube.com/watch?v=Q_OqHIIUPjE)
-for an overview of how this is acomplished in GitLab!**
+for an overview of how this is accomplished in GitLab!**
## Requirements
@@ -36,7 +40,7 @@ To create an executable runbook, you will need:
can run the helm CLI in a safe environment.
1. **Ingress** - Ingress can provide load balancing, SSL termination, and name-based
virtual hosting. It acts as a web proxy for your applications.
-1. **JupyterHub** - JupyterHub is a multi-user service for managing notebooks across
+1. **JupyterHub** - [JupyterHub](https://jupyterhub.readthedocs.io/) is a multi-user service for managing notebooks across
a team. Jupyter Notebooks provide a web-based interactive programming environment
used for data analysis, visualization, and machine learning.
diff --git a/doc/user/project/clusters/serverless/img/app-domain.png b/doc/user/project/clusters/serverless/img/app-domain.png
deleted file mode 100644
index d113dfadd2e..00000000000
--- a/doc/user/project/clusters/serverless/img/app-domain.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/dns-entry.png b/doc/user/project/clusters/serverless/img/dns-entry.png
index 678743f256e..351e40b77d5 100644
--- a/doc/user/project/clusters/serverless/img/dns-entry.png
+++ b/doc/user/project/clusters/serverless/img/dns-entry.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/install-knative.png b/doc/user/project/clusters/serverless/img/install-knative.png
index 93b1cbe602f..ecc2f8fb481 100644
--- a/doc/user/project/clusters/serverless/img/install-knative.png
+++ b/doc/user/project/clusters/serverless/img/install-knative.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/img/serverless-page.png b/doc/user/project/clusters/serverless/img/serverless-page.png
index 814b8532205..a872fda7740 100644
--- a/doc/user/project/clusters/serverless/img/serverless-page.png
+++ b/doc/user/project/clusters/serverless/img/serverless-page.png
Binary files differ
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index fc3a4a757d0..e6804666e22 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -19,18 +19,27 @@ For more information on Knative, visit the [Knative docs repo](https://github.co
With GitLab serverless, you can deploy both functions-as-a-service (FaaS) and serverless applications.
-## Requirements
+## Prerequisites
To run Knative on Gitlab, you will need:
+1. **Existing GitLab project:** You will need a GitLab project to associate all resources. The simplest way to get started:
+
+ - If you are planning on deploying functions, clone the [functions example project](https://gitlab.com/knative-examples/functions) to get started.
+ - If you are planning on deploying a serverless application, clone the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
+
1. **Kubernetes Cluster:** An RBAC-enabled Kubernetes cluster is required to deploy Knative.
The simplest way to get started is to add a cluster using [GitLab's GKE integration](../index.md#adding-and-creating-a-new-gke-cluster-via-gitlab).
+ The set of minimum recommended cluster specifications to run Knative is 3 nodes, 6 vCPUs, and 22.50 GB memory.
1. **Helm Tiller:** Helm is a package manager for Kubernetes and is required to install
Knative.
+1. **GitLab Runner:** A runner is required to run the CI jobs that will deploy serverless
+ applications or functions onto your cluster. You can install the GitLab Runner
+ onto the existing Kubernetes cluster. See [Installing Applications](../index.md#installing-applications) for more information.
1. **Domain Name:** Knative will provide its own load balancer using Istio. It will provide an
- external IP address for all the applications served by Knative. You will be prompted to enter a
+ external IP address or hostname for all the applications served by Knative. You will be prompted to enter a
wildcard domain where your applications will be served. Configure your DNS server to use the
- external IP address for that domain.
+ external IP address or hostname for that domain.
1. **`.gitlab-ci.yml`:** GitLab uses [Kaniko](https://github.com/GoogleContainerTools/kaniko)
to build the application and the [TriggerMesh CLI](https://github.com/triggermesh/tm) to simplify the
deployment of knative services and functions.
@@ -39,39 +48,40 @@ To run Knative on Gitlab, you will need:
runtime being used.
1. **`Dockerfile`** (for [applications only](#deploying-serverless-applications): Knative requires a `Dockerfile` in order to build your application. It should be included
at the root of your project's repo and expose port `8080`.
+1. **Prometheus** (optional): Installing Prometheus allows you to monitor the scale and traffic of your serverless function/application.
+ See [Installing Applications](../index.md#installing-applications) for more information.
## Installing Knative via GitLab's Kubernetes integration
NOTE: **Note:**
The minimum recommended cluster size to run Knative is 3-nodes, 6 vCPUs, and 22.50 GB memory. **RBAC must be enabled.**
-1. [Add a Kubernetes cluster](../index.md) and install Helm.
-1. Once Helm has been successfully installed, on the Knative app section, enter the domain to be used with
- your application and click "Install".
+1. [Add a Kubernetes cluster](../index.md) and [install Helm](../index.md#installing-applications).
+1. Once Helm has been successfully installed, scroll down to the Knative app section. Enter the domain to be used with
+ your application/functions (e.g. `example.com`) and click **Install**.
![install-knative](img/install-knative.png)
-1. After the Knative installation has finished, you can wait for the IP address to be displayed in the
- **Knative IP Address** field or retrieve the Istio Ingress IP address by running the following command:
+1. After the Knative installation has finished, you can wait for the IP address or hostname to be displayed in the
+ **Knative Endpoint** field or [retrieve the Istio Ingress Endpoint manually](../#manually-determining-the-external-endpoint).
- ```bash
- kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
- ```
-
- Output:
-
- ```bash
- 35.161.143.124 my-machine-name:~ my-user$
- ```
+ NOTE: **Note:**
+ Running `kubectl` commands on your cluster requires setting up access to the cluster first.
+ For clusters created on GKE, see [GKE Cluster Access](https://cloud.google.com/kubernetes-engine/docs/how-to/cluster-access-for-kubectl),
+ for other platforms [Install kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/).
1. The ingress is now available at this address and will route incoming requests to the proper service based on the DNS
name in the request. To support this, a wildcard DNS A record should be created for the desired domain name. For example,
- if your Knative base domain is `knative.info` then you need to create an A record with domain `*.knative.info`
- pointing the ip address of the ingress.
+ if your Knative base domain is `knative.info` then you need to create an A record or CNAME record with domain `*.knative.info`
+ pointing the ip address or hostname of the ingress.
![dns entry](img/dns-entry.png)
-## Deploying Functions
+NOTE: **Note:**
+You can deploy either [functions](#deploying-functions) or [serverless applications](#deploying-serverless-applications)
+on a given project but not both. The current implementation makes use of a `serverless.yml` file to signal a FaaS project.
+
+## Deploying functions
> Introduced in GitLab 11.6.
@@ -84,9 +94,9 @@ Currently the following [runtimes](https://gitlab.com/triggermesh/runtimes) are
- node.js
- kaniko
-You can find all the files referenced in this doc in the [functions example project](https://gitlab.com/knative-examples/functions).
+You can find and import all the files referenced in this doc in the **[functions example project](https://gitlab.com/knative-examples/functions)**.
-Follow these steps to deploy a function using the Node.js runtime to your Knative instance:
+Follow these steps to deploy a function using the Node.js runtime to your Knative instance (you can skip these steps if you've cloned the example project):
1. Create a directory that will house the function. In this example we will create a directory called `echo` at the root of the project.
@@ -94,28 +104,35 @@ Follow these steps to deploy a function using the Node.js runtime to your Knativ
- Public, continue to the next step.
- Private, you will need to [create a GitLab deploy token](../../deploy_tokens/index.md#creating-a-deploy-token) with `gitlab-deploy-token` as the name and the `read_registry` scope.
-1. `.gitlab-ci.yml`: This template allows to define the stage, environment, and
- image to be used for your functions. It must be included at the root of your repository:
+1. `.gitlab-ci.yml`: this defines a pipeline used to deploy your functions.
+ It must be included at the root of your repository:
```yaml
- stages:
- - deploy
+ include:
+ template: Serverless.gitlab-ci.yml
functions:
- stage: deploy
- environment: test
- image: gcr.io/triggermesh/tm:v0.0.9
- script:
- - tm -n "$KUBE_NAMESPACE" set registry-auth gitlab-registry --registry "$CI_REGISTRY" --username "$CI_REGISTRY_USER" --password "$CI_JOB_TOKEN" --push
- - tm -n "$KUBE_NAMESPACE" set registry-auth gitlab-registry --registry "$CI_REGISTRY" --username "$CI_DEPLOY_USER" --password "$CI_DEPLOY_PASSWORD" --pull
- - tm -n "$KUBE_NAMESPACE" deploy --wait
-
+ extends: .serverless:deploy:functions
+ environment: production
```
- The `gitlab-ci.yml` template creates a `Deploy` stage with a `functions` job that invokes the `tm` CLI with the required parameters.
+ This `.gitlab-ci.yml` creates a `functions` job that invokes some
+ predefined commands to deploy your functions to your cluster.
-2. `serverless.yml`: This file contains the metadata for your functions,
- such as name, runtime, and environment. It must be included at the root of your repository. The following is a sample `echo` function which shows the required structure for the file. You can find the relevant files for this project in the [functions example project](https://gitlab.com/knative-examples/functions).
+ `Serverless.gitlab-ci.yml` is a template that allows customization.
+ You can either import it with `include` parameter and use `extends` to
+ customize your jobs, or you can inline the entire template by choosing it
+ from **Apply a template** dropdown when editing the `.gitlab-ci.yml` file through
+ the user interface.
+
+2. `serverless.yml`: this file contains the metadata for your functions,
+ such as name, runtime, and environment.
+
+ It must be included at the root of your repository.
+ The following is a sample `echo` function which shows the required structure
+ for the file.
+
+ You can find the relevant files for this project in the [functions example project](https://gitlab.com/knative-examples/functions).
```yaml
service: my-functions
@@ -180,7 +197,7 @@ appear under **Operations > Serverless**.
This page contains all functions available for the project, the description for
accessing the function, and, if available, the function's runtime information.
The details are derived from the Knative installation inside each of the project's
-Kubernetes cluster.
+Kubernetes cluster. Click on each function to obtain detailed scale and invocation data.
The function details can be retrieved directly from Knative on the cluster:
@@ -188,47 +205,48 @@ The function details can be retrieved directly from Knative on the cluster:
kubectl -n "$KUBE_NAMESPACE" get services.serving.knative.dev
```
-The sample function can now be triggered from any HTTP client using a simple `POST` call.
+The sample function can now be triggered from any HTTP client using a simple `POST` call:
+
+ 1. Using curl (replace the URL on the last line with the URL of your application):
-![function exection](img/function-execution.png)
+ ```bash
+ curl \
+ --header "Content-Type: application/json" \
+ --request POST \
+ --data '{"GitLab":"FaaS"}' \
+ <http://functions-echo.functions-1.functions.example.com/>
+ ```
+ 2. Using a web-based tool (ie. postman, restlet, etc)
+
+ ![function execution](img/function-execution.png)
## Deploying Serverless applications
> Introduced in GitLab 11.5.
NOTE: **Note:**
-You can reference the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
+You can reference and import the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get started.
Add the following `.gitlab-ci.yml` to the root of your repository
-(you may skip this step if using the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) mentioned above):
+(you may skip this step if you've previously cloned the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) mentioned above):
```yaml
-stages:
- - build
- - deploy
+include:
+ template: Serverless.gitlab-ci.yml
build:
- stage: build
- image:
- name: gcr.io/kaniko-project/executor:debug
- entrypoint: [""]
- only:
- - master
- script:
- - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE
+ extends: .serverless:build:image
deploy:
- stage: deploy
- image: gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5
- only:
- - master
- environment: production
- script:
- - echo "$CI_REGISTRY_IMAGE"
- - tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait
+ extends: .serverless:deploy:image
```
+`Serverless.gitlab-ci.yml` is a template that allows customization.
+You can either import it with `include` parameter and use `extends` to
+customize your jobs, or you can inline the entire template by choosing it
+from **Apply a template** dropdown when editing the `.gitlab-ci.yml` file through
+the user interface.
+
### Deploy the application with Knative
With all the pieces in place, the next time a CI pipeline runs, the Knative application will be deployed. Navigate to
@@ -236,11 +254,7 @@ With all the pieces in place, the next time a CI pipeline runs, the Knative appl
### Obtain the URL for the Knative deployment
-Go to the **Operations > Serverless** page to find the URL for your deployment in the **Domain** column.
-
-![app domain](img/app-domain.png)
-
-Alternatively, use the CI/CD deployment job output to obtain the deployment URL. Once all the stages of the pipeline finish, click the **deploy** stage.
+Go to the **CI/CD > Pipelines** and click on the pipeline that deployed your app. Once all the stages of the pipeline finish, click the **deploy** stage.
![deploy stage](img/deploy-stage.png)
@@ -262,7 +276,7 @@ registry.staging.gitlab.com/danielgruesso/knative
$ tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait
Deployment started. Run "tm -n knative-4342902 describe service knative" to see the details
Waiting for ready state.......
-Service domain: knative.knative-4342902.knative.info
+Service domain: knative.knative-4342902.example.com
Job succeeded
```
diff --git a/doc/user/project/container_registry.md b/doc/user/project/container_registry.md
index dec6eac2508..83b268db967 100644
--- a/doc/user/project/container_registry.md
+++ b/doc/user/project/container_registry.md
@@ -1,7 +1,8 @@
# GitLab Container Registry
> **Notes:**
-> [Introduced][ce-4040] in GitLab 8.8.
+>
+> - [Introduced][ce-4040] in GitLab 8.8.
> - Docker Registry manifest `v1` support was added in GitLab 8.9 to support Docker
> versions earlier than 1.10.
> - This document is about the user guide. To learn how to enable GitLab Container
@@ -10,7 +11,7 @@
> - Starting from GitLab 8.12, if you have 2FA enabled in your account, you need
> to pass a [personal access token][pat] instead of your password in order to
> login to GitLab's Container Registry.
-> - Multiple level image names support was added in GitLab 9.1
+> - Multiple level image names support was added in GitLab 9.1.
With the Docker Container Registry integrated into GitLab, every project can
have its own space to store its Docker images.
@@ -41,6 +42,7 @@ to enable it.
## Build and push images
> **Notes:**
+>
> - Moving or renaming existing container registry repositories is not supported
> once you have pushed images because the images are signed, and the
> signature includes the repository name.
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index 529a82ded9e..19ab911e39f 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -3,45 +3,40 @@
> [Introduced][ce-5986] in GitLab 8.12. Further features were added in GitLab
8.14.
-Cycle Analytics measures the time it takes to go from an [idea to production] for
-each project you have. This is achieved by not only indicating the total time it
-takes to reach that point, but the total time is broken down into the
-multiple stages an idea has to pass through to be shipped.
+Cycle Analytics measures the time spent to go from an [idea to production] for
+each of your projects. Cycle Analytics displays the median time for an idea to
+reach production, along with the time typically spent in each DevOps stage along the way.
+
+Cycle Analytics is useful in order to quickly determine the velocity of a given
+project. It points to bottlenecks in the development process, enabling management
+to uncover, triage, and root-cause slowdowns in the software development life cycle.
Cycle Analytics is tightly coupled with the [GitLab flow] and
calculates a separate median for each stage.
## Overview
-You can find the Cycle Analytics page under your project's **Pipelines âž” Cycle
+You can find the Cycle Analytics page under your project's **Project âž” Cycle
Analytics** tab.
![Cycle Analytics landing page](img/cycle_analytics_landing_page.png)
-You can see that there are seven stages in total:
+There are seven stages that are tracked as part of the Cycle Analytics calculations.
- **Issue** (Tracker)
- - Median time from issue creation until given a milestone or list label
- (first assignment, any milestone, milestone date or assignee is not required)
+ - Time to schedule an issue (by milestone or by adding it to an issue board)
- **Plan** (Board)
- - Median time from giving an issue a milestone or label until pushing the
- first commit to the branch
+ - Time to first commit
- **Code** (IDE)
- - Median time from the first commit to the branch until the merge request is
- created
+ - Time to create a merge request
- **Test** (CI)
- - Median total test time for all commits/merges
+ - Time it takes GitLab CI/CD to test your code
- **Review** (Merge Request/MR)
- - Median time from merge request creation until the merge request is merged
- (closed merge requests won't be taken into account)
+ - Time spent on code review
- **Staging** (Continuous Deployment)
- - Median time from when the merge request got merged until the deploy to
- production (production is last stage/environment)
+ - Time between merging and deploying to production
- **Production** (Total)
- - Sum of all the above stages' times excluding the Test (CI) time. To clarify,
- it's not so much that CI time is "excluded", but rather CI time is already
- counted in the review stage since CI is done automatically. Most of the
- other stages are purely sequential, but **Test** is not.
+ - Total lifecycle time; i.e. the velocity of the project or team
## How the data is measured
@@ -80,6 +75,7 @@ Here's a little explanation of how this works behind the scenes:
To sum up, anything that doesn't follow the [GitLab flow] won't be tracked at all.
So, the Cycle Analytics dashboard won't present any data:
+
- For merge requests that do not close an issue.
- For issues not labeled with a label present in the Issue Board.
- For issues not assigned a milestone.
diff --git a/doc/user/project/gpg_signed_commits/index.md b/doc/user/project/gpg_signed_commits/index.md
index 261eedeb412..bd9a5313489 100644
--- a/doc/user/project/gpg_signed_commits/index.md
+++ b/doc/user/project/gpg_signed_commits/index.md
@@ -1 +1,5 @@
+---
+redirect_to: '../repository/gpg_signed_commits/index.md'
+---
+
This document was moved to [another location](../repository/gpg_signed_commits/index.md).
diff --git a/doc/user/project/img/cycle_analytics_landing_page.png b/doc/user/project/img/cycle_analytics_landing_page.png
index 8b17fae5e05..cf46098b9a4 100644
--- a/doc/user/project/img/cycle_analytics_landing_page.png
+++ b/doc/user/project/img/cycle_analytics_landing_page.png
Binary files differ
diff --git a/doc/user/project/img/issue_boards_multiple.png b/doc/user/project/img/issue_boards_multiple.png
index 242460925f7..4b1a8356dc9 100644
--- a/doc/user/project/img/issue_boards_multiple.png
+++ b/doc/user/project/img/issue_boards_multiple.png
Binary files differ
diff --git a/doc/user/project/import/bitbucket_server.md b/doc/user/project/import/bitbucket_server.md
index ebf87890cfd..d51a0c0ccca 100644
--- a/doc/user/project/import/bitbucket_server.md
+++ b/doc/user/project/import/bitbucket_server.md
@@ -22,14 +22,14 @@ Import your projects from Bitbucket Server to GitLab with minimal effort.
## 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.
+ 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.
+ import will collapse this into one discussion and quote part of the original
+ comment.
1. Declined pull requests have unreachable commits, which prevents the GitLab
-importer from generating a proper diff. These pull requests will show up as
-empty changes.
+ 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
diff --git a/doc/user/project/import/fogbugz.md b/doc/user/project/import/fogbugz.md
index 17222c53675..13409c93929 100644
--- a/doc/user/project/import/fogbugz.md
+++ b/doc/user/project/import/fogbugz.md
@@ -23,6 +23,6 @@ users to GitLab users.
![Import Project](img/fogbugz_import_select_project.png)
1. Once the import has finished click the link to take you to the project
-dashboard. Follow the directions to push your existing repository.
+ dashboard. Follow the directions to push your existing repository.
![Finished](img/fogbugz_import_finished.png)
diff --git a/doc/user/project/import/github.md b/doc/user/project/import/github.md
index cf99dded5e2..63b90dd76fd 100644
--- a/doc/user/project/import/github.md
+++ b/doc/user/project/import/github.md
@@ -67,7 +67,7 @@ developer documentation.
Before you begin, ensure that any GitHub users who you want to map to GitLab users have either:
- A GitLab account that has logged in using the GitHub icon
-\- or -
+ \- or -
- A GitLab account with an email address that matches the [public email address](https://help.github.com/articles/setting-your-commit-email-address-on-github/) of the GitHub user
User-matching attempts occur in that order, and if a user is not identified either way, the activity is associated with
diff --git a/doc/user/project/import/svn.md b/doc/user/project/import/svn.md
index a5923986292..4825b005a85 100644
--- a/doc/user/project/import/svn.md
+++ b/doc/user/project/import/svn.md
@@ -8,7 +8,7 @@ between the two, for more information consult your favorite search engine.
There are two approaches to SVN to Git migration:
-1. [Git/SVN Mirror](#smooth-migration-with-a-git-svn-mirror-using-subgit) which:
+1. [Git/SVN Mirror](#smooth-migration-with-a-gitsvn-mirror-using-subgit) which:
- Makes the GitLab repository to mirror the SVN project.
- Git and SVN repositories are kept in sync; you can use either one.
- Smoothens the migration process and allows to manage migration risks.
diff --git a/doc/user/project/index.md b/doc/user/project/index.md
index 6a1aadf058e..64139f9dbe9 100644
--- a/doc/user/project/index.md
+++ b/doc/user/project/index.md
@@ -8,7 +8,7 @@ Your projects can be [available](../../public_access/public_access.md)
publicly, internally, or privately, at your choice. GitLab does not limit
the number of private projects you create.
-## Project's features
+## Project features
When you create a project in GitLab, you'll have access to a large number of
[features](https://about.gitlab.com/features/):
@@ -17,9 +17,9 @@ When you create a project in GitLab, you'll have access to a large number of
- [Issue tracker](issues/index.md): Discuss implementations with your team within issues
- [Issue Boards](issue_board.md): Organize and prioritize your workflow
- - [Multiple Issue Boards](https://docs.gitlab.com/ee/user/project/issue_board.html#multiple-issue-boards): Allow your teams to create their own workflows (Issue Boards) for the same project **[STARTER]**
+ - [Multiple Issue Boards](issue_board.md#multiple-issue-boards-starter): Allow your teams to create their own workflows (Issue Boards) for the same project **[STARTER]**
- [Repositories](repository/index.md): Host your code in a fully
-integrated platform
+ integrated platform
- [Branches](repository/branches/index.md): use Git branching strategies to
collaborate on code
- [Protected branches](protected_branches.md): Prevent collaborators
@@ -29,7 +29,7 @@ integrated platform
- [Signing commits](gpg_signed_commits/index.md): use GPG to sign your commits
- [Deploy tokens](deploy_tokens/index.md): Manage project-based deploy tokens that allow permanent access to the repository and Container Registry.
- [Merge Requests](merge_requests/index.md): Apply your branching
-strategy and get reviewed by your team
+ strategy and get reviewed by your team
- [Merge Request Approvals](https://docs.gitlab.com/ee/user/project/merge_requests/merge_request_approvals.html): Ask for approval before
implementing a change **[STARTER]**
- [Fix merge conflicts from the UI](merge_requests/resolve_conflicts.md):
@@ -38,13 +38,13 @@ strategy and get reviewed by your team
of the changes proposed in a merge request in a per-branch basis
- [Labels](labels.md): Organize issues and merge requests by labels
- [Time Tracking](../../workflow/time_tracking.md): Track estimate time
-and time spent on
+ and time spent on
the conclusion of an issue or merge request
- [Milestones](milestones/index.md): Work towards a target date
- [Description templates](description_templates.md): Define context-specific
-templates for issue and merge request description fields for your project
+ templates for issue and merge request description fields for your project
- [Slash commands (quick actions)](quick_actions.md): Textual shortcuts for
-common actions on issues or merge requests
+ common actions on issues or merge requests
- [Web IDE](web_ide/index.md)
**GitLab CI/CD:**
@@ -55,11 +55,11 @@ common actions on issues or merge requests
- [Auto Deploy](../../ci/autodeploy/index.md): Configure GitLab CI/CD
to automatically set up your app's deployment
- [Enable and disable GitLab CI](../../ci/enable_or_disable_ci.md)
- - [Pipelines](../../ci/pipelines.md#pipelines): Configure and visualize
+ - [Pipelines](../../ci/pipelines.md): Configure and visualize
your GitLab CI/CD pipelines from the UI
- [Scheduled Pipelines](pipelines/schedules.md): Schedule a pipeline
to start at a chosen time
- - [Pipeline Graphs](../../ci/pipelines.md#pipeline-graphs): View your
+ - [Pipeline Graphs](../../ci/pipelines.md#visualizing-pipelines): View your
entire pipeline from the UI
- [Job artifacts](pipelines/job_artifacts.md): Define,
browse, and download job artifacts
@@ -68,7 +68,7 @@ common actions on issues or merge requests
- [Kubernetes cluster integration](clusters/index.md): Connecting your GitLab project
with a Kubernetes cluster
- [GitLab Pages](pages/index.md): Build, test, and deploy your static
-website with GitLab Pages
+ website with GitLab Pages
**Other features:**
@@ -76,13 +76,13 @@ website with GitLab Pages
- [Snippets](../snippets.md): store, share and collaborate on code snippets.
- [Cycle Analytics](cycle_analytics.md): review your development lifecycle.
- [Syntax highlighting](highlighting.md): an alternative to customize
-your code blocks, overriding GitLab's default choice of language.
+ your code blocks, overriding GitLab's default choice of language.
- [Badges](badges.md): badges for the project overview.
- [Releases](releases/index.md): a way to track deliverables in your project as snapshot in time of
-the source, build output, and other metadata or artifacts
-associated with a released version of your code.
+ the source, build output, and other metadata or artifacts
+ associated with a released version of your code.
-### Project's integrations
+### Project integrations
[Integrate your project](integrations/index.md) with Jira, Mattermost,
Kubernetes, Slack, and a lot more.
@@ -96,7 +96,7 @@ Learn how to [create a new project](../../gitlab-basics/create-project.md) in Gi
You can [fork a project](../../gitlab-basics/fork-project.md) in order to:
- Collaborate on code by forking a project and creating a merge request
-from your fork to the upstream project
+ from your fork to the upstream project
- Fork a sample project to work on the top of that
## Project settings
@@ -116,7 +116,7 @@ Read through the documentation on [project settings](settings/index.md).
- [Export a project from GitLab](settings/import_export.md#exporting-a-project-and-its-data)
- [Importing and exporting projects between GitLab instances](settings/import_export.md)
-## Project's members
+## Project members
Learn how to [add members to your projects](members/index.md).
@@ -137,7 +137,7 @@ and Git push/pull redirects.
Depending on the situation, different things apply.
When [renaming a user](../profile/index.md#changing-your-username),
-[changing a group path](../group/index.md#changing-a-group-s-path) or [renaming a repository](settings/index.md#renaming-a-repository):
+[changing a group path](../group/index.md#changing-a-groups-path) or [renaming a repository](settings/index.md#renaming-a-repository):
- Existing web URLs for the namespace and anything under it (e.g., projects) will
redirect to the new URLs.
@@ -170,3 +170,23 @@ password <personal_access_token>
To quickly access a project from the GitLab UI using the project ID,
visit the `/projects/:id` URL in your browser or other tool accessing the project.
+
+## Project APIs
+
+There are numerous [APIs](../../api/README.md) to use with your projects:
+
+- [Badges](../../api/project_badges.md)
+- [Clusters](../../api/project_clusters.md)
+- [Discussions](../../api/discussions.md)
+- [General](../../api/projects.md)
+- [Import/export](../../api/project_import_export.md)
+- [Issue Board](../../api/boards.md)
+- [Labels](../../api/labels.md)
+- [Markdown](../../api/markdown.md)
+- [Merge Requests](../../api/merge_requests.md)
+- [Milestones](../../api/milestones.md)
+- [Services](../../api/services.md)
+- [Snippets](../../api/project_snippets.md)
+- [Templates](../../api/project_templates.md)
+- [Traffic](../../api/project_statistics.md)
+- [Variables](../../api/project_level_variables.md)
diff --git a/doc/user/project/integrations/img/issue_configuration.png b/doc/user/project/integrations/img/issue_configuration.png
deleted file mode 100644
index 5dfd85974d8..00000000000
--- a/doc/user/project/integrations/img/issue_configuration.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/integrations/irker.md b/doc/user/project/integrations/irker.md
index f220fa8497a..4fb753d1707 100644
--- a/doc/user/project/integrations/irker.md
+++ b/doc/user/project/integrations/irker.md
@@ -30,12 +30,12 @@ need to follow the firsts steps of the next section.
1. Click "Irker".
1. Select the "Active" checkbox.
1. Enter the server host address where `irkerd` runs (defaults to `localhost`)
-in the `Server host` field on the Web page
+ in the `Server host` field on the Web page
1. Enter the server port of `irkerd` (e.g. defaults to 6659) in the
-`Server port` field on the Web page.
+ `Server port` field on the Web page.
1. Optional: if `Default IRC URI` is set, it has to be in the format
-`irc[s]://domain.name` and will be prepend to each and every channel provided
-by the user which is not a full URI.
+ `irc[s]://domain.name` and will be prepend to each and every channel provided
+ by the user which is not a full URI.
1. Specify the recipients (e.g. #channel1, user1, etc.)
1. Save or optionally click "Test Settings".
diff --git a/doc/user/project/integrations/jira.md b/doc/user/project/integrations/jira.md
index 754711f5919..a90167b9767 100644
--- a/doc/user/project/integrations/jira.md
+++ b/doc/user/project/integrations/jira.md
@@ -56,6 +56,7 @@ When connecting to **JIRA Cloud**, which supports authentication via API token,
### Configuring GitLab
> **Notes:**
+>
> - The currently supported Jira versions are `v6.x` and `v7.x.`. GitLab 7.8 or
> higher is required.
> - GitLab 8.14 introduced a new way to integrate with Jira which greatly simplified
@@ -142,6 +143,7 @@ the same goal:
where `PROJECT-1` is the issue ID of the Jira project.
> **Notes:**
+>
> - Only commits and merges into the project's default branch (usually **master**) will
> close an issue in Jira. You can change your projects default branch under
> [project settings](img/jira_project_settings.png).
diff --git a/doc/user/project/integrations/kubernetes.md b/doc/user/project/integrations/kubernetes.md
index 9342a2cbb00..ab43eb2da7e 100644
--- a/doc/user/project/integrations/kubernetes.md
+++ b/doc/user/project/integrations/kubernetes.md
@@ -1 +1,5 @@
+---
+redirect_to: '../clusters/index.md'
+---
+
This document was moved to [another location](../clusters/index.md).
diff --git a/doc/user/project/integrations/mattermost_slash_commands.md b/doc/user/project/integrations/mattermost_slash_commands.md
index e031dcad2c3..9c69437537a 100644
--- a/doc/user/project/integrations/mattermost_slash_commands.md
+++ b/doc/user/project/integrations/mattermost_slash_commands.md
@@ -152,7 +152,7 @@ trigger word followed by <kbd>help</kbd>. Example: <samp>/gitlab help</samp>
## Permissions
The permissions to run the [available commands](#available-slash-commands) derive from
-the [permissions you have on the project](../../permissions.md#project).
+the [permissions you have on the project](../../permissions.md#project-members-permissions).
## Further reading
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index cec9018b67f..e2f23827360 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -50,6 +50,7 @@ Click on the service links to see further configuration instructions and details
| [Prometheus](prometheus.md) | Monitor the performance of your deployed apps |
| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop |
| [Redmine](redmine.md) | Redmine issue tracker |
+| [YouTrack](youtrack.md) | YouTrack issue tracker |
## Services templates
diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md
index ed289b0c4eb..43a9e24526d 100644
--- a/doc/user/project/integrations/prometheus.md
+++ b/doc/user/project/integrations/prometheus.md
@@ -13,7 +13,7 @@ There are two ways to set up Prometheus integration, depending on where your app
- For deployments on Kubernetes, GitLab can automatically [deploy and manage Prometheus](#managed-prometheus-on-kubernetes).
- For other deployment targets, simply [specify the Prometheus server](#manual-configuration-of-prometheus).
-Once enabled, GitLab will automatically detect metrics from known services in the [metric library](#monitoring-ci-cd-environments).
+Once enabled, GitLab will automatically detect metrics from known services in the [metric library](#monitoring-cicd-environments).
## Enabling Prometheus Integration
@@ -120,7 +120,7 @@ If the "No data found" screen continues to appear, it could be due to:
- No successful deployments have occurred to this environment.
- Prometheus does not have performance data for this environment, or the metrics
are not labeled correctly. To test this, connect to the Prometheus server and
- [run a query](#gitlab-prometheus-queries), replacing `$CI_ENVIRONMENT_SLUG`
+ [run a query](prometheus_library/kubernetes.html#metrics-supported), replacing `$CI_ENVIRONMENT_SLUG`
with the name of your environment.
[autodeploy]: ../../../ci/autodeploy/index.md
diff --git a/doc/user/project/integrations/prometheus_library/metrics.md b/doc/user/project/integrations/prometheus_library/metrics.md
index 37a5388d2fc..7ace0ec5a93 100644
--- a/doc/user/project/integrations/prometheus_library/metrics.md
+++ b/doc/user/project/integrations/prometheus_library/metrics.md
@@ -1 +1,5 @@
+---
+redirect_to: 'index.md'
+---
+
This document was moved to [another location](index.md).
diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress.md b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
index b7601f26802..de7fc93f0a4 100644
--- a/doc/user/project/integrations/prometheus_library/nginx_ingress.md
+++ b/doc/user/project/integrations/prometheus_library/nginx_ingress.md
@@ -30,7 +30,7 @@ For other deployments, there is [some configuration](#manually-setting-up-nginx-
### About managed NGINX Ingress deployments
-NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's IP](../../clusters/index.md#getting-the-external-ip-address).
+NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's Endpoint](../../clusters/index.md#getting-the-external-endpoint).
NGINX is configured for Prometheus monitoring, by setting:
diff --git a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
index 081eb8732ad..31ac53c0d14 100644
--- a/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
+++ b/doc/user/project/integrations/prometheus_library/nginx_ingress_vts.md
@@ -30,7 +30,7 @@ For other deployments, there is [some configuration](#manually-setting-up-nginx-
### About managed NGINX Ingress deployments
-NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's IP](../../clusters/index.md#getting-the-external-ip-address).
+NGINX Ingress is deployed into the `gitlab-managed-apps` namespace, using the [official Helm chart](https://github.com/kubernetes/charts/tree/master/stable/nginx-ingress). NGINX Ingress will be [externally reachable via the Load Balancer's Endpoint](../../clusters/index.md#getting-the-external-endpoint).
NGINX is configured for Prometheus monitoring, by setting:
diff --git a/doc/user/project/integrations/redmine.md b/doc/user/project/integrations/redmine.md
index 76a2617125e..bac7eecfce4 100644
--- a/doc/user/project/integrations/redmine.md
+++ b/doc/user/project/integrations/redmine.md
@@ -1,9 +1,9 @@
# Redmine Service
1. To enable the Redmine integration in a project, navigate to the
-[Integrations page](project_services.md#accessing-the-project-services), click
-the **Redmine** service, and fill in the required details on the page as described
-in the table below.
+ [Integrations page](project_services.md#accessing-the-project-services), click
+ the **Redmine** service, and fill in the required details on the page as described
+ in the table below.
| Field | Description |
| ----- | ----------- |
@@ -18,9 +18,7 @@ in the table below.
![Redmine configuration](img/redmine_configuration.png)
-1. To disable the internal issue tracking system in a project, navigate to the General page, expand [Permissions](../settings/index.md#sharing-and-permissions), and slide the Issues switch invalid.
-
- ![Issue configuration](img/issue_configuration.png)
+1. To disable the internal issue tracking system in a project, navigate to the General page, expand the [permissions](../settings/index.md#sharing-and-permissions) section and switch the **Issues** toggle to disabled.
## Referencing issues in Redmine
diff --git a/doc/user/project/integrations/webhooks.md b/doc/user/project/integrations/webhooks.md
index cbf08a4f30a..d324e0de0cc 100644
--- a/doc/user/project/integrations/webhooks.md
+++ b/doc/user/project/integrations/webhooks.md
@@ -48,9 +48,9 @@ Navigate to the webhooks page by going to your project's
## Use-cases
- You can set up a webhook in GitLab to send a notification to
-[Slack](https://api.slack.com/incoming-webhooks) every time a build fails, for example
+ [Slack](https://api.slack.com/incoming-webhooks) every time a build fails, for example
- You can [integrate with Twilio to be notified via SMS](https://www.datadoghq.com/blog/send-alerts-sms-customizable-webhooks-twilio/)
-every time an issue is created for a specific project or group within GitLab
+ every time an issue is created for a specific project or group within GitLab
- You can use them to [automatically assign labels to merge requests](https://about.gitlab.com/2016/08/19/applying-gitlab-labels-automatically/).
## Webhook endpoint tips
@@ -1041,7 +1041,12 @@ X-Gitlab-Event: Pipeline Hook
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
- "runner": null,
+ "runner": {
+ "id":380987,
+ "description":"shared-runners-manager-6.gitlab.com",
+ "active":true,
+ "is_shared":true
+ },
"artifacts_file":{
"filename": null,
"size": null
@@ -1062,7 +1067,12 @@ X-Gitlab-Event: Pipeline Hook
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
- "runner": null,
+ "runner": {
+ "id":380987,
+ "description":"shared-runners-manager-6.gitlab.com",
+ "active":true,
+ "is_shared":true
+ },
"artifacts_file":{
"filename": null,
"size": null
@@ -1083,7 +1093,12 @@ X-Gitlab-Event: Pipeline Hook
"username": "root",
"avatar_url": "http://www.gravatar.com/avatar/e32bd13e2add097461cb96824b7a829c?s=80\u0026d=identicon"
},
- "runner": null,
+ "runner": {
+ "id":380987,
+ "description":"shared-runners-manager-6.gitlab.com",
+ "active":true,
+ "is_shared":true
+ },
"artifacts_file":{
"filename": null,
"size": null
diff --git a/doc/user/project/integrations/youtrack.md b/doc/user/project/integrations/youtrack.md
new file mode 100644
index 00000000000..a2a468b6fe4
--- /dev/null
+++ b/doc/user/project/integrations/youtrack.md
@@ -0,0 +1,38 @@
+# YouTrack Service
+
+JetBrains [YouTrack](https://www.jetbrains.com/help/youtrack/standalone/YouTrack-Documentation.html) is a web-based issue tracking and project management platform.
+
+You can configure YouTrack as an [External Issue Tracker](../../../integration/external-issue-tracker.md) in GitLab.
+
+## Enable the YouTrack integration
+
+To enable YouTrack integration in a project:
+
+1. Navigate to the project's **Settings > [Integrations](project_services.md#accessing-the-project-services)** page.
+1. Click the **YouTrack** service, ensure it's active, and enter the required details on the page as described in the table below.
+
+ | Field | Description |
+ |:----------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+ | **Description** | Name for the issue tracker (to differentiate between instances, for example). |
+ | **Project url** | URL to the project in YouTrack which is being linked to this GitLab project. |
+ | **Issues url** | URL to the issue in YouTrack project that is linked to this GitLab project. Note that the **Issues url** requires `:id` in the URL. This ID is used by GitLab as a placeholder to replace the issue number. |
+
+1. Click the **Save changes** button.
+
+Once you have configured and enabled YouTrack, you'll see the YouTrack link on the GitLab project pages that takes you to the appropriate YouTrack project.
+
+## Disable the internal issue tracker
+
+To disable the internal issue tracker in a project:
+
+1. Navigate to the project's **Settings > General** page.
+1. Expand the [permissions section](../settings/index.md#sharing-and-permissions) and switch the **Issues** toggle to disabled.
+
+## Referencing YouTrack issues in GitLab
+
+Issues in YouTrack can be referenced as `<PROJECT>-<ID>`. `<PROJECT>`
+must start with a capital letter and can then be followed by capital or lower case
+letters, numbers or underscores. `<ID>` is a number. An example reference is `YT-101` or `Api_32-143`.
+
+References to `<PROJECT>-<ID>` in merge requests, commits, or comments are automatically linked to the YouTrack issue URL.
+For more information, see the [External Issue Tracker](../../../integration/external-issue-tracker.md) documentation.
diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md
index 7962eeada5c..ca19ce4d328 100644
--- a/doc/user/project/issue_board.md
+++ b/doc/user/project/issue_board.md
@@ -28,11 +28,11 @@ Issue Boards** (version introduced in GitLab 8.11 - August 2016).
### Advanced features of Issue Boards
With [GitLab Starter](https://about.gitlab.com/pricing/), you can create
-[multiple issue boards](#multiple-issue-boards) for a given project. **[STARTER]**
+[multiple issue boards](#multiple-issue-boards-starter) for a given project. **[STARTER]**
With [GitLab Premium](https://about.gitlab.com/pricing/), you can also create multiple
-issue boards for your groups, and add lists for [assignees](#assignee-lists) and
-[milestones](#milestone-lists). **[PREMIUM]**
+issue boards for your groups, and add lists for [assignees](#assignee-lists-premium) and
+[milestones](#milestone-lists-premium). **[PREMIUM]**
Check all the [advanced features of Issue Boards](#gitlab-enterprise-features-for-issue-boards)
below.
@@ -78,9 +78,9 @@ with labels, and from there organize and prioritize them with Issue Boards.
For example, let's consider this simplified development workflow:
1. You have a repository hosting your app's codebase
-and your team actively contributing to code
+ and your team actively contributing to code
1. Your **backend** team starts working a new
-implementation, gathers feedback and approval, and pass it over to **frontend**
+ implementation, gathers feedback and approval, and pass it over to **frontend**
1. When frontend is complete, the new feature is deployed to **staging** to be tested
1. When successful, it is deployed to **production**
@@ -88,7 +88,7 @@ If we have the labels "**backend**", "**frontend**", "**staging**", and
"**production**", and an Issue Board with a list for each, we can:
- Visualize the entire flow of implementations since the
-beginning of the development lifecycle until deployed to production
+ beginning of the development lifecycle until deployed to production
- Prioritize the issues in a list by moving them vertically
- Move issues between lists to organize them according to the labels you've set
- Add multiple issues to lists in the board by selecting one or more existing issues
@@ -97,7 +97,7 @@ 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
+With [Multiple Issue Boards](#multiple-issue-boards-starter), available only in
[GitLab Enterprise Edition](https://about.gitlab.com/pricing/),
each team can have their own board to organize their workflow individually.
@@ -173,19 +173,21 @@ or in situations where a repository is used to host the code of multiple
products.
Clicking on the current board name in the upper left corner will reveal a
-menu from where you can create another Issue Board and rename or delete the
-existing one.
+menu from where you can create another Issue Board or delete the existing one.
Using the search box at the top of the menu, you can filter the listed boards.
-When you're revisiting an issue board in a project or group with multiple boards,
+When you have 10 or more boards available, a "Recent" section is also shown in the menu.
+These are shortcuts to your last 4 visited boards.
+
+![Multiple Issue Boards](img/issue_boards_multiple.png)
+
+When you're revisiting an issue board in a project or group with multiple boards,
GitLab will automatically load the last board you visited.
NOTE: **Note:**
The Multiple Issue Boards feature is available for
**projects in GitLab Starter Edition** and for **groups in GitLab Premium Edition**.
-![Multiple Issue Boards](img/issue_boards_multiple.png)
-
### Configurable Issue Boards **[STARTER]**
> Introduced in [GitLab Starter Edition 10.2](https://about.gitlab.com/2017/11/22/gitlab-10-2-released/#issue-boards-configuration).
@@ -218,7 +220,7 @@ Click the button at the top right to toggle focus mode on and off. In focus mode
The top of each list indicates the sum of issue weights for the issues that
belong to that list. This is useful when using boards for capacity allocation,
-especially in combination with [assignee lists](#assignee-lists).
+especially in combination with [assignee lists](#assignee-lists-premium).
![Issue Board summed weights](img/issue_board_summed_weights.png)
@@ -234,7 +236,7 @@ group-level objects are available.
NOTE: **Note:**
Multiple group issue boards were originally introduced in [GitLab 10.0 Premium](https://about.gitlab.com/2017/09/22/gitlab-10-0-released/#group-issue-boards) and
-one group issue board per group was made available in GitLab 10.6 Core.
+one group issue board per group was made available in GitLab 10.6 Core.
![Group issue board](img/group_issue_board.png)
diff --git a/doc/user/project/issues/automatic_issue_closing.md b/doc/user/project/issues/automatic_issue_closing.md
index afb7d9ada5f..c3e06b219ff 100644
--- a/doc/user/project/issues/automatic_issue_closing.md
+++ b/doc/user/project/issues/automatic_issue_closing.md
@@ -1,6 +1,7 @@
# Automatic issue closing
>**Notes:**
+>
> - This is the user docs. In order to change the default issue closing pattern,
> follow the steps in the [administration docs].
> - For performance reasons, automatic issue closing is disabled for the very
diff --git a/doc/user/project/issues/confidential_issues.md b/doc/user/project/issues/confidential_issues.md
index 8eada25234f..2c755e0fb4d 100644
--- a/doc/user/project/issues/confidential_issues.md
+++ b/doc/user/project/issues/confidential_issues.md
@@ -1,6 +1,6 @@
# Confidential issues
-> [Introduced][ce-3282] in GitLab 8.6.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3282) in GitLab 8.6.
Confidential issues are issues visible only to members of a project with
[sufficient permissions](#permissions-and-access-to-confidential-issues).
@@ -67,7 +67,7 @@ There is also an indicator on the sidebar denoting confidentiality.
There are two kinds of level access for confidential issues. The general rule
is that confidential issues are visible only to members of a project with at
-least [Reporter access][permissions]. However, a guest user can also create
+least [Reporter access](../../permissions.md#project-members-permissions). However, a guest user can also create
confidential issues, but can only view the ones that they created themselves.
Confidential issues are also hidden in search results for unprivileged users.
@@ -77,6 +77,3 @@ project's search results respectively.
| Maintainer access | Guest access |
| :-----------: | :----------: |
| ![Confidential issues search master](img/confidential_issues_search_master.png) | ![Confidential issues search guest](img/confidential_issues_search_guest.png) |
-
-[permissions]: ../../permissions.md#project
-[ce-3282]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3282
diff --git a/doc/user/project/issues/create_new_issue.md b/doc/user/project/issues/create_new_issue.md
index 2bf4fa287e9..40040e44d64 100644
--- a/doc/user/project/issues/create_new_issue.md
+++ b/doc/user/project/issues/create_new_issue.md
@@ -79,6 +79,6 @@ in the same URL (since a description template just populates the description fie
Follow these examples to form your new issue URL with prefilled fields.
- For a new issue in the GitLab Community Edition project with a pre-entered title
-and a pre-entered description, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
+ and a pre-entered description, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issue[description]=Research%20idea`
- For a new issue in the GitLab Community Edition project with a pre-entered title
-and a pre-entered description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
+ and a pre-entered description template, the URL would be `https://gitlab.com/gitlab-org/gitlab-ce/issues/new?issue[title]=Validate%20new%20concept&issuable_template=Research%20proposal`
diff --git a/doc/user/project/issues/crosslinking_issues.md b/doc/user/project/issues/crosslinking_issues.md
index 786d1c81b1b..ff5b1f2ce50 100644
--- a/doc/user/project/issues/crosslinking_issues.md
+++ b/doc/user/project/issues/crosslinking_issues.md
@@ -48,13 +48,12 @@ issues in merge requests.
## From Merge Requests
-Mentioning issues in merge request comments work exactly the same way
+Mentioning issues in merge request comments works exactly the same way as
they do for [related issues](#from-related-issues).
-When you mention an issue in a merge request description, you can either
-[close the issue as soon as the merge request is merged](closing_issues.md#via-merge-request),
-or simply link both issue and merge request as described in the
-[closing issues documentation](closing_issues.md#from-related-issues).
+When you mention an issue in a merge request description, it will simply
+[link the issue and merge request together](#from-related-issues). Additionally,
+you can also [set an issue to close as soon as the merge request is merged](closing_issues.md#via-merge-request).
![issue mentioned in MR](img/mention_in_merge_request.png)
diff --git a/doc/user/project/issues/due_dates.md b/doc/user/project/issues/due_dates.md
index 93306437c6c..987c16dfab6 100644
--- a/doc/user/project/issues/due_dates.md
+++ b/doc/user/project/issues/due_dates.md
@@ -1,11 +1,11 @@
# Due dates
-> [Introduced][ce-3614] in GitLab 8.7.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3614) in GitLab 8.7.
Please read through the [GitLab Issue Documentation](index.md) for an overview on GitLab Issues.
Due dates can be used in issues to keep track of deadlines and make sure
-features are shipped on time. Due dates require at least [Reporter permissions][permissions]
+features are shipped on time. Due dates require at least [Reporter permissions](../../permissions.md#project-members-permissions)
to be able to edit them. On the contrary, they can be seen by everybody.
## Setting a due date
@@ -42,10 +42,8 @@ server's timezone.
Issues with due dates can also be exported as an iCalendar feed. The URL of the
feed can be added to calendar applications. The feed is accessible by clicking
on the _Subscribe to calendar_ button on the following pages:
+
- on the **Assigned Issues** page that is linked on the right-hand side of the
GitLab header
- on the **Project Issues** page
- on the **Group Issues** page
-
-[ce-3614]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/3614
-[permissions]: ../../permissions.md#project
diff --git a/doc/user/project/issues/index.md b/doc/user/project/issues/index.md
index 5a3ac9c175b..675c280a12a 100644
--- a/doc/user/project/issues/index.md
+++ b/doc/user/project/issues/index.md
@@ -109,7 +109,7 @@ issue within your team only, you can make that
[issue confidential](confidential_issues.md). Even if your project
is public, that issue will be preserved. The browser will
respond with a 404 error whenever someone who is not a project
-member with at least [Reporter level](../../permissions.md#project) tries to
+member with at least [Reporter level](../../permissions.md#project-members-permissions) tries to
access that issue's URL.
Learn more about them on the [confidential issues documentation](confidential_issues.md).
@@ -140,7 +140,7 @@ Read through the documentation for [Issue Boards](../issue_board.md)
to find out more about this feature.
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).
+create various boards per project with [Multiple Issue Boards](../issue_board.html#multiple-issue-boards-starter).
### Import Issues from CSV
@@ -155,7 +155,7 @@ For further details, see [Importing issues from CSV](csv_import.md)
Alternatively to GitLab's built-in Issue Tracker, you can also use an [external
tracker](../../../integration/external-issue-tracker.md) such as Jira, Redmine,
-or Bugzilla.
+YouTrack, or Bugzilla.
### Issue API
diff --git a/doc/user/project/issues/issues_functionalities.md b/doc/user/project/issues/issues_functionalities.md
index e4a3ff52e07..27b9dc51760 100644
--- a/doc/user/project/issues/issues_functionalities.md
+++ b/doc/user/project/issues/issues_functionalities.md
@@ -99,16 +99,16 @@ Learn more in the [Issue Weight documentation](https://docs.gitlab.com/ee/workfl
#### 10. Notifications
- Subscribe: if you are not a participant of the discussion on that issue, but
-want to receive notifications on each new input, subscribe to it.
+ want to receive notifications on each new input, subscribe to it.
- Unsubscribe: if you are receiving notifications on that issue but no
-longer want to receive them, unsubscribe from it.
+ longer want to receive them, unsubscribe from it.
Read more in the [notifications documentation](../../../workflow/notifications.md#issue--merge-request-events).
#### 11. Reference
- A quick "copy to clipboard" button for that issue's reference, `foo/bar#xxx`, where `foo` is the `username` or `groupname`, `bar`
-is the `project-name`, and `xxx` is the issue number.
+ is the `project-name`, and `xxx` is the issue number.
#### 12. Title and description
@@ -138,7 +138,7 @@ interpreted as spam.
#### 14. Related Merge Requests
- Any merge requests mentioned in that issue's description
-or in the issue discussion thread.
+ or in the issue discussion thread.
#### 15. Award emoji
@@ -152,8 +152,8 @@ know you like it without spamming them.
#### 16. Thread
- Comments: collaborate to that issue by posting comments in its thread.
-These text fields also fully support
-[GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm).
+ These text fields also fully support
+ [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm).
#### 17. Comment, start a discussion, or comment and close
@@ -166,7 +166,7 @@ Once you write a comment, you can either:
#### 18. New Merge Request
- Create a new merge request (with a new source branch named after the issue) in one action.
-The merge request will automatically inherit the milestone and labels of the issue. The merge
-request will automatically close that issue when it is merged.
+ The merge request will automatically inherit the milestone and labels of the issue. The merge
+ request will automatically close that issue when it is merged.
- Optionally, you can just create a [new branch](../repository/web_editor.md#create-a-new-branch-from-an-issue)
-named after that issue.
+ named after that issue.
diff --git a/doc/user/project/merge_requests.md b/doc/user/project/merge_requests.md
index 84a79f04094..1d7ebc856c3 100644
--- a/doc/user/project/merge_requests.md
+++ b/doc/user/project/merge_requests.md
@@ -1 +1,5 @@
+---
+redirect_to: 'merge_requests/index.md'
+---
+
This document was moved to [merge_requests/index.md](merge_requests/index.md).
diff --git a/doc/user/project/merge_requests/img/filter_wip_merge_requests.png b/doc/user/project/merge_requests/img/filter_wip_merge_requests.png
index 81878709487..8df6a3c9a29 100644
--- a/doc/user/project/merge_requests/img/filter_wip_merge_requests.png
+++ b/doc/user/project/merge_requests/img/filter_wip_merge_requests.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/wip_blocked_accept_button.png b/doc/user/project/merge_requests/img/wip_blocked_accept_button.png
index 31f23be4d3d..b6d38d85165 100644
--- a/doc/user/project/merge_requests/img/wip_blocked_accept_button.png
+++ b/doc/user/project/merge_requests/img/wip_blocked_accept_button.png
Binary files differ
diff --git a/doc/user/project/merge_requests/img/wip_mark_as_wip.png b/doc/user/project/merge_requests/img/wip_mark_as_wip.png
deleted file mode 100644
index 2c2a263b316..00000000000
--- a/doc/user/project/merge_requests/img/wip_mark_as_wip.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/img/wip_unmark_as_wip.png b/doc/user/project/merge_requests/img/wip_unmark_as_wip.png
deleted file mode 100644
index 327ad9a8448..00000000000
--- a/doc/user/project/merge_requests/img/wip_unmark_as_wip.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 593eb80e044..01a3a5bbbe1 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -18,7 +18,7 @@ With GitLab merge requests, you can:
- Live preview the changes when [Review Apps](../../../ci/review_apps/index.md) is configured for your project
- Build, test, and deploy your code in a per-branch basis with built-in [GitLab CI/CD](../../../ci/README.md)
- Prevent the merge request from being merged before it's ready with [WIP MRs](#work-in-progress-merge-requests)
-- View the deployment process through [Pipeline Graphs](../../../ci/pipelines.md#pipeline-graphs)
+- View the deployment process through [Pipeline Graphs](../../../ci/pipelines.md#visualizing-pipelines)
- [Automatically close the issue(s)](../../project/issues/closing_issues.md#via-merge-request) that originated the implementation proposed in the merge request
- Assign it to any registered user, and change the assignee how many times you need
- Assign a [milestone](../../project/milestones/index.md) and track the development of a broader implementation
diff --git a/doc/user/project/merge_requests/maintainer_access.md b/doc/user/project/merge_requests/maintainer_access.md
index d59afecd375..fe7e1f558c7 100644
--- a/doc/user/project/merge_requests/maintainer_access.md
+++ b/doc/user/project/merge_requests/maintainer_access.md
@@ -1 +1,5 @@
+---
+redirect_to: 'allow_collaboration.md'
+---
+
This document was moved to [another location](allow_collaboration.md).
diff --git a/doc/user/project/merge_requests/merge_request_discussion_resolution.md b/doc/user/project/merge_requests/merge_request_discussion_resolution.md
index 200965875a1..a554d727ca4 100644
--- a/doc/user/project/merge_requests/merge_request_discussion_resolution.md
+++ b/doc/user/project/merge_requests/merge_request_discussion_resolution.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../discussions/index.md'
+---
+
This document was moved to [another location](../../discussions/index.md).
diff --git a/doc/user/project/merge_requests/merge_when_build_succeeds.md b/doc/user/project/merge_requests/merge_when_build_succeeds.md
index 2167fdfbf7e..cf5e3af16c9 100644
--- a/doc/user/project/merge_requests/merge_when_build_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_build_succeeds.md
@@ -1,5 +1,7 @@
-This document was moved to [merge_when_pipeline_succeeds](merge_when_pipeline_succeeds.md).
+---
+redirect_to: 'merge_when_pipeline_succeeds.md'
+---
->[Introduced][ce-7135] by the "Rename MWBS service to Merge When Pipeline Succeeds" change.
+This document was moved to [merge_when_pipeline_succeeds](merge_when_pipeline_succeeds.md).
-[ce-7135]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7135
+>[Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7135) by the "Rename MWBS service to Merge When Pipeline Succeeds" change.
diff --git a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
index bdd7d0022e6..f8af71ab46b 100644
--- a/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
+++ b/doc/user/project/merge_requests/merge_when_pipeline_succeeds.md
@@ -1,4 +1,4 @@
-# Merge When Pipeline Succeeds
+# Merge when pipeline succeeds
When reviewing a merge request that looks ready to merge but still has one or
more CI jobs running, you can set it to be merged automatically when the
diff --git a/doc/user/project/merge_requests/versions.md b/doc/user/project/merge_requests/versions.md
index 90500fd9c21..70bd1e60594 100644
--- a/doc/user/project/merge_requests/versions.md
+++ b/doc/user/project/merge_requests/versions.md
@@ -1,6 +1,7 @@
# Merge requests versions
> **Notes:**
+>
> - [Introduced][ce-5467] in GitLab 8.12.
> - Comments are disabled while viewing outdated merge versions or comparing to
> versions other than base.
diff --git a/doc/user/project/merge_requests/work_in_progress_merge_requests.md b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
index 66ac7740157..6f33eb9a482 100644
--- a/doc/user/project/merge_requests/work_in_progress_merge_requests.md
+++ b/doc/user/project/merge_requests/work_in_progress_merge_requests.md
@@ -1,25 +1,46 @@
# "Work In Progress" Merge Requests
-To prevent merge requests from accidentally being accepted before they're
-completely ready, GitLab blocks the "Accept" button for merge requests that
-have been marked a **Work In Progress**.
+If a merge request is not yet ready to be merged, perhaps due to continued development
+or open discussions, you can prevent it from being accepted before it's ready by flagging
+it as a **Work In Progress**. This will disable the "Merge" button, preventing it from
+being merged, and it will stay disabled until the "WIP" flag has been removed.
![Blocked Accept Button](img/wip_blocked_accept_button.png)
-To mark a merge request a Work In Progress, simply start its title with `[WIP]`
-or `WIP:`. As an alternative, you're also able to do it by sending a commit
-with its title starting with `wip` or `WIP` to the merge request's source branch.
-
-![Mark as WIP](img/wip_mark_as_wip.png)
-
-To allow a Work In Progress merge request to be accepted again when it's ready,
-simply remove the `WIP` prefix.
-
-![Unmark as WIP](img/wip_unmark_as_wip.png)
-
-## Filtering merge requests with WIP Status
-
-To filter merge requests with the `WIP` status, you can type `wip`
-and select the value for your filter from the merge request search input.
+## Adding the "Work In Progress" flag to a Merge Request
+
+There are several ways to flag a merge request as a Work In Progress:
+
+- Add "[WIP]" or "WIP:" to the start of the merge request's title. Clicking on
+ **Start the title with WIP:**, under the title box, when editing the merge request's
+ description will have the same effect.
+- Add the `/wip` [quick action](../quick_actions.md#quick-actions-for-issues-and-merge-requests)
+ in a discussion comment in the merge request. This is a toggle, and can be repeated
+ to change the status back. Note that any other text in the comment will be discarded.
+- Add "wip" or "WIP" to the start of a commit message targeting the merge request's
+ source branch. This is not a toggle, and doing it again in another commit will have
+ no effect.
+
+## Removing the "Work In Progress" flag from a Merge Request
+
+Similar to above, when a Merge Request is ready to be merged, you can remove the
+"Work in Progress" flag in several ways:
+
+- Remove "[WIP]" or "WIP:" from the start of the merge request's title. Clicking on
+ **Remove the WIP: prefix from the title**, under the title box, when editing the merge
+ request's description, will have the same effect.
+- Add the `/wip` [quick action](../quick_actions.md#quick-actions-for-issues-and-merge-requests)
+ in a discussion comment in the merge request. This is a toggle, and can be repeated
+ to change the status back. Note that any other text in the comment will be discarded.
+- Click on the **Resolve WIP status** button near the bottom of the merge request description,
+ next to the "Merge" button (see [image above](#work-in-progress-merge-requests)).
+ Must have at least Developer level permissions on the project for the button to
+ be visible.
+
+## Including/Excluding WIP Merge Requests when searching
+
+When viewing/searching the merge requests list, you can choose to include or exclude
+WIP merge requests by adding a "WIP" filter in the search box, and choosing "Yes"
+(to include) or "No" (to exclude).
![Filter WIP MRs](img/filter_wip_merge_requests.png)
diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md
index e6033ca8655..a7d6144e3ec 100644
--- a/doc/user/project/milestones/index.md
+++ b/doc/user/project/milestones/index.md
@@ -92,7 +92,7 @@ When filtering by milestone, in addition to choosing a specific project mileston
- **None**: Show issues or merge requests with no assigned milestone.
- **Any**: Show issues or merge requests that have an assigned milestone.
- **Upcoming**: Show issues or merge requests that have been assigned the open milestone that has the next upcoming due date (i.e. nearest due date in the future).
-- **Started**: Show issues or merge requests that have an assigned milestone with a start date that is before today.
+- **Started**: Show issues or merge requests that have an open assigned milestone with a start date that is before today.
## Milestone view
diff --git a/doc/user/project/new_ci_build_permissions_model.md b/doc/user/project/new_ci_build_permissions_model.md
index d7a1a69f29d..6c3fa5eb463 100644
--- a/doc/user/project/new_ci_build_permissions_model.md
+++ b/doc/user/project/new_ci_build_permissions_model.md
@@ -49,7 +49,7 @@ It is important to note that we have a few types of users:
Administrator will have to be a member of it in order to have access to it
via another project's job.
-- **External users**: CI jobs created by [external users][ext] will have
+- **External users**: CI jobs created by [external users](../permissions.md#external-users-permissions) will have
access only to projects to which user has at least reporter access. This
rules out accessing all internal projects by default,
@@ -60,7 +60,7 @@ Let's consider the following scenario:
hosted in private repositories and you have multiple CI jobs that make use
of these repositories.
-1. You invite a new [external user][ext]. CI jobs created by that user do not
+1. You invite a new [external user](../permissions.md#external-users-permissions). CI jobs created by that user do not
have access to internal repositories, because the user also doesn't have the
access from within GitLab. You as an employee have to grant explicit access
for this user. This allows us to prevent from accidental data leakage.
@@ -205,6 +205,7 @@ With the update permission model we also extended the support for accessing
Container Registries for private projects.
> **Notes:**
+>
> - GitLab Runner versions prior to 1.8 don't incorporate the introduced changes
> for permissions. This makes the `image:` directive to not work with private
> projects automatically and it needs to be configured manually on Runner's host
@@ -232,7 +233,6 @@ test:
[job permissions]: ../permissions.md#job-permissions
[comment]: https://gitlab.com/gitlab-org/gitlab-ce/issues/22484#note_16648302
-[ext]: ../permissions.md#external-users
[gitsub]: ../../ci/git_submodules.md
[https]: ../admin_area/settings/visibility_and_access_controls.md#enabled-git-access-protocols
[triggers]: ../../ci/triggers/README.md
diff --git a/doc/user/project/operations/error_tracking.md b/doc/user/project/operations/error_tracking.md
index 90bb92d2062..1b319c5641c 100644
--- a/doc/user/project/operations/error_tracking.md
+++ b/doc/user/project/operations/error_tracking.md
@@ -21,9 +21,13 @@ GitLab provides an easy way to connect Sentry to your project:
1. Sign up to Sentry.io or [deploy your own](#deploying-sentry) Sentry instance.
1. [Find or generate](https://docs.sentry.io/api/auth/) a Sentry auth token for your Sentry project.
-Make sure to give the token at least the following scopes: `event:read` and `project:read`.
-1. Navigate to your project’s **Settings > Operations** and provide the Sentry API URL and auth token.
-1. Ensure that the 'Active' checkbox is set.
+ Make sure to give the token at least the following scopes: `event:read` and `project:read`.
+1. Navigate to your project’s **Settings > Operations**.
+1. Ensure that the **Active** checkbox is set.
+1. In the **Sentry API URL** field, enter your Sentry hostname. For example, `https://sentry.example.com`.
+1. In the **Auth Token** field, enter the token you previously generated.
+1. Click the **Connect** button to test the connection to Sentry and populate the **Project** dropdown.
+1. From the **Project** dropdown, choose a Sentry project to link to your GitLab project.
1. Click **Save changes** for the changes to take effect.
1. You can now visit **Operations > Error Tracking** in your project's sidebar to [view a list](#error-tracking-list) of Sentry errors.
diff --git a/doc/user/project/pages/getting_started_part_four.md b/doc/user/project/pages/getting_started_part_four.md
index e4ee2f7cdfa..f552f60a07e 100644
--- a/doc/user/project/pages/getting_started_part_four.md
+++ b/doc/user/project/pages/getting_started_part_four.md
@@ -9,14 +9,26 @@ date: 2017-02-22
# Creating and Tweaking GitLab CI/CD for GitLab Pages
-[GitLab CI](https://about.gitlab.com/gitlab-ci/) serves
+To [get started with GitLab Pages](index.md#getting-started), you can
+use one of the project templates, a `.gitlab-ci.yml` template,
+or fork an existing example project. Therefore, you don't need to
+understand _all_ the ins and odds of GitLab CI/CD to get your site
+deployed. Still, there are cases where you want to write your own
+script or tweak an existing one. This document guides you through
+this process.
+
+This guide also provides a general overview and clear introduction
+for **getting familiar with the `.gitlab-ci.yml` file and writing
+one for the first time.**
+
+[GitLab CI/CD](../../../ci/README.md) serves
numerous purposes, to build, test, and deploy your app
from GitLab through
-[Continuous Integration, Continuous Delivery, and Continuous Deployment](https://about.gitlab.com/2016/08/05/continuous-integration-delivery-and-deployment-with-gitlab/)
+[Continuous Integration, Continuous Delivery, and Continuous Deployment](../../../ci/introduction/index.md#introduction-to-cicd-methodologies)
methods. You will need it to build your website with GitLab Pages,
and deploy it to the Pages server.
-To implement GitLab CI/CD, the first thing we need is a configuration
+To implement GitLab CI/CD, the first thing you need is a configuration
file called `.gitlab-ci.yml` placed at your website's root directory.
What this file actually does is telling the
@@ -26,7 +38,7 @@ terminal. GitLab CI/CD tells the Runner which commands to run.
Both are built-in in GitLab, and you don't need to set up
anything for them to work.
-Explaining [every detail of GitLab CI](https://docs.gitlab.com/ce/ci/yaml/README.html)
+Explaining [every detail of GitLab CI/CD](../../../ci/yaml/README.md)
and GitLab Runner is out of the scope of this guide, but we'll
need to understand just a few things to be able to write our own
`.gitlab-ci.yml` or tweak an existing one. It's an
@@ -376,11 +388,11 @@ manually in the past. Read through the
to understand how to go even further on your scripts.
- On this blog post, understand the concept of
-[using GitLab CI `environments` to deploy your
-web app to staging and production](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/).
+ [using GitLab CI `environments` to deploy your
+ web app to staging and production](https://about.gitlab.com/2016/08/26/ci-deployment-and-environments/).
- On this post, learn [how to run jobs sequentially,
-in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
+ in parallel, or build a custom pipeline](https://about.gitlab.com/2016/07/29/the-basics-of-gitlab-ci/)
- On this blog post, we go through the process of
-[pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
-to deploy this website you're looking at, docs.gitlab.com.
+ [pulling specific directories from different projects](https://about.gitlab.com/2016/12/07/building-a-new-gitlab-docs-site-with-nanoc-gitlab-ci-and-gitlab-pages/)
+ to deploy this website you're looking at, docs.gitlab.com.
- On this blog post, we teach you [how to use GitLab Pages to produce a code coverage report](https://about.gitlab.com/2016/11/03/publish-code-coverage-report-with-gitlab-pages/).
diff --git a/doc/user/project/pages/getting_started_part_one.md b/doc/user/project/pages/getting_started_part_one.md
index 595241b2cba..f1e2771dcb9 100644
--- a/doc/user/project/pages/getting_started_part_one.md
+++ b/doc/user/project/pages/getting_started_part_one.md
@@ -59,7 +59,7 @@ which is highly recommendable and much faster than hardcoding.
If you set up a GitLab Pages project on GitLab.com,
it will automatically be accessible under a
-[subdomain of `namespace.gitlab.io`](introduction.md#gitlab-pages-on-gitlab-com).
+[subdomain of `namespace.gitlab.io`](introduction.md#gitlab-pages-on-gitlabcom).
The `namespace` is defined by your username on GitLab.com,
or the group name you created this project under.
@@ -77,41 +77,41 @@ Learn more about [namespaces](../../group/index.md#namespaces).
#### Project Websites
- You created a project called `blog` under your username `john`,
-therefore your project URL is `https://gitlab.com/john/blog/`.
-Once you enable GitLab Pages for this project, and build your site,
-it will be available under `https://john.gitlab.io/blog/`.
+ therefore your project URL is `https://gitlab.com/john/blog/`.
+ Once you enable GitLab Pages for this project, and build your site,
+ it will be available under `https://john.gitlab.io/blog/`.
- You created a group for all your websites called `websites`,
-and a project within this group is called `blog`. Your project
-URL is `https://gitlab.com/websites/blog/`. Once you enable
-GitLab Pages for this project, the site will live under
-`https://websites.gitlab.io/blog/`.
+ and a project within this group is called `blog`. Your project
+ URL is `https://gitlab.com/websites/blog/`. Once you enable
+ GitLab Pages for this project, the site will live under
+ `https://websites.gitlab.io/blog/`.
- You created a group for your engineering department called `engineering`,
-a subgroup for all your documentation websites called `docs`,
-and a project within this subgroup is called `workflows`. Your project
-URL is `https://gitlab.com/engineering/docs/workflows/`. Once you enable
-GitLab Pages for this project, the site will live under
-`https://engineering.gitlab.io/docs/workflows`.
+ a subgroup for all your documentation websites called `docs`,
+ and a project within this subgroup is called `workflows`. Your project
+ URL is `https://gitlab.com/engineering/docs/workflows/`. Once you enable
+ GitLab Pages for this project, the site will live under
+ `https://engineering.gitlab.io/docs/workflows`.
#### User and Group Websites
- Under your username, `john`, you created a project called
-`john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`.
-Once you enable GitLab Pages for your project, your website
-will be published under `https://john.gitlab.io`.
+ `john.gitlab.io`. Your project URL will be `https://gitlab.com/john/john.gitlab.io`.
+ Once you enable GitLab Pages for your project, your website
+ will be published under `https://john.gitlab.io`.
- Under your group `websites`, you created a project called
-`websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`.
-Once you enable GitLab Pages for your project,
-your website will be published under `https://websites.gitlab.io`.
+ `websites.gitlab.io`. your project's URL will be `https://gitlab.com/websites/websites.gitlab.io`.
+ Once you enable GitLab Pages for your project,
+ your website will be published under `https://websites.gitlab.io`.
> Support for subgroup project's websites was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/30548) in GitLab 11.8.
**General example:**
- On GitLab.com, a project site will always be available under
-`https://namespace.gitlab.io/project-name`
+ `https://namespace.gitlab.io/project-name`
- On GitLab.com, a user or group website will be available under
-`https://namespace.gitlab.io/`
+ `https://namespace.gitlab.io/`
- On your GitLab instance, replace `gitlab.io` above with your
-Pages server domain. Ask your sysadmin for this information.
+ Pages server domain. Ask your sysadmin for this information.
_Read on about [Projects for GitLab Pages and URL structure](getting_started_part_two.md)._
diff --git a/doc/user/project/pages/getting_started_part_three.md b/doc/user/project/pages/getting_started_part_three.md
index b2da1c85c62..756b8b698c7 100644
--- a/doc/user/project/pages/getting_started_part_three.md
+++ b/doc/user/project/pages/getting_started_part_three.md
@@ -19,14 +19,14 @@ To use one or more custom domain with your Pages site, there are two things
you should consider first, which we'll cover in this guide:
1. Either if you're adding a **root domain** or a **subdomain**, for which
-you'll need to set up [DNS records](#dns-records)
-1. Whether you want to add an [SSL/TLS certificate](#ssl-tls-certificates) or not
+ you'll need to set up [DNS records](#dns-records)
+1. Whether you want to add an [SSL/TLS certificate](#ssltls-certificates) or not
To finish the association, you need to [add your domain to your project's Pages settings](#add-your-custom-domain-to-gitlab-pages-settings).
Let's start from the beginning with [DNS records](#dns-records).
If you already know how they work and want to skip the introduction to DNS,
-you may be interested in skipping it until the [TL;DR](#tl-dr) section below.
+you may be interested in skipping it until the [TL;DR](#tldr) section below.
### DNS Records
@@ -136,19 +136,19 @@ verify your domain's ownership with a TXT record:
> **Notes**:
>
> - **Do not** use a CNAME record if you want to point your
-`domain.com` to your GitLab Pages site. Use an `A` record instead.
+ `domain.com` to your GitLab Pages site. Use an `A` record instead.
> - **Do not** add any special chars after the default Pages
-domain. E.g., **do not** point your `subdomain.domain.com` to
-`namespace.gitlab.io.` or `namespace.gitlab.io/`.
+ domain. E.g., **do not** point your `subdomain.domain.com` to
+ `namespace.gitlab.io.` or `namespace.gitlab.io/`.
> - GitLab Pages IP on GitLab.com [was changed](https://about.gitlab.com/2017/03/06/we-are-changing-the-ip-of-gitlab-pages-on-gitlab-com/) in 2017
> - GitLab Pages IP on GitLab.com [has been changed](https://about.gitlab.com/2018/07/19/gcp-move-update/#gitlab-pages-and-custom-domains)
-from `52.167.214.135` to `35.185.44.232` in 2018
+ from `52.167.214.135` to `35.185.44.232` in 2018
### Add your custom domain to GitLab Pages settings
Once you've set the DNS record, you'll need navigate to your project's
**Setting > Pages** and click **+ New domain** to add your custom domain to
-GitLab Pages. You can choose whether to add an [SSL/TLS certificate](#ssl-tls-certificates)
+GitLab Pages. You can choose whether to add an [SSL/TLS certificate](#ssltls-certificates)
to make your website accessible under HTTPS or leave it blank. If don't add a certificate,
your site will be accessible only via HTTP:
@@ -278,15 +278,15 @@ These fields are found under your **Project**'s **Settings** > **Pages** > **New
### What's what?
- A PEM certificate is the certificate generated by the CA,
-which needs to be added to the field **Certificate (PEM)**.
+ which needs to be added to the field **Certificate (PEM)**.
- An [intermediate certificate](https://en.wikipedia.org/wiki/Intermediate_certificate_authority) (aka "root certificate") is
-the part of the encryption keychain that identifies the CA.
-Usually it's combined with the PEM certificate, but there are
-some cases in which you need to add them manually.
-[CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/)
-are one of these cases.
+ the part of the encryption keychain that identifies the CA.
+ Usually it's combined with the PEM certificate, but there are
+ some cases in which you need to add them manually.
+ [CloudFlare certs](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/)
+ are one of these cases.
- A private key is an encrypted key which validates
-your PEM against your domain.
+ your PEM against your domain.
### Now what?
@@ -295,9 +295,9 @@ of this, it's simple:
- Your PEM certificate needs to be added to the first field
- If your certificate is missing its intermediate, copy
-and paste the root certificate (usually available from your CA website)
-and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/),
-just jumping a line between them.
+ and paste the root certificate (usually available from your CA website)
+ and paste it in the [same field as your PEM certificate](https://about.gitlab.com/2017/02/07/setting-up-gitlab-pages-with-cloudflare-certificates/),
+ just jumping a line between them.
- Copy your private key and paste it in the last field
>**Note:**
diff --git a/doc/user/project/pages/getting_started_part_two.md b/doc/user/project/pages/getting_started_part_two.md
index c9081a6d72b..901fb226cda 100644
--- a/doc/user/project/pages/getting_started_part_two.md
+++ b/doc/user/project/pages/getting_started_part_two.md
@@ -1,5 +1,5 @@
---
-last_updated: 2018-02-16
+last_updated: 2019-03-05
author: Marcia Ramos
author_gitlab: marcia
level: beginner
@@ -16,13 +16,13 @@ To get started with GitLab Pages, you need:
1. A project
1. A configuration file (`.gitlab-ci.yml`) to deploy your site
1. A specific `job` called `pages` in the configuration file
-that will make GitLab aware that you are deploying a GitLab Pages website
+ that will make GitLab aware that you are deploying a GitLab Pages website
1. A `public` directory with the content of the website
Optional Features:
-1. A custom domain or subdomain
-1. A DNS pointing your (sub)domain to your Pages site
+1. A custom domain or subdomain.
+1. A DNS pointing your (sub)domain to your Pages site.
1. **Optional**: an SSL/TLS certificate so your custom
domain is accessible under HTTPS.
@@ -33,68 +33,87 @@ The optional settings, custom domain, DNS records, and SSL/TLS certificates, are
Your GitLab Pages project is a regular project created the
same way you do for the other ones. To get started with GitLab Pages, you have three ways:
-- Use one of the popular templates already in the app,
-- Fork one of the templates from Page Examples, or
-- Create a new project from scratch
-
-Let's go over each option.
+- [Use one of the popular project templates bundled with GitLab](#use-one-of-the-popular-pages-templates-bundled-with-gitlab).
+- [Fork one of the templates from Page Examples](#fork-a-project-to-get-started-from).
+- [Create a new project from scratch](#create-a-project-from-scratch).
### Use one of the popular Pages templates bundled with GitLab
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/47857)
in GitLab 11.8.
-The simplest way to create a GitLab Pages site is to use one of the most
-popular templates, which come already bundled and ready to go. To use one
-of these templates:
-
-1. From the top navigation, click the **+** button and select **New project**
-1. Select **Create from Template**
-1. Choose one of the templates starting with **Pages**
+The simplest way to create a GitLab Pages site is to
+[use one of the most popular templates](index.md#getting-started),
+which come already bundled with GitLab and are ready to go.
### Fork a project to get started from
-To make things easy for you, we've created this
-[group](https://gitlab.com/pages) of default projects
-containing the most popular SSGs templates.
-
-Watch the [video tutorial](https://youtu.be/TWqh9MtT4Bg) we've
-created for the steps below.
-
-1. [Fork a sample project](../../../gitlab-basics/fork-project.md) from the [Pages group](https://gitlab.com/pages)
-1. Trigger a build (push a change to any file)
-1. As soon as the build passes, your website will have been deployed with GitLab Pages. Your website URL will be available under your project's **Settings** > **Pages**
-1. Optionally, remove the fork relationship by navigating to your project's **Settings** > expanding **Advanced settings** and scrolling down to **Remove fork relationship**:
+If you don't find an existing project template that suits you,
+we've created this [group](https://gitlab.com/pages) of default projects
+containing the most popular SSGs templates to get you started.
+
+<table class="borderless-table center fixed-table middle width-80">
+ <tr>
+ <td style="width: 30%"><img src="img/icons/fork.png" alt="Fork" class="image-noshadow half-width"></td>
+ <td style="width: 10%">
+ <strong>
+ <i class="fa fa-angle-double-right" aria-hidden="true"></i>
+ </strong>
+ </td>
+ <td style="width: 30%"><img src="img/icons/terminal.png" alt="Deploy" class="image-noshadow half-width"></td>
+ <td style="width: 10%">
+ <strong>
+ <i class="fa fa-angle-double-right" aria-hidden="true"></i>
+ </strong>
+ </td>
+ <td style="width: 30%"><img src="img/icons/click.png" alt="Visit" class="image-noshadow half-width"></td>
+ </tr>
+ <tr>
+ <td><em>Fork an example project</em></td>
+ <td></td>
+ <td><em>Deploy your website</em></td>
+ <td></td>
+ <td><em>Visit your website's URL</em></td>
+ </tr>
+</table>
+
+**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> Watch a [video tutorial](https://www.youtube.com/watch?v=TWqh9MtT4Bg) with all the steps below.**
+
+1. [Fork](../../../gitlab-basics/fork-project.md) a sample project from the [GitLab Pages examples](https://gitlab.com/pages) group.
+1. From the left sidebar, navigate to your project's **CI/CD > Pipelines**
+and click **Run pipeline** to trigger GitLab CI/CD to build and deploy your
+site to the server.
+1. Once the pipeline has finished successfully, find the link to visit your
+website from your project's **Settings > Pages**.
+
+You can also take some **optional** further steps:
+
+- _Remove the fork relationship._ The fork relashionship is necessary to contribute back to the project you originally forked from. If you don't have any intentions to do so, you can remove it. To do so, navigate to your project's **Settings**, expand **Advanced settings**, and scroll down to **Remove fork relationship**:
![remove fork relationship](img/remove_fork_relationship.png)
-To turn a **project website** forked from the Pages group into a **user/group** website, you'll need to:
-
-- Rename it to `namespace.gitlab.io`: navigate to project's **Settings** > expand **Advanced settings** > and scroll down to **Rename repository**
-- Adjust your SSG's [base URL](#urls-and-baseurls) from `"project-name"` to `""`. This setting will be at a different place for each SSG, as each of them have their own structure and file tree. Most likely, it will be in the SSG's config file.
-
-> **Notes:**
->
-> Why do I need to remove the fork relationship?
->
-> Unless you want to contribute to the original project,
-you won't need it connected to the upstream. A
-[fork](https://about.gitlab.com/2016/12/01/how-to-keep-your-fork-up-to-date-with-its-origin/#fork)
-is useful for submitting merge requests to the upstream.
+- _Make it a user or group website._ To turn a **project website** forked
+from the Pages group into a **user/group** website, you'll need to:
+ - Rename it to `namespace.gitlab.io`: navigate to project's **Settings** >
+ expand **Advanced settings** > and scroll down to **Rename repository**.
+ - Adjust your SSG's [base URL](#urls-and-baseurls) from `"project-name"` to
+ `""`. This setting will be at a different place for each SSG, as each of them
+ have their own structure and file tree. Most likely, it will be in the SSG's
+ config file.
### Create a project from scratch
1. From your **Project**'s **[Dashboard](https://gitlab.com/dashboard/projects)**,
-click **New project**, and name it considering the
-[practical examples](getting_started_part_one.md#practical-examples).
+ click **New project**, and name it considering the
+ [practical examples](getting_started_part_one.md#practical-examples).
1. Clone it to your local computer, add your website
-files to your project, add, commit and push to GitLab.
+ files to your project, add, commit and push to GitLab.
1. From the your **Project**'s page, click **Set up CI/CD**:
![setup GitLab CI/CD](img/setup_ci.png)
1. Choose one of the templates from the dropbox menu.
-Pick up the template corresponding to the SSG you're using (or plain HTML).
+ Pick up the template corresponding to the SSG you're using (or plain HTML).
![gitlab-ci templates](img/choose_ci_template.png)
@@ -107,20 +126,20 @@ where you'll find its default URL.
> **Notes:**
>
> - GitLab Pages [supports any SSG](https://about.gitlab.com/2016/06/17/ssg-overview-gitlab-pages-part-3-examples-ci/), but,
-if you don't find yours among the templates, you'll need
-to configure your own `.gitlab-ci.yml`. To do that, please
-read through the article [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among
-the [example projects](https://gitlab.com/pages). If you set
-up a new one, please
-[contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md)
-to our examples.
+ if you don't find yours among the templates, you'll need
+ to configure your own `.gitlab-ci.yml`. To do that, please
+ read through the article [Creating and Tweaking GitLab CI/CD for GitLab Pages](getting_started_part_four.md). New SSGs are very welcome among
+ the [example projects](https://gitlab.com/pages). If you set
+ up a new one, please
+ [contribute](https://gitlab.com/pages/pages.gitlab.io/blob/master/CONTRIBUTING.md)
+ to our examples.
>
> - The second step _"Clone it to your local computer"_, can be done
-differently, achieving the same results: instead of cloning the bare
-repository to you local computer and moving your site files into it,
-you can run `git init` in your local website directory, add the
-remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`,
-then add, commit, and push.
+ differently, achieving the same results: instead of cloning the bare
+ repository to you local computer and moving your site files into it,
+ you can run `git init` in your local website directory, add the
+ remote URL: `git remote add origin git@gitlab.com:namespace/project-name.git`,
+ then add, commit, and push.
## URLs and Baseurls
diff --git a/doc/user/project/pages/img/pages_project_templates_11-8.png b/doc/user/project/pages/img/pages_project_templates_11-8.png
new file mode 100644
index 00000000000..a645d28260b
--- /dev/null
+++ b/doc/user/project/pages/img/pages_project_templates_11-8.png
Binary files differ
diff --git a/doc/user/project/pages/index.md b/doc/user/project/pages/index.md
index 2de3fb7e080..885df9f0850 100644
--- a/doc/user/project/pages/index.md
+++ b/doc/user/project/pages/index.md
@@ -1,5 +1,6 @@
---
description: 'Learn how to use GitLab Pages to deploy a static website at no additional cost.'
+last_updated: 2019-03-05
---
# GitLab Pages
@@ -8,8 +9,8 @@ description: 'Learn how to use GitLab Pages to deploy a static website at no add
directly from a repository in GitLab.**
You can use it either for personal or business websites, such as
-portfolios, documentation, manifestos, and business presentations,
-and attribute any license to your content.
+portfolios, documentation, manifestos, and business presentations.
+You can also attribute any license to your content.
<table class="borderless-table center fixed-table">
<tr>
@@ -91,57 +92,33 @@ site under the HTTPS protocol.
## Getting started
-To get started with GitLab Pages, you can either [create a project from scratch](getting_started_part_two.md#create-a-project-from-scratch),
-use a [bundled template](getting_started_part_two.md#use-one-of-the-popular-pages-templates-bundled-with-gitlab), or copy any of our existing example projects:
+To get started with GitLab Pages, you can either:
-1. Choose an [example project](https://gitlab.com/pages) to [fork](../../../gitlab-basics/fork-project.md#how-to-fork-a-project):
- by forking a project, you create a copy of the codebase you're forking from to start from a template instead of starting from scratch.
-1. From the left sidebar, navigate to your project's **CI/CD > Pipelines** and click
-**Run pipeline** so that GitLab CI/CD will build and deploy your site to the server.
-1. Once the pipeline has finished successfully, find the link to visit your website from your
- project's **Settings > Pages**.
+- [Create a project from scratch](getting_started_part_two.md#create-a-project-from-scratch).
+- [Copy an existing example project](getting_started_part_two.md#fork-a-project-to-get-started-from).
+- Use a bundled project template that is ready to go ([introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/47857)
+in GitLab 11.8), as follows:
-<table class="borderless-table center fixed-table middle width-80">
- <tr>
- <td style="width: 30%"><img src="img/icons/fork.png" alt="Fork" class="image-noshadow half-width"></td>
- <td style="width: 10%">
- <strong>
- <i class="fa fa-angle-double-right" aria-hidden="true"></i>
- </strong>
- </td>
- <td style="width: 30%"><img src="img/icons/terminal.png" alt="Deploy" class="image-noshadow half-width"></td>
- <td style="width: 10%">
- <strong>
- <i class="fa fa-angle-double-right" aria-hidden="true"></i>
- </strong>
- </td>
- <td style="width: 30%"><img src="img/icons/click.png" alt="Visit" class="image-noshadow half-width"></td>
- </tr>
- <tr>
- <td><em>Fork an example project</em></td>
- <td></td>
- <td><em>Deploy your website</em></td>
- <td></td>
- <td><em>Visit your website's URL</em></td>
- </tr>
-</table>
-
-Your website is then visible on your domain, and you can modify your files
-as you wish. For every modification pushed to your repository, GitLab CI/CD will run
-a new pipeline to publish your changes to the server.
+1. From the top navigation, click the **+** button and select **New project**.
+1. Select **Create from Template**.
+1. Choose one of the templates starting with **Pages**:
-You can also take some optional further steps:
+ ![Project templates for Pages](img/pages_project_templates_11-8.png)
-- Remove the [fork relationship](getting_started_part_two.md#fork-a-project-to-get-started-from)
- (_You don't need the relationship unless you intent to contribute back to the example project you forked from_).
-- Make it a [user/group website](getting_started_part_one.md#user-and-group-websites)
+1. From the left sidebar, navigate to your project's **CI/CD > Pipelines**
+and click **Run pipeline** to trigger GitLab CI/CD to build and deploy your
+site to the server.
+1. Once the pipeline has finished successfully, find the link to visit your
+website from your project's **Settings > Pages**.
-**<i class="fa fa-youtube-play youtube" aria-hidden="true"></i> Watch a [video tutorial](https://www.youtube.com/watch?v=TWqh9MtT4Bg) with all the steps above!**
+Your website is then visible on your domain, and you can modify yourfiles
+as you wish. For every modification pushed to your repository, GitLab CI/CD
+will run a new pipeline to publish your changes to the server.
_Advanced options:_
- [Use a custom domain](getting_started_part_three.md#adding-your-custom-domain-to-gitlab-pages)
-- Apply [SSL/TLS certification](getting_started_part_three.md#ssl-tls-certificates) to your custom domain
+- Apply [SSL/TLS certification](getting_started_part_three.md#ssltls-certificates) to your custom domain
## Explore GitLab Pages
@@ -164,7 +141,7 @@ with Pages, read through this series:
### GitLab Pages with SSL/TLS certificates
-If you're using GitLab Pages default domain (`.gitlab.io`), your website will be
+If you're using GitLab Pages default domain (`.gitlab.io`), your website will be
automatically secure and available under HTTPS. If you're using your own domain, you can
optionally secure it with SSL/TLS certificates. You can read the following
tutorials to learn how to use these third-party certificates with GitLab Pages:
diff --git a/doc/user/project/pages/introduction.md b/doc/user/project/pages/introduction.md
index fa65a206273..39f14a1126f 100644
--- a/doc/user/project/pages/introduction.md
+++ b/doc/user/project/pages/introduction.md
@@ -1,6 +1,7 @@
# Exploring GitLab Pages
> **Notes:**
+>
> - This feature was [introduced][ee-80] in GitLab EE 8.3.
> - Custom CNAMEs with TLS support were [introduced][ee-173] in GitLab EE 8.5.
> - GitLab Pages [was ported][ce-14605] to Community Edition in GitLab 8.17.
@@ -11,7 +12,7 @@ With GitLab Pages you can host for free your static websites on GitLab.
Combined with the power of [GitLab CI] and the help of [GitLab Runner] you can
deploy static pages for your individual projects, your user or your group.
-Read [GitLab Pages on GitLab.com](#gitlab-pages-on-gitlab-com) for specific
+Read [GitLab Pages on GitLab.com](#gitlab-pages-on-gitlabcom) for specific
information, if you are using GitLab.com to host your website.
## Getting started with GitLab Pages domains
@@ -115,7 +116,7 @@ gives you absolute control over the build process. You can actually watch your
website being built live by following the CI job traces.
For a simplified user guide on setting up GitLab CI/CD for Pages, read through
-the article [GitLab Pages from A to Z: Part 4 - Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md#creating-and-tweaking-gitlab-ci-yml-for-gitlab-pages)
+the article [GitLab Pages from A to Z: Part 4 - Creating and Tweaking `.gitlab-ci.yml` for GitLab Pages](getting_started_part_four.md)
> **Note:**
> Before reading this section, make sure you familiarize yourself with GitLab CI
@@ -151,7 +152,7 @@ Depending on how you plan to publish your website, the steps defined in the
Be aware that Pages are by default branch/tag agnostic and their deployment
relies solely on what you specify in `.gitlab-ci.yml`. If you don't limit the
-`pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except),
+`pages` job with the [`only` parameter](../../../ci/yaml/README.md#onlyexcept-basic),
whenever a new commit is pushed to whatever branch or tag, the Pages will be
overwritten. In the example below, we limit the Pages to be deployed whenever
a commit is pushed only on the `master` branch:
@@ -252,7 +253,7 @@ get you started.
Remember that GitLab Pages are by default branch/tag agnostic and their
deployment relies solely on what you specify in `.gitlab-ci.yml`. You can limit
-the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#only-and-except),
+the `pages` job with the [`only` parameter](../../../ci/yaml/README.md#onlyexcept-basic),
whenever a new commit is pushed to a branch that will be used specifically for
your pages.
@@ -410,7 +411,7 @@ file for both the `/data` and `/data/` URL paths.
### Add a custom domain to your Pages website
For a complete guide on Pages domains, read through the article
-[GitLab Pages from A to Z: Part 3 - Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md#setting-up-custom-domains-dns-records-and-ssl-tls-certificates)
+[GitLab Pages from A to Z: Part 3 - GitLab Pages custom domains and SSL/TLS Certificates](getting_started_part_three.md)
If this setting is enabled by your GitLab administrator, you should be able to
see the **New Domain** button when visiting your project's settings through the
@@ -451,7 +452,7 @@ private key when adding a new domain.
![Pages upload cert](img/pages_upload_cert.png)
For a complete guide on Pages domains, read through the article
-[GitLab Pages from A to Z: Part 3 - Setting Up Custom Domains - DNS Records and SSL/TLS Certificates](getting_started_part_three.md#setting-up-custom-domains-dns-records-and-ssl-tls-certificates)
+[GitLab Pages from A to Z: Part 3 - GitLab Pages custom domains and SSL/TLS Certificates](getting_started_part_three.md)
### Custom error codes pages
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index 4ab66063dcf..a3f40c20192 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -1,6 +1,7 @@
# Introduction to job artifacts
> **Notes:**
+>
> - Since GitLab 8.2 and GitLab Runner 0.7.0, job artifacts that are created by
> GitLab Runner are uploaded to GitLab and are downloadable as a single archive
> (`tar.gz`) using the GitLab UI.
@@ -38,7 +39,7 @@ turn are defined with the `paths` keyword. All paths to files and directories
are relative to the repository that was cloned during the build. These uploaded
artifacts will be kept in GitLab for 1 week as defined by the `expire_in`
definition. You have the option to keep the artifacts from expiring via the
-[web interface](#browsing-job-artifacts). If the expiry time is not defined,
+[web interface](#browsing-artifacts). If the expiry time is not defined,
it defaults to the [instance wide
setting](../../admin_area/settings/continuous_integration.md#default-artifacts-expiration-core-only).
@@ -152,7 +153,7 @@ For example:
https://gitlab.com/gitlab-org/gitlab-ce/-/jobs/artifacts/master/browse?job=coverage
```
-There is also a URL to specific files, including html files that
+There is also a URL to specific files, including html files that
are shown in [GitLab Pages](../../../administration/pages/index.md):
```
@@ -183,7 +184,7 @@ information in the UI.
DANGER: **Warning:**
This is a destructive action that leads to data loss. Use with caution.
-If you have at least Developer [permissions](../../permissions.md#gitlab-ci-cd-permissions)
+If you have at least Developer [permissions](../../permissions.md#gitlab-cicd-permissions)
on the project, you can erase a single job via the UI which will also remove the
artifacts and the job's trace.
@@ -191,9 +192,9 @@ artifacts and the job's trace.
1. Click the trash icon at the top right of the job's trace.
1. Confirm the deletion.
-## Retrieve artifacts of private projects when using GitLab CI
+## Retrieve artifacts of private projects when using GitLab CI
In order to retrieve a job artifact of a different project, you might need to use a private token in order to [authenticate and download](../../../api/jobs.md#get-job-artifacts) the artifacts.
-[expiry date]: ../../../ci/yaml/README.md#artifacts-expire_in
-[ce-14399]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14399 \ No newline at end of file
+[expiry date]: ../../../ci/yaml/README.md#artifactsexpire_in
+[ce-14399]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/14399
diff --git a/doc/user/project/pipelines/schedules.md b/doc/user/project/pipelines/schedules.md
index ec8b8444d99..2911a56cf67 100644
--- a/doc/user/project/pipelines/schedules.md
+++ b/doc/user/project/pipelines/schedules.md
@@ -1,6 +1,7 @@
-# Pipeline Schedules
+# Pipeline schedules
> **Notes**:
+>
> - This feature was introduced in 9.1 as [Trigger Schedule][ce-10533].
> - In 9.2, the feature was [renamed to Pipeline Schedule][ce-10853].
> - Cron notation is parsed by [Fugit](https://github.com/floraison/fugit).
@@ -58,7 +59,7 @@ GitLab CI so that they can be used in your `.gitlab-ci.yml` file.
To configure that a job can be executed only when the pipeline has been
scheduled (or the opposite), you can use
-[only and except](../../../ci/yaml/README.md#only-and-except-simplified) configuration keywords.
+[only and except](../../../ci/yaml/README.md#onlyexcept-basic) configuration keywords.
```
job:on-schedule:
diff --git a/doc/user/project/pipelines/settings.md b/doc/user/project/pipelines/settings.md
index cce330aecc7..4a989afad4d 100644
--- a/doc/user/project/pipelines/settings.md
+++ b/doc/user/project/pipelines/settings.md
@@ -23,7 +23,7 @@ in `.gitlab-ci.yml`.
## Timeout
Timeout defines the maximum amount of time in minutes that a job is able run.
-This is configureable under your project's **Settings > CI/CD > General pipelines settings**.
+This is configurable under your project's **Settings > CI/CD > General pipelines settings**.
The default value is 60 minutes. Decrease the time limit if you want to impose
a hard limit on your jobs' running time or increase it otherwise. In any case,
if the job surpasses the threshold, it is marked as failed.
@@ -134,7 +134,7 @@ Depending on the status of your job, a badge can have the following values:
You can access a pipeline status badge image using the following link:
```text
-https://example.gitlab.com/<namespace>/<project>/badges/<branch>/build.svg
+https://example.gitlab.com/<namespace>/<project>/badges/<branch>/pipeline.svg
```
### Test coverage report badge
diff --git a/doc/user/project/protected_branches.md b/doc/user/project/protected_branches.md
index db706e5020e..2060b5dd4a2 100644
--- a/doc/user/project/protected_branches.md
+++ b/doc/user/project/protected_branches.md
@@ -94,6 +94,25 @@ all matching branches:
![Protected branch matches](img/protected_branches_matches.png)
+## Creating a protected branch
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/53361) in GitLab 11.9.
+
+When a protected branch or wildcard protected branches are set to
+[**No one** is **Allowed to push**](#using-the-allowed-to-merge-and-allowed-to-push-settings),
+Developers (and users with higher [permission levels](../permissions.md)) are allowed
+to create a new protected branch, but only via the UI or through the API (to avoid
+creating protected branches accidentally from the command line or from a Git
+client application).
+
+To create a new branch through the user interface:
+
+1. Visit **Repository > Branches**.
+1. Click on **New branch**.
+1. Fill in the branch name and select an existing branch, tag, or commit that
+ the new branch will be based off. Only existing protected branches and commits
+ that are already in protected branches will be accepted.
+
## Deleting a protected branch
> [Introduced][ce-21393] in GitLab 9.3.
@@ -125,6 +144,10 @@ for details about the pipelines security model.
## Changelog
+**11.9**
+
+- [Allow protected branches to be created](https://gitlab.com/gitlab-org/gitlab-ce/issues/53361) by Developers (and users with higher permission levels) through the API and the user interface.
+
**9.2**
- Allow deletion of protected branches via the web interface [gitlab-org/gitlab-ce#21393][ce-21393]
diff --git a/doc/user/project/releases.md b/doc/user/project/releases.md
index 737842962a9..baa7a295273 100644
--- a/doc/user/project/releases.md
+++ b/doc/user/project/releases.md
@@ -1 +1,5 @@
+---
+redirect_to: 'releases/index.md'
+---
+
This document was moved to [another location](releases/index.md).
diff --git a/doc/user/project/repository/branches/index.md b/doc/user/project/repository/branches/index.md
index f05554ffc5b..13e4f2ce163 100644
--- a/doc/user/project/repository/branches/index.md
+++ b/doc/user/project/repository/branches/index.md
@@ -11,7 +11,7 @@ Read through GiLab's branching documentation:
See also:
- [Branches API](../../../../api/branches.md), for information on operating on repository branches using the GitLab API.
-- [GitLab Flow](../../../../university/training/gitlab_flow.md#gitlab-flow). Use the best of GitLab for your branching strategies.
+- [GitLab Flow](../../../../university/training/gitlab_flow.md). Use the best of GitLab for your branching strategies.
- [Getting started with Git](../../../../topics/git/index.md) and GitLab.
## Default branch
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index fac5975a0dc..22d912cd9d1 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -146,9 +146,9 @@ you are introducing those changes to your branch.
Via command line, you can commit multiple times before pushing.
- **Commit message:**
-A commit message is important to identity what is being changed and,
-more importantly, why. In GitLab, you can add keywords to the commit
-message that will perform one of the actions below:
+ A commit message is important to identity what is being changed and,
+ more importantly, why. In GitLab, you can add keywords to the commit
+ message that will perform one of the actions below:
- **Trigger a GitLab CI/CD pipeline:**
If you have your project configured with [GitLab CI/CD](../../../ci/README.md),
you will trigger a pipeline per push, not per commit.
@@ -162,14 +162,14 @@ message that will perform one of the actions below:
If you mention an issue or a merge request in a commit message, they will be shown
on their respective thread.
- **Cherry-pick a commit:**
-In GitLab, you can
-[cherry-pick a commit](../merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
-right from the UI.
+ In GitLab, you can
+ [cherry-pick a commit](../merge_requests/cherry_pick_changes.md#cherry-picking-a-commit)
+ right from the UI.
- **Revert a commit:**
-Easily [revert a commit](../merge_requests/revert_changes.md#reverting-a-commit)
-from the UI to a selected branch.
+ Easily [revert a commit](../merge_requests/revert_changes.md#reverting-a-commit)
+ from the UI to a selected branch.
- **Sign a commit:**
-Use GPG to [sign your commits](gpg_signed_commits/index.md).
+ Use GPG to [sign your commits](gpg_signed_commits/index.md).
## Repository size
diff --git a/doc/user/project/slash_commands.md b/doc/user/project/slash_commands.md
index e9103a3f49c..1280524bc9b 100644
--- a/doc/user/project/slash_commands.md
+++ b/doc/user/project/slash_commands.md
@@ -1 +1,5 @@
+---
+redirect_to: 'quick_actions.md'
+---
+
This document was moved to [user/project/quick_actions.md](quick_actions.md).
diff --git a/doc/user/project/web_ide/index.md b/doc/user/project/web_ide/index.md
index 3e85e97d7a5..46a1b2bc3aa 100644
--- a/doc/user/project/web_ide/index.md
+++ b/doc/user/project/web_ide/index.md
@@ -24,21 +24,21 @@ file path fragments to start seeing results.
## Syntax highlighting
-As expected from an IDE, syntax highlighting for many languages within
+As expected from an IDE, syntax highlighting for many languages within
the Web IDE will make your direct editing even easier.
The Web IDE currently provides:
-- Basic syntax colorization for a variety of programming, scripting and markup
-languages such as XML, PHP, C#, C++, Markdown, Java, VB, Batch, Python, Ruby
-and Objective-C.
-- IntelliSense and validation support (displaying errors and warnings, providing
-smart completions, formatting, and outlining) for some languages. For example:
-TypeScript, JavaScript, CSS, LESS, SCSS, JSON and HTML.
+- Basic syntax colorization for a variety of programming, scripting and markup
+ languages such as XML, PHP, C#, C++, Markdown, Java, VB, Batch, Python, Ruby
+ and Objective-C.
+- IntelliSense and validation support (displaying errors and warnings, providing
+ smart completions, formatting, and outlining) for some languages. For example:
+TypeScript, JavaScript, CSS, LESS, SCSS, JSON and HTML.
-Because the Web IDE is based on the [Monaco Editor](https://microsoft.github.io/monaco-editor/),
-you can find a more complete list of supported languages in the
-[Monaco languages](https://github.com/Microsoft/monaco-languages) repository.
+Because the Web IDE is based on the [Monaco Editor](https://microsoft.github.io/monaco-editor/),
+you can find a more complete list of supported languages in the
+[Monaco languages](https://github.com/Microsoft/monaco-languages) repository.
NOTE: **Note:**
Single file editing is based on the [Ace Editor](https://ace.c9.io).
diff --git a/doc/user/project/wiki/index.md b/doc/user/project/wiki/index.md
index 127a30d6669..95f606fd786 100644
--- a/doc/user/project/wiki/index.md
+++ b/doc/user/project/wiki/index.md
@@ -12,10 +12,6 @@ You can create Wiki pages in the web interface or
[locally using Git](#adding-and-editing-wiki-pages-locally) since every Wiki is
a separate Git repository.
->**Note:**
-A [permission level][permissions] of **Guest** is needed to view a Wiki and
-**Developer** is needed to create and edit Wiki pages.
-
## First time creating the Home page
The first time you visit a Wiki, you will be directed to create the Home page.
@@ -28,6 +24,9 @@ message.
## Creating a new wiki page
+NOTE: **Note:**
+Requires Developer [permissions](../../permissions.md).
+
Create a new page by clicking the **New page** button that can be found
in all wiki pages. You will be asked to fill in the page name from which GitLab
will create the path to the page. You can specify a full path for the new file
@@ -58,12 +57,18 @@ repository, you will have to upload them again.
## Editing a wiki page
+NOTE: **Note:**
+Requires Developer [permissions](../../permissions.md).
+
To edit a page, simply click on the **Edit** button. From there on, you can
change its content. When done, click **Save changes** for the changes to take
effect.
## Deleting a wiki page
+NOTE: **Note:**
+Requires Maintainer [permissions](../../permissions.md).
+
You can find the **Delete** button only when editing a page. Click on it and
confirm you want the page to be deleted.
@@ -109,8 +114,6 @@ them like you would do with every other Git repository.
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
diff --git a/doc/user/reserved_names.md b/doc/user/reserved_names.md
index a14df6c8402..9aa81e33fc0 100644
--- a/doc/user/reserved_names.md
+++ b/doc/user/reserved_names.md
@@ -5,6 +5,7 @@ existing routes used by GitLab.
For a list of words that are not allowed to be used as group or project names, see the
[`path_regex.rb` file][reserved] under the `TOP_LEVEL_ROUTES`, `PROJECT_WILDCARD_ROUTES` and `GROUP_ROUTES` lists:
+
- `TOP_LEVEL_ROUTES`: are names that are reserved as usernames or top level groups
- `PROJECT_WILDCARD_ROUTES`: are names that are reserved for child groups or projects.
- `GROUP_ROUTES`: are names that are reserved for all groups or projects.
diff --git a/doc/user/search/index.md b/doc/user/search/index.md
index 770cef42995..705983cce2a 100644
--- a/doc/user/search/index.md
+++ b/doc/user/search/index.md
@@ -96,7 +96,7 @@ quickly access issues and merge requests created or assigned to you within that
Your [todos](../../workflow/todos.md#gitlab-todos) can be searched by "to do" and "done".
You can [filter](../../workflow/todos.md#filtering-your-todos) them per project,
author, type, and action. Also, you can sort them by
-[**Label priority**](../../user/project/labels.md#prioritize-labels),
+[**Label priority**](../../user/project/labels.md#label-priority),
**Last created** and **Oldest created**.
## Projects
diff --git a/doc/user/snippets.md b/doc/user/snippets.md
index 569bdc9e2d5..7b580a057f2 100644
--- a/doc/user/snippets.md
+++ b/doc/user/snippets.md
@@ -20,7 +20,7 @@ and private. See [Public access](../public_access/public_access.md) for more inf
## Project snippets
Project snippets are always related to a specific project.
-See [Project's features](project/index.md#projects-features) for more information.
+See [Project features](project/index.md#project-features) for more information.
## Discover snippets
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index 0ebe5eea173..fffb6a5d86d 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/integrations/webhooks.md'
+---
+
This document was moved to [project/integrations/webhooks](../user/project/integrations/webhooks.md).
diff --git a/doc/workflow/add-user/add-user.md b/doc/workflow/add-user/add-user.md
index 35cc080d2b7..f1ec771dd9a 100644
--- a/doc/workflow/add-user/add-user.md
+++ b/doc/workflow/add-user/add-user.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/members/index.md'
+---
+
This document was moved to [../../user/project/members/index.md](../../user/project/members/index.md)
diff --git a/doc/workflow/authorization_for_merge_requests.md b/doc/workflow/authorization_for_merge_requests.md
index 7bf80a3ad0d..8e43d340613 100644
--- a/doc/workflow/authorization_for_merge_requests.md
+++ b/doc/workflow/authorization_for_merge_requests.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/merge_requests/authorization_for_merge_requests.md'
+---
+
This document was moved to [user/project/merge_requests/authorization_for_merge_requests](../user/project/merge_requests/authorization_for_merge_requests.md)
diff --git a/doc/workflow/award_emoji.md b/doc/workflow/award_emoji.md
index d74378cc564..02db97b8dd6 100644
--- a/doc/workflow/award_emoji.md
+++ b/doc/workflow/award_emoji.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/award_emojis.md'
+---
+
This document was moved to [another location](../user/award_emojis.md).
diff --git a/doc/workflow/cherry_pick_changes.md b/doc/workflow/cherry_pick_changes.md
index 663ffd3f746..29c4f854416 100644
--- a/doc/workflow/cherry_pick_changes.md
+++ b/doc/workflow/cherry_pick_changes.md
@@ -1 +1,5 @@
-This document was moved to [user/project/merge_requests/cherry_pick_changes](../user/project/merge_requests/cherry_pick_changes.md).
+---
+redirect_to: '../user/project/merge_requests/cherry_pick_changes.md'
+---
+
+This document was moved to [another location](../user/project/merge_requests/cherry_pick_changes.md).
diff --git a/doc/workflow/groups.md b/doc/workflow/groups.md
index 06eec1ed928..c7f4647baa9 100644
--- a/doc/workflow/groups.md
+++ b/doc/workflow/groups.md
@@ -1,2 +1,5 @@
+---
+redirect_to: '../user/group/index.md'
+---
This document was moved to [another location](../user/group/index.md).
diff --git a/doc/workflow/importing/README.md b/doc/workflow/importing/README.md
index e0a445920b4..29da321ba46 100644
--- a/doc/workflow/importing/README.md
+++ b/doc/workflow/importing/README.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/import/index.md'
+---
+
This document was moved to [another location](../../user/project/import/index.md).
diff --git a/doc/workflow/importing/import_projects_from_bitbucket.md b/doc/workflow/importing/import_projects_from_bitbucket.md
index ec9a11f390e..a42ba7d4518 100644
--- a/doc/workflow/importing/import_projects_from_bitbucket.md
+++ b/doc/workflow/importing/import_projects_from_bitbucket.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/import/bitbucket.md'
+---
+
This document was moved to [another location](../../user/project/import/bitbucket.md).
diff --git a/doc/workflow/importing/import_projects_from_fogbugz.md b/doc/workflow/importing/import_projects_from_fogbugz.md
index 876eb0434f0..f5c791dc6de 100644
--- a/doc/workflow/importing/import_projects_from_fogbugz.md
+++ b/doc/workflow/importing/import_projects_from_fogbugz.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/import/fogbugz.md'
+---
+
This document was moved to [another location](../../user/project/import/fogbugz.md).
diff --git a/doc/workflow/importing/import_projects_from_gitea.md b/doc/workflow/importing/import_projects_from_gitea.md
index 8b55b6c23eb..df053835b44 100644
--- a/doc/workflow/importing/import_projects_from_gitea.md
+++ b/doc/workflow/importing/import_projects_from_gitea.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/import/gitea.md'
+---
+
This document was moved to [another location](../../user/project/import/gitea.md).
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 72dfe5403c3..6397fcc74b8 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/import/github.md'
+---
+
This document was moved to [another location](../../user/project/import/github.md).
diff --git a/doc/workflow/importing/import_projects_from_gitlab_com.md b/doc/workflow/importing/import_projects_from_gitlab_com.md
index 3256088c014..135b9704df9 100644
--- a/doc/workflow/importing/import_projects_from_gitlab_com.md
+++ b/doc/workflow/importing/import_projects_from_gitlab_com.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/import/gitlab_com.md'
+---
+
This document was moved to [another location](../../user/project/import/gitlab_com.md).
diff --git a/doc/workflow/importing/migrating_from_svn.md b/doc/workflow/importing/migrating_from_svn.md
index 32a75a6c6af..99f13d6354c 100644
--- a/doc/workflow/importing/migrating_from_svn.md
+++ b/doc/workflow/importing/migrating_from_svn.md
@@ -1 +1,5 @@
+---
+redirect_to: '../../user/project/import/svn.md'
+---
+
This document was moved to [another location](../../user/project/import/svn.md).
diff --git a/doc/workflow/labels.md b/doc/workflow/labels.md
index 5c09891dfdd..3d07d411dd4 100644
--- a/doc/workflow/labels.md
+++ b/doc/workflow/labels.md
@@ -1,3 +1,7 @@
+---
+redirect_to: '../user/project/labels.md'
+---
+
# Labels
This document was moved to [user/project/labels.md](../user/project/labels.md).
diff --git a/doc/workflow/merge_requests.md b/doc/workflow/merge_requests.md
index dc6da1938f3..fd9f9b81bc9 100644
--- a/doc/workflow/merge_requests.md
+++ b/doc/workflow/merge_requests.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/merge_requests/index.md'
+---
+
This document was moved to [user/project/merge_requests/index.md](../user/project/merge_requests/index.md).
diff --git a/doc/workflow/merge_when_build_succeeds.md b/doc/workflow/merge_when_build_succeeds.md
index b4f6d6117de..41e6ff0cdd6 100644
--- a/doc/workflow/merge_when_build_succeeds.md
+++ b/doc/workflow/merge_when_build_succeeds.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/merge_requests/merge_when_pipeline_succeeds.md'
+---
+
This document was moved to [merge_when_pipeline_succeeds](../user/project/merge_requests/merge_when_pipeline_succeeds.md).
diff --git a/doc/workflow/milestones.md b/doc/workflow/milestones.md
index 69eb6b286b0..18dc15f7327 100644
--- a/doc/workflow/milestones.md
+++ b/doc/workflow/milestones.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/milestones/index.md'
+---
+
This document was moved to [another location](../user/project/milestones/index.md).
diff --git a/doc/workflow/project_features.md b/doc/workflow/project_features.md
index feb88712f5a..f54afb768a1 100644
--- a/doc/workflow/project_features.md
+++ b/doc/workflow/project_features.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/index.md'
+---
+
This document was moved to [../user/project/index.md](../user/project/index.md)
diff --git a/doc/workflow/protected_branches.md b/doc/workflow/protected_branches.md
index ced7d391ace..1bcac4a2de5 100644
--- a/doc/workflow/protected_branches.md
+++ b/doc/workflow/protected_branches.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/protected_branches.md'
+---
+
This document was moved to [another location](../user/project/protected_branches.md).
diff --git a/doc/workflow/repository_mirroring.md b/doc/workflow/repository_mirroring.md
index 8a2f4e1b40e..ae1624b7dc0 100644
--- a/doc/workflow/repository_mirroring.md
+++ b/doc/workflow/repository_mirroring.md
@@ -408,3 +408,11 @@ settings are recommended:
Perforce Helix.
Read about [Git Fusion settings on Perforce.com](https://www.perforce.com/perforce/doc.current/manuals/git-fusion/Content/Git-Fusion/section_vss_bdw_w3.html#section_zdp_zz1_3l).
+
+## Troubleshooting
+
+Should an error occur during a push, GitLab will display an "Error" highlight for that repository. Details on the error can then be seen by hovering over the highlight text.
+
+### 13:Received RST_STREAM with error code 2 with GitHub
+
+If you receive an "13:Received RST_STREAM with error code 2" while mirroring to a GitHub repository, your GitHub settings might be set to block pushes that expose your email address used in commits. Either set your email address on GitHub to be public, or disable the [Block command line pushes that expose my email](http://github.com/settings/emails) setting.
diff --git a/doc/workflow/revert_changes.md b/doc/workflow/revert_changes.md
index cf1292253fc..15f199af703 100644
--- a/doc/workflow/revert_changes.md
+++ b/doc/workflow/revert_changes.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/merge_requests/revert_changes.md'
+---
+
This document was moved to [user/project/merge_requests/revert_changes](../user/project/merge_requests/revert_changes.md).
diff --git a/doc/workflow/share_projects_with_other_groups.md b/doc/workflow/share_projects_with_other_groups.md
index 2eb4d24958a..c39cd78f32d 100644
--- a/doc/workflow/share_projects_with_other_groups.md
+++ b/doc/workflow/share_projects_with_other_groups.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/members/share_project_with_groups.md'
+---
+
This document was moved to [../user/project/members/share_project_with_groups.md](../user/project/members/share_project_with_groups.md)
diff --git a/doc/workflow/share_with_group.md b/doc/workflow/share_with_group.md
index 2eb4d24958a..c39cd78f32d 100644
--- a/doc/workflow/share_with_group.md
+++ b/doc/workflow/share_with_group.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/members/share_project_with_groups.md'
+---
+
This document was moved to [../user/project/members/share_project_with_groups.md](../user/project/members/share_project_with_groups.md)
diff --git a/doc/workflow/shortcuts.md b/doc/workflow/shortcuts.md
index 7359e1c6119..6ed6b0bda66 100644
--- a/doc/workflow/shortcuts.md
+++ b/doc/workflow/shortcuts.md
@@ -82,6 +82,8 @@ You can see GitLab's keyboard shortcuts by using 'shift + ?'
| <kbd>r</kbd> | Reply (quoting selected text) |
| <kbd>e</kbd> | Edit issue/merge request |
| <kbd>l</kbd> | Change label |
+| <kbd>]</kbd> or <kbd>j</kbd> | Move to next file |
+| <kbd>[</kbd> or <kbd>k</kbd> | Move to previous file |
## Wiki pages
diff --git a/doc/workflow/time_tracking.md b/doc/workflow/time_tracking.md
index ad43189148c..e60b6819bf1 100644
--- a/doc/workflow/time_tracking.md
+++ b/doc/workflow/time_tracking.md
@@ -11,7 +11,7 @@ Time Tracking lets you:
- Record the time spent working on an issue or a merge request.
- Add an estimate of the amount of time needed to complete an issue or a merge
-request.
+ request.
You don't have to indicate an estimate to enter the time spent, and vice versa.
diff --git a/doc/workflow/web_editor.md b/doc/workflow/web_editor.md
index 595c7da155b..2366372d984 100644
--- a/doc/workflow/web_editor.md
+++ b/doc/workflow/web_editor.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/repository/web_editor.md'
+---
+
This document was moved to [user/project/repository/web_editor](../user/project/repository/web_editor.md).
diff --git a/doc/workflow/wip_merge_requests.md b/doc/workflow/wip_merge_requests.md
index abb8002f442..020455dcbdc 100644
--- a/doc/workflow/wip_merge_requests.md
+++ b/doc/workflow/wip_merge_requests.md
@@ -1 +1,5 @@
+---
+redirect_to: '../user/project/merge_requests/work_in_progress_merge_requests.md'
+---
+
This document was moved to [user/project/merge_requests/work_in_progress_merge_requests](../user/project/merge_requests/work_in_progress_merge_requests.md).
diff --git a/jest.config.js b/jest.config.js
index 4dab7c2891a..cd0d311779d 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -13,11 +13,14 @@ if (process.env.CI) {
// eslint-disable-next-line import/no-commonjs
module.exports = {
- testMatch: ['<rootDir>/spec/frontend/**/*_spec.js'],
+ testMatch: ['<rootDir>/spec/frontend/**/*_spec.js', '<rootDir>/ee/spec/frontend/**/*_spec.js'],
moduleFileExtensions: ['js', 'json', 'vue'],
moduleNameMapper: {
'^~(.*)$': '<rootDir>/app/assets/javascripts$1',
+ '^ee(.*)$': '<rootDir>/ee/app/assets/javascripts$1',
'^helpers(.*)$': '<rootDir>/spec/frontend/helpers$1',
+ '^vendor(.*)$': '<rootDir>/vendor/assets/javascripts$1',
+ '\\.(jpg|jpeg|png|svg)$': '<rootDir>/spec/frontend/__mocks__/file_mock.js',
},
collectCoverageFrom: ['<rootDir>/app/assets/javascripts/**/*.{js,vue}'],
coverageDirectory: '<rootDir>/coverage-frontend/',
@@ -25,10 +28,14 @@ module.exports = {
cacheDirectory: '<rootDir>/tmp/cache/jest',
modulePathIgnorePatterns: ['<rootDir>/.yarn-cache/'],
reporters,
- setupTestFrameworkScriptFile: '<rootDir>/spec/frontend/test_setup.js',
+ setupFilesAfterEnv: ['<rootDir>/spec/frontend/test_setup.js'],
restoreMocks: true,
transform: {
+ '^.+\\.(gql|graphql)$': 'jest-transform-graphql',
'^.+\\.js$': 'babel-jest',
'^.+\\.vue$': 'vue-jest',
},
+ transformIgnorePatterns: ['node_modules/(?!(@gitlab/ui)/)'],
+ timers: 'fake',
+ testEnvironment: '<rootDir>/spec/frontend/environment.js',
};
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 4dd1b459554..bf8ddba6f0d 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -141,6 +141,7 @@ module API
mount ::API::Projects
mount ::API::ProjectSnapshots
mount ::API::ProjectSnippets
+ mount ::API::ProjectStatistics
mount ::API::ProjectTemplates
mount ::API::ProtectedBranches
mount ::API::ProtectedTags
diff --git a/lib/api/commits.rb b/lib/api/commits.rb
index 8defc59224d..65eb9bfb87e 100644
--- a/lib/api/commits.rb
+++ b/lib/api/commits.rb
@@ -99,6 +99,7 @@ module API
optional :author_email, type: String, desc: 'Author email for commit'
optional :author_name, type: String, desc: 'Author name for commit'
optional :stats, type: Boolean, default: true, desc: 'Include commit stats'
+ optional :force, type: Boolean, default: false, desc: 'When `true` overwrites the target branch with a new commit based on the `start_branch`'
end
post ':id/repository/commits' do
authorize_push_to_branch!(params[:branch])
@@ -318,10 +319,18 @@ module API
use :pagination
end
get ':id/repository/commits/:sha/merge_requests', requirements: API::COMMIT_ENDPOINT_REQUIREMENTS do
+ authorize! :read_merge_request, user_project
+
commit = user_project.commit(params[:sha])
not_found! 'Commit' unless commit
- present paginate(commit.merge_requests), with: Entities::MergeRequestBasic
+ commit_merge_requests = MergeRequestsFinder.new(
+ current_user,
+ project_id: user_project.id,
+ commit_sha: commit.sha
+ ).execute
+
+ present paginate(commit_merge_requests), with: Entities::MergeRequestBasic
end
desc "Get a commit's GPG signature" do
diff --git a/lib/api/discussions.rb b/lib/api/discussions.rb
index 91eb6a23701..8afe6dda414 100644
--- a/lib/api/discussions.rb
+++ b/lib/api/discussions.rb
@@ -7,9 +7,7 @@ module API
before { authenticate! }
- NOTEABLE_TYPES = [Issue, Snippet, MergeRequest, Commit].freeze
-
- NOTEABLE_TYPES.each do |noteable_type|
+ Helpers::DiscussionsHelpers.noteable_types.each do |noteable_type|
parent_type = noteable_type.parent_class.to_s.underscore
noteables_str = noteable_type.to_s.underscore.pluralize
noteables_path = noteable_type == Commit ? "repository/#{noteables_str}" : noteables_str
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9199f898ea0..0871ea8d21e 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -86,6 +86,10 @@ module API
expose :admin?, as: :is_admin
end
+ class UserDetailsWithAdmin < UserWithAdmin
+ expose :highest_role
+ end
+
class UserStatus < Grape::Entity
expose :emoji
expose :message
@@ -156,7 +160,7 @@ module API
class BasicProjectDetails < ProjectIdentity
include ::API::ProjectsRelationBuilder
- expose :default_branch
+ expose :default_branch, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
# Avoids an N+1 query: https://github.com/mbleigh/acts-as-taggable-on/issues/91#issuecomment-168273770
expose :tag_list do |project|
# project.tags.order(:name).pluck(:name) is the most suitable option
@@ -261,7 +265,7 @@ module API
expose :open_issues_count, if: lambda { |project, options| project.feature_available?(:issues, options[:current_user]) }
expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
expose :public_builds, as: :public_jobs
- expose :ci_config_path
+ expose :ci_config_path, if: -> (project, options) { Ability.allowed?(options[:current_user], :download_code, project) }
expose :shared_with_groups do |project, options|
SharedGroup.represent(project.project_group_links, options)
end
@@ -270,8 +274,9 @@ module API
expose :only_allow_merge_if_all_discussions_are_resolved
expose :printing_merge_request_link_enabled
expose :merge_method
-
- expose :statistics, using: 'API::Entities::ProjectStatistics', if: :statistics
+ expose :statistics, using: 'API::Entities::ProjectStatistics', if: -> (project, options) {
+ options[:statistics] && Ability.allowed?(options[:current_user], :read_statistics, project)
+ }
# rubocop: disable CodeReuse/ActiveRecord
def self.preload_relation(projects_relation, options = {})
@@ -300,6 +305,18 @@ module API
expose :build_artifacts_size, as: :job_artifacts_size
end
+ class ProjectDailyFetches < Grape::Entity
+ expose :fetch_count, as: :count
+ expose :date
+ end
+
+ class ProjectDailyStatistics < Grape::Entity
+ expose :fetches do
+ expose :total_fetch_count, as: :total
+ expose :fetches, as: :days, using: ProjectDailyFetches
+ end
+ end
+
class Member < Grape::Entity
expose :user, merge: true, using: UserBasic
expose :access_level
@@ -473,6 +490,12 @@ module API
expose(:project_id) { |entity| entity&.project.try(:id) }
expose :title, :description
expose :state, :created_at, :updated_at
+
+ # Avoids an N+1 query when metadata is included
+ def issuable_metadata(subject, options, method)
+ cached_subject = options.dig(:issuable_metadata, subject.id)
+ (cached_subject || subject).public_send(method) # rubocop: disable GitlabSecurity/PublicSend
+ end
end
class Diff < Grape::Entity
@@ -518,54 +541,32 @@ module API
class IssueBasic < ProjectEntity
expose :closed_at
expose :closed_by, using: Entities::UserBasic
- expose :labels do |issue, options|
+ expose :labels do |issue|
# Avoids an N+1 query since labels are preloaded
issue.labels.map(&:title).sort
end
expose :milestone, using: Entities::Milestone
expose :assignees, :author, using: Entities::UserBasic
- expose :assignee, using: ::API::Entities::UserBasic do |issue, options|
+ expose :assignee, using: ::API::Entities::UserBasic do |issue|
issue.assignees.first
end
- expose :user_notes_count
- expose :upvotes do |issue, options|
- if options[:issuable_metadata]
- # Avoids an N+1 query when metadata is included
- options[:issuable_metadata][issue.id].upvotes
- else
- issue.upvotes
- end
- end
- expose :downvotes do |issue, options|
- if options[:issuable_metadata]
- # Avoids an N+1 query when metadata is included
- options[:issuable_metadata][issue.id].downvotes
- else
- issue.downvotes
- end
- end
+ expose(:user_notes_count) { |issue, options| issuable_metadata(issue, options, :user_notes_count) }
+ expose(:merge_requests_count) { |issue, options| issuable_metadata(issue, options, :merge_requests_count) }
+ expose(:upvotes) { |issue, options| issuable_metadata(issue, options, :upvotes) }
+ expose(:downvotes) { |issue, options| issuable_metadata(issue, options, :downvotes) }
expose :due_date
expose :confidential
expose :discussion_locked
- expose :web_url do |issue, options|
+ expose :web_url do |issue|
Gitlab::UrlBuilder.build(issue)
end
expose :time_stats, using: 'API::Entities::IssuableTimeStats' do |issue|
issue
end
-
- expose :merge_requests_count do |issue, options|
- if options[:issuable_metadata]
- # Avoids an N+1 query when metadata is included
- options[:issuable_metadata][issue.id].merge_requests_count
- else
- issue.merge_requests_closing_issues.count
- end
- end
end
class Issue < IssueBasic
@@ -659,23 +660,12 @@ module API
MarkupHelper.markdown_field(entity, :description)
end
expose :target_branch, :source_branch
- expose :upvotes do |merge_request, options|
- if options[:issuable_metadata]
- options[:issuable_metadata][merge_request.id].upvotes
- else
- merge_request.upvotes
- end
- end
- expose :downvotes do |merge_request, options|
- if options[:issuable_metadata]
- options[:issuable_metadata][merge_request.id].downvotes
- else
- merge_request.downvotes
- end
- end
+ expose(:user_notes_count) { |merge_request, options| issuable_metadata(merge_request, options, :user_notes_count) }
+ expose(:upvotes) { |merge_request, options| issuable_metadata(merge_request, options, :upvotes) }
+ expose(:downvotes) { |merge_request, options| issuable_metadata(merge_request, options, :downvotes) }
expose :author, :assignee, using: Entities::UserBasic
expose :source_project_id, :target_project_id
- expose :labels do |merge_request, options|
+ expose :labels do |merge_request|
# Avoids an N+1 query since labels are preloaded
merge_request.labels.map(&:title).sort
end
@@ -693,7 +683,6 @@ module API
end
expose :diff_head_sha, as: :sha
expose :merge_commit_sha
- expose :user_notes_count
expose :discussion_locked
expose :should_remove_source_branch?, as: :should_remove_source_branch
expose :force_remove_source_branch?, as: :force_remove_source_branch
@@ -701,7 +690,7 @@ module API
# Deprecated
expose :allow_collaboration, as: :allow_maintainer_to_push, if: -> (merge_request, _) { merge_request.for_fork? }
- expose :web_url do |merge_request, options|
+ expose :web_url do |merge_request|
Gitlab::UrlBuilder.build(merge_request)
end
@@ -898,7 +887,8 @@ module API
expose :target_type
expose :target do |todo, options|
- todo_target_class(todo.target_type).represent(todo.target, options)
+ todo_options = options.fetch(todo.target_type, {})
+ todo_target_class(todo.target_type).represent(todo.target, todo_options)
end
expose :target_url do |todo, options|
@@ -1385,13 +1375,9 @@ module API
class GitInfo < Grape::Entity
expose :repo_url, :ref, :sha, :before_sha
- expose :ref_type do |model|
- if model.tag
- 'tag'
- else
- 'branch'
- end
- end
+ expose :ref_type
+ expose :refspecs
+ expose :git_depth, as: :depth
end
class RunnerInfo < Grape::Entity
@@ -1572,8 +1558,6 @@ module API
class Suggestion < Grape::Entity
expose :id
- expose :from_original_line
- expose :to_original_line
expose :from_line
expose :to_line
expose :appliable?, as: :appliable
diff --git a/lib/api/environments.rb b/lib/api/environments.rb
index 0278c6c54a5..5b0f3b914cb 100644
--- a/lib/api/environments.rb
+++ b/lib/api/environments.rb
@@ -22,7 +22,7 @@ module API
get ':id/environments' do
authorize! :read_environment, user_project
- present paginate(user_project.environments), with: Entities::Environment
+ present paginate(user_project.environments), with: Entities::Environment, current_user: current_user
end
desc 'Creates a new environment' do
@@ -40,7 +40,7 @@ module API
environment = user_project.environments.create(declared_params)
if environment.persisted?
- present environment, with: Entities::Environment
+ present environment, with: Entities::Environment, current_user: current_user
else
render_validation_error!(environment)
end
@@ -63,7 +63,7 @@ module API
update_params = declared_params(include_missing: false).extract!(:name, :external_url)
if environment.update(update_params)
- present environment, with: Entities::Environment
+ present environment, with: Entities::Environment, current_user: current_user
else
render_validation_error!(environment)
end
@@ -99,7 +99,7 @@ module API
environment.stop_with_action!(current_user)
status 200
- present environment, with: Entities::Environment
+ present environment, with: Entities::Environment, current_user: current_user
end
end
end
diff --git a/lib/api/groups.rb b/lib/api/groups.rb
index 64958ff982a..9fcf476f537 100644
--- a/lib/api/groups.rb
+++ b/lib/api/groups.rb
@@ -20,8 +20,19 @@ module API
optional :share_with_group_lock, type: Boolean, desc: 'Prevent sharing a project with another group within this group'
end
+ if Gitlab.ee?
+ params :optional_params_ee do
+ optional :membership_lock, type: Boolean, desc: 'Prevent adding new members to project membership within this group'
+ optional :ldap_cn, type: String, desc: 'LDAP Common Name'
+ optional :ldap_access, type: Integer, desc: 'A valid access level'
+ optional :shared_runners_minutes_limit, type: Integer, desc: '(admin-only) Pipeline minutes quota for this group'
+ all_or_none_of :ldap_cn, :ldap_access
+ end
+ end
+
params :optional_params do
use :optional_params_ce
+ use :optional_params_ee if Gitlab.ee?
end
params :statistics_params do
@@ -58,6 +69,22 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ def create_group
+ # This is a separate method so that EE can extend its behaviour, without
+ # having to modify this code directly.
+ ::Groups::CreateService
+ .new(current_user, declared_params(include_missing: false))
+ .execute
+ end
+
+ def update_group(group)
+ # This is a separate method so that EE can extend its behaviour, without
+ # having to modify this code directly.
+ ::Groups::UpdateService
+ .new(group, current_user, declared_params(include_missing: false))
+ .execute
+ end
+
def find_group_projects(params)
group = find_group!(params[:id])
options = {
@@ -127,7 +154,7 @@ module API
authorize! :create_group
end
- group = ::Groups::CreateService.new(current_user, declared_params(include_missing: false)).execute
+ group = create_group
if group.persisted?
present group, with: Entities::GroupDetail, current_user: current_user
@@ -148,12 +175,16 @@ module API
optional :name, type: String, desc: 'The name of the group'
optional :path, type: String, desc: 'The path of the group'
use :optional_params
+
+ if Gitlab.ee?
+ optional :file_template_project_id, type: Integer, desc: 'The ID of a project to use for custom templates in this group'
+ end
end
put ':id' do
group = find_group!(params[:id])
authorize! :admin_group, group
- if ::Groups::UpdateService.new(group, current_user, declared_params(include_missing: false)).execute
+ if update_group(group)
present group, with: Entities::GroupDetail, current_user: current_user
else
render_validation_error!(group)
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 54cd4cd9cdb..8a21d44b4bf 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -6,6 +6,7 @@ module API
include Helpers::Pagination
SUDO_HEADER = "HTTP_SUDO".freeze
+ GITLAB_SHARED_SECRET_HEADER = "Gitlab-Shared-Secret".freeze
SUDO_PARAM = :sudo
API_USER_ENV = 'gitlab.api.user'.freeze
@@ -212,10 +213,12 @@ module API
end
def authenticate_by_gitlab_shell_token!
- input = params['secret_token'].try(:chomp)
- unless Devise.secure_compare(secret_token, input)
- unauthorized!
- end
+ input = params['secret_token']
+ input ||= Base64.decode64(headers[GITLAB_SHARED_SECRET_HEADER]) if headers.key?(GITLAB_SHARED_SECRET_HEADER)
+
+ input&.chomp!
+
+ unauthorized! unless Devise.secure_compare(secret_token, input)
end
def authenticated_with_full_private_access!
@@ -244,6 +247,10 @@ module API
authorize! :read_build, user_project
end
+ def authorize_destroy_artifacts!
+ authorize! :destroy_artifacts, user_project
+ end
+
def authorize_update_builds!
authorize! :update_build, user_project
end
@@ -295,6 +302,12 @@ module API
end
# rubocop: enable CodeReuse/ActiveRecord
+ # rubocop: disable CodeReuse/ActiveRecord
+ def filter_by_title(items, title)
+ items.where(title: title)
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
def filter_by_search(items, text)
items.search(text)
end
diff --git a/lib/api/helpers/custom_validators.rb b/lib/api/helpers/custom_validators.rb
index 1058f4e8a5e..c86eae6f2da 100644
--- a/lib/api/helpers/custom_validators.rb
+++ b/lib/api/helpers/custom_validators.rb
@@ -22,9 +22,22 @@ module API
message: "should be an integer, 'None' or 'Any'"
end
end
+
+ class ArrayNoneAny < Grape::Validations::Base
+ def validate_param!(attr_name, params)
+ value = params[attr_name]
+
+ return if value.is_a?(Array) ||
+ [IssuableFinder::FILTER_NONE, IssuableFinder::FILTER_ANY].include?(value.to_s.downcase)
+
+ raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
+ message: "should be an array, 'None' or 'Any'"
+ end
+ end
end
end
end
Grape::Validations.register_validator(:absence, ::API::Helpers::CustomValidators::Absence)
Grape::Validations.register_validator(:integer_none_any, ::API::Helpers::CustomValidators::IntegerNoneAny)
+Grape::Validations.register_validator(:array_none_any, ::API::Helpers::CustomValidators::ArrayNoneAny)
diff --git a/lib/api/helpers/discussions_helpers.rb b/lib/api/helpers/discussions_helpers.rb
new file mode 100644
index 00000000000..94a5bf75c39
--- /dev/null
+++ b/lib/api/helpers/discussions_helpers.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module DiscussionsHelpers
+ def self.noteable_types
+ # This is a method instead of a constant, allowing EE to more easily
+ # extend it.
+ [Issue, Snippet, MergeRequest, Commit]
+ end
+ end
+ end
+end
diff --git a/lib/api/helpers/graphql_helpers.rb b/lib/api/helpers/graphql_helpers.rb
new file mode 100644
index 00000000000..94010ab1bc2
--- /dev/null
+++ b/lib/api/helpers/graphql_helpers.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ # GraphqlHelpers is used by the REST API when it is acting like a client
+ # against the graphql API. Helper code for the graphql server implementation
+ # should be in app/graphql/ or lib/gitlab/graphql/
+ module GraphqlHelpers
+ def conditionally_graphql!(fallback:, query:, context: {}, transform: nil)
+ return fallback.call unless Feature.enabled?(:graphql)
+
+ result = GitlabSchema.execute(query, context: context)
+
+ if transform
+ transform.call(result)
+ else
+ result
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/helpers/internal_helpers.rb b/lib/api/helpers/internal_helpers.rb
index fe78049af87..3fd824877ae 100644
--- a/lib/api/helpers/internal_helpers.rb
+++ b/lib/api/helpers/internal_helpers.rb
@@ -5,9 +5,11 @@ module API
module InternalHelpers
attr_reader :redirected_path
- def wiki?
- set_project unless defined?(@wiki) # rubocop:disable Gitlab/ModuleWithInstanceVariables
- @wiki # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ delegate :wiki?, to: :repo_type
+
+ def repo_type
+ set_project unless defined?(@repo_type) # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ @repo_type # rubocop:disable Gitlab/ModuleWithInstanceVariables
end
def project
@@ -67,10 +69,10 @@ module API
# rubocop:disable Gitlab/ModuleWithInstanceVariables
def set_project
if params[:gl_repository]
- @project, @wiki = Gitlab::GlRepository.parse(params[:gl_repository])
+ @project, @repo_type = Gitlab::GlRepository.parse(params[:gl_repository])
@redirected_path = nil
else
- @project, @wiki, @redirected_path = Gitlab::RepoPath.parse(params[:project])
+ @project, @repo_type, @redirected_path = Gitlab::RepoPath.parse(params[:project])
end
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
@@ -78,7 +80,7 @@ module API
# Project id to pass between components that don't share/don't have
# access to the same filesystem mounts
def gl_repository
- Gitlab::GlRepository.gl_repository(project, wiki?)
+ repo_type.identifier_for_subject(project)
end
def gl_project_path
@@ -92,7 +94,7 @@ module API
# Return the repository depending on whether we want the wiki or the
# regular repository
def repository
- if wiki?
+ if repo_type.wiki?
project.wiki.repository
else
project.repository
diff --git a/lib/api/helpers/issues_helpers.rb b/lib/api/helpers/issues_helpers.rb
new file mode 100644
index 00000000000..f6762910b0c
--- /dev/null
+++ b/lib/api/helpers/issues_helpers.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module IssuesHelpers
+ def self.update_params_at_least_one_of
+ [
+ :assignee_id,
+ :assignee_ids,
+ :confidential,
+ :created_at,
+ :description,
+ :discussion_locked,
+ :due_date,
+ :labels,
+ :milestone_id,
+ :state_event,
+ :title
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/api/helpers/notes_helpers.rb b/lib/api/helpers/notes_helpers.rb
index 216b2c45741..a068de4361c 100644
--- a/lib/api/helpers/notes_helpers.rb
+++ b/lib/api/helpers/notes_helpers.rb
@@ -3,6 +3,12 @@
module API
module Helpers
module NotesHelpers
+ def self.noteable_types
+ # This is a method instead of a constant, allowing EE to more easily
+ # extend it.
+ [Issue, MergeRequest, Snippet]
+ end
+
def update_note(noteable, note_id)
note = noteable.notes.find(params[:note_id])
@@ -70,14 +76,7 @@ module API
def find_noteable(parent, noteables_str, noteable_id)
noteable = public_send("find_#{parent}_#{noteables_str.singularize}", noteable_id) # rubocop:disable GitlabSecurity/PublicSend
- readable =
- if noteable.is_a?(Commit)
- # for commits there is not :read_commit policy, check if user
- # has :read_note permission on the commit's project
- can?(current_user, :read_note, user_project)
- else
- can?(current_user, noteable_read_ability_name(noteable), noteable)
- end
+ readable = can?(current_user, noteable_read_ability_name(noteable), noteable)
return not_found!(noteables_str) unless readable
@@ -89,12 +88,11 @@ module API
end
def create_note(noteable, opts)
- policy_object = noteable.is_a?(Commit) ? user_project : noteable
- authorize!(:create_note, policy_object)
+ authorize!(:create_note, noteable)
parent = noteable_parent(noteable)
- opts.delete(:created_at) unless current_user.can?(:set_note_created_at, policy_object)
+ opts.delete(:created_at) unless current_user.can?(:set_note_created_at, noteable)
opts[:updated_at] = opts[:created_at] if opts[:created_at]
diff --git a/lib/api/helpers/pagination.rb b/lib/api/helpers/pagination.rb
index de59c915d66..94b58a64d26 100644
--- a/lib/api/helpers/pagination.rb
+++ b/lib/api/helpers/pagination.rb
@@ -13,6 +13,33 @@ module API
strategy.new(self).paginate(relation)
end
+ class Base
+ private
+
+ def per_page
+ @per_page ||= params[:per_page]
+ end
+
+ def base_request_uri
+ @base_request_uri ||= URI.parse(request.url).tap do |uri|
+ uri.host = Gitlab.config.gitlab.host
+ uri.port = nil
+ end
+ end
+
+ def build_page_url(query_params:)
+ base_request_uri.tap do |uri|
+ uri.query = query_params
+ end.to_s
+ end
+
+ def page_href(next_page_params = {})
+ query_params = params.merge(**next_page_params, per_page: per_page).to_query
+
+ build_page_url(query_params: query_params)
+ end
+ end
+
class KeysetPaginationInfo
attr_reader :relation, :request_context
@@ -85,7 +112,7 @@ module API
end
end
- class KeysetPaginationStrategy
+ class KeysetPaginationStrategy < Base
attr_reader :request_context
delegate :params, :header, :request, to: :request_context
@@ -122,7 +149,7 @@ module API
def conditions(pagination)
fields = pagination.fields
- return nil if fields.empty?
+ return if fields.empty?
placeholder = fields.map { '?' }
@@ -141,10 +168,6 @@ module API
]
end
- def per_page
- params[:per_page]
- end
-
def add_default_pagination_headers
header 'X-Per-Page', per_page.to_s
end
@@ -154,22 +177,12 @@ module API
header 'Link', link_for('next', next_page_params)
end
- def page_href(next_page_params)
- request_url = request.url.split('?').first
- request_params = params.dup
- request_params[:per_page] = per_page
-
- request_params.merge!(next_page_params) if next_page_params
-
- "#{request_url}?#{request_params.to_query}"
- end
-
def link_for(rel, next_page_params)
%(<#{page_href(next_page_params)}>; rel="#{rel}")
end
end
- class DefaultPaginationStrategy
+ class DefaultPaginationStrategy < Base
attr_reader :request_context
delegate :params, :header, :request, to: :request_context
@@ -198,15 +211,13 @@ module API
end
end
- # rubocop: disable CodeReuse/ActiveRecord
def add_default_order(relation)
if relation.is_a?(ActiveRecord::Relation) && relation.order_values.empty?
- relation = relation.order(:id)
+ relation = relation.order(:id) # rubocop: disable CodeReuse/ActiveRecord
end
relation
end
- # rubocop: enable CodeReuse/ActiveRecord
def add_pagination_headers(paginated_data)
header 'X-Per-Page', paginated_data.limit_value.to_s
@@ -222,27 +233,13 @@ module API
end
def pagination_links(paginated_data)
- request_url = request.url.split('?').first
- request_params = params.clone
- request_params[:per_page] = paginated_data.limit_value
-
- links = []
-
- request_params[:page] = paginated_data.prev_page
- links << %(<#{request_url}?#{request_params.to_query}>; rel="prev") if request_params[:page]
-
- request_params[:page] = paginated_data.next_page
- links << %(<#{request_url}?#{request_params.to_query}>; rel="next") if request_params[:page]
-
- request_params[:page] = 1
- links << %(<#{request_url}?#{request_params.to_query}>; rel="first")
-
- unless data_without_counts?(paginated_data)
- request_params[:page] = total_pages(paginated_data)
- links << %(<#{request_url}?#{request_params.to_query}>; rel="last")
- end
+ [].tap do |links|
+ links << %(<#{page_href(page: paginated_data.prev_page)}>; rel="prev") if paginated_data.prev_page
+ links << %(<#{page_href(page: paginated_data.next_page)}>; rel="next") if paginated_data.next_page
+ links << %(<#{page_href(page: 1)}>; rel="first")
- links.join(', ')
+ links << %(<#{page_href(page: total_pages(paginated_data))}>; rel="last") unless data_without_counts?(paginated_data)
+ end.join(', ')
end
def total_pages(paginated_data)
diff --git a/lib/api/helpers/projects_helpers.rb b/lib/api/helpers/projects_helpers.rb
index e6a72b949f9..7b858dc2e72 100644
--- a/lib/api/helpers/projects_helpers.rb
+++ b/lib/api/helpers/projects_helpers.rb
@@ -31,11 +31,50 @@ module API
optional :initialize_with_readme, type: Boolean, desc: "Initialize a project with a README.md"
end
+ if Gitlab.ee?
+ params :optional_project_params_ee do
+ optional :repository_storage, type: String, desc: 'Which storage shard the repository is on. Available only to admins'
+ optional :approvals_before_merge, type: Integer, desc: 'How many approvers should approve merge request by default'
+ optional :external_authorization_classification_label, type: String, desc: 'The classification label for the project'
+ optional :mirror, type: Boolean, desc: 'Enables pull mirroring in a project'
+ optional :mirror_trigger_builds, type: Boolean, desc: 'Pull mirroring triggers builds'
+ end
+ end
+
params :optional_project_params do
use :optional_project_params_ce
+ use :optional_project_params_ee if Gitlab.ee?
end
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
end
end
end
diff --git a/lib/api/helpers/resource_label_events_helpers.rb b/lib/api/helpers/resource_label_events_helpers.rb
new file mode 100644
index 00000000000..23574deb59b
--- /dev/null
+++ b/lib/api/helpers/resource_label_events_helpers.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module ResourceLabelEventsHelpers
+ def self.eventable_types
+ # This is a method instead of a constant, allowing EE to more easily
+ # extend it.
+ [Issue, MergeRequest]
+ end
+ end
+ end
+end
diff --git a/lib/api/helpers/runner.rb b/lib/api/helpers/runner.rb
index 16df8e830e1..ff73a49d5e8 100644
--- a/lib/api/helpers/runner.rb
+++ b/lib/api/helpers/runner.rb
@@ -26,7 +26,7 @@ module API
end
def get_runner_ip
- { ip_address: request.env["HTTP_X_FORWARDED_FOR"] || request.ip }
+ { ip_address: env["action_dispatch.remote_ip"].to_s || request.ip }
end
def current_runner
diff --git a/lib/api/helpers/search_helpers.rb b/lib/api/helpers/search_helpers.rb
new file mode 100644
index 00000000000..0e052e0e273
--- /dev/null
+++ b/lib/api/helpers/search_helpers.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ module SearchHelpers
+ def self.global_search_scopes
+ # This is a separate method so that EE can redefine it.
+ %w(projects issues merge_requests milestones snippet_titles snippet_blobs users)
+ end
+
+ def self.group_search_scopes
+ # This is a separate method so that EE can redefine it.
+ %w(projects issues merge_requests milestones users)
+ end
+
+ def self.project_search_scopes
+ # This is a separate method so that EE can redefine it.
+ %w(issues merge_requests milestones notes wiki_blobs commits blobs users)
+ end
+ end
+ end
+end
diff --git a/lib/api/helpers/services_helpers.rb b/lib/api/helpers/services_helpers.rb
new file mode 100644
index 00000000000..8582c45798f
--- /dev/null
+++ b/lib/api/helpers/services_helpers.rb
@@ -0,0 +1,721 @@
+# frozen_string_literal: true
+
+module API
+ module Helpers
+ # Helpers module for API::Services
+ #
+ # The data structures inside this model are returned using class methods,
+ # allowing EE to extend them where necessary.
+ module ServicesHelpers
+ def self.chat_notification_settings
+ [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'The chat webhook'
+ },
+ {
+ required: false,
+ name: :username,
+ type: String,
+ desc: 'The chat username'
+ },
+ {
+ required: false,
+ name: :channel,
+ type: String,
+ desc: 'The default chat channel'
+ }
+ ].freeze
+ end
+
+ def self.chat_notification_flags
+ [
+ {
+ required: false,
+ name: :notify_only_broken_pipelines,
+ type: Boolean,
+ desc: 'Send notifications for broken pipelines'
+ },
+ {
+ required: false,
+ name: :notify_only_default_branch,
+ type: Boolean,
+ desc: 'Send notifications only for the default branch'
+ }
+ ].freeze
+ end
+
+ def self.chat_notification_channels
+ [
+ {
+ required: false,
+ name: :push_channel,
+ type: String,
+ desc: 'The name of the channel to receive push_events notifications'
+ },
+ {
+ required: false,
+ name: :issue_channel,
+ type: String,
+ desc: 'The name of the channel to receive issues_events notifications'
+ },
+ {
+ required: false,
+ name: :confidential_issue_channel,
+ type: String,
+ desc: 'The name of the channel to receive confidential_issues_events notifications'
+ },
+ {
+ required: false,
+ name: :merge_request_channel,
+ type: String,
+ desc: 'The name of the channel to receive merge_requests_events notifications'
+ },
+ {
+ required: false,
+ name: :note_channel,
+ type: String,
+ desc: 'The name of the channel to receive note_events notifications'
+ },
+ {
+ required: false,
+ name: :tag_push_channel,
+ type: String,
+ desc: 'The name of the channel to receive tag_push_events notifications'
+ },
+ {
+ required: false,
+ name: :pipeline_channel,
+ type: String,
+ desc: 'The name of the channel to receive pipeline_events notifications'
+ },
+ {
+ required: false,
+ name: :wiki_page_channel,
+ type: String,
+ desc: 'The name of the channel to receive wiki_page_events notifications'
+ }
+ ].freeze
+ end
+
+ def self.chat_notification_events
+ [
+ {
+ required: false,
+ name: :push_events,
+ type: Boolean,
+ desc: 'Enable notifications for push_events'
+ },
+ {
+ required: false,
+ name: :issues_events,
+ type: Boolean,
+ desc: 'Enable notifications for issues_events'
+ },
+ {
+ required: false,
+ name: :confidential_issues_events,
+ type: Boolean,
+ desc: 'Enable notifications for confidential_issues_events'
+ },
+ {
+ required: false,
+ name: :merge_requests_events,
+ type: Boolean,
+ desc: 'Enable notifications for merge_requests_events'
+ },
+ {
+ required: false,
+ name: :note_events,
+ type: Boolean,
+ desc: 'Enable notifications for note_events'
+ },
+ {
+ required: false,
+ name: :tag_push_events,
+ type: Boolean,
+ desc: 'Enable notifications for tag_push_events'
+ },
+ {
+ required: false,
+ name: :pipeline_events,
+ type: Boolean,
+ desc: 'Enable notifications for pipeline_events'
+ },
+ {
+ required: false,
+ name: :wiki_page_events,
+ type: Boolean,
+ desc: 'Enable notifications for wiki_page_events'
+ }
+ ].freeze
+ end
+
+ def self.services
+ {
+ 'asana' => [
+ {
+ required: true,
+ name: :api_key,
+ type: String,
+ desc: 'User API token'
+ },
+ {
+ required: false,
+ name: :restrict_to_branch,
+ type: String,
+ desc: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches'
+ }
+ ],
+ 'assembla' => [
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'The authentication token'
+ },
+ {
+ required: false,
+ name: :subdomain,
+ type: String,
+ desc: 'Subdomain setting'
+ }
+ ],
+ 'bamboo' => [
+ {
+ required: true,
+ name: :bamboo_url,
+ type: String,
+ desc: 'Bamboo root URL like https://bamboo.example.com'
+ },
+ {
+ required: true,
+ name: :build_key,
+ type: String,
+ desc: 'Bamboo build plan key like'
+ },
+ {
+ required: true,
+ name: :username,
+ type: String,
+ desc: 'A user with API access, if applicable'
+ },
+ {
+ required: true,
+ name: :password,
+ type: String,
+ desc: 'Password of the user'
+ }
+ ],
+ 'bugzilla' => [
+ {
+ required: true,
+ name: :new_issue_url,
+ type: String,
+ desc: 'New issue URL'
+ },
+ {
+ required: true,
+ name: :issues_url,
+ type: String,
+ desc: 'Issues URL'
+ },
+ {
+ required: true,
+ name: :project_url,
+ type: String,
+ desc: 'Project URL'
+ },
+ {
+ required: false,
+ name: :description,
+ type: String,
+ desc: 'Description'
+ },
+ {
+ required: false,
+ name: :title,
+ type: String,
+ desc: 'Title'
+ }
+ ],
+ 'buildkite' => [
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'Buildkite project GitLab token'
+ },
+ {
+ required: true,
+ name: :project_url,
+ type: String,
+ desc: 'The buildkite project URL'
+ },
+ {
+ required: false,
+ name: :enable_ssl_verification,
+ type: Boolean,
+ desc: 'Enable SSL verification for communication'
+ }
+ ],
+ 'campfire' => [
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'Campfire token'
+ },
+ {
+ required: false,
+ name: :subdomain,
+ type: String,
+ desc: 'Campfire subdomain'
+ },
+ {
+ required: false,
+ name: :room,
+ type: String,
+ desc: 'Campfire room'
+ }
+ ],
+ 'custom-issue-tracker' => [
+ {
+ required: true,
+ name: :new_issue_url,
+ type: String,
+ desc: 'New issue URL'
+ },
+ {
+ required: true,
+ name: :issues_url,
+ type: String,
+ desc: 'Issues URL'
+ },
+ {
+ required: true,
+ name: :project_url,
+ type: String,
+ desc: 'Project URL'
+ },
+ {
+ required: false,
+ name: :description,
+ type: String,
+ desc: 'Description'
+ },
+ {
+ required: false,
+ name: :title,
+ type: String,
+ desc: 'Title'
+ }
+ ],
+ 'discord' => [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'Discord webhook. e.g. https://discordapp.com/api/webhooks/…'
+ }
+ ],
+ 'drone-ci' => [
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'Drone CI token'
+ },
+ {
+ required: true,
+ name: :drone_url,
+ type: String,
+ desc: 'Drone CI URL'
+ },
+ {
+ required: false,
+ name: :enable_ssl_verification,
+ type: Boolean,
+ desc: 'Enable SSL verification for communication'
+ }
+ ],
+ 'emails-on-push' => [
+ {
+ required: true,
+ name: :recipients,
+ type: String,
+ desc: 'Comma-separated list of recipient email addresses'
+ },
+ {
+ required: false,
+ name: :disable_diffs,
+ type: Boolean,
+ desc: 'Disable code diffs'
+ },
+ {
+ required: false,
+ name: :send_from_committer_email,
+ type: Boolean,
+ desc: 'Send from committer'
+ }
+ ],
+ 'external-wiki' => [
+ {
+ required: true,
+ name: :external_wiki_url,
+ type: String,
+ desc: 'The URL of the external Wiki'
+ }
+ ],
+ 'flowdock' => [
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'Flowdock token'
+ }
+ ],
+ 'hangouts-chat' => [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…'
+ },
+ chat_notification_events
+ ].flatten,
+ 'irker' => [
+ {
+ required: true,
+ name: :recipients,
+ type: String,
+ desc: 'Recipients/channels separated by whitespaces'
+ },
+ {
+ required: false,
+ name: :default_irc_uri,
+ type: String,
+ desc: 'Default: irc://irc.network.net:6697'
+ },
+ {
+ required: false,
+ name: :server_host,
+ type: String,
+ desc: 'Server host. Default localhost'
+ },
+ {
+ required: false,
+ name: :server_port,
+ type: Integer,
+ desc: 'Server port. Default 6659'
+ },
+ {
+ required: false,
+ name: :colorize_messages,
+ type: Boolean,
+ desc: 'Colorize messages'
+ }
+ ],
+ 'jira' => [
+ {
+ required: true,
+ name: :url,
+ type: String,
+ desc: 'The base URL to the JIRA instance web interface which is being linked to this GitLab project. E.g., https://jira.example.com'
+ },
+ {
+ required: false,
+ name: :api_url,
+ type: String,
+ desc: 'The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., https://jira-api.example.com'
+ },
+ {
+ required: true,
+ name: :username,
+ type: String,
+ desc: 'The username of the user created to be used with GitLab/JIRA'
+ },
+ {
+ required: true,
+ name: :password,
+ type: String,
+ desc: 'The password of the user created to be used with GitLab/JIRA'
+ },
+ {
+ required: false,
+ name: :jira_issue_transition_id,
+ type: String,
+ desc: 'The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`'
+ }
+ ],
+ 'kubernetes' => [
+ {
+ required: true,
+ name: :namespace,
+ type: String,
+ desc: 'The Kubernetes namespace to use'
+ },
+ {
+ required: true,
+ name: :api_url,
+ type: String,
+ desc: 'The URL to the Kubernetes cluster API, e.g., https://kubernetes.example.com'
+ },
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'The service token to authenticate against the Kubernetes cluster with'
+ },
+ {
+ required: false,
+ name: :ca_pem,
+ type: String,
+ desc: 'A custom certificate authority bundle to verify the Kubernetes cluster with (PEM format)'
+ }
+ ],
+ 'mattermost-slash-commands' => [
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'The Mattermost token'
+ }
+ ],
+ 'slack-slash-commands' => [
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'The Slack token'
+ }
+ ],
+ 'packagist' => [
+ {
+ required: true,
+ name: :username,
+ type: String,
+ desc: 'The username'
+ },
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'The Packagist API token'
+ },
+ {
+ required: false,
+ name: :server,
+ type: String,
+ desc: 'The server'
+ }
+ ],
+ 'pipelines-email' => [
+ {
+ required: true,
+ name: :recipients,
+ type: String,
+ desc: 'Comma-separated list of recipient email addresses'
+ },
+ {
+ required: false,
+ name: :notify_only_broken_pipelines,
+ type: Boolean,
+ desc: 'Notify only broken pipelines'
+ }
+ ],
+ 'pivotaltracker' => [
+ {
+ required: true,
+ name: :token,
+ type: String,
+ desc: 'The Pivotaltracker token'
+ },
+ {
+ required: false,
+ name: :restrict_to_branch,
+ type: String,
+ desc: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.'
+ }
+ ],
+ 'prometheus' => [
+ {
+ required: true,
+ name: :api_url,
+ type: String,
+ desc: 'Prometheus API Base URL, like http://prometheus.example.com/'
+ }
+ ],
+ 'pushover' => [
+ {
+ required: true,
+ name: :api_key,
+ type: String,
+ desc: 'The application key'
+ },
+ {
+ required: true,
+ name: :user_key,
+ type: String,
+ desc: 'The user key'
+ },
+ {
+ required: true,
+ name: :priority,
+ type: String,
+ desc: 'The priority'
+ },
+ {
+ required: true,
+ name: :device,
+ type: String,
+ desc: 'Leave blank for all active devices'
+ },
+ {
+ required: true,
+ name: :sound,
+ type: String,
+ desc: 'The sound of the notification'
+ }
+ ],
+ 'redmine' => [
+ {
+ required: true,
+ name: :new_issue_url,
+ type: String,
+ desc: 'The new issue URL'
+ },
+ {
+ required: true,
+ name: :project_url,
+ type: String,
+ desc: 'The project URL'
+ },
+ {
+ required: true,
+ name: :issues_url,
+ type: String,
+ desc: 'The issues URL'
+ },
+ {
+ required: false,
+ name: :description,
+ type: String,
+ desc: 'The description of the tracker'
+ }
+ ],
+ 'youtrack' => [
+ {
+ required: true,
+ name: :project_url,
+ type: String,
+ desc: 'The project URL'
+ },
+ {
+ required: true,
+ name: :issues_url,
+ type: String,
+ desc: 'The issues URL'
+ },
+ {
+ required: false,
+ name: :description,
+ type: String,
+ desc: 'The description of the tracker'
+ }
+ ],
+ 'slack' => [
+ chat_notification_settings,
+ chat_notification_flags,
+ chat_notification_channels,
+ chat_notification_events
+ ].flatten,
+ 'microsoft-teams' => [
+ {
+ required: true,
+ name: :webhook,
+ type: String,
+ desc: 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
+ }
+ ],
+ 'mattermost' => [
+ chat_notification_settings,
+ chat_notification_flags,
+ chat_notification_channels,
+ chat_notification_events
+ ].flatten,
+ 'teamcity' => [
+ {
+ required: true,
+ name: :teamcity_url,
+ type: String,
+ desc: 'TeamCity root URL like https://teamcity.example.com'
+ },
+ {
+ required: true,
+ name: :build_type,
+ type: String,
+ desc: 'Build configuration ID'
+ },
+ {
+ required: true,
+ name: :username,
+ type: String,
+ desc: 'A user with permissions to trigger a manual build'
+ },
+ {
+ required: true,
+ name: :password,
+ type: String,
+ desc: 'The password of the user'
+ }
+ ]
+ }
+ end
+
+ def self.service_classes
+ [
+ ::AsanaService,
+ ::AssemblaService,
+ ::BambooService,
+ ::BugzillaService,
+ ::BuildkiteService,
+ ::CampfireService,
+ ::CustomIssueTrackerService,
+ ::DiscordService,
+ ::DroneCiService,
+ ::EmailsOnPushService,
+ ::ExternalWikiService,
+ ::FlowdockService,
+ ::HangoutsChatService,
+ ::IrkerService,
+ ::JiraService,
+ ::KubernetesService,
+ ::MattermostSlashCommandsService,
+ ::SlackSlashCommandsService,
+ ::PackagistService,
+ ::PipelinesEmailService,
+ ::PivotaltrackerService,
+ ::PrometheusService,
+ ::PushoverService,
+ ::RedmineService,
+ ::YoutrackService,
+ ::SlackService,
+ ::MattermostService,
+ ::MicrosoftTeamsService,
+ ::TeamcityService
+ ]
+ end
+
+ def self.development_service_classes
+ [
+ ::MockCiService,
+ ::MockDeploymentService,
+ ::MockMonitoringService
+ ]
+ end
+ end
+ end
+end
diff --git a/lib/api/internal.rb b/lib/api/internal.rb
index 70b32f7d758..cb9aa849eeb 100644
--- a/lib/api/internal.rb
+++ b/lib/api/internal.rb
@@ -15,6 +15,12 @@ module API
status code
{ status: success, message: message }.merge(extra_options).compact
end
+
+ def lfs_authentication_url(project)
+ # This is a separate method so that EE can alter its behaviour more
+ # easily.
+ project.http_url_to_repo
+ end
end
namespace 'internal' do
@@ -53,7 +59,7 @@ module API
actor
end
- access_checker_klass = wiki? ? Gitlab::GitAccessWiki : Gitlab::GitAccess
+ access_checker_klass = repo_type.access_checker_class
access_checker = access_checker_klass.new(actor, project,
protocol, authentication_abilities: ssh_authentication_abilities,
namespace_path: namespace_path, project_path: project_path,
@@ -81,11 +87,6 @@ module API
gl_id: Gitlab::GlId.gl_id(user),
gl_username: user&.username,
git_config_options: [],
-
- # This repository_path is a bogus value but gitlab-shell still requires
- # its presence. https://gitlab.com/gitlab-org/gitlab-shell/issues/135
- repository_path: '/',
-
gitaly: gitaly_payload(params[:action])
}
@@ -118,7 +119,9 @@ module API
raise ActiveRecord::RecordNotFound.new("No key_id or user_id passed!")
end
- Gitlab::LfsToken.new(actor).authentication_payload(project.http_url_to_repo)
+ Gitlab::LfsToken
+ .new(actor)
+ .authentication_payload(lfs_authentication_url(project))
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 94ed9ac6fb1..fae20e45bf9 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -8,15 +8,6 @@ module API
helpers ::Gitlab::IssuableMetadata
- # EE::API::Issues would override the following helpers
- helpers do
- params :issues_params_ee do
- end
-
- params :issue_params_ee do
- end
- end
-
helpers do
# rubocop: disable CodeReuse/ActiveRecord
def find_issues(args = {})
@@ -28,13 +19,23 @@ module API
args[:scope] = args[:scope].underscore if args[:scope]
issues = IssuesFinder.new(current_user, args).execute
- .preload(:assignees, :labels, :notes, :timelogs, :project, :author, :closed_by)
+ .with_api_entity_associations
issues.reorder(order_options_with_tie_breaker)
end
# rubocop: enable CodeReuse/ActiveRecord
+ if Gitlab.ee?
+ params :issues_params_ee do
+ optional :weight, types: [Integer, String], integer_none_any: true, desc: 'The weight of the issue'
+ end
+
+ params :issue_params_ee do
+ optional :weight, type: Integer, desc: 'The weight of the issue'
+ end
+ end
+
params :issues_params do
- optional :labels, type: String, desc: 'Comma-separated list of label names'
+ optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :milestone, type: String, desc: 'Milestone title'
optional :order_by, type: String, values: %w[created_at updated_at], default: 'created_at',
desc: 'Return issues ordered by `created_at` or `updated_at` fields.'
@@ -54,9 +55,10 @@ module API
optional :scope, type: String, values: %w[created-by-me assigned-to-me created_by_me assigned_to_me all],
desc: 'Return issues for the given scope: `created_by_me`, `assigned_to_me` or `all`'
optional :my_reaction_emoji, type: String, desc: 'Return issues reacted by the authenticated user by the given emoji'
+ optional :confidential, type: Boolean, desc: 'Filter confidential or public issues'
use :pagination
- use :issues_params_ee
+ use :issues_params_ee if Gitlab.ee?
end
params :issue_params do
@@ -64,12 +66,12 @@ module API
optional :assignee_ids, type: Array[Integer], desc: 'The array of user IDs to assign issue'
optional :assignee_id, type: Integer, desc: '[Deprecated] The ID of a user to assign issue'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign issue'
- optional :labels, type: String, desc: 'Comma-separated list of label names'
+ optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :due_date, type: String, desc: 'Date string in the format YEAR-MONTH-DAY'
optional :confidential, type: Boolean, desc: 'Boolean parameter if the issue should be confidential'
optional :discussion_locked, type: Boolean, desc: " Boolean parameter indicating if the issue's discussion is locked"
- use :issue_params_ee
+ use :issue_params_ee if Gitlab.ee?
end
end
@@ -218,8 +220,8 @@ module API
desc: 'Date time when the issue was updated. Available only for admins and project owners.'
optional :state_event, type: String, values: %w[reopen close], desc: 'State of the issue'
use :issue_params
- at_least_one_of :title, :description, :assignee_ids, :assignee_id, :milestone_id, :discussion_locked,
- :labels, :created_at, :due_date, :confidential, :state_event
+
+ at_least_one_of(*Helpers::IssuesHelpers.update_params_at_least_one_of)
end
# rubocop: disable CodeReuse/ActiveRecord
put ':id/issues/:issue_iid' do
diff --git a/lib/api/job_artifacts.rb b/lib/api/job_artifacts.rb
index 933bd067e26..e7fed55170e 100644
--- a/lib/api/job_artifacts.rb
+++ b/lib/api/job_artifacts.rb
@@ -109,6 +109,22 @@ module API
status 200
present build, with: Entities::Job
end
+
+ desc 'Delete the artifacts files from a job' do
+ detail 'This feature was introduced in GitLab 11.9'
+ end
+ params do
+ requires :job_id, type: Integer, desc: 'The ID of a job'
+ end
+ delete ':id/jobs/:job_id/artifacts' do
+ authorize_destroy_artifacts!
+ build = find_build!(params[:job_id])
+ authorize!(:destroy_artifacts, build)
+
+ build.erase_erasable_artifacts!
+
+ status :no_content
+ end
end
end
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index f8d2ba49d2f..98dcc388f44 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -12,6 +12,9 @@ module API
helpers do
params :optional_params_ee do
end
+
+ params :optional_merge_requests_search_params do
+ end
end
def self.update_params_at_least_one_of
@@ -45,7 +48,7 @@ module API
return merge_requests if args[:view] == 'simple'
merge_requests
- .preload(:notes, :author, :assignee, :milestone, :latest_merge_request_diff, :labels, :timelogs, metrics: [:latest_closed_by, :merged_by])
+ .with_api_entity_associations
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -95,7 +98,7 @@ module API
optional :sort, type: String, values: %w[asc desc], default: 'desc',
desc: 'Return merge requests sorted in `asc` or `desc` order.'
optional :milestone, type: String, desc: 'Return merge requests for a specific milestone'
- optional :labels, type: String, desc: 'Comma-separated list of label names'
+ optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
optional :created_after, type: DateTime, desc: 'Return merge requests created after the specified time'
optional :created_before, type: DateTime, desc: 'Return merge requests created before the specified time'
optional :updated_after, type: DateTime, desc: 'Return merge requests updated after the specified time'
@@ -112,6 +115,8 @@ module API
optional :search, type: String, desc: 'Search merge requests for text present in the title, description, or any combination of these'
optional :in, type: String, desc: '`title`, `description`, or a string joining them with comma'
optional :wip, type: String, values: %w[yes no], desc: 'Search merge requests for WIP in the title'
+
+ use :optional_merge_requests_search_params
use :pagination
end
end
@@ -179,8 +184,8 @@ module API
optional :description, type: String, desc: 'The description of the merge request'
optional :assignee_id, type: Integer, desc: 'The ID of a user to assign the merge request'
optional :milestone_id, type: Integer, desc: 'The ID of a milestone to assign the merge request'
- optional :labels, type: String, desc: 'Comma-separated list of label names'
- optional :remove_source_branch, type: Boolean, desc: 'Delete source branch when merging'
+ optional :labels, type: Array[String], coerce_with: Validations::Types::LabelsList.coerce, desc: 'Comma-separated list of label names'
+ optional :remove_source_branch, type: Boolean, desc: 'Remove source branch when merging'
optional :allow_collaboration, type: Boolean, desc: 'Allow commits from members who can merge to the target branch'
optional :allow_maintainer_to_push, type: Boolean, as: :allow_collaboration, desc: '[deprecated] See allow_collaboration'
optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
@@ -369,11 +374,11 @@ module API
merge_request.update(squash: params[:squash]) if params[:squash]
- merge_params = {
+ merge_params = HashWithIndifferentAccess.new(
commit_message: params[:merge_commit_message],
squash_commit_message: params[:squash_commit_message],
should_remove_source_branch: params[:should_remove_source_branch]
- }
+ )
if merge_when_pipeline_succeeds && merge_request.head_pipeline && merge_request.head_pipeline.active?
::MergeRequests::MergeWhenPipelineSucceedsService
@@ -388,6 +393,31 @@ module API
present merge_request, with: Entities::MergeRequest, current_user: current_user, project: user_project
end
+ desc 'Merge a merge request to its default temporary merge ref path'
+ params do
+ optional :merge_commit_message, type: String, desc: 'Custom merge commit message'
+ end
+ put ':id/merge_requests/:merge_request_iid/merge_to_ref' do
+ merge_request = find_project_merge_request(params[:merge_request_iid])
+
+ authorize! :admin_merge_request, user_project
+
+ merge_params = {
+ commit_message: params[:merge_commit_message]
+ }
+
+ result = ::MergeRequests::MergeToRefService
+ .new(merge_request.target_project, current_user, merge_params)
+ .execute(merge_request)
+
+ if result[:status] == :success
+ present result.slice(:commit_id), 200
+ else
+ http_status = result[:http_status] || 400
+ render_api_error!(result[:message], http_status)
+ end
+ end
+
desc 'Cancel merge if "Merge When Pipeline Succeeds" is enabled' do
success Entities::MergeRequest
end
diff --git a/lib/api/milestone_responses.rb b/lib/api/milestone_responses.rb
index a0ca39b69d4..62e159ab003 100644
--- a/lib/api/milestone_responses.rb
+++ b/lib/api/milestone_responses.rb
@@ -16,6 +16,7 @@ module API
optional :state, type: String, values: %w[active closed all], default: 'all',
desc: 'Return "active", "closed", or "all" milestones'
optional :iids, type: Array[Integer], desc: 'The IIDs of the milestones'
+ optional :title, type: String, desc: 'The title of the milestones'
optional :search, type: String, desc: 'The search criteria for the title or description of the milestone'
use :pagination
end
@@ -33,6 +34,7 @@ module API
milestones = parent.milestones.order_id_desc
milestones = Milestone.filter_by_state(milestones, params[:state])
milestones = filter_by_iid(milestones, params[:iids]) if params[:iids].present?
+ milestones = filter_by_title(milestones, params[:title]) if params[:title]
milestones = filter_by_search(milestones, params[:search]) if params[:search]
present paginate(milestones), with: Entities::Milestone
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index f7bd092ce50..416cf39d3ec 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -7,9 +7,7 @@ module API
before { authenticate! }
- NOTEABLE_TYPES = [Issue, MergeRequest, Snippet].freeze
-
- NOTEABLE_TYPES.each do |noteable_type|
+ Helpers::NotesHelpers.noteable_types.each do |noteable_type|
parent_type = noteable_type.parent_class.to_s.underscore
noteables_str = noteable_type.to_s.underscore.pluralize
diff --git a/lib/api/project_milestones.rb b/lib/api/project_milestones.rb
index da31bcb8dac..9ecbf37b49a 100644
--- a/lib/api/project_milestones.rb
+++ b/lib/api/project_milestones.rb
@@ -98,6 +98,21 @@ module API
milestone_issuables_for(user_project, :merge_request)
end
+
+ desc 'Promote a milestone to group milestone' do
+ detail 'This feature was introduced in GitLab 11.9'
+ end
+ post ':id/milestones/:milestone_id/promote' do
+ authorize! :admin_milestone, user_project
+ authorize! :admin_milestone, user_project.group
+
+ milestone = user_project.milestones.find(params[:milestone_id])
+ Milestones::PromoteService.new(user_project, current_user).execute(milestone)
+
+ status(200)
+ rescue Milestones::PromoteService::PromoteMilestoneError => error
+ render_api_error!(error.message, 400)
+ end
end
end
end
diff --git a/lib/api/project_statistics.rb b/lib/api/project_statistics.rb
new file mode 100644
index 00000000000..2f73785f72d
--- /dev/null
+++ b/lib/api/project_statistics.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module API
+ class ProjectStatistics < Grape::API
+ before do
+ authenticate!
+ not_found! unless user_project.daily_statistics_enabled?
+ authorize! :daily_statistics, user_project
+ end
+
+ params do
+ requires :id, type: String, desc: 'The ID of a project'
+ end
+ resource :projects, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
+ desc 'Get the list of project fetch statistics for the last 30 days'
+ get ":id/statistics" do
+ statistic_finder = ::Projects::DailyStatisticsFinder.new(user_project)
+
+ present statistic_finder, with: Entities::ProjectDailyStatistics
+ end
+ end
+ end
+end
diff --git a/lib/api/project_templates.rb b/lib/api/project_templates.rb
index d05ddad7466..119902a189c 100644
--- a/lib/api/project_templates.rb
+++ b/lib/api/project_templates.rb
@@ -36,7 +36,10 @@ module API
optional :project, type: String, desc: 'The project name to use when expanding placeholders in the template. Only affects licenses'
optional :fullname, type: String, desc: 'The full name of the copyright holder to use when expanding placeholders in the template. Only affects licenses'
end
- get ':id/templates/:type/:name', requirements: { name: /[\w\.-]+/ } do
+ # The regex is needed to ensure a period (e.g. agpl-3.0)
+ # isn't confused with a format type. We also need to allow encoded
+ # values (e.g. C%2B%2B for C++), so allow % and + as well.
+ get ':id/templates/:type/:name', requirements: { name: /[\w%.+-]+/ } do
template = TemplateFinder
.build(params[:type], user_project, name: params[:name])
.execute
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6a93ef9f3ad..0f4a47677d9 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -11,12 +11,20 @@ module API
before { authenticate_non_get! }
helpers do
- params :optional_filter_params_ee do
- # EE::API::Projects would override this helper
- end
+ if Gitlab.ee?
+ params :optional_filter_params_ee do
+ optional :wiki_checksum_failed, type: Grape::API::Boolean, default: false, desc: 'Limit by projects where wiki checksum is failed'
+ optional :repository_checksum_failed, type: Grape::API::Boolean, default: false, desc: 'Limit by projects where repository checksum is failed'
+ end
- params :optional_update_params_ee do
- # EE::API::Projects would override this helper
+ params :optional_update_params_ee do
+ optional :mirror_user_id, type: Integer, desc: 'User responsible for all the activity surrounding a pull mirror event'
+ optional :only_mirror_protected_branches, type: Grape::API::Boolean, desc: 'Only mirror protected branches'
+ optional :mirror_overwrites_diverged_branches, type: Grape::API::Boolean, desc: 'Pull mirror overwrites diverged branches'
+ optional :import_url, type: String, desc: 'URL from which the project is imported'
+ optional :packages_enabled, type: Grape::API::Boolean, desc: 'Enable project packages feature'
+ optional :fallback_approvals_required, type: Integer, desc: 'Overall approvals required when no rule is present'
+ end
end
# EE::API::Projects would override this method
@@ -35,34 +43,6 @@ module API
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'
@@ -97,7 +77,7 @@ module API
optional :with_programming_language, type: String, desc: 'Limit to repositories which use the given programming language'
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
+ use :optional_filter_params_ee if Gitlab.ee?
end
params :create_params do
@@ -184,7 +164,8 @@ module API
if project.saved?
present project, with: Entities::Project,
- user_can_admin_project: can?(current_user, :admin_project, project)
+ user_can_admin_project: can?(current_user, :admin_project, project),
+ current_user: current_user
else
if project.errors[:limit_reached].present?
error!(project.errors[:limit_reached], 403)
@@ -217,7 +198,8 @@ module API
if project.saved?
present project, with: Entities::Project,
- user_can_admin_project: can?(current_user, :admin_project, project)
+ user_can_admin_project: can?(current_user, :admin_project, project),
+ current_user: current_user
else
render_validation_error!(project)
end
@@ -258,6 +240,8 @@ module API
end
params do
optional :namespace, type: String, desc: 'The ID or name of the namespace that the project will be forked into'
+ optional :path, type: String, desc: 'The path that will be assigned to the fork'
+ optional :name, type: String, desc: 'The name that will be assigned to the fork'
end
post ':id/fork' do
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-ce/issues/42284')
@@ -279,7 +263,8 @@ module API
conflict!(forked_project.errors.messages)
else
present forked_project, with: Entities::Project,
- user_can_admin_project: can?(current_user, :admin_project, forked_project)
+ user_can_admin_project: can?(current_user, :admin_project, forked_project),
+ current_user: current_user
end
end
@@ -311,8 +296,9 @@ module API
optional :path, type: String, desc: 'The path of the repository'
use :optional_project_params
+ use :optional_update_params_ee if Gitlab.ee?
- at_least_one_of(*::API::Projects.update_params_at_least_one_of)
+ at_least_one_of(*Helpers::ProjectsHelpers.update_params_at_least_one_of)
end
put ':id' do
authorize_admin_project
@@ -328,7 +314,8 @@ module API
if result[:status] == :success
present user_project, with: Entities::Project,
- user_can_admin_project: can?(current_user, :admin_project, user_project)
+ user_can_admin_project: can?(current_user, :admin_project, user_project),
+ current_user: current_user
else
render_validation_error!(user_project)
end
@@ -342,7 +329,7 @@ module API
::Projects::UpdateService.new(user_project, current_user, archived: true).execute
- present user_project, with: Entities::Project
+ present user_project, with: Entities::Project, current_user: current_user
end
desc 'Unarchive a project' do
@@ -353,7 +340,7 @@ module API
::Projects::UpdateService.new(@project, current_user, archived: false).execute
- present user_project, with: Entities::Project
+ present user_project, with: Entities::Project, current_user: current_user
end
desc 'Star a project' do
@@ -366,7 +353,7 @@ module API
current_user.toggle_star(user_project)
user_project.reload
- present user_project, with: Entities::Project
+ present user_project, with: Entities::Project, current_user: current_user
end
end
@@ -378,7 +365,7 @@ module API
current_user.toggle_star(user_project)
user_project.reload
- present user_project, with: Entities::Project
+ present user_project, with: Entities::Project, current_user: current_user
else
not_modified!
end
@@ -386,7 +373,11 @@ module API
desc 'Get languages in project repository'
get ':id/languages' do
- user_project.repository.languages.map { |language| language.values_at(:label, :value) }.to_h
+ if user_project.repository_languages.present?
+ user_project.repository_languages.map { |l| [l.name, l.share] }.to_h
+ else
+ user_project.repository.languages.map { |language| language.values_at(:label, :value) }.to_h
+ end
end
desc 'Remove a project'
@@ -414,7 +405,7 @@ module API
result = ::Projects::ForkService.new(fork_from_project, current_user).execute(user_project)
if result
- present user_project.reload, with: Entities::Project
+ present user_project.reload, with: Entities::Project, current_user: current_user
else
render_api_error!("Project already forked", 409) if user_project.forked?
end
@@ -436,27 +427,24 @@ module API
end
params do
requires :group_id, type: Integer, desc: 'The ID of a group'
- requires :group_access, type: Integer, values: Gitlab::Access.values, desc: 'The group access level'
+ requires :group_access, type: Integer, values: Gitlab::Access.values, as: :link_group_access, desc: 'The group access level'
optional :expires_at, type: Date, desc: 'Share expiration date'
end
post ":id/share" do
authorize! :admin_project, user_project
group = Group.find_by_id(params[:group_id])
- unless group && can?(current_user, :read_group, group)
- not_found!('Group')
- end
-
unless user_project.allowed_to_share_with_group?
break render_api_error!("The project sharing with group is disabled", 400)
end
- link = user_project.project_group_links.new(declared_params(include_missing: false))
+ result = ::Projects::GroupLinks::CreateService.new(user_project, current_user, declared_params(include_missing: false))
+ .execute(group)
- if link.save
- present link, with: Entities::ProjectGroupLink
+ if result[:status] == :success
+ present result[:link], with: Entities::ProjectGroupLink
else
- render_api_error!(link.errors.full_messages.first, 409)
+ render_api_error!(result[:message], result[:http_status])
end
end
@@ -520,7 +508,7 @@ module API
result = ::Projects::TransferService.new(user_project, current_user).execute(namespace)
if result
- present user_project, with: Entities::Project
+ present user_project, with: Entities::Project, current_user: current_user
else
render_api_error!("Failed to transfer project #{user_project.errors.messages}", 400)
end
diff --git a/lib/api/protected_branches.rb b/lib/api/protected_branches.rb
index 5af43448727..f8cce1ed784 100644
--- a/lib/api/protected_branches.rb
+++ b/lib/api/protected_branches.rb
@@ -51,6 +51,30 @@ module API
optional :merge_access_level, type: Integer,
values: ProtectedBranch::MergeAccessLevel.allowed_access_levels,
desc: 'Access levels allowed to merge (defaults: `40`, maintainer access level)'
+
+ if Gitlab.ee?
+ optional :unprotect_access_level, type: Integer,
+ values: ProtectedBranch::UnprotectAccessLevel.allowed_access_levels,
+ desc: 'Access levels allowed to unprotect (defaults: `40`, maintainer access level)'
+
+ optional :allowed_to_push, type: Array, desc: 'An array of users/groups allowed to push' do
+ optional :access_level, type: Integer, values: ProtectedBranch::PushAccessLevel.allowed_access_levels
+ optional :user_id, type: Integer
+ optional :group_id, type: Integer
+ end
+
+ optional :allowed_to_merge, type: Array, desc: 'An array of users/groups allowed to merge' do
+ optional :access_level, type: Integer, values: ProtectedBranch::MergeAccessLevel.allowed_access_levels
+ optional :user_id, type: Integer
+ optional :group_id, type: Integer
+ end
+
+ optional :allowed_to_unprotect, type: Array, desc: 'An array of users/groups allowed to unprotect' do
+ optional :access_level, type: Integer, values: ProtectedBranch::UnprotectAccessLevel.allowed_access_levels
+ optional :user_id, type: Integer
+ optional :group_id, type: Integer
+ end
+ end
end
# rubocop: disable CodeReuse/ActiveRecord
post ':id/protected_branches' do
diff --git a/lib/api/release/links.rb b/lib/api/release/links.rb
index e3072684ef7..5d1b40e3bff 100644
--- a/lib/api/release/links.rb
+++ b/lib/api/release/links.rb
@@ -8,6 +8,8 @@ module API
RELEASE_ENDPOINT_REQUIREMETS = API::NAMESPACE_OR_PROJECT_REQUIREMENTS
.merge(tag_name: API::NO_SLASH_URL_PART_REGEX)
+ before { authorize! :read_release, user_project }
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 32e05d84491..4106a2cdf38 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -89,11 +89,9 @@ module API
optional :format, type: String, desc: 'The archive format'
end
get ':id/repository/archive', requirements: { format: Gitlab::PathRegex.archive_formats_regex } do
- begin
- send_git_archive user_project.repository, ref: params[:sha], format: params[:format], append_sha: true
- rescue
- not_found!('File')
- end
+ send_git_archive user_project.repository, ref: params[:sha], format: params[:format], append_sha: true
+ rescue
+ not_found!('File')
end
desc 'Compare two branches, tags, or commits' do
@@ -118,12 +116,10 @@ module API
optional :sort, type: String, values: %w[asc desc], default: 'asc', desc: 'Sort by asc (ascending) or desc (descending)'
end
get ':id/repository/contributors' do
- begin
- contributors = ::Kaminari.paginate_array(user_project.repository.contributors(order_by: params[:order_by], sort: params[:sort]))
- present paginate(contributors), with: Entities::Contributor
- rescue
- not_found!
- end
+ contributors = ::Kaminari.paginate_array(user_project.repository.contributors(order_by: params[:order_by], sort: params[:sort]))
+ present paginate(contributors), with: Entities::Contributor
+ rescue
+ not_found!
end
desc 'Get the common ancestor between commits' do
diff --git a/lib/api/resource_label_events.rb b/lib/api/resource_label_events.rb
index 0c328f7268e..448bef12cec 100644
--- a/lib/api/resource_label_events.rb
+++ b/lib/api/resource_label_events.rb
@@ -7,9 +7,7 @@ module API
before { authenticate! }
- EVENTABLE_TYPES = [Issue, MergeRequest].freeze
-
- EVENTABLE_TYPES.each do |eventable_type|
+ Helpers::ResourceLabelEventsHelpers.eventable_types.each do |eventable_type|
parent_type = eventable_type.parent_class.to_s.underscore
eventables_str = eventable_type.to_s.underscore.pluralize
diff --git a/lib/api/runners.rb b/lib/api/runners.rb
index f72b33605a7..f3fea463e7f 100644
--- a/lib/api/runners.rb
+++ b/lib/api/runners.rb
@@ -17,6 +17,7 @@ module API
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
+ optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get do
@@ -24,6 +25,7 @@ module API
runners = filter_runners(runners, params[:scope], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
+ runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
present paginate(runners), with: Entities::Runner
end
@@ -38,6 +40,7 @@ module API
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
+ optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get 'all' do
@@ -47,6 +50,7 @@ module API
runners = filter_runners(runners, params[:scope])
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
+ runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
present paginate(runners), with: Entities::Runner
end
@@ -139,6 +143,7 @@ module API
desc: 'The type of the runners to show'
optional :status, type: String, values: Ci::Runner::AVAILABLE_STATUSES,
desc: 'The status of the runners to show'
+ optional :tag_list, type: Array[String], desc: 'The tags of the runners to show'
use :pagination
end
get ':id/runners' do
@@ -146,6 +151,7 @@ module API
runners = filter_runners(runners, params[:scope])
runners = filter_runners(runners, params[:type], allowed_scopes: Ci::Runner::AVAILABLE_TYPES)
runners = filter_runners(runners, params[:status], allowed_scopes: Ci::Runner::AVAILABLE_STATUSES)
+ runners = runners.tagged_with(params[:tag_list]) if params[:tag_list]
present paginate(runners), with: Entities::Runner
end
diff --git a/lib/api/search.rb b/lib/api/search.rb
index f5db692afe5..60095300ea1 100644
--- a/lib/api/search.rb
+++ b/lib/api/search.rb
@@ -17,7 +17,8 @@ module API
blobs: Entities::Blob,
wiki_blobs: Entities::Blob,
snippet_titles: Entities::Snippet,
- snippet_blobs: Entities::Snippet
+ snippet_blobs: Entities::Snippet,
+ users: Entities::UserBasic
}.freeze
def search(additional_params = {})
@@ -45,6 +46,18 @@ module API
def entity
SCOPE_ENTITY[params[:scope].to_sym]
end
+
+ def verify_search_scope!
+ # In EE we have additional validation requirements for searches.
+ # Defining this method here as a noop allows us to easily extend it in
+ # EE, without having to modify this file directly.
+ end
+
+ def check_users_search_allowed!
+ if params[:scope].to_sym == :users && Feature.disabled?(:users_search, default_enabled: true)
+ render_api_error!({ error: _("Scope not supported with disabled 'users_search' feature!") }, 400)
+ end
+ end
end
resource :search do
@@ -55,12 +68,14 @@ module API
requires :search, type: String, desc: 'The expression it should be searched for'
requires :scope,
type: String,
- desc: 'The scope of search, available scopes:
- projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs',
- values: %w(projects issues merge_requests milestones snippet_titles snippet_blobs)
+ desc: 'The scope of the search',
+ values: Helpers::SearchHelpers.global_search_scopes
use :pagination
end
get do
+ verify_search_scope!
+ check_users_search_allowed!
+
present search, with: entity
end
end
@@ -74,12 +89,14 @@ module API
requires :search, type: String, desc: 'The expression it should be searched for'
requires :scope,
type: String,
- desc: 'The scope of search, available scopes:
- projects, issues, merge_requests, milestones',
- values: %w(projects issues merge_requests milestones)
+ desc: 'The scope of the search',
+ values: Helpers::SearchHelpers.group_search_scopes
use :pagination
end
get ':id/(-/)search' do
+ verify_search_scope!
+ check_users_search_allowed!
+
present search(group_id: user_group.id), with: entity
end
end
@@ -93,12 +110,13 @@ module API
requires :search, type: String, desc: 'The expression it should be searched for'
requires :scope,
type: String,
- desc: 'The scope of search, available scopes:
- issues, merge_requests, milestones, notes, wiki_blobs, commits, blobs',
- values: %w(issues merge_requests milestones notes wiki_blobs commits blobs)
+ desc: 'The scope of the search',
+ values: Helpers::SearchHelpers.project_search_scopes
use :pagination
end
get ':id/(-/)search' do
+ check_users_search_allowed!
+
present search(project_id: user_project.id), with: entity
end
end
diff --git a/lib/api/services.rb b/lib/api/services.rb
index 145897516a0..bc77fae87fa 100644
--- a/lib/api/services.rb
+++ b/lib/api/services.rb
@@ -1,675 +1,8 @@
# frozen_string_literal: true
module API
class Services < Grape::API
- CHAT_NOTIFICATION_SETTINGS = [
- {
- required: true,
- name: :webhook,
- type: String,
- desc: 'The chat webhook'
- },
- {
- required: false,
- name: :username,
- type: String,
- desc: 'The chat username'
- },
- {
- required: false,
- name: :channel,
- type: String,
- desc: 'The default chat channel'
- }
- ].freeze
-
- CHAT_NOTIFICATION_FLAGS = [
- {
- required: false,
- name: :notify_only_broken_pipelines,
- type: Boolean,
- desc: 'Send notifications for broken pipelines'
- },
- {
- required: false,
- name: :notify_only_default_branch,
- type: Boolean,
- desc: 'Send notifications only for the default branch'
- }
- ].freeze
-
- CHAT_NOTIFICATION_CHANNELS = [
- {
- required: false,
- name: :push_channel,
- type: String,
- desc: 'The name of the channel to receive push_events notifications'
- },
- {
- required: false,
- name: :issue_channel,
- type: String,
- desc: 'The name of the channel to receive issues_events notifications'
- },
- {
- required: false,
- name: :confidential_issue_channel,
- type: String,
- desc: 'The name of the channel to receive confidential_issues_events notifications'
- },
- {
- required: false,
- name: :merge_request_channel,
- type: String,
- desc: 'The name of the channel to receive merge_requests_events notifications'
- },
- {
- required: false,
- name: :note_channel,
- type: String,
- desc: 'The name of the channel to receive note_events notifications'
- },
- {
- required: false,
- name: :tag_push_channel,
- type: String,
- desc: 'The name of the channel to receive tag_push_events notifications'
- },
- {
- required: false,
- name: :pipeline_channel,
- type: String,
- desc: 'The name of the channel to receive pipeline_events notifications'
- },
- {
- required: false,
- name: :wiki_page_channel,
- type: String,
- desc: 'The name of the channel to receive wiki_page_events notifications'
- }
- ].freeze
-
- CHAT_NOTIFICATION_EVENTS = [
- {
- required: false,
- name: :push_events,
- type: Boolean,
- desc: 'Enable notifications for push_events'
- },
- {
- required: false,
- name: :issues_events,
- type: Boolean,
- desc: 'Enable notifications for issues_events'
- },
- {
- required: false,
- name: :confidential_issues_events,
- type: Boolean,
- desc: 'Enable notifications for confidential_issues_events'
- },
- {
- required: false,
- name: :merge_requests_events,
- type: Boolean,
- desc: 'Enable notifications for merge_requests_events'
- },
- {
- required: false,
- name: :note_events,
- type: Boolean,
- desc: 'Enable notifications for note_events'
- },
- {
- required: false,
- name: :tag_push_events,
- type: Boolean,
- desc: 'Enable notifications for tag_push_events'
- },
- {
- required: false,
- name: :pipeline_events,
- type: Boolean,
- desc: 'Enable notifications for pipeline_events'
- },
- {
- required: false,
- name: :wiki_page_events,
- type: Boolean,
- desc: 'Enable notifications for wiki_page_events'
- }
- ].freeze
-
- services = {
- 'asana' => [
- {
- required: true,
- name: :api_key,
- type: String,
- desc: 'User API token'
- },
- {
- required: false,
- name: :restrict_to_branch,
- type: String,
- desc: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches'
- }
- ],
- 'assembla' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'The authentication token'
- },
- {
- required: false,
- name: :subdomain,
- type: String,
- desc: 'Subdomain setting'
- }
- ],
- 'bamboo' => [
- {
- required: true,
- name: :bamboo_url,
- type: String,
- desc: 'Bamboo root URL like https://bamboo.example.com'
- },
- {
- required: true,
- name: :build_key,
- type: String,
- desc: 'Bamboo build plan key like'
- },
- {
- required: true,
- name: :username,
- type: String,
- desc: 'A user with API access, if applicable'
- },
- {
- required: true,
- name: :password,
- type: String,
- desc: 'Passord of the user'
- }
- ],
- 'bugzilla' => [
- {
- required: true,
- name: :new_issue_url,
- type: String,
- desc: 'New issue URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'Issues URL'
- },
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'Project URL'
- },
- {
- required: false,
- name: :description,
- type: String,
- desc: 'Description'
- },
- {
- required: false,
- name: :title,
- type: String,
- desc: 'Title'
- }
- ],
- 'buildkite' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'Buildkite project GitLab token'
- },
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'The buildkite project URL'
- },
- {
- required: false,
- name: :enable_ssl_verification,
- type: Boolean,
- desc: 'Enable SSL verification for communication'
- }
- ],
- 'campfire' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'Campfire token'
- },
- {
- required: false,
- name: :subdomain,
- type: String,
- desc: 'Campfire subdomain'
- },
- {
- required: false,
- name: :room,
- type: String,
- desc: 'Campfire room'
- }
- ],
- 'custom-issue-tracker' => [
- {
- required: true,
- name: :new_issue_url,
- type: String,
- desc: 'New issue URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'Issues URL'
- },
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'Project URL'
- },
- {
- required: false,
- name: :description,
- type: String,
- desc: 'Description'
- },
- {
- required: false,
- name: :title,
- type: String,
- desc: 'Title'
- }
- ],
- 'discord' => [
- {
- required: true,
- name: :webhook,
- type: String,
- desc: 'Discord webhook. e.g. https://discordapp.com/api/webhooks/…'
- }
- ],
- 'drone-ci' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'Drone CI token'
- },
- {
- required: true,
- name: :drone_url,
- type: String,
- desc: 'Drone CI URL'
- },
- {
- required: false,
- name: :enable_ssl_verification,
- type: Boolean,
- desc: 'Enable SSL verification for communication'
- }
- ],
- 'emails-on-push' => [
- {
- required: true,
- name: :recipients,
- type: String,
- desc: 'Comma-separated list of recipient email addresses'
- },
- {
- required: false,
- name: :disable_diffs,
- type: Boolean,
- desc: 'Disable code diffs'
- },
- {
- required: false,
- name: :send_from_committer_email,
- type: Boolean,
- desc: 'Send from committer'
- }
- ],
- 'external-wiki' => [
- {
- required: true,
- name: :external_wiki_url,
- type: String,
- desc: 'The URL of the external Wiki'
- }
- ],
- 'flowdock' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'Flowdock token'
- }
- ],
- 'hangouts-chat' => [
- {
- required: true,
- name: :webhook,
- type: String,
- desc: 'The Hangouts Chat webhook. e.g. https://chat.googleapis.com/v1/spaces…'
- },
- CHAT_NOTIFICATION_EVENTS
- ].flatten,
- 'irker' => [
- {
- required: true,
- name: :recipients,
- type: String,
- desc: 'Recipients/channels separated by whitespaces'
- },
- {
- required: false,
- name: :default_irc_uri,
- type: String,
- desc: 'Default: irc://irc.network.net:6697'
- },
- {
- required: false,
- name: :server_host,
- type: String,
- desc: 'Server host. Default localhost'
- },
- {
- required: false,
- name: :server_port,
- type: Integer,
- desc: 'Server port. Default 6659'
- },
- {
- required: false,
- name: :colorize_messages,
- type: Boolean,
- desc: 'Colorize messages'
- }
- ],
- 'jira' => [
- {
- required: true,
- name: :url,
- type: String,
- desc: 'The base URL to the JIRA instance web interface which is being linked to this GitLab project. E.g., https://jira.example.com'
- },
- {
- required: false,
- name: :api_url,
- type: String,
- desc: 'The base URL to the JIRA instance API. Web URL value will be used if not set. E.g., https://jira-api.example.com'
- },
- {
- required: true,
- name: :username,
- type: String,
- desc: 'The username of the user created to be used with GitLab/JIRA'
- },
- {
- required: true,
- name: :password,
- type: String,
- desc: 'The password of the user created to be used with GitLab/JIRA'
- },
- {
- required: false,
- name: :jira_issue_transition_id,
- type: String,
- desc: 'The ID of a transition that moves issues to a closed state. You can find this number under the JIRA workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column ([see screenshot][trans]). By default, this ID is set to `2`'
- }
- ],
-
- 'kubernetes' => [
- {
- required: true,
- name: :namespace,
- type: String,
- desc: 'The Kubernetes namespace to use'
- },
- {
- required: true,
- name: :api_url,
- type: String,
- desc: 'The URL to the Kubernetes cluster API, e.g., https://kubernetes.example.com'
- },
- {
- required: true,
- name: :token,
- type: String,
- desc: 'The service token to authenticate against the Kubernetes cluster with'
- },
- {
- required: false,
- name: :ca_pem,
- type: String,
- desc: 'A custom certificate authority bundle to verify the Kubernetes cluster with (PEM format)'
- }
- ],
- 'mattermost-slash-commands' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'The Mattermost token'
- }
- ],
- 'slack-slash-commands' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'The Slack token'
- }
- ],
- 'packagist' => [
- {
- required: true,
- name: :username,
- type: String,
- desc: 'The username'
- },
- {
- required: true,
- name: :token,
- type: String,
- desc: 'The Packagist API token'
- },
- {
- required: false,
- name: :server,
- type: String,
- desc: 'The server'
- }
- ],
- 'pipelines-email' => [
- {
- required: true,
- name: :recipients,
- type: String,
- desc: 'Comma-separated list of recipient email addresses'
- },
- {
- required: false,
- name: :notify_only_broken_pipelines,
- type: Boolean,
- desc: 'Notify only broken pipelines'
- }
- ],
- 'pivotaltracker' => [
- {
- required: true,
- name: :token,
- type: String,
- desc: 'The Pivotaltracker token'
- },
- {
- required: false,
- name: :restrict_to_branch,
- type: String,
- desc: 'Comma-separated list of branches which will be automatically inspected. Leave blank to include all branches.'
- }
- ],
- 'prometheus' => [
- {
- required: true,
- name: :api_url,
- type: String,
- desc: 'Prometheus API Base URL, like http://prometheus.example.com/'
- }
- ],
- 'pushover' => [
- {
- required: true,
- name: :api_key,
- type: String,
- desc: 'The application key'
- },
- {
- required: true,
- name: :user_key,
- type: String,
- desc: 'The user key'
- },
- {
- required: true,
- name: :priority,
- type: String,
- desc: 'The priority'
- },
- {
- required: true,
- name: :device,
- type: String,
- desc: 'Leave blank for all active devices'
- },
- {
- required: true,
- name: :sound,
- type: String,
- desc: 'The sound of the notification'
- }
- ],
- 'redmine' => [
- {
- required: true,
- name: :new_issue_url,
- type: String,
- desc: 'The new issue URL'
- },
- {
- required: true,
- name: :project_url,
- type: String,
- desc: 'The project URL'
- },
- {
- required: true,
- name: :issues_url,
- type: String,
- desc: 'The issues URL'
- },
- {
- required: false,
- name: :description,
- type: String,
- desc: 'The description of the tracker'
- }
- ],
- 'slack' => [
- CHAT_NOTIFICATION_SETTINGS,
- CHAT_NOTIFICATION_FLAGS,
- CHAT_NOTIFICATION_CHANNELS,
- CHAT_NOTIFICATION_EVENTS
- ].flatten,
- 'microsoft-teams' => [
- {
- required: true,
- name: :webhook,
- type: String,
- desc: 'The Microsoft Teams webhook. e.g. https://outlook.office.com/webhook/…'
- }
- ],
- 'mattermost' => [
- CHAT_NOTIFICATION_SETTINGS,
- CHAT_NOTIFICATION_FLAGS,
- CHAT_NOTIFICATION_CHANNELS,
- CHAT_NOTIFICATION_EVENTS
- ].flatten,
- 'teamcity' => [
- {
- required: true,
- name: :teamcity_url,
- type: String,
- desc: 'TeamCity root URL like https://teamcity.example.com'
- },
- {
- required: true,
- name: :build_type,
- type: String,
- desc: 'Build configuration ID'
- },
- {
- required: true,
- name: :username,
- type: String,
- desc: 'A user with permissions to trigger a manual build'
- },
- {
- required: true,
- name: :password,
- type: String,
- desc: 'The password of the user'
- }
- ]
- }
-
- service_classes = [
- AsanaService,
- AssemblaService,
- BambooService,
- BugzillaService,
- BuildkiteService,
- CampfireService,
- CustomIssueTrackerService,
- DiscordService,
- DroneCiService,
- EmailsOnPushService,
- ExternalWikiService,
- FlowdockService,
- HangoutsChatService,
- IrkerService,
- JiraService,
- KubernetesService,
- MattermostSlashCommandsService,
- SlackSlashCommandsService,
- PackagistService,
- PipelinesEmailService,
- PivotaltrackerService,
- PrometheusService,
- PushoverService,
- RedmineService,
- SlackService,
- MattermostService,
- MicrosoftTeamsService,
- TeamcityService
- ]
+ services = Helpers::ServicesHelpers.services
+ service_classes = Helpers::ServicesHelpers.service_classes
if Rails.env.development?
services['mock-ci'] = [
@@ -683,11 +16,7 @@ module API
services['mock-deployment'] = []
services['mock-monitoring'] = []
- service_classes += [
- MockCiService,
- MockDeploymentService,
- MockMonitoringService
- ]
+ service_classes += Helpers::ServicesHelpers.development_service_classes
end
SERVICES = services.freeze
diff --git a/lib/api/settings.rb b/lib/api/settings.rb
index b16faffe335..d742c6c97c1 100644
--- a/lib/api/settings.rb
+++ b/lib/api/settings.rb
@@ -9,6 +9,11 @@ module API
@current_setting ||=
(ApplicationSetting.current_without_cache || ApplicationSetting.create_from_defaults)
end
+
+ def filter_attributes_using_license(attrs)
+ # This method will be redefined in EE.
+ attrs
+ end
end
desc 'Get the current application settings' do
@@ -130,8 +135,44 @@ module API
desc: "Restrictions on the complexity of uploaded #{type.upcase} keys. A value of #{ApplicationSetting::FORBIDDEN_KEY_VALUE} disables all #{type.upcase} keys."
end
+ if Gitlab.ee?
+ optional :elasticsearch_aws, type: Boolean, desc: 'Enable support for AWS hosted elasticsearch'
+
+ given elasticsearch_aws: ->(val) { val } do
+ optional :elasticsearch_aws_access_key, type: String, desc: 'AWS IAM access key'
+ requires :elasticsearch_aws_region, type: String, desc: 'The AWS region the elasticsearch domain is configured'
+ optional :elasticsearch_aws_secret_access_key, type: String, desc: 'AWS IAM secret access key'
+ end
+
+ optional :elasticsearch_indexing, type: Boolean, desc: 'Enable Elasticsearch indexing'
+
+ given elasticsearch_indexing: ->(val) { val } do
+ optional :elasticsearch_search, type: Boolean, desc: 'Enable Elasticsearch search'
+ requires :elasticsearch_url, type: String, desc: 'The url to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., "http://localhost:9200, http://localhost:9201")'
+ end
+
+ optional :email_additional_text, type: String, desc: 'Additional text added to the bottom of every email for legal/auditing/compliance reasons'
+ optional :help_text, type: String, desc: 'GitLab server administrator information'
+ optional :repository_size_limit, type: Integer, desc: 'Size limit per repository (MB)'
+ optional :file_template_project_id, type: Integer, desc: 'ID of project where instance-level file templates are stored.'
+ optional :repository_storages, type: Array[String], desc: 'A list of names of enabled storage paths, taken from `gitlab.yml`. New projects will be created in one of these stores, chosen at random.'
+ optional :snowplow_enabled, type: Boolean, desc: 'Enable Snowplow'
+
+ given snowplow_enabled: ->(val) { val } do
+ requires :snowplow_collector_uri, type: String, desc: 'Snowplow Collector URI'
+ optional :snowplow_cookie_domain, type: String, desc: 'Snowplow cookie domain'
+ optional :snowplow_site_id, type: String, desc: 'Snowplow Site/Application ID'
+ end
+
+ optional :usage_ping_enabled, type: Boolean, desc: 'Every week GitLab will report license usage back to GitLab, Inc.'
+ end
+
optional_attributes = ::ApplicationSettingsHelper.visible_attributes << :performance_bar_allowed_group_id
+ if Gitlab.ee?
+ optional_attributes += EE::ApplicationSettingsHelper.possible_licensed_attributes
+ end
+
optional(*optional_attributes)
at_least_one_of(*optional_attributes)
end
@@ -156,6 +197,8 @@ module API
attrs[:password_authentication_enabled_for_web] = attrs.delete(:password_authentication_enabled)
end
+ attrs = filter_attributes_using_license(attrs)
+
if ApplicationSettings::UpdateService.new(current_settings, current_user, attrs).execute
present current_settings, with: Entities::ApplicationSetting
else
diff --git a/lib/api/snippets.rb b/lib/api/snippets.rb
index 326d55afd0e..f8b37b33348 100644
--- a/lib/api/snippets.rb
+++ b/lib/api/snippets.rb
@@ -16,6 +16,10 @@ module API
def public_snippets
SnippetsFinder.new(current_user, scope: :are_public).execute
end
+
+ def snippets
+ SnippetsFinder.new(current_user).execute
+ end
end
desc 'Get a snippets list for authenticated user' do
@@ -48,7 +52,10 @@ module API
requires :id, type: Integer, desc: 'The ID of a snippet'
end
get ':id' do
- snippet = snippets_for_current_user.find(params[:id])
+ snippet = snippets.find_by_id(params[:id])
+
+ break not_found!('Snippet') unless snippet
+
present snippet, with: Entities::PersonalSnippet
end
@@ -94,9 +101,8 @@ module API
desc: 'The visibility of the snippet'
at_least_one_of :title, :file_name, :content, :visibility
end
- # rubocop: disable CodeReuse/ActiveRecord
put ':id' do
- snippet = snippets_for_current_user.find_by(id: params.delete(:id))
+ snippet = snippets_for_current_user.find_by_id(params.delete(:id))
break not_found!('Snippet') unless snippet
authorize! :update_personal_snippet, snippet
@@ -113,7 +119,6 @@ module API
render_validation_error!(snippet)
end
end
- # rubocop: enable CodeReuse/ActiveRecord
desc 'Remove snippet' do
detail 'This feature was introduced in GitLab 8.15.'
@@ -122,16 +127,14 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
- # rubocop: disable CodeReuse/ActiveRecord
delete ':id' do
- snippet = snippets_for_current_user.find_by(id: params.delete(:id))
+ snippet = snippets_for_current_user.find_by_id(params.delete(:id))
break not_found!('Snippet') unless snippet
authorize! :destroy_personal_snippet, snippet
destroy_conditionally!(snippet)
end
- # rubocop: enable CodeReuse/ActiveRecord
desc 'Get a raw snippet' do
detail 'This feature was introduced in GitLab 8.15.'
@@ -139,9 +142,8 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
- # rubocop: disable CodeReuse/ActiveRecord
get ":id/raw" do
- snippet = snippets_for_current_user.find_by(id: params.delete(:id))
+ snippet = snippets.find_by_id(params.delete(:id))
break not_found!('Snippet') unless snippet
env['api.format'] = :txt
@@ -149,7 +151,6 @@ module API
header['Content-Disposition'] = 'attachment'
present snippet.content
end
- # rubocop: enable CodeReuse/ActiveRecord
desc 'Get the user agent details for a snippet' do
success Entities::UserAgentDetail
@@ -157,17 +158,15 @@ module API
params do
requires :id, type: Integer, desc: 'The ID of a snippet'
end
- # rubocop: disable CodeReuse/ActiveRecord
get ":id/user_agent_detail" do
authenticated_as_admin!
- snippet = Snippet.find_by!(id: params[:id])
+ snippet = Snippet.find_by_id!(params[:id])
break not_found!('UserAgentDetail') unless snippet.user_agent_detail
present snippet.user_agent_detail, with: Entities::UserAgentDetail
end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/api/todos.rb b/lib/api/todos.rb
index 64ac8ece56c..d2196f05173 100644
--- a/lib/api/todos.rb
+++ b/lib/api/todos.rb
@@ -6,6 +6,8 @@ module API
before { authenticate! }
+ helpers ::Gitlab::IssuableMetadata
+
ISSUABLE_TYPES = {
'merge_requests' => ->(iid) { find_merge_request_with_access(iid) },
'issues' => ->(iid) { find_project_issue(iid) }
@@ -42,6 +44,30 @@ module API
def find_todos
TodosFinder.new(current_user, params).execute
end
+
+ def issuable_and_awardable?(type)
+ obj_type = Object.const_get(type)
+
+ (obj_type < Issuable) && (obj_type < Awardable)
+ rescue NameError
+ false
+ end
+
+ def batch_load_issuable_metadata(todos, options)
+ # This should be paginated and will cause Rails to SELECT for all the Todos
+ todos_by_type = todos.group_by(&:target_type)
+
+ todos_by_type.keys.each do |type|
+ next unless issuable_and_awardable?(type)
+
+ collection = todos_by_type[type]
+
+ next unless collection
+
+ targets = collection.map(&:target)
+ options[type] = { issuable_metadata: issuable_meta_data(targets, type) }
+ end
+ end
end
desc 'Get a todo list' do
@@ -51,7 +77,11 @@ module API
use :pagination
end
get do
- present paginate(find_todos), with: Entities::Todo, current_user: current_user
+ todos = paginate(find_todos.with_api_entity_associations)
+ options = { with: Entities::Todo, current_user: current_user }
+ batch_load_issuable_metadata(todos, options)
+
+ present todos, options
end
desc 'Mark a todo as done' do
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index 8fc7c7361e1..0e829c5699b 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -13,7 +13,7 @@ module API
end
params do
requires :ref, type: String, desc: 'The commit sha or name of a branch or tag', allow_blank: false
- requires :token, type: String, desc: 'The unique token of trigger'
+ requires :token, type: String, desc: 'The unique token of trigger or job token'
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
end
post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 7d88880d412..776329622e2 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -51,6 +51,10 @@ module API
optional :avatar, type: File, desc: 'Avatar image for user'
optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile'
all_or_none_of :extern_uid, :provider
+
+ if Gitlab.ee?
+ optional :shared_runners_minutes_limit, type: Integer, desc: 'Pipeline minutes quota for this user'
+ end
end
params :sort_params do
@@ -80,6 +84,10 @@ module API
use :sort_params
use :pagination
use :with_custom_attributes
+
+ if Gitlab.ee?
+ optional :skip_ldap, type: Boolean, default: false, desc: 'Skip LDAP users'
+ end
end
# rubocop: disable CodeReuse/ActiveRecord
get do
@@ -124,7 +132,7 @@ module API
user = User.find_by(id: params[:id])
not_found!('User') unless user && can?(current_user, :read_user, user)
- opts = { with: current_user&.admin? ? Entities::UserWithAdmin : Entities::User, current_user: current_user }
+ opts = { with: current_user&.admin? ? Entities::UserDetailsWithAdmin : Entities::User, current_user: current_user }
user, opts = with_custom_attributes(user, opts)
present user, opts
diff --git a/lib/api/validations/types/labels_list.rb b/lib/api/validations/types/labels_list.rb
new file mode 100644
index 00000000000..47cd83c29cf
--- /dev/null
+++ b/lib/api/validations/types/labels_list.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+module API
+ module Validations
+ module Types
+ class LabelsList
+ def self.coerce
+ lambda do |value|
+ case value
+ when String
+ value.split(',').map(&:strip)
+ when Array
+ value.map { |v| v.to_s.split(',').map(&:strip) }.flatten
+ when LabelsList
+ value
+ else
+ []
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
index 148deb86c4c..3489ba827e4 100644
--- a/lib/api/variables.rb
+++ b/lib/api/variables.rb
@@ -7,6 +7,14 @@ module API
before { authenticate! }
before { authorize! :admin_build, user_project }
+ helpers do
+ def filter_variable_parameters(params)
+ # This method exists so that EE can more easily filter out certain
+ # parameters, without having to modify the source code directly.
+ params
+ end
+ end
+
params do
requires :id, type: String, desc: 'The ID of a project'
end
@@ -47,9 +55,14 @@ module API
requires :key, type: String, desc: 'The key of the variable'
requires :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
+
+ if Gitlab.ee?
+ optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
+ end
end
post ':id/variables' do
variable_params = declared_params(include_missing: false)
+ variable_params = filter_variable_parameters(variable_params)
variable = user_project.variables.create(variable_params)
@@ -67,6 +80,10 @@ module API
optional :key, type: String, desc: 'The key of the variable'
optional :value, type: String, desc: 'The value of the variable'
optional :protected, type: String, desc: 'Whether the variable is protected'
+
+ if Gitlab.ee?
+ optional :environment_scope, type: String, desc: 'The environment_scope of the variable'
+ end
end
# rubocop: disable CodeReuse/ActiveRecord
put ':id/variables/:key' do
@@ -75,6 +92,7 @@ module API
break not_found!('Variable') unless variable
variable_params = declared_params(include_missing: false).except(:key)
+ variable_params = filter_variable_parameters(variable_params)
if variable.update(variable_params)
present variable, with: Entities::Variable
diff --git a/lib/api/version.rb b/lib/api/version.rb
index 74cd857f447..eca1b529094 100644
--- a/lib/api/version.rb
+++ b/lib/api/version.rb
@@ -2,13 +2,29 @@
module API
class Version < Grape::API
+ helpers ::API::Helpers::GraphqlHelpers
+
before { authenticate! }
+ METADATA_QUERY = <<~EOF
+ {
+ metadata {
+ version
+ revision
+ }
+ }
+ EOF
+
desc 'Get the version information of the GitLab instance.' do
detail 'This feature was introduced in GitLab 8.13.'
end
get '/version' do
- { version: Gitlab::VERSION, revision: Gitlab.revision }
+ conditionally_graphql!(
+ query: METADATA_QUERY,
+ context: { current_user: current_user },
+ transform: ->(result) { result.dig('data', 'metadata') },
+ fallback: -> { { version: Gitlab::VERSION, revision: Gitlab.revision } }
+ )
end
end
end
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index e6bf3d1856f..cd8e29d14d3 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -4,6 +4,7 @@ require 'yaml'
module Backup
class Database
+ include Backup::Helper
attr_reader :progress
attr_reader :config, :db_file_name
@@ -17,7 +18,7 @@ module Backup
FileUtils.mkdir_p(File.dirname(db_file_name))
FileUtils.rm_f(db_file_name)
compress_rd, compress_wr = IO.pipe
- compress_pid = spawn(*%w(gzip -1 -c), in: compress_rd, out: [db_file_name, 'w', 0600])
+ compress_pid = spawn(gzip_cmd, in: compress_rd, out: [db_file_name, 'w', 0600])
compress_rd.close
dump_pid =
diff --git a/lib/backup/files.rb b/lib/backup/files.rb
index 2bac84846c5..098f2da6d88 100644
--- a/lib/backup/files.rb
+++ b/lib/backup/files.rb
@@ -31,10 +31,10 @@ module Backup
raise Backup::Error, 'Backup failed'
end
- run_pipeline!([%W(#{tar} --exclude=lost+found -C #{@backup_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600])
+ run_pipeline!([%W(#{tar} --exclude=lost+found -C #{@backup_files_dir} -cf - .), gzip_cmd], out: [backup_tarball, 'w', 0600])
FileUtils.rm_rf(@backup_files_dir)
else
- run_pipeline!([%W(#{tar} --exclude=lost+found -C #{app_files_dir} -cf - .), %w(gzip -c -1)], out: [backup_tarball, 'w', 0600])
+ run_pipeline!([%W(#{tar} --exclude=lost+found -C #{app_files_dir} -cf - .), gzip_cmd], out: [backup_tarball, 'w', 0600])
end
end
diff --git a/lib/backup/helper.rb b/lib/backup/helper.rb
index 22f00aef569..2c2e35add0e 100644
--- a/lib/backup/helper.rb
+++ b/lib/backup/helper.rb
@@ -29,5 +29,13 @@ module Backup
EOS
raise message
end
+
+ def gzip_cmd
+ @gzip_cmd ||= if ENV['GZIP_RSYNCABLE'] == 'yes'
+ "gzip --rsyncable -c -1"
+ else
+ "gzip -c -1"
+ end
+ end
end
end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 06b0338b1ed..aeaf61cda39 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -235,7 +235,11 @@ module Backup
end
def tar_file
- @tar_file ||= "#{backup_information[:backup_created_at].strftime('%s_%Y_%m_%d_')}#{backup_information[:gitlab_version]}#{FILE_NAME_SUFFIX}"
+ @tar_file ||= if ENV['BACKUP']
+ ENV['BACKUP'] + "#{FILE_NAME_SUFFIX}"
+ else
+ "#{backup_information[:backup_created_at].strftime('%s_%Y_%m_%d_')}#{backup_information[:gitlab_version]}#{FILE_NAME_SUFFIX}"
+ end
end
def backup_information
diff --git a/lib/backup/uploads.rb b/lib/backup/uploads.rb
index 9577df2634a..5a20b6ae0a6 100644
--- a/lib/backup/uploads.rb
+++ b/lib/backup/uploads.rb
@@ -9,7 +9,7 @@ module Backup
def initialize(progress)
@progress = progress
- super('uploads', Rails.root.join('public/uploads'))
+ super('uploads', File.join(Gitlab.config.uploads.storage_path, "uploads"))
end
end
end
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index 4764f8e1e19..5f8aca104aa 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -181,9 +181,10 @@ module Banzai
title = object_link_title(object, matches)
klass = reference_class(object_sym)
- data = data_attributes_for(link_content || match, parent, object,
- link_content: !!link_content,
- link_reference: link_reference)
+ data_attributes = data_attributes_for(link_content || match, parent, object,
+ link_content: !!link_content,
+ link_reference: link_reference)
+ data = data_attribute(data_attributes)
url =
if matches.names.include?("url") && matches[:url]
@@ -206,13 +207,13 @@ module Banzai
def data_attributes_for(text, parent, object, link_content: false, link_reference: false)
object_parent_type = parent.is_a?(Group) ? :group : :project
- data_attribute(
+ {
original: text,
link: link_content,
link_reference: link_reference,
object_parent_type => parent.id,
object_sym => object.id
- )
+ }
end
def object_link_text_extras(object, matches)
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 7098767b583..f05902078dc 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -20,7 +20,9 @@ module Banzai
end
def object_link_title(object, matches)
- object_link_commit_title(object, matches) || super
+ # The method will return `nil` if object is not a commit
+ # allowing for properly handling the extended MR Tooltip
+ object_link_commit_title(object, matches)
end
def object_link_text_extras(object, matches)
@@ -53,6 +55,14 @@ module Banzai
.includes(target_project: :namespace)
end
+ def reference_class(object_sym, options = {})
+ super(object_sym, tooltip: false)
+ end
+
+ def data_attributes_for(text, parent, object, data = {})
+ super.merge(project_path: parent.full_path, iid: object.iid, mr_title: object.title)
+ end
+
private
def object_link_commit_title(object, matches)
diff --git a/lib/banzai/filter/output_safety.rb b/lib/banzai/filter/output_safety.rb
new file mode 100644
index 00000000000..d4ebce5d9c9
--- /dev/null
+++ b/lib/banzai/filter/output_safety.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module Banzai
+ module Filter
+ module OutputSafety
+ def escape_once(html)
+ html.html_safe? ? html : ERB::Util.html_escape_once(html)
+ end
+ end
+ end
+end
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 42f9b3a689c..b3ce9200b49 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -12,6 +12,7 @@ module Banzai
# :only_path - Generate path-only links.
class ReferenceFilter < HTML::Pipeline::Filter
include RequestStoreReferenceCache
+ include OutputSafety
class << self
attr_accessor :reference_type
@@ -43,10 +44,6 @@ module Banzai
end.join(' ')
end
- def escape_once(html)
- html.html_safe? ? html : ERB::Util.html_escape_once(html)
- end
-
def ignore_ancestor_query
@ignore_ancestor_query ||= begin
parents = %w(pre code a style)
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 93e6d6470f1..2745905c5ff 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -150,7 +150,10 @@ module Banzai
end
def uri_type(path)
- @uri_types[path] ||= current_commit.uri_type(path)
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58011
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ @uri_types[path] ||= current_commit.uri_type(path)
+ end
end
def current_commit
diff --git a/lib/banzai/filter/suggestion_filter.rb b/lib/banzai/filter/suggestion_filter.rb
index 9950db373d8..848aca10a20 100644
--- a/lib/banzai/filter/suggestion_filter.rb
+++ b/lib/banzai/filter/suggestion_filter.rb
@@ -6,11 +6,15 @@ module Banzai
class SuggestionFilter < HTML::Pipeline::Filter
# Class used for tagging elements that should be rendered
TAG_CLASS = 'js-render-suggestion'.freeze
+ SUGGESTION_REGEX = Gitlab::Diff::SuggestionsParser::SUGGESTION_CONTEXT
def call
return doc unless suggestions_filter_enabled?
doc.search('pre.suggestion > code').each do |node|
+ # TODO: Remove once multi-line suggestions FF get removed (#59178).
+ remove_multi_line_params(node.parent)
+
node.add_class(TAG_CLASS)
end
@@ -20,6 +24,20 @@ module Banzai
def suggestions_filter_enabled?
context[:suggestions_filter_enabled]
end
+
+ private
+
+ def project
+ context[:project]
+ end
+
+ def remove_multi_line_params(node)
+ return if Feature.enabled?(:multi_line_suggestions, project)
+
+ if node[SyntaxHighlightFilter::LANG_PARAMS_ATTR]&.match?(SUGGESTION_REGEX)
+ node.remove_attribute(SyntaxHighlightFilter::LANG_PARAMS_ATTR)
+ end
+ end
end
end
end
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index 9ffde52b5f2..fe56f9a1e33 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -8,6 +8,11 @@ module Banzai
# HTML Filter to highlight fenced code blocks
#
class SyntaxHighlightFilter < HTML::Pipeline::Filter
+ include OutputSafety
+
+ PARAMS_DELIMITER = ':'.freeze
+ LANG_PARAMS_ATTR = 'data-lang-params'.freeze
+
def call
doc.search('pre > code').each do |node|
highlight_node(node)
@@ -18,7 +23,7 @@ module Banzai
def highlight_node(node)
css_classes = +'code highlight js-syntax-highlight'
- lang = node.attr('lang')
+ lang, lang_params = parse_lang_params(node.attr('lang'))
retried = false
if use_rouge?(lang)
@@ -46,7 +51,10 @@ module Banzai
retry
end
- highlighted = %(<pre class="#{css_classes}" lang="#{language}" v-pre="true"><code>#{code}</code></pre>)
+ highlighted = %(<pre class="#{css_classes}"
+ lang="#{language}"
+ #{lang_params}
+ v-pre="true"><code>#{code}</code></pre>)
# Extracted to a method to measure it
replace_parent_pre_element(node, highlighted)
@@ -54,6 +62,15 @@ module Banzai
private
+ def parse_lang_params(language)
+ return unless language
+
+ lang, params = language.split(PARAMS_DELIMITER, 2)
+ formatted_params = %(#{LANG_PARAMS_ATTR}="#{escape_once(params)}") if params
+
+ [lang, formatted_params]
+ end
+
# Separate method so it can be instrumented.
def lex(lexer, code)
lexer.lex(code)
diff --git a/lib/banzai/pipeline/markup_pipeline.rb b/lib/banzai/pipeline/markup_pipeline.rb
index db79a22549c..ceba082cd4f 100644
--- a/lib/banzai/pipeline/markup_pipeline.rb
+++ b/lib/banzai/pipeline/markup_pipeline.rb
@@ -7,7 +7,8 @@ module Banzai
@filters ||= FilterArray[
Filter::SanitizationFilter,
Filter::ExternalLinkFilter,
- Filter::PlantumlFilter
+ Filter::PlantumlFilter,
+ Filter::SyntaxHighlightFilter
]
end
end
diff --git a/lib/banzai/suggestions_parser.rb b/lib/banzai/suggestions_parser.rb
index 09f36635020..0d7f751bfc1 100644
--- a/lib/banzai/suggestions_parser.rb
+++ b/lib/banzai/suggestions_parser.rb
@@ -1,5 +1,7 @@
# frozen_string_literal: true
+# TODO: Delete when https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/26107
+# exchange this parser by `Gitlab::Diff::SuggestionsParser`.
module Banzai
module SuggestionsParser
# Returns the content of each suggestion code block.
diff --git a/lib/bitbucket_server/collection.rb b/lib/bitbucket_server/collection.rb
index 7e4b2277bbe..f549acbd87f 100644
--- a/lib/bitbucket_server/collection.rb
+++ b/lib/bitbucket_server/collection.rb
@@ -25,13 +25,13 @@ module BitbucketServer
end
def prev_page
- return nil unless current_page > 1
+ return unless current_page > 1
current_page - 1
end
def next_page
- return nil unless has_next_page?
+ return unless has_next_page?
current_page + 1
end
diff --git a/lib/bitbucket_server/connection.rb b/lib/bitbucket_server/connection.rb
index 9c14b26c65a..fbd451efb23 100644
--- a/lib/bitbucket_server/connection.rb
+++ b/lib/bitbucket_server/connection.rb
@@ -77,6 +77,7 @@ module BitbucketServer
private
def check_errors!(response)
+ return if ActionDispatch::Response::NO_CONTENT_CODES.include?(response.code)
raise ConnectionError, "Response is not valid JSON" unless response.parsed_response.is_a?(Hash)
return if response.code >= 200 && response.code < 300
diff --git a/lib/constraints/project_url_constrainer.rb b/lib/constraints/project_url_constrainer.rb
index eadfbf7bc01..d41490d2ebd 100644
--- a/lib/constraints/project_url_constrainer.rb
+++ b/lib/constraints/project_url_constrainer.rb
@@ -2,12 +2,13 @@
module Constraints
class ProjectUrlConstrainer
- def matches?(request)
+ def matches?(request, existence_check: true)
namespace_path = request.params[:namespace_id]
project_path = request.params[:project_id] || request.params[:id]
full_path = [namespace_path, project_path].join('/')
return false unless ProjectPathValidator.valid_path?(full_path)
+ return true unless existence_check
# We intentionally allow SELECT(*) here so result of this query can be used
# as cache for further Project.find_by_full_path calls within request
diff --git a/lib/declarative_policy/rule.rb b/lib/declarative_policy/rule.rb
index f38f4f0a50f..964d35cde9e 100644
--- a/lib/declarative_policy/rule.rb
+++ b/lib/declarative_policy/rule.rb
@@ -84,7 +84,7 @@ module DeclarativePolicy
# returns nil unless it's already cached
def cached_pass?(context)
condition = context.condition(@name)
- return nil unless condition.cached?
+ return unless condition.cached?
condition.pass?
end
@@ -124,7 +124,7 @@ module DeclarativePolicy
def cached_pass?(context)
condition = delegated_context(context).condition(@name)
- return nil unless condition.cached?
+ return unless condition.cached?
condition.pass?
rescue MissingDelegate
@@ -161,7 +161,7 @@ module DeclarativePolicy
def cached_pass?(context)
runner = context.runner(@ability)
- return nil unless runner.cached?
+ return unless runner.cached?
runner.pass?
end
diff --git a/lib/event_filter.rb b/lib/event_filter.rb
index 24fdcd6fbb1..85bf9c14f26 100644
--- a/lib/event_filter.rb
+++ b/lib/event_filter.rb
@@ -33,7 +33,7 @@ class EventFilter
when TEAM
events.where(action: [Event::JOINED, Event::LEFT, Event::EXPIRED])
when ISSUE
- events.where(action: [Event::CREATED, Event::UPDATED, Event::CLOSED, Event::REOPENED])
+ events.where(action: [Event::CREATED, Event::UPDATED, Event::CLOSED, Event::REOPENED], target_type: 'Issue')
else
events
end
diff --git a/lib/gitlab.rb b/lib/gitlab.rb
index e073450283b..f42ca5a9cd6 100644
--- a/lib/gitlab.rb
+++ b/lib/gitlab.rb
@@ -58,6 +58,10 @@ module Gitlab
Rails.env.development? || org? || com?
end
+ def self.ee?
+ Object.const_defined?(:License)
+ end
+
def self.process_name
return 'sidekiq' if Sidekiq.server?
return 'console' if defined?(Rails::Console)
diff --git a/lib/gitlab/access.rb b/lib/gitlab/access.rb
index ec090aea784..6c8ca8f219c 100644
--- a/lib/gitlab/access.rb
+++ b/lib/gitlab/access.rb
@@ -46,6 +46,12 @@ module Gitlab
)
end
+ def options_with_none
+ options_with_owner.merge(
+ "None" => NO_ACCESS
+ )
+ end
+
def sym_options
{
guest: GUEST,
@@ -75,12 +81,20 @@ module Gitlab
def human_access(access)
options_with_owner.key(access)
end
+
+ def human_access_with_none(access)
+ options_with_none.key(access)
+ end
end
def human_access
Gitlab::Access.human_access(access_field)
end
+ def human_access_with_none
+ Gitlab::Access.human_access_with_none(access_field)
+ end
+
def owner?
access_field == OWNER
end
diff --git a/lib/gitlab/auth/ldap/config.rb b/lib/gitlab/auth/ldap/config.rb
index 7ceb96f502b..47d63eb53cf 100644
--- a/lib/gitlab/auth/ldap/config.rb
+++ b/lib/gitlab/auth/ldap/config.rb
@@ -75,7 +75,8 @@ module Gitlab
encryption: options['encryption'],
filter: omniauth_user_filter,
name_proc: name_proc,
- disable_verify_certificates: !options['verify_certificates']
+ disable_verify_certificates: !options['verify_certificates'],
+ tls_options: tls_options
)
if has_auth?
@@ -85,9 +86,6 @@ module Gitlab
)
end
- opts[:ca_file] = options['ca_file'] if options['ca_file'].present?
- opts[:ssl_version] = options['ssl_version'] if options['ssl_version'].present?
-
opts
end
@@ -196,24 +194,28 @@ module Gitlab
end
def encryption_options
- method = translate_method(options['encryption'])
- return nil unless method
+ method = translate_method
+ return unless method
{
method: method,
- tls_options: tls_options(method)
+ tls_options: tls_options
}
end
- def translate_method(method_from_config)
- NET_LDAP_ENCRYPTION_METHOD[method_from_config.to_sym]
+ def translate_method
+ NET_LDAP_ENCRYPTION_METHOD[options['encryption']&.to_sym]
end
- def tls_options(method)
- return { verify_mode: OpenSSL::SSL::VERIFY_NONE } unless method
+ def tls_options
+ return @tls_options if defined?(@tls_options)
+
+ method = translate_method
+ return unless method
- opts = if options['verify_certificates']
- OpenSSL::SSL::SSLContext::DEFAULT_PARAMS
+ opts = if options['verify_certificates'] && method != 'plain'
+ # Dup so we don't accidentally overwrite the constant
+ OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.dup
else
# It is important to explicitly set verify_mode for two reasons:
# 1. The behavior of OpenSSL is undefined when verify_mode is not set.
@@ -222,10 +224,35 @@ module Gitlab
{ verify_mode: OpenSSL::SSL::VERIFY_NONE }
end
- opts[:ca_file] = options['ca_file'] if options['ca_file'].present?
- opts[:ssl_version] = options['ssl_version'] if options['ssl_version'].present?
+ opts.merge!(custom_tls_options)
- opts
+ @tls_options = opts
+ end
+
+ def custom_tls_options
+ return {} unless options['tls_options']
+
+ # Dup so we don't overwrite the original value
+ custom_options = options['tls_options'].dup.delete_if { |_, value| value.nil? || value.blank? }
+ custom_options.symbolize_keys!
+
+ if custom_options[:cert]
+ begin
+ custom_options[:cert] = OpenSSL::X509::Certificate.new(custom_options[:cert])
+ rescue OpenSSL::X509::CertificateError => e
+ Rails.logger.error "LDAP TLS Options 'cert' is invalid for provider #{provider}: #{e.message}"
+ end
+ end
+
+ if custom_options[:key]
+ begin
+ custom_options[:key] = OpenSSL::PKey.read(custom_options[:key])
+ rescue OpenSSL::PKey::PKeyError => e
+ Rails.logger.error "LDAP TLS Options 'key' is invalid for provider #{provider}: #{e.message}"
+ end
+ end
+
+ custom_options
end
def auth_options
diff --git a/lib/gitlab/auth/ldap/person.rb b/lib/gitlab/auth/ldap/person.rb
index 48d134f91b0..c1517222956 100644
--- a/lib/gitlab/auth/ldap/person.rb
+++ b/lib/gitlab/auth/ldap/person.rb
@@ -69,7 +69,7 @@ module Gitlab
end
def name
- attribute_value(:name).first
+ attribute_value(:name)&.first
end
def uid
@@ -112,7 +112,7 @@ module Gitlab
attributes = Array(config.attributes[attribute.to_s])
selected_attr = attributes.find { |attr| entry.respond_to?(attr) }
- return nil unless selected_attr
+ return unless selected_attr
entry.public_send(selected_attr) # rubocop:disable GitlabSecurity/PublicSend
end
diff --git a/lib/gitlab/auth/o_auth/user.rb b/lib/gitlab/auth/o_auth/user.rb
index f38c5d57c44..09d1d79fefc 100644
--- a/lib/gitlab/auth/o_auth/user.rb
+++ b/lib/gitlab/auth/o_auth/user.rb
@@ -146,7 +146,6 @@ module Gitlab
Gitlab::Auth::LDAP::Person.find_by_uid(auth_hash.uid, adapter) ||
Gitlab::Auth::LDAP::Person.find_by_email(auth_hash.uid, adapter) ||
Gitlab::Auth::LDAP::Person.find_by_dn(auth_hash.uid, adapter)
-
rescue Gitlab::Auth::LDAP::LDAPConnectionError
nil
end
@@ -200,22 +199,19 @@ module Gitlab
# Give preference to LDAP for sensitive information when creating a linked account
if creating_linked_ldap_user?
username = ldap_person.username.presence
+ name = ldap_person.name.presence
email = ldap_person.email.first.presence
end
username ||= auth_hash.username
+ name ||= auth_hash.name
email ||= auth_hash.email
valid_username = ::Namespace.clean_path(username)
-
- uniquify = Uniquify.new
- valid_username = uniquify.string(valid_username) { |s| !NamespacePathValidator.valid_path?(s) }
-
- name = auth_hash.name
- name = valid_username if name.strip.empty?
+ valid_username = Uniquify.new.string(valid_username) { |s| !NamespacePathValidator.valid_path?(s) }
{
- name: name,
+ name: name.strip.presence || valid_username,
username: valid_username,
email: email,
password: auth_hash.password,
@@ -248,8 +244,9 @@ module Gitlab
metadata.provider = auth_hash.provider
end
- if creating_linked_ldap_user? && gl_user.email == ldap_person.email.first
- metadata.set_attribute_synced(:email, true)
+ if creating_linked_ldap_user?
+ metadata.set_attribute_synced(:name, true) if gl_user.name == ldap_person.name
+ metadata.set_attribute_synced(:email, true) if gl_user.email == ldap_person.email.first
metadata.provider = ldap_person.provider
end
end
diff --git a/lib/gitlab/auth/omniauth_identity_linker_base.rb b/lib/gitlab/auth/omniauth_identity_linker_base.rb
index 253445570f2..c620fc5d6bd 100644
--- a/lib/gitlab/auth/omniauth_identity_linker_base.rb
+++ b/lib/gitlab/auth/omniauth_identity_linker_base.rb
@@ -12,7 +12,7 @@ module Gitlab
end
def link
- save if identity.new_record?
+ save if unlinked?
end
def changed?
@@ -35,6 +35,10 @@ module Gitlab
@changed = identity.save
end
+ def unlinked?
+ identity.new_record?
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def identity
@identity ||= current_user.identities
diff --git a/lib/gitlab/auth/saml/auth_hash.rb b/lib/gitlab/auth/saml/auth_hash.rb
index 1af9fa40c3a..b0df9757bbd 100644
--- a/lib/gitlab/auth/saml/auth_hash.rb
+++ b/lib/gitlab/auth/saml/auth_hash.rb
@@ -10,11 +10,11 @@ module Gitlab
def authn_context
response_object = auth_hash.extra[:response_object]
- return nil if response_object.blank?
+ return if response_object.blank?
document = response_object.decrypted_document
document ||= response_object.document
- return nil if document.blank?
+ return if document.blank?
extract_authn_context(document)
end
diff --git a/lib/gitlab/authorized_keys.rb b/lib/gitlab/authorized_keys.rb
new file mode 100644
index 00000000000..3fe72f5fd43
--- /dev/null
+++ b/lib/gitlab/authorized_keys.rb
@@ -0,0 +1,149 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class AuthorizedKeys
+ KeyError = Class.new(StandardError)
+
+ attr_reader :logger
+
+ # Initializes the class
+ #
+ # @param [Gitlab::Logger] logger
+ def initialize(logger = Gitlab::AppLogger)
+ @logger = logger
+ end
+
+ # Add id and its key to the authorized_keys file
+ #
+ # @param [String] id identifier of key prefixed by `key-`
+ # @param [String] key public key to be added
+ # @return [Boolean]
+ def add_key(id, key)
+ lock do
+ public_key = strip(key)
+ logger.info("Adding key (#{id}): #{public_key}")
+ open_authorized_keys_file('a') { |file| file.puts(key_line(id, public_key)) }
+ end
+
+ true
+ end
+
+ # Atomically add all the keys to the authorized_keys file
+ #
+ # @param [Array<::Key>] keys list of Key objects to be added
+ # @return [Boolean]
+ def batch_add_keys(keys)
+ lock(300) do # Allow 300 seconds (5 minutes) for batch_add_keys
+ open_authorized_keys_file('a') do |file|
+ keys.each do |key|
+ public_key = strip(key.key)
+ logger.info("Adding key (#{key.shell_id}): #{public_key}")
+ file.puts(key_line(key.shell_id, public_key))
+ end
+ end
+ end
+
+ true
+ rescue Gitlab::AuthorizedKeys::KeyError
+ false
+ end
+
+ # Remove key by ID from the authorized_keys file
+ #
+ # @param [String] id identifier of the key to be removed prefixed by `key-`
+ # @return [Boolean]
+ def rm_key(id)
+ lock do
+ logger.info("Removing key (#{id})")
+ open_authorized_keys_file('r+') do |f|
+ while line = f.gets
+ next unless line.start_with?("command=\"#{command(id)}\"")
+
+ f.seek(-line.length, IO::SEEK_CUR)
+ # Overwrite the line with #'s. Because the 'line' variable contains
+ # a terminating '\n', we write line.length - 1 '#' characters.
+ f.write('#' * (line.length - 1))
+ end
+ end
+ end
+
+ true
+ rescue Errno::ENOENT
+ false
+ end
+
+ # Clear the authorized_keys file
+ #
+ # @return [Boolean]
+ def clear
+ open_authorized_keys_file('w') { |file| file.puts '# Managed by gitlab-rails' }
+
+ true
+ end
+
+ # Read the authorized_keys file and return IDs of each key
+ #
+ # @return [Array<Integer>]
+ def list_key_ids
+ logger.info('Listing all key IDs')
+
+ [].tap do |a|
+ open_authorized_keys_file('r') do |f|
+ f.each_line do |line|
+ key_id = line.match(/key-(\d+)/)
+
+ next unless key_id
+
+ a << key_id[1].chomp.to_i
+ end
+ end
+ end
+ rescue Errno::ENOENT
+ []
+ end
+
+ private
+
+ def lock(timeout = 10)
+ File.open("#{authorized_keys_file}.lock", "w+") do |f|
+ f.flock File::LOCK_EX
+ Timeout.timeout(timeout) { yield }
+ ensure
+ f.flock File::LOCK_UN
+ end
+ end
+
+ def open_authorized_keys_file(mode)
+ File.open(authorized_keys_file, mode, 0o600) do |file|
+ file.chmod(0o600)
+ yield file
+ end
+ end
+
+ def key_line(id, key)
+ key = key.chomp
+
+ if key.include?("\n") || key.include?("\t")
+ raise KeyError, "Invalid public_key: #{key.inspect}"
+ end
+
+ %Q(command="#{command(id)}",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty #{strip(key)})
+ end
+
+ def command(id)
+ unless /\A[a-z0-9-]+\z/ =~ id
+ raise KeyError, "Invalid ID: #{id.inspect}"
+ end
+
+ "#{File.join(Gitlab.config.gitlab_shell.path, 'bin', 'gitlab-shell')} #{id}"
+ end
+
+ def strip(key)
+ key.split(/[ ]+/)[0, 2].join(' ')
+ end
+
+ def authorized_keys_file
+ Gitlab.config.gitlab_shell.authorized_keys_file
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/archive_legacy_traces.rb b/lib/gitlab/background_migration/archive_legacy_traces.rb
index 92096e29ef1..7ee783b8489 100644
--- a/lib/gitlab/background_migration/archive_legacy_traces.rb
+++ b/lib/gitlab/background_migration/archive_legacy_traces.rb
@@ -11,11 +11,10 @@ module Gitlab
# So we chose a way to use ::Ci::Build directly and we don't change the `archive!` method until 11.1
::Ci::Build.finished.without_archived_trace
.where(id: start_id..stop_id).find_each do |build|
- begin
- build.trace.archive!
- rescue => e
- Rails.logger.error "Failed to archive live trace. id: #{build.id} message: #{e.message}"
- end
+
+ build.trace.archive!
+ rescue => e
+ Rails.logger.error "Failed to archive live trace. id: #{build.id} message: #{e.message}"
end
end
end
diff --git a/lib/gitlab/background_migration/encrypt_columns.rb b/lib/gitlab/background_migration/encrypt_columns.rb
index b9ad8267e37..173543b7c25 100644
--- a/lib/gitlab/background_migration/encrypt_columns.rb
+++ b/lib/gitlab/background_migration/encrypt_columns.rb
@@ -91,7 +91,8 @@ module Gitlab
# No need to do anything if the plaintext is nil, or an encrypted
# value already exists
- return nil unless plaintext.present? && !ciphertext.present?
+ return unless plaintext.present?
+ return if ciphertext.present?
# attr_encrypted will calculate and set the expected value for us
instance.public_send("#{plain_column}=", plaintext) # rubocop:disable GitlabSecurity/PublicSend
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 38fecac1bfe..42fcaa87e66 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
@@ -24,7 +24,7 @@ module Gitlab
def commit_title
commit = commits.last
- return nil unless commit && commit[:message]
+ return unless commit && commit[:message]
index = commit[:message].index("\n")
message = index ? commit[:message][0..index] : commit[:message]
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 698f5e46c0c..48aa369705f 100644
--- a/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
+++ b/lib/gitlab/background_migration/normalize_ldap_extern_uids_range.rb
@@ -302,14 +302,12 @@ module Gitlab
ldap_identities = Identity.where("provider like 'ldap%'").where(id: start_id..end_id)
ldap_identities.each do |identity|
- begin
- identity.extern_uid = Gitlab::Auth::LDAP::DN.new(identity.extern_uid).to_normalized_s
- unless identity.save
- Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\". Skipping."
- end
- rescue Gitlab::Auth::LDAP::DN::FormatError => e
- Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\" due to \"#{e.message}\". Skipping."
+ identity.extern_uid = Gitlab::Auth::LDAP::DN.new(identity.extern_uid).to_normalized_s
+ unless identity.save
+ Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\". Skipping."
end
+ rescue Gitlab::Auth::LDAP::DN::FormatError => e
+ Rails.logger.info "Unable to normalize \"#{identity.extern_uid}\" due to \"#{e.message}\". Skipping."
end
end
diff --git a/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb b/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb
new file mode 100644
index 00000000000..a4c6540c61b
--- /dev/null
+++ b/lib/gitlab/background_migration/populate_merge_request_assignees_table.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # This background migration creates records on merge_request_assignees according
+ # to the given merge request IDs range. A _single_ INSERT is issued for the given range.
+ # This is required for supporting multiple assignees on merge requests.
+ class PopulateMergeRequestAssigneesTable
+ def perform(from_id, to_id)
+ select_sql =
+ MergeRequest
+ .where(merge_request_assignees_not_exists_clause)
+ .where(id: from_id..to_id)
+ .where('assignee_id IS NOT NULL')
+ .select(:id, :assignee_id)
+ .to_sql
+
+ execute("INSERT INTO merge_request_assignees (merge_request_id, user_id) #{select_sql}")
+ end
+
+ private
+
+ def merge_request_assignees_not_exists_clause
+ <<~SQL
+ NOT EXISTS (SELECT 1 FROM merge_request_assignees
+ WHERE merge_request_assignees.merge_request_id = merge_requests.id)
+ SQL
+ end
+
+ def execute(sql)
+ @connection ||= ActiveRecord::Base.connection
+ @connection.execute(sql)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/background_migration/populate_untracked_uploads.rb b/lib/gitlab/background_migration/populate_untracked_uploads.rb
index a19dc9747fb..755b5ee725a 100644
--- a/lib/gitlab/background_migration/populate_untracked_uploads.rb
+++ b/lib/gitlab/background_migration/populate_untracked_uploads.rb
@@ -34,18 +34,16 @@ module Gitlab
def filter_error_files(files)
files.partition do |file|
- begin
- file.to_h
- true
- rescue => e
- msg = <<~MSG
+ file.to_h
+ true
+ rescue => e
+ msg = <<~MSG
Error parsing path "#{file.path}":
#{e.message}
#{e.backtrace.join("\n ")}
MSG
- Rails.logger.error(msg)
- false
- end
+ Rails.logger.error(msg)
+ false
end
end
diff --git a/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
index 4a9a62aaeb5..a84f794bfae 100644
--- a/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
+++ b/lib/gitlab/background_migration/populate_untracked_uploads_dependencies.rb
@@ -127,7 +127,7 @@ module Gitlab
full_path = matchd[1]
project = Gitlab::BackgroundMigration::PopulateUntrackedUploadsDependencies::Project.find_by_full_path(full_path)
- return nil unless project
+ return unless project
project.id
end
diff --git a/lib/gitlab/badge/pipeline/template.rb b/lib/gitlab/badge/pipeline/template.rb
index 64c3dfcd10b..2c5f9654496 100644
--- a/lib/gitlab/badge/pipeline/template.rb
+++ b/lib/gitlab/badge/pipeline/template.rb
@@ -15,6 +15,7 @@ module Gitlab
failed: '#e05d44',
running: '#dfb317',
pending: '#dfb317',
+ preparing: '#dfb317',
canceled: '#9f9f9f',
skipped: '#9f9f9f',
unknown: '#9f9f9f'
diff --git a/lib/gitlab/bare_repository_import/importer.rb b/lib/gitlab/bare_repository_import/importer.rb
index 3cd327f5109..144ba2ec031 100644
--- a/lib/gitlab/bare_repository_import/importer.rb
+++ b/lib/gitlab/bare_repository_import/importer.rb
@@ -108,7 +108,7 @@ module Gitlab
end
def find_or_create_groups
- return nil unless group_path.present?
+ return unless group_path.present?
log " * Using namespace: #{group_path}"
diff --git a/lib/gitlab/bitbucket_import/importer.rb b/lib/gitlab/bitbucket_import/importer.rb
index 75a3f17f549..769d3279f91 100644
--- a/lib/gitlab/bitbucket_import/importer.rb
+++ b/lib/gitlab/bitbucket_import/importer.rb
@@ -47,7 +47,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def find_user_id(username)
- return nil unless username
+ return unless username
return users[username] if users.key?(username)
@@ -79,31 +79,29 @@ module Gitlab
create_labels
client.issues(repo).each do |issue|
- begin
- description = ''
- description += @formatter.author_line(issue.author) unless find_user_id(issue.author)
- description += issue.description
-
- label_name = issue.kind
- milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil
-
- gitlab_issue = project.issues.create!(
- iid: issue.iid,
- title: issue.title,
- description: description,
- state: issue.state,
- author_id: gitlab_user_id(project, issue.author),
- milestone: milestone,
- created_at: issue.created_at,
- updated_at: issue.updated_at
- )
-
- gitlab_issue.labels << @labels[label_name]
-
- import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted?
- rescue StandardError => e
- errors << { type: :issue, iid: issue.iid, errors: e.message }
- end
+ description = ''
+ description += @formatter.author_line(issue.author) unless find_user_id(issue.author)
+ description += issue.description
+
+ label_name = issue.kind
+ milestone = issue.milestone ? project.milestones.find_or_create_by(title: issue.milestone) : nil
+
+ gitlab_issue = project.issues.create!(
+ iid: issue.iid,
+ title: issue.title,
+ description: description,
+ state: issue.state,
+ author_id: gitlab_user_id(project, issue.author),
+ milestone: milestone,
+ created_at: issue.created_at,
+ updated_at: issue.updated_at
+ )
+
+ gitlab_issue.labels << @labels[label_name]
+
+ import_issue_comments(issue, gitlab_issue) if gitlab_issue.persisted?
+ rescue StandardError => e
+ errors << { type: :issue, iid: issue.iid, errors: e.message }
end
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -150,37 +148,35 @@ module Gitlab
pull_requests = client.pull_requests(repo)
pull_requests.each do |pull_request|
- begin
- description = ''
- description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author)
- description += pull_request.description
-
- source_branch_sha = pull_request.source_branch_sha
- target_branch_sha = pull_request.target_branch_sha
- source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha
- target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha
-
- merge_request = project.merge_requests.create!(
- iid: pull_request.iid,
- title: pull_request.title,
- description: description,
- source_project: project,
- source_branch: pull_request.source_branch_name,
- source_branch_sha: source_branch_sha,
- target_project: project,
- target_branch: pull_request.target_branch_name,
- target_branch_sha: target_branch_sha,
- state: pull_request.state,
- author_id: gitlab_user_id(project, pull_request.author),
- assignee_id: nil,
- created_at: pull_request.created_at,
- updated_at: pull_request.updated_at
- )
-
- import_pull_request_comments(pull_request, merge_request) if merge_request.persisted?
- 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
+ description = ''
+ description += @formatter.author_line(pull_request.author) unless find_user_id(pull_request.author)
+ description += pull_request.description
+
+ source_branch_sha = pull_request.source_branch_sha
+ target_branch_sha = pull_request.target_branch_sha
+ source_branch_sha = project.repository.commit(source_branch_sha)&.sha || source_branch_sha
+ target_branch_sha = project.repository.commit(target_branch_sha)&.sha || target_branch_sha
+
+ merge_request = project.merge_requests.create!(
+ iid: pull_request.iid,
+ title: pull_request.title,
+ description: description,
+ source_project: project,
+ source_branch: pull_request.source_branch_name,
+ source_branch_sha: source_branch_sha,
+ target_project: project,
+ target_branch: pull_request.target_branch_name,
+ target_branch_sha: target_branch_sha,
+ state: pull_request.state,
+ author_id: gitlab_user_id(project, pull_request.author),
+ assignee_id: nil,
+ created_at: pull_request.created_at,
+ updated_at: pull_request.updated_at
+ )
+
+ import_pull_request_comments(pull_request, merge_request) if merge_request.persisted?
+ 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
@@ -211,23 +207,21 @@ module Gitlab
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 = pull_request_comment_attributes(comment)
+ attributes[:discussion_id] = discussion_map[comment.parent_id] if comment.has_parent?
- attributes.merge!(
- position: position_map[comment.iid],
- type: 'DiffNote')
+ attributes.merge!(
+ position: position_map[comment.iid],
+ type: 'DiffNote')
- note = 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
+ # 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
end
@@ -245,11 +239,9 @@ module Gitlab
def import_standalone_pr_comments(pr_comments, merge_request)
pr_comments.each do |comment|
- begin
- merge_request.notes.create!(pull_request_comment_attributes(comment))
- rescue StandardError => e
- errors << { type: :pull_request, iid: comment.iid, errors: e.message }
- end
+ merge_request.notes.create!(pull_request_comment_attributes(comment))
+ rescue StandardError => e
+ errors << { type: :pull_request, iid: comment.iid, errors: e.message }
end
end
diff --git a/lib/gitlab/bitbucket_server_import/importer.rb b/lib/gitlab/bitbucket_server_import/importer.rb
index dbbedd5dcbe..1d3ddeeb0f1 100644
--- a/lib/gitlab/bitbucket_server_import/importer.rb
+++ b/lib/gitlab/bitbucket_server_import/importer.rb
@@ -65,7 +65,7 @@ module Gitlab
end
def find_user_id(email)
- return nil unless email
+ return unless email
return users[email] if users.key?(email)
@@ -162,27 +162,23 @@ module Gitlab
restore_branches(batch) if recover_missing_commits
batch.each do |pull_request|
- begin
- import_bitbucket_pull_request(pull_request)
- rescue StandardError => e
- backtrace = Gitlab::Profiler.clean_backtrace(e.backtrace)
- log_error(stage: 'import_pull_requests', iid: pull_request.iid, error: e.message, backtrace: backtrace)
-
- errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, backtrace: backtrace.join("\n"), raw_response: pull_request.raw }
- end
+ import_bitbucket_pull_request(pull_request)
+ rescue StandardError => e
+ backtrace = Gitlab::Profiler.clean_backtrace(e.backtrace)
+ log_error(stage: 'import_pull_requests', iid: pull_request.iid, error: e.message, backtrace: backtrace)
+
+ errors << { type: :pull_request, iid: pull_request.iid, errors: e.message, backtrace: backtrace.join("\n"), raw_response: pull_request.raw }
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
- log_error(stage: 'delete_temp_branches', branch: branch.name, error: e.message)
- @errors << { type: :delete_temp_branches, branch_name: branch.name, errors: e.message }
- end
+ client.delete_branch(project_key, repository_slug, branch.name, branch.sha)
+ project.repository.delete_branch(branch.name)
+ rescue BitbucketServer::Connection::ConnectionError => e
+ log_error(stage: 'delete_temp_branches', branch: branch.name, error: e.message)
+ @errors << { type: :delete_temp_branches, branch_name: branch.name, errors: e.message }
end
end
@@ -323,16 +319,14 @@ module Gitlab
def import_standalone_pr_comments(pr_comments, merge_request)
pr_comments.each do |comment|
- begin
- merge_request.notes.create!(pull_request_comment_attributes(comment))
+ 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
- log_error(stage: 'import_standalone_pr_comments', merge_request_id: merge_request.id, comment_id: comment.id, error: e.message)
- errors << { type: :pull_request, comment_id: comment.id, errors: e.message }
+ comment.comments.each do |replies|
+ merge_request.notes.create!(pull_request_comment_attributes(replies))
end
+ rescue StandardError => e
+ log_error(stage: 'import_standalone_pr_comments', merge_request_id: merge_request.id, comment_id: comment.id, error: e.message)
+ errors << { type: :pull_request, comment_id: comment.id, errors: e.message }
end
end
diff --git a/lib/gitlab/chat.rb b/lib/gitlab/chat.rb
new file mode 100644
index 00000000000..23d4fb36b66
--- /dev/null
+++ b/lib/gitlab/chat.rb
@@ -0,0 +1,10 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ # Returns `true` if Chatops is available for the current instance.
+ def self.available?
+ ::Feature.enabled?(:chatops, default_enabled: true)
+ end
+ end
+end
diff --git a/lib/gitlab/chat/command.rb b/lib/gitlab/chat/command.rb
new file mode 100644
index 00000000000..49b7dcf4bbe
--- /dev/null
+++ b/lib/gitlab/chat/command.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ # Class for scheduling chat pipelines.
+ #
+ # A Command takes care of creating a `Ci::Pipeline` with all the data
+ # necessary to execute a chat command. This includes data such as the chat
+ # data (e.g. the response URL) and any environment variables that should be
+ # exposed to the chat command.
+ class Command
+ include Utils::StrongMemoize
+
+ attr_reader :project, :chat_name, :name, :arguments, :response_url,
+ :channel
+
+ # project - The Project to schedule the command for.
+ # chat_name - The ChatName belonging to the user that scheduled the
+ # command.
+ # name - The name of the chat command to run.
+ # arguments - The arguments (as a String) to pass to the command.
+ # channel - The channel the message was sent from.
+ # response_url - The URL to send the response back to.
+ def initialize(project:, chat_name:, name:, arguments:, channel:, response_url:)
+ @project = project
+ @chat_name = chat_name
+ @name = name
+ @arguments = arguments
+ @channel = channel
+ @response_url = response_url
+ end
+
+ # Tries to create a new pipeline.
+ #
+ # This method will return a pipeline that _may_ be persisted, or `nil` if
+ # the pipeline could not be created.
+ def try_create_pipeline
+ return unless valid?
+
+ create_pipeline
+ end
+
+ def create_pipeline
+ service = ::Ci::CreatePipelineService.new(
+ project,
+ chat_name.user,
+ ref: branch,
+ sha: commit,
+ chat_data: {
+ chat_name_id: chat_name.id,
+ command: name,
+ arguments: arguments,
+ response_url: response_url
+ }
+ )
+
+ service.execute(:chat) do |pipeline|
+ build_environment_variables(pipeline)
+ build_chat_data(pipeline)
+ end
+ end
+
+ # pipeline - The `Ci::Pipeline` to create the environment variables for.
+ def build_environment_variables(pipeline)
+ pipeline.variables.build(
+ [{ key: 'CHAT_INPUT', value: arguments },
+ { key: 'CHAT_CHANNEL', value: channel }]
+ )
+ end
+
+ # pipeline - The `Ci::Pipeline` to create the chat data for.
+ def build_chat_data(pipeline)
+ pipeline.build_chat_data(
+ chat_name_id: chat_name.id,
+ response_url: response_url
+ )
+ end
+
+ def valid?
+ branch && commit
+ end
+
+ def branch
+ strong_memoize(:branch) { project.default_branch }
+ end
+
+ def commit
+ strong_memoize(:commit) do
+ project.commit(branch)&.id if branch
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat/output.rb b/lib/gitlab/chat/output.rb
new file mode 100644
index 00000000000..411b1555a7d
--- /dev/null
+++ b/lib/gitlab/chat/output.rb
@@ -0,0 +1,93 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ # Class for gathering and formatting the output of a `Ci::Build`.
+ class Output
+ attr_reader :build
+
+ MissingBuildSectionError = Class.new(StandardError)
+
+ # The primary trace section to look for.
+ PRIMARY_SECTION = 'chat_reply'
+
+ # The backup trace section in case the primary one could not be found.
+ FALLBACK_SECTION = 'build_script'
+
+ # build - The `Ci::Build` to obtain the output from.
+ def initialize(build)
+ @build = build
+ end
+
+ # Returns a `String` containing the output of the build.
+ #
+ # The output _does not_ include the command that was executed.
+ def to_s
+ offset, length = read_offset_and_length
+
+ trace.read do |stream|
+ stream.seek(offset)
+
+ output = stream
+ .stream
+ .read(length)
+ .force_encoding(Encoding.default_external)
+
+ without_executed_command_line(output)
+ end
+ end
+
+ # Returns the offset to seek to and the number of bytes to read relative
+ # to the offset.
+ def read_offset_and_length
+ section = find_build_trace_section(PRIMARY_SECTION) ||
+ find_build_trace_section(FALLBACK_SECTION)
+
+ unless section
+ raise(
+ MissingBuildSectionError,
+ "The build_script trace section could not be found for build #{build.id}"
+ )
+ end
+
+ length = section[:byte_end] - section[:byte_start]
+
+ [section[:byte_start], length]
+ end
+
+ # Removes the line containing the executed command from the build output.
+ #
+ # output - A `String` containing the output of a trace section.
+ def without_executed_command_line(output)
+ # If `output.split("\n")` produces an empty Array then the slicing that
+ # follows it will produce a nil. For example:
+ #
+ # "\n".split("\n") # => []
+ # "\n".split("\n")[1..-1] # => nil
+ #
+ # To work around this we only "join" if we're given an Array.
+ if (converted = output.split("\n")[1..-1])
+ converted.join("\n")
+ else
+ ''
+ end
+ end
+
+ # Returns the trace section for the given name, or `nil` if the section
+ # could not be found.
+ #
+ # name - The name of the trace section to find.
+ def find_build_trace_section(name)
+ trace_sections.find { |s| s[:name] == name }
+ end
+
+ def trace_sections
+ @trace_sections ||= trace.extract_sections
+ end
+
+ def trace
+ @trace ||= build.trace
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat/responder.rb b/lib/gitlab/chat/responder.rb
new file mode 100644
index 00000000000..6267fbc20e2
--- /dev/null
+++ b/lib/gitlab/chat/responder.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ module Responder
+ # Returns an instance of the responder to use for generating chat
+ # responses.
+ #
+ # This method will return `nil` if no formatter is available for the given
+ # build.
+ #
+ # build - A `Ci::Build` that executed a chat command.
+ def self.responder_for(build)
+ service = build.pipeline.chat_data&.chat_name&.service
+
+ if (responder = service.try(:chat_responder))
+ responder.new(build)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat/responder/base.rb b/lib/gitlab/chat/responder/base.rb
new file mode 100644
index 00000000000..f1ad0e36793
--- /dev/null
+++ b/lib/gitlab/chat/responder/base.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ module Responder
+ class Base
+ attr_reader :build
+
+ # build - The `Ci::Build` that was executed.
+ def initialize(build)
+ @build = build
+ end
+
+ def pipeline
+ build.pipeline
+ end
+
+ def project
+ pipeline.project
+ end
+
+ def success(*)
+ raise NotImplementedError, 'You must implement #success(output)'
+ end
+
+ def failure
+ raise NotImplementedError, 'You must implement #failure'
+ end
+
+ def send_response(output)
+ raise NotImplementedError, 'You must implement #send_response(output)'
+ end
+
+ def scheduled_output
+ raise NotImplementedError, 'You must implement #scheduled_output'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/chat/responder/slack.rb b/lib/gitlab/chat/responder/slack.rb
new file mode 100644
index 00000000000..0cf02c92a67
--- /dev/null
+++ b/lib/gitlab/chat/responder/slack.rb
@@ -0,0 +1,80 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Chat
+ module Responder
+ class Slack < Responder::Base
+ SUCCESS_COLOR = '#B3ED8E'
+ FAILURE_COLOR = '#FF5640'
+ RESPONSE_TYPE = :in_channel
+
+ # Slack breaks messages apart if they're around 4 KB in size. We use a
+ # slightly smaller limit here to account for user mentions.
+ MESSAGE_SIZE_LIMIT = 3.5.kilobytes
+
+ # Sends a response back to Slack
+ #
+ # output - The output to send back to Slack, as a Hash.
+ def send_response(output)
+ Gitlab::HTTP.post(
+ pipeline.chat_data.response_url,
+ {
+ headers: { Accept: 'application/json' },
+ body: output.to_json
+ }
+ )
+ end
+
+ # Sends the output for a build that completed successfully.
+ #
+ # output - The output produced by the chat command.
+ def success(output)
+ return if output.empty?
+
+ send_response(
+ text: message_text(limit_output(output)),
+ response_type: RESPONSE_TYPE
+ )
+ end
+
+ # Sends the output for a build that failed.
+ def failure
+ send_response(
+ text: message_text("<#{build_url}|Sorry, the build failed!>"),
+ response_type: RESPONSE_TYPE
+ )
+ end
+
+ # Returns the output to send back after a command has been scheduled.
+ def scheduled_output
+ # We return an empty message so that Slack still shows the input
+ # command, without polluting the channel with standard "The job has
+ # been scheduled" (or similar) responses.
+ { text: '' }
+ end
+
+ private
+
+ def limit_output(output)
+ if output.bytesize <= MESSAGE_SIZE_LIMIT
+ output
+ else
+ "<#{build_url}|The output is too large to be sent back directly!>"
+ end
+ end
+
+ def mention_user
+ "<@#{pipeline.chat_data.chat_name.chat_id}>"
+ end
+
+ def message_text(output)
+ "#{mention_user}: #{output}"
+ end
+
+ def build_url
+ ::Gitlab::Routing.url_helpers.project_build_url(project, build)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/checks/branch_check.rb b/lib/gitlab/checks/branch_check.rb
index d06b2df36f2..1dbd564fb6f 100644
--- a/lib/gitlab/checks/branch_check.rb
+++ b/lib/gitlab/checks/branch_check.rb
@@ -9,13 +9,17 @@ module Gitlab
non_master_delete_protected_branch: 'You are not allowed to delete protected branches from this project. Only a project maintainer or owner can delete a protected branch.',
non_web_delete_protected_branch: 'You can only delete protected branches using the web interface.',
merge_protected_branch: 'You are not allowed to merge code into protected branches on this project.',
- push_protected_branch: 'You are not allowed to push code to protected branches on this project.'
+ push_protected_branch: 'You are not allowed to push code to protected branches on this project.',
+ create_protected_branch: 'You are not allowed to create protected branches on this project.',
+ invalid_commit_create_protected_branch: 'You can only use an existing protected branch ref as the basis of a new protected branch.',
+ non_web_create_protected_branch: 'You can only create protected branches using the web interface and API.'
}.freeze
LOG_MESSAGES = {
delete_default_branch_check: "Checking if default branch is being deleted...",
protected_branch_checks: "Checking if you are force pushing to a protected branch...",
protected_branch_push_checks: "Checking if you are allowed to push to the protected branch...",
+ protected_branch_creation_checks: "Checking if you are allowed to create a protected branch...",
protected_branch_deletion_checks: "Checking if you are allowed to delete the protected branch..."
}.freeze
@@ -42,13 +46,35 @@ module Gitlab
end
end
- if deletion?
+ if project.empty_repo?
+ protected_branch_push_checks
+ elsif creation? && protected_branch_creation_enabled?
+ protected_branch_creation_checks
+ elsif deletion?
protected_branch_deletion_checks
else
protected_branch_push_checks
end
end
+ def protected_branch_creation_checks
+ logger.log_timed(LOG_MESSAGES[:protected_branch_creation_checks]) do
+ break if user_access.can_push_to_branch?(branch_name)
+
+ unless user_access.can_merge_to_branch?(branch_name)
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:create_protected_branch]
+ end
+
+ unless safe_commit_for_new_protected_branch?
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:invalid_commit_create_protected_branch]
+ end
+
+ unless updated_from_web?
+ raise GitAccess::UnauthorizedError, ERROR_MESSAGES[:non_web_create_protected_branch]
+ end
+ end
+ end
+
def protected_branch_deletion_checks
logger.log_timed(LOG_MESSAGES[:protected_branch_deletion_checks]) do
unless user_access.can_delete_branch?(branch_name)
@@ -98,6 +124,10 @@ module Gitlab
Gitlab::Routing.url_helpers.project_project_members_url(project)
end
+ def protected_branch_creation_enabled?
+ Feature.enabled?(:protected_branch_creation, project, default_enabled: true)
+ end
+
def matching_merge_request?
Checks::MatchingMergeRequest.new(newrev, branch_name, project).match?
end
@@ -105,6 +135,10 @@ module Gitlab
def forced_push?
Gitlab::Checks::ForcePush.force_push?(project, oldrev, newrev)
end
+
+ def safe_commit_for_new_protected_branch?
+ ProtectedBranch.any_protected?(project, project.repository.branch_names_contains_sha(newrev))
+ end
end
end
end
diff --git a/lib/gitlab/ci/ansi2html.rb b/lib/gitlab/ci/ansi2html.rb
index 4dcb3869d4f..fba0de20ced 100644
--- a/lib/gitlab/ci/ansi2html.rb
+++ b/lib/gitlab/ci/ansi2html.rb
@@ -313,7 +313,7 @@ module Gitlab
def get_term_color_class(color_index, prefix)
color_name = COLOR[color_index]
- return nil if color_name.nil?
+ return if color_name.nil?
get_color_class(["term", prefix, color_name])
end
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
index 08dac756cc1..7011dd1aaf2 100644
--- a/lib/gitlab/ci/build/artifacts/metadata.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -98,12 +98,12 @@ module Gitlab
def read_uint32(gz)
binary = gz.read(4)
- binary.unpack('L>')[0] if binary
+ binary.unpack1('L>') if binary
end
def read_string(gz)
string_size = read_uint32(gz)
- return nil unless string_size
+ return unless string_size
gz.read(string_size)
end
diff --git a/lib/gitlab/ci/build/artifacts/metadata/entry.rb b/lib/gitlab/ci/build/artifacts/metadata/entry.rb
index d0a80518ae8..80e69cdcc95 100644
--- a/lib/gitlab/ci/build/artifacts/metadata/entry.rb
+++ b/lib/gitlab/ci/build/artifacts/metadata/entry.rb
@@ -44,7 +44,7 @@ module Gitlab
end
def parent
- return nil unless has_parent?
+ return unless has_parent?
self.class.new(@path.to_s.chomp(basename), @entries)
end
diff --git a/lib/gitlab/ci/build/policy/refs.rb b/lib/gitlab/ci/build/policy/refs.rb
index 0e9bb5c94bb..df5f5ffc253 100644
--- a/lib/gitlab/ci/build/policy/refs.rb
+++ b/lib/gitlab/ci/build/policy/refs.rb
@@ -29,8 +29,8 @@ module Gitlab
def matches_pattern?(pattern, pipeline)
return true if pipeline.tag? && pattern == 'tags'
return true if pipeline.branch? && pattern == 'branches'
- return true if pipeline.source == pattern
- return true if pipeline.source&.pluralize == pattern
+ return true if sanitized_source_name(pipeline) == pattern
+ return true if sanitized_source_name(pipeline)&.pluralize == pattern
# patterns can be matched only when branch or tag is used
# the pattern matching does not work for merge requests pipelines
@@ -42,6 +42,10 @@ module Gitlab
end
end
end
+
+ def sanitized_source_name(pipeline)
+ @sanitized_source_name ||= pipeline&.source&.delete_suffix('_event')
+ end
end
end
end
diff --git a/lib/gitlab/ci/build/prerequisite/base.rb b/lib/gitlab/ci/build/prerequisite/base.rb
new file mode 100644
index 00000000000..156aa22d95b
--- /dev/null
+++ b/lib/gitlab/ci/build/prerequisite/base.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Prerequisite
+ class Base
+ include Utils::StrongMemoize
+
+ attr_reader :build
+
+ def initialize(build)
+ @build = build
+ end
+
+ def unmet?
+ raise NotImplementedError
+ end
+
+ def complete!
+ raise NotImplementedError
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/prerequisite/factory.rb b/lib/gitlab/ci/build/prerequisite/factory.rb
new file mode 100644
index 00000000000..60cdf7af418
--- /dev/null
+++ b/lib/gitlab/ci/build/prerequisite/factory.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Prerequisite
+ class Factory
+ attr_reader :build
+
+ def self.prerequisites
+ [KubernetesNamespace]
+ end
+
+ def initialize(build)
+ @build = build
+ end
+
+ def unmet
+ build_prerequisites.select(&:unmet?)
+ end
+
+ private
+
+ def build_prerequisites
+ self.class.prerequisites.map do |prerequisite|
+ prerequisite.new(build)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
new file mode 100644
index 00000000000..41135ae62bb
--- /dev/null
+++ b/lib/gitlab/ci/build/prerequisite/kubernetes_namespace.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Build
+ module Prerequisite
+ class KubernetesNamespace < Base
+ def unmet?
+ deployment_cluster.present? && kubernetes_namespace.new_record?
+ end
+
+ def complete!
+ return unless unmet?
+
+ create_or_update_namespace
+ end
+
+ private
+
+ def deployment_cluster
+ build.deployment&.cluster
+ end
+
+ def kubernetes_namespace
+ strong_memoize(:kubernetes_namespace) do
+ deployment_cluster.find_or_initialize_kubernetes_namespace_for_project(build.project)
+ end
+ end
+
+ def create_or_update_namespace
+ Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService.new(
+ cluster: deployment_cluster,
+ kubernetes_namespace: kubernetes_namespace
+ ).execute
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config.rb b/lib/gitlab/ci/config.rb
index 5875479183e..15643fa03ac 100644
--- a/lib/gitlab/ci/config.rb
+++ b/lib/gitlab/ci/config.rb
@@ -84,7 +84,8 @@ module Gitlab
Config::External::Processor.new(config,
project: project,
sha: sha || project.repository.root_ref_sha,
- user: user).perform
+ user: user,
+ expandset: Set.new).perform
end
end
end
diff --git a/lib/gitlab/ci/config/entry/global.rb b/lib/gitlab/ci/config/entry/global.rb
index 09ecb5fdb99..2b5a59c078e 100644
--- a/lib/gitlab/ci/config/entry/global.rb
+++ b/lib/gitlab/ci/config/entry/global.rb
@@ -17,6 +17,9 @@ module Gitlab
entry :image, Entry::Image,
description: 'Docker image that will be used to execute jobs.'
+ entry :include, Entry::Includes,
+ description: 'List of external YAML files to include.'
+
entry :services, Entry::Services,
description: 'Docker images that will be linked to the container.'
diff --git a/lib/gitlab/ci/config/entry/include.rb b/lib/gitlab/ci/config/entry/include.rb
new file mode 100644
index 00000000000..f2f3dd84eda
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/include.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a single include.
+ #
+ class Include < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ ALLOWED_KEYS = %i[local file remote template].freeze
+
+ validations do
+ validates :config, hash_or_string: true
+ validates :config, allowed_keys: ALLOWED_KEYS
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/entry/includes.rb b/lib/gitlab/ci/config/entry/includes.rb
new file mode 100644
index 00000000000..82b2b1ccf4b
--- /dev/null
+++ b/lib/gitlab/ci/config/entry/includes.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ class Config
+ module Entry
+ ##
+ # Entry that represents a list of include.
+ #
+ class Includes < ::Gitlab::Config::Entry::Node
+ include ::Gitlab::Config::Entry::Validatable
+
+ validations do
+ validates :config, type: Array
+ end
+
+ def self.aspects
+ super.append -> do
+ @config = Array.wrap(@config)
+
+ @config.each_with_index do |config, i|
+ @entries[i] = ::Gitlab::Config::Entry::Factory.new(Entry::Include)
+ .value(config || {})
+ .create!
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/config/external/file/base.rb b/lib/gitlab/ci/config/external/file/base.rb
index a747886093c..2ffbb214a92 100644
--- a/lib/gitlab/ci/config/external/file/base.rb
+++ b/lib/gitlab/ci/config/external/file/base.rb
@@ -12,7 +12,7 @@ module Gitlab
YAML_WHITELIST_EXTENSION = /.+\.(yml|yaml)$/i.freeze
- Context = Struct.new(:project, :sha, :user)
+ Context = Struct.new(:project, :sha, :user, :expandset)
def initialize(params, context)
@params = params
@@ -43,13 +43,27 @@ module Gitlab
end
def to_hash
- @hash ||= Gitlab::Config::Loader::Yaml.new(content).load!
- rescue Gitlab::Config::Loader::FormatError
- nil
+ expanded_content_hash
end
protected
+ def expanded_content_hash
+ return unless content_hash
+
+ strong_memoize(:expanded_content_yaml) do
+ expand_includes(content_hash)
+ end
+ end
+
+ def content_hash
+ strong_memoize(:content_yaml) do
+ Gitlab::Config::Loader::Yaml.new(content).load!
+ end
+ rescue Gitlab::Config::Loader::FormatError
+ nil
+ end
+
def validate!
validate_location!
validate_content! if errors.none?
@@ -73,6 +87,14 @@ module Gitlab
errors.push("Included file `#{location}` does not have valid YAML syntax!")
end
end
+
+ def expand_includes(hash)
+ External::Processor.new(hash, **expand_context).perform
+ end
+
+ def expand_context
+ { project: nil, sha: nil, user: nil, expandset: context.expandset }
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/external/file/local.rb b/lib/gitlab/ci/config/external/file/local.rb
index 2535d178ba8..229a06451e8 100644
--- a/lib/gitlab/ci/config/external/file/local.rb
+++ b/lib/gitlab/ci/config/external/file/local.rb
@@ -31,6 +31,13 @@ module Gitlab
def fetch_local_content
context.project.repository.blob_data_at(context.sha, location)
end
+
+ def expand_context
+ super.merge(
+ project: context.project,
+ sha: context.sha,
+ user: context.user)
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/external/file/project.rb b/lib/gitlab/ci/config/external/file/project.rb
index e75540dbe5a..b828f77835c 100644
--- a/lib/gitlab/ci/config/external/file/project.rb
+++ b/lib/gitlab/ci/config/external/file/project.rb
@@ -64,6 +64,13 @@ module Gitlab
project.commit(ref_name).try(:sha)
end
end
+
+ def expand_context
+ super.merge(
+ project: project,
+ sha: sha,
+ user: context.user)
+ end
end
end
end
diff --git a/lib/gitlab/ci/config/external/mapper.rb b/lib/gitlab/ci/config/external/mapper.rb
index 108bfd5eb43..aff5c5b9651 100644
--- a/lib/gitlab/ci/config/external/mapper.rb
+++ b/lib/gitlab/ci/config/external/mapper.rb
@@ -7,6 +7,8 @@ module Gitlab
class Mapper
include Gitlab::Utils::StrongMemoize
+ MAX_INCLUDES = 50
+
FILE_CLASSES = [
External::File::Remote,
External::File::Template,
@@ -14,25 +16,34 @@ module Gitlab
External::File::Project
].freeze
- AmbigiousSpecificationError = Class.new(StandardError)
+ Error = Class.new(StandardError)
+ AmbigiousSpecificationError = Class.new(Error)
+ DuplicateIncludesError = Class.new(Error)
+ TooManyIncludesError = Class.new(Error)
+
+ def initialize(values, project:, sha:, user:, expandset:)
+ raise Error, 'Expanded needs to be `Set`' unless expandset.is_a?(Set)
- def initialize(values, project:, sha:, user:)
@locations = Array.wrap(values.fetch(:include, []))
@project = project
@sha = sha
@user = user
+ @expandset = expandset
end
def process
+ return [] if locations.empty?
+
locations
.compact
.map(&method(:normalize_location))
+ .each(&method(:verify_duplicates!))
.map(&method(:select_first_matching))
end
private
- attr_reader :locations, :project, :sha, :user
+ attr_reader :locations, :project, :sha, :user, :expandset
# convert location if String to canonical form
def normalize_location(location)
@@ -51,6 +62,23 @@ module Gitlab
end
end
+ def verify_duplicates!(location)
+ if expandset.count >= MAX_INCLUDES
+ raise TooManyIncludesError, "Maximum of #{MAX_INCLUDES} nested includes are allowed!"
+ end
+
+ # We scope location to context, as this allows us to properly support
+ # relative incldues, and similarly looking relative in another project
+ # does not trigger duplicate error
+ scoped_location = location.merge(
+ context_project: project,
+ context_sha: sha)
+
+ unless expandset.add?(scoped_location)
+ raise DuplicateIncludesError, "Include `#{location.to_json}` was already included!"
+ end
+ end
+
def select_first_matching(location)
matching = FILE_CLASSES.map do |file_class|
file_class.new(location, context)
@@ -63,7 +91,7 @@ module Gitlab
def context
strong_memoize(:context) do
- External::File::Base::Context.new(project, sha, user)
+ External::File::Base::Context.new(project, sha, user, expandset)
end
end
end
diff --git a/lib/gitlab/ci/config/external/processor.rb b/lib/gitlab/ci/config/external/processor.rb
index 69bc164a039..1dd2d42016a 100644
--- a/lib/gitlab/ci/config/external/processor.rb
+++ b/lib/gitlab/ci/config/external/processor.rb
@@ -7,11 +7,11 @@ module Gitlab
class Processor
IncludeError = Class.new(StandardError)
- def initialize(values, project:, sha:, user:)
+ def initialize(values, project:, sha:, user:, expandset:)
@values = values
- @external_files = External::Mapper.new(values, project: project, sha: sha, user: user).process
+ @external_files = External::Mapper.new(values, project: project, sha: sha, user: user, expandset: expandset).process
@content = {}
- rescue External::Mapper::AmbigiousSpecificationError => e
+ rescue External::Mapper::Error => e
raise IncludeError, e.message
end
diff --git a/lib/gitlab/ci/model.rb b/lib/gitlab/ci/model.rb
index fbdb84c0522..1625cb841b6 100644
--- a/lib/gitlab/ci/model.rb
+++ b/lib/gitlab/ci/model.rb
@@ -8,7 +8,7 @@ module Gitlab
end
def model_name
- @model_name ||= ActiveModel::Name.new(self, nil, self.name.split("::").last)
+ @model_name ||= ActiveModel::Name.new(self, nil, self.name.demodulize)
end
end
end
diff --git a/lib/gitlab/ci/pipeline/chain/build.rb b/lib/gitlab/ci/pipeline/chain/build.rb
index 41632211374..164a4634d84 100644
--- a/lib/gitlab/ci/pipeline/chain/build.rb
+++ b/lib/gitlab/ci/pipeline/chain/build.rb
@@ -12,6 +12,8 @@ module Gitlab
ref: @command.ref,
sha: @command.sha,
before_sha: @command.before_sha,
+ source_sha: @command.source_sha,
+ target_sha: @command.target_sha,
tag: @command.tag_exists?,
trigger_requests: Array(@command.trigger_request),
user: @command.current_user,
diff --git a/lib/gitlab/ci/pipeline/chain/command.rb b/lib/gitlab/ci/pipeline/chain/command.rb
index e62d547d862..bf9f03f6134 100644
--- a/lib/gitlab/ci/pipeline/chain/command.rb
+++ b/lib/gitlab/ci/pipeline/chain/command.rb
@@ -7,10 +7,11 @@ module Gitlab
module Chain
Command = Struct.new(
:source, :project, :current_user,
- :origin_ref, :checkout_sha, :after_sha, :before_sha,
+ :origin_ref, :checkout_sha, :after_sha, :before_sha, :source_sha, :target_sha,
:trigger_request, :schedule, :merge_request,
:ignore_skip_ci, :save_incompleted,
- :seeds_block, :variables_attributes, :push_options
+ :seeds_block, :variables_attributes, :push_options,
+ :chat_data, :allow_mirror_update
) do
include Gitlab::Utils::StrongMemoize
diff --git a/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb b/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
index 0f687a4ce9b..1e09b417311 100644
--- a/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
+++ b/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs.rb
@@ -6,7 +6,13 @@ module Gitlab
module Chain
class RemoveUnwantedChatJobs < Chain::Base
def perform!
- # to be overriden in EE
+ return unless pipeline.config_processor && pipeline.chat?
+
+ # When scheduling a chat pipeline we only want to run the build
+ # that matches the chat command.
+ pipeline.config_processor.jobs.select! do |name, _|
+ name.to_s == command.chat_data[:command].to_s
+ end
end
def break?
diff --git a/lib/gitlab/ci/status/build/factory.rb b/lib/gitlab/ci/status/build/factory.rb
index 6e4bfe23f2b..f7d0715e617 100644
--- a/lib/gitlab/ci/status/build/factory.rb
+++ b/lib/gitlab/ci/status/build/factory.rb
@@ -11,6 +11,7 @@ module Gitlab
Status::Build::Manual,
Status::Build::Canceled,
Status::Build::Created,
+ Status::Build::Preparing,
Status::Build::Pending,
Status::Build::Skipped],
[Status::Build::Cancelable,
diff --git a/lib/gitlab/ci/status/build/failed.rb b/lib/gitlab/ci/status/build/failed.rb
index d40454df737..76dfe7b7639 100644
--- a/lib/gitlab/ci/status/build/failed.rb
+++ b/lib/gitlab/ci/status/build/failed.rb
@@ -15,7 +15,8 @@ module Gitlab
runner_unsupported: 'unsupported runner',
stale_schedule: 'stale schedule',
job_execution_timeout: 'job execution timeout',
- archived_failure: 'archived failure'
+ archived_failure: 'archived failure',
+ unmet_prerequisites: 'unmet prerequisites'
}.freeze
private_constant :REASONS
diff --git a/lib/gitlab/ci/status/build/preparing.rb b/lib/gitlab/ci/status/build/preparing.rb
new file mode 100644
index 00000000000..1fddcb05f79
--- /dev/null
+++ b/lib/gitlab/ci/status/build/preparing.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Status
+ module Build
+ class Preparing < Status::Extended
+ ##
+ # TODO: image is shared with 'pending'
+ # until we get a dedicated one
+ #
+ def illustration
+ {
+ image: 'illustrations/job_not_triggered.svg',
+ size: 'svg-306',
+ title: _('This job is preparing to start'),
+ content: _('This job is performing tasks that must complete before it can start')
+ }
+ end
+
+ def self.matches?(build, _)
+ build.preparing?
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/status/preparing.rb b/lib/gitlab/ci/status/preparing.rb
new file mode 100644
index 00000000000..62985d0a9f9
--- /dev/null
+++ b/lib/gitlab/ci/status/preparing.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Ci
+ module Status
+ class Preparing < Status::Core
+ def text
+ s_('CiStatusText|preparing')
+ end
+
+ def label
+ s_('CiStatusLabel|preparing')
+ end
+
+ ##
+ # TODO: shared with 'created'
+ # until we get one for 'preparing'
+ #
+ def icon
+ 'status_created'
+ end
+
+ ##
+ # TODO: shared with 'created'
+ # until we get one for 'preparing'
+ #
+ def favicon
+ 'favicon_status_created'
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml b/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
index 9c534b2b8e7..8e767b22360 100644
--- a/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Android-Fastlane.gitlab-ci.yml
@@ -118,4 +118,3 @@ promoteProduction:
- master
script:
- bundle exec fastlane promote_beta_to_production
- \ No newline at end of file
diff --git a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
index 527a432d72d..7ec786b6d5d 100644
--- a/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Auto-DevOps.gitlab-ci.yml
@@ -48,9 +48,10 @@ variables:
POSTGRES_PASSWORD: testing-password
POSTGRES_ENABLED: "true"
POSTGRES_DB: $CI_ENVIRONMENT_SLUG
+ POSTGRES_VERSION: 9.6.2
- KUBERNETES_VERSION: 1.11.6
- HELM_VERSION: 2.12.2
+ KUBERNETES_VERSION: 1.11.7
+ HELM_VERSION: 2.12.3
DOCKER_DRIVER: overlay2
@@ -73,14 +74,14 @@ stages:
build:
stage: build
- image: docker:stable-git
+ image: "registry.gitlab.com/gitlab-org/cluster-integration/auto-build-image/master:stable"
services:
- - docker:stable-dind
+ - docker:stable-dind
script:
- - setup_docker
- - build
+ - /build/build.sh
only:
- branches
+ - tags
test:
services:
@@ -95,6 +96,7 @@ test:
- /bin/herokuish buildpack test
only:
- branches
+ - tags
except:
variables:
- $TEST_DISABLED
@@ -112,6 +114,7 @@ code_quality:
paths: [gl-code-quality-report.json]
only:
- branches
+ - tags
except:
variables:
- $CODE_QUALITY_DISABLED
@@ -129,6 +132,7 @@ license_management:
only:
refs:
- branches
+ - tags
variables:
- $GITLAB_FEATURES =~ /\blicense_management\b/
except:
@@ -151,6 +155,7 @@ performance:
only:
refs:
- branches
+ - tags
kubernetes: active
except:
variables:
@@ -171,6 +176,7 @@ sast:
only:
refs:
- branches
+ - tags
variables:
- $GITLAB_FEATURES =~ /\bsast\b/
except:
@@ -192,6 +198,7 @@ dependency_scanning:
only:
refs:
- branches
+ - tags
variables:
- $GITLAB_FEATURES =~ /\bdependency_scanning\b/
except:
@@ -212,6 +219,7 @@ container_scanning:
only:
refs:
- branches
+ - tags
variables:
- $GITLAB_FEATURES =~ /\bcontainer_scanning\b/
except:
@@ -231,6 +239,7 @@ dast:
only:
refs:
- branches
+ - tags
kubernetes: active
variables:
- $GITLAB_FEATURES =~ /\bdast\b/
@@ -253,13 +262,14 @@ review:
- persist_environment_url
environment:
name: review/$CI_COMMIT_REF_NAME
- url: http://$CI_PROJECT_PATH_SLUG-$CI_ENVIRONMENT_SLUG.$KUBE_INGRESS_BASE_DOMAIN
+ url: http://$CI_PROJECT_ID-$CI_ENVIRONMENT_SLUG.$KUBE_INGRESS_BASE_DOMAIN
on_stop: stop_review
artifacts:
paths: [environment_url.txt]
only:
refs:
- branches
+ - tags
kubernetes: active
except:
refs:
@@ -283,6 +293,7 @@ stop_review:
only:
refs:
- branches
+ - tags
kubernetes: active
except:
refs:
@@ -488,9 +499,13 @@ rollout 100%:
[[ "$TRACE" ]] && set -x
auto_database_url=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${CI_ENVIRONMENT_SLUG}-postgres:5432/${POSTGRES_DB}
export DATABASE_URL=${DATABASE_URL-$auto_database_url}
- export CI_APPLICATION_REPOSITORY=$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG
- export CI_APPLICATION_TAG=$CI_COMMIT_SHA
- export CI_CONTAINER_NAME=ci_job_build_${CI_JOB_ID}
+ if [[ -z "$CI_COMMIT_TAG" ]]; then
+ export CI_APPLICATION_REPOSITORY=$CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG
+ export CI_APPLICATION_TAG=$CI_COMMIT_SHA
+ else
+ export CI_APPLICATION_REPOSITORY=$CI_REGISTRY_IMAGE
+ export CI_APPLICATION_TAG=$CI_COMMIT_TAG
+ fi
export TILLER_NAMESPACE=$KUBE_NAMESPACE
# Extract "MAJOR.MINOR" from CI_SERVER_VERSION and generate "MAJOR-MINOR-stable" for Security Products
export SP_VERSION=$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')
@@ -507,7 +522,7 @@ rollout 100%:
registry_login
docker run -d --name db arminc/clair-db:latest
- docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.1
+ docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.6
apk add -U wget ca-certificates
docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
@@ -682,6 +697,8 @@ rollout 100%:
helm upgrade --install \
--wait \
--set service.enabled="$service_enabled" \
+ --set gitlab.app="$CI_PROJECT_PATH_SLUG" \
+ --set gitlab.env="$CI_ENVIRONMENT_SLUG" \
--set releaseOverride="$CI_ENVIRONMENT_SLUG" \
--set image.repository="$CI_APPLICATION_REPOSITORY" \
--set image.tag="$CI_APPLICATION_TAG" \
@@ -700,6 +717,7 @@ rollout 100%:
--set postgresql.postgresUser="$POSTGRES_USER" \
--set postgresql.postgresPassword="$POSTGRES_PASSWORD" \
--set postgresql.postgresDatabase="$POSTGRES_DB" \
+ --set postgresql.imageTag="$POSTGRES_VERSION" \
--set application.initializeCommand="$DB_INITIALIZE" \
--namespace="$KUBE_NAMESPACE" \
"$name" \
@@ -718,6 +736,8 @@ rollout 100%:
helm upgrade --install \
--wait \
--set service.enabled="$service_enabled" \
+ --set gitlab.app="$CI_PROJECT_PATH_SLUG" \
+ --set gitlab.env="$CI_ENVIRONMENT_SLUG" \
--set releaseOverride="$CI_ENVIRONMENT_SLUG" \
--set image.repository="$CI_APPLICATION_REPOSITORY" \
--set image.tag="$CI_APPLICATION_TAG" \
@@ -764,18 +784,18 @@ rollout 100%:
function install_dependencies() {
apk add -U openssl curl tar gzip bash ca-certificates git
- curl -L -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
- curl -L -O https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk
+ curl -sSL -o /etc/apk/keys/sgerrand.rsa.pub https://alpine-pkgs.sgerrand.com/sgerrand.rsa.pub
+ curl -sSL -O https://github.com/sgerrand/alpine-pkg-glibc/releases/download/2.28-r0/glibc-2.28-r0.apk
apk add glibc-2.28-r0.apk
rm glibc-2.28-r0.apk
- curl "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
+ curl -sS "https://kubernetes-helm.storage.googleapis.com/helm-v${HELM_VERSION}-linux-amd64.tar.gz" | tar zx
mv linux-amd64/helm /usr/bin/
mv linux-amd64/tiller /usr/bin/
helm version --client
tiller -version
- curl -L -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
+ curl -sSL -o /usr/bin/kubectl "https://storage.googleapis.com/kubernetes-release/release/v${KUBERNETES_VERSION}/bin/linux/amd64/kubectl"
chmod +x /usr/bin/kubectl
kubectl version --client
}
@@ -828,7 +848,7 @@ rollout 100%:
# Function to ensure backwards compatibility with AUTO_DEVOPS_DOMAIN
function ensure_kube_ingress_base_domain() {
- if [ -z ${KUBE_INGRESS_BASE_DOMAIN+x} ]; then
+ if [ -z ${KUBE_INGRESS_BASE_DOMAIN+x} ] && [ -n "$AUTO_DEVOPS_DOMAIN" ] ; then
export KUBE_INGRESS_BASE_DOMAIN=$AUTO_DEVOPS_DOMAIN
fi
}
@@ -849,50 +869,6 @@ rollout 100%:
fi
}
- function build() {
- registry_login
-
- if [[ -f Dockerfile ]]; then
- echo "Building Dockerfile-based application..."
- docker build \
- --build-arg HTTP_PROXY="$HTTP_PROXY" \
- --build-arg http_proxy="$http_proxy" \
- --build-arg HTTPS_PROXY="$HTTPS_PROXY" \
- --build-arg https_proxy="$https_proxy" \
- --build-arg FTP_PROXY="$FTP_PROXY" \
- --build-arg ftp_proxy="$ftp_proxy" \
- --build-arg NO_PROXY="$NO_PROXY" \
- --build-arg no_proxy="$no_proxy" \
- -t "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" .
- else
- echo "Building Heroku-based application using gliderlabs/herokuish docker image..."
- docker run -i \
- -e BUILDPACK_URL \
- -e HTTP_PROXY \
- -e http_proxy \
- -e HTTPS_PROXY \
- -e https_proxy \
- -e FTP_PROXY \
- -e ftp_proxy \
- -e NO_PROXY \
- -e no_proxy \
- --name="$CI_CONTAINER_NAME" -v "$(pwd):/tmp/app:ro" gliderlabs/herokuish /bin/herokuish buildpack build
- docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
- docker rm "$CI_CONTAINER_NAME" >/dev/null
- echo ""
-
- echo "Configuring $CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG docker image..."
- docker create --expose 5000 --env PORT=5000 --name="$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG" /bin/herokuish procfile start web
- docker commit "$CI_CONTAINER_NAME" "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
- docker rm "$CI_CONTAINER_NAME" >/dev/null
- echo ""
- fi
-
- echo "Pushing to GitLab Container Registry..."
- docker push "$CI_APPLICATION_REPOSITORY:$CI_APPLICATION_TAG"
- echo ""
- }
-
function initialize_tiller() {
echo "Checking Tiller..."
diff --git a/lib/gitlab/ci/templates/Bash.gitlab-ci.yml b/lib/gitlab/ci/templates/Bash.gitlab-ci.yml
index 2d218b2e164..368069844ea 100644
--- a/lib/gitlab/ci/templates/Bash.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Bash.gitlab-ci.yml
@@ -7,28 +7,28 @@ before_script:
- echo "Before script section"
- echo "For example you might run an update here or install a build dependency"
- echo "Or perhaps you might print out some debugging details"
-
+
after_script:
- echo "After script section"
- echo "For example you might do some cleanup here"
-
+
build1:
stage: build
script:
- echo "Do your build here"
-
+
test1:
stage: test
- script:
+ script:
- echo "Do a test here"
- echo "For example run a test suite"
-
+
test2:
stage: test
- script:
+ script:
- echo "Do another parallel test here"
- echo "For example run a lint test"
-
+
deploy1:
stage: deploy
script:
diff --git a/lib/gitlab/ci/templates/C++.gitlab-ci.yml b/lib/gitlab/ci/templates/C++.gitlab-ci.yml
index c83c49d8c95..9a8fa9d7091 100644
--- a/lib/gitlab/ci/templates/C++.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/C++.gitlab-ci.yml
@@ -7,9 +7,9 @@ build:
stage: build
# instead of calling g++ directly you can also use some build toolkit like make
# install the necessary build tools when needed
- # before_script:
- # - apt update && apt -y install make autoconf
- script:
+ # before_script:
+ # - apt update && apt -y install make autoconf
+ script:
- g++ helloworld.cpp -o mybinary
artifacts:
paths:
diff --git a/lib/gitlab/ci/templates/Chef.gitlab-ci.yml b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml
index 4d5b6484d6e..33507aa58e4 100644
--- a/lib/gitlab/ci/templates/Chef.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml
@@ -41,7 +41,7 @@ chefspec:
# - apt-get -y install rsync
# script:
# - kitchen verify default-centos-6 --destroy=always
-#
+#
#verify-centos-7:
# stage: functional
# before_script:
diff --git a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml
index f066285b1ad..0610cb9ccc0 100644
--- a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml
@@ -5,9 +5,9 @@ image: clojure:lein-2.7.0
# Make sure you configure the connection as well
before_script:
- # If you need to install any external applications, like a
+ # If you need to install any external applications, like a
# postgres client, you may want to uncomment the line below:
- #
+ #
#- apt-get update -y
#
# Retrieve project dependencies
@@ -17,6 +17,6 @@ before_script:
test:
script:
- # If you need to run any migrations or configure the database, this
- # would be the point to do it.
+ # If you need to run any migrations or configure the database, this
+ # would be the point to do it.
- lein test
diff --git a/lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml b/lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml
new file mode 100644
index 00000000000..10b25af904f
--- /dev/null
+++ b/lib/gitlab/ci/templates/Code-Quality.gitlab-ci.yml
@@ -0,0 +1,17 @@
+code_quality:
+ image: docker:stable
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ variables:
+ DOCKER_DRIVER: overlay2
+ script:
+ - 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:11-8-stable" /code
+ artifacts:
+ reports:
+ codequality: gl-code-quality-report.json
+ expire_in: 1 week
diff --git a/lib/gitlab/ci/templates/Django.gitlab-ci.yml b/lib/gitlab/ci/templates/Django.gitlab-ci.yml
index 57afcbbe8b5..1d8be6f017e 100644
--- a/lib/gitlab/ci/templates/Django.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Django.gitlab-ci.yml
@@ -21,7 +21,7 @@ cache:
# This is a basic example for a gem or script which doesn't use
# services such as redis or postgres
before_script:
- - python -V # Print out python version for debugging
+ - python -V # Print out python version for debugging
# Uncomment next line if your Django app needs a JS runtime:
# - apt-get update -q && apt-get install nodejs -yqq
- pip install -r requirements.txt
diff --git a/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml b/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
index 48d98dddfad..cbf4d58bdad 100644
--- a/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Gradle.gitlab-ci.yml
@@ -23,7 +23,6 @@ build:
- build
- .gradle
-
test:
stage: test
script: gradle check
@@ -33,4 +32,3 @@ test:
paths:
- build
- .gradle
-
diff --git a/lib/gitlab/ci/templates/Grails.gitlab-ci.yml b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml
index 7fc698d50cf..dbc868238f8 100644
--- a/lib/gitlab/ci/templates/Grails.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml
@@ -13,7 +13,7 @@ image: java:8
variables:
GRAILS_VERSION: "3.1.9"
GRADLE_VERSION: "2.13"
-
+
# We use SDKMan as tool for managing versions
before_script:
- apt-get update -qq && apt-get install -y -qq unzip
@@ -23,10 +23,10 @@ before_script:
- sdk install gradle $GRADLE_VERSION < /dev/null
- sdk use gradle $GRADLE_VERSION
# As it's not a good idea to version gradle.properties feel free to add your
-# environments variable here
+# environments variable here
- echo grailsVersion=$GRAILS_VERSION > gradle.properties
- echo gradleWrapperVersion=2.14 >> gradle.properties
-# refresh dependencies from your project
+# refresh dependencies from your project
- ./gradlew --refresh-dependencies
# Be aware that if you are using Angular profile,
# Bower cannot be run as root if you don't allow it before.
@@ -36,5 +36,5 @@ before_script:
# This build job does the full grails pipeline
# (compile, test, integrationTest, war, assemble).
build:
- script:
- - ./gradlew build \ No newline at end of file
+ script:
+ - ./gradlew build
diff --git a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
index 04c21b4725d..2c4683fbfbb 100644
--- a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml
@@ -30,7 +30,7 @@
test:0.7:
image: julia:0.7
<<: *test_definition
-
+
test:1.0:
image: julia:1.0
<<: *test_definition
diff --git a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
index d0cad285572..e1cd29ecc94 100644
--- a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml
@@ -22,33 +22,25 @@ cache:
# This is a basic example for a gem or script which doesn't use
# services such as redis or postgres
before_script:
- # Update packages
+ # Update packages
- apt-get update -yqq
-
# Prep for Node
- apt-get install gnupg -yqq
-
# Upgrade to Node 8
- curl -sL https://deb.nodesource.com/setup_8.x | bash -
-
# Install dependencies
- apt-get install git nodejs libcurl4-gnutls-dev libicu-dev libmcrypt-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libpq-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev -yqq
-
# Install php extensions
- docker-php-ext-install mbstring pdo_mysql curl json intl gd xml zip bz2 opcache
-
# Install & enable Xdebug for code coverage reports
- pecl install xdebug
- docker-php-ext-enable xdebug
-
# Install Composer and project dependencies.
- curl -sS https://getcomposer.org/installer | php
- php composer.phar install
-
# Install Node dependencies.
# comment this out if you don't have a node dependency
- npm install
-
# Copy over testing configuration.
# Don't forget to set the database config in .env.testing correctly
# DB_HOST=mysql
@@ -56,20 +48,16 @@ before_script:
# DB_USERNAME=root
# DB_PASSWORD=secret
- cp .env.testing .env
-
# Run npm build
# comment this out if you don't have a frontend build
# you can change this to to your frontend building script like
# npm run build
- npm run dev
-
# Generate an application key. Re-cache.
- php artisan key:generate
- php artisan config:cache
-
# Run database migrations.
- php artisan migrate
-
# Run database seed
- php artisan db:seed
@@ -77,7 +65,6 @@ test:
script:
# run laravel tests
- php vendor/bin/phpunit --coverage-text --colors=never
-
# run frontend tests
# if you have any task for testing frontend
# set it in your package.json script
diff --git a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
index 492b3d03db2..c9838c7a7ff 100644
--- a/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Maven.gitlab-ci.yml
@@ -66,7 +66,6 @@ verify:jdk8:
<<: *verify
image: maven:3.3.9-jdk-8
-
# For `master` branch run `mvn deploy` automatically.
# Here you need to decide whether you want to use JDK7 or 8.
# To get this working you need to define a volume while configuring your gitlab-ci-multi-runner.
@@ -85,7 +84,6 @@ deploy:jdk8:
- target/staging
image: maven:3.3.9-jdk-8
-
pages:
image: busybox:latest
stage: deploy
diff --git a/lib/gitlab/ci/templates/Mono.gitlab-ci.yml b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
index 3585f99760f..86d62b93313 100644
--- a/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml
@@ -32,11 +32,11 @@ release:
# The output path is relative to the position of the csproj-file
- msbuild /p:Configuration="Release" /p:Platform="Any CPU"
/p:OutputPath="./../../build/release/" "MyProject.sln"
-
+
debug:
stage: test
script:
# The output path is relative to the position of the csproj-file
- msbuild /p:Configuration="Debug" /p:Platform="Any CPU"
/p:OutputPath="./../../build/debug/" "MyProject.sln"
- - mono packages/NUnit.ConsoleRunner.3.6.0/tools/nunit3-console.exe build/debug/MyProject.Test.dll \ No newline at end of file
+ - mono packages/NUnit.ConsoleRunner.3.6.0/tools/nunit3-console.exe build/debug/MyProject.Test.dll
diff --git a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
index 7fcc0b436b5..d6de8cab5d1 100644
--- a/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Brunch.gitlab-ci.yml
@@ -5,7 +5,6 @@ pages:
cache:
paths:
- node_modules/
-
script:
- npm install -g brunch
- brunch build --production
diff --git a/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
index dd3ef149668..4b58003ee10 100644
--- a/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Harp.gitlab-ci.yml
@@ -5,7 +5,6 @@ pages:
cache:
paths:
- node_modules
-
script:
- npm install -g harp
- harp compile ./ public
diff --git a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
index b8cfb0f56f6..f9ddcc6fb0a 100644
--- a/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Hugo.gitlab-ci.yml
@@ -9,7 +9,7 @@ pages:
- public
only:
- master
-
+
test:
script:
- hugo
diff --git a/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml
index 7abfaf53e8e..7a485f8d135 100644
--- a/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/JBake.gitlab-ci.yml
@@ -13,7 +13,6 @@ image: java:8
variables:
JBAKE_VERSION: 2.5.1
-
# We use SDKMan as tool for managing versions
before_script:
- apt-get update -qq && apt-get install -y -qq unzip zip
@@ -29,4 +28,4 @@ pages:
- jbake . public
artifacts:
paths:
- - public \ No newline at end of file
+ - public
diff --git a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
index 0e5fb410a4e..5ca4619e200 100644
--- a/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Jigsaw.gitlab-ci.yml
@@ -11,24 +11,19 @@ cache:
- node_modules/
before_script:
- # Update packages
+ # Update packages
- apt-get update -yqq
-
# Install dependencies
- apt-get install -yqq gnupg zlib1g-dev libpng-dev
-
# Install Node 8
- curl -sL https://deb.nodesource.com/setup_8.x | bash -
- apt-get install -yqq nodejs
-
# Install php extensions
- docker-php-ext-install zip
-
- # Install Composer and project dependencies.
+ # Install Composer and project dependencies
- curl -sS https://getcomposer.org/installer | php
- - php composer.phar install
-
- # Install Node dependencies.
+ - php composer.phar install
+ # Install Node dependencies
- npm install
pages:
diff --git a/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
index 50e8b7ccd46..c6ded272150 100644
--- a/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Pages/Metalsmith.gitlab-ci.yml
@@ -5,7 +5,6 @@ pages:
cache:
paths:
- node_modules/
-
script:
- npm install -g metalsmith
- npm install
diff --git a/lib/gitlab/ci/templates/Python.gitlab-ci.yml b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
index 098abe4daf5..3eaed4e91cd 100644
--- a/lib/gitlab/ci/templates/Python.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Python.gitlab-ci.yml
@@ -18,7 +18,7 @@ cache:
- venv/
before_script:
- - python -V # Print out python version for debugging
+ - python -V # Print out python version for debugging
- pip install virtualenv
- virtualenv venv
- source venv/bin/activate
@@ -26,7 +26,7 @@ before_script:
test:
script:
- python setup.py test
- - pip install tox flake8 # you can also use tox
+ - pip install tox flake8 # you can also use tox
- tox -e py36,flake8
run:
diff --git a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
index 0d12cbc6460..93196dbd475 100644
--- a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml
@@ -21,7 +21,7 @@ cache:
# This is a basic example for a gem or script which doesn't use
# services such as redis or postgres
before_script:
- - ruby -v # Print out ruby version for debugging
+ - ruby -v # Print out ruby version for debugging
# Uncomment next line if your rails app needs a JS runtime:
# - apt-get update -q && apt-get install nodejs -yqq
- bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby
diff --git a/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
new file mode 100644
index 00000000000..ea1e6ae5fdc
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/Container-Scanning.gitlab-ci.yml
@@ -0,0 +1,48 @@
+# Read more about this feature here: https://docs.gitlab.com/ee/user/project/merge_requests/container_scanning.html
+
+container_scanning:
+ stage: test
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ # Defining two new variables based on GitLab's CI/CD predefined variables
+ # https://docs.gitlab.com/ee/ci/variables/#predefined-environment-variables
+ CI_APPLICATION_REPOSITORY: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG
+ CI_APPLICATION_TAG: $CI_COMMIT_SHA
+ # Prior to this, you need to have the Container Registry running for your project and setup a build job
+ # with at least the following steps:
+ #
+ # docker build -t $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG .
+ # docker push $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
+ #
+ # Container Scanning deals with Docker images only so no need to import the project's Git repository:
+ GIT_STRATEGY: none
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - docker run -d --name db arminc/clair-db:latest
+ - docker run -p 6060:6060 --link db:postgres -d --name clair --restart on-failure arminc/clair-local-scan:v2.0.6
+ - apk add -U wget ca-certificates
+ - docker pull ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG}
+ - wget https://github.com/arminc/clair-scanner/releases/download/v8/clair-scanner_linux_amd64
+ - mv clair-scanner_linux_amd64 clair-scanner
+ - chmod +x clair-scanner
+ - touch clair-whitelist.yml
+ - while( ! wget -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; done
+ - retries=0
+ - echo "Waiting for clair daemon to start"
+ - while( ! wget -T 10 -q -O /dev/null http://docker:6060/v1/namespaces ) ; do sleep 1 ; echo -n "." ; if [ $retries -eq 10 ] ; then echo " Timeout, aborting." ; exit 1 ; fi ; retries=$(($retries+1)) ; done
+ - ./clair-scanner -c http://docker:6060 --ip $(hostname -i) -r gl-container-scanning-report.json -l clair.log -w clair-whitelist.yml ${CI_APPLICATION_REPOSITORY}:${CI_APPLICATION_TAG} || true
+ artifacts:
+ reports:
+ container_scanning: gl-container-scanning-report.json
+ dependencies: []
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bcontainer_scanning\b/
+ except:
+ variables:
+ - $CONTAINER_SCANNING_DISABLED
diff --git a/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
new file mode 100644
index 00000000000..ef6d7866e85
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/DAST.gitlab-ci.yml
@@ -0,0 +1,59 @@
+# Read more about this feature here: https://docs.gitlab.com/ee/user/project/merge_requests/dast.html
+
+# Configure the scanning tool through the environment variables.
+# List of the variables: https://gitlab.com/gitlab-org/security-products/dast#settings
+# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables
+
+variables:
+ DAST_WEBSITE: http://example.com # Please edit to be your website to scan for vulnerabilities
+
+stages:
+ - build
+ - test
+ - deploy
+ - dast
+
+dast:
+ stage: dast
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - export DAST_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
+ - |
+ function dast_run() {
+ docker run \
+ --env DAST_TARGET_AVAILABILITY_TIMEOUT \
+ --volume "$PWD:/output" \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ -w /output \
+ "registry.gitlab.com/gitlab-org/security-products/dast:$DAST_VERSION" \
+ /analyze -t $DAST_WEBSITE \
+ "$@"
+ }
+ - |
+ if [ -n "$DAST_AUTH_URL" ]
+ then
+ dast_run \
+ --auth-url $DAST_AUTH_URL \
+ --auth-username $DAST_USERNAME \
+ --auth-password $DAST_PASSWORD \
+ --auth-username-field $DAST_USERNAME_FIELD \
+ --auth-password-field $DAST_PASSWORD_FIELD
+ else
+ dast_run
+ fi
+ artifacts:
+ reports:
+ dast: gl-dast-report.json
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bdast\b/
+ except:
+ variables:
+ - $DAST_DISABLED
diff --git a/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
new file mode 100644
index 00000000000..fd666541d41
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/Dependency-Scanning.gitlab-ci.yml
@@ -0,0 +1,41 @@
+# Read more about this feature here: https://docs.gitlab.com/ee/user/project/merge_requests/dependency_scanning.html
+#
+# Configure the scanning tool through the environment variables.
+# List of the variables: https://gitlab.com/gitlab-org/security-products/dependency-scanning#settings
+# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables
+
+dependency_scanning:
+ stage: test
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - export DS_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
+ - |
+ docker run \
+ --env DS_ANALYZER_IMAGES \
+ --env DS_ANALYZER_IMAGE_PREFIX \
+ --env DS_ANALYZER_IMAGE_TAG \
+ --env DS_DEFAULT_ANALYZERS \
+ --env DEP_SCAN_DISABLE_REMOTE_CHECKS \
+ --env DS_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
+ --env DS_PULL_ANALYZER_IMAGE_TIMEOUT \
+ --env DS_RUN_ANALYZER_TIMEOUT \
+ --volume "$PWD:/code" \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ "registry.gitlab.com/gitlab-org/security-products/dependency-scanning:$DS_VERSION" /code
+ artifacts:
+ reports:
+ dependency_scanning: gl-dependency-scanning-report.json
+ dependencies: []
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bdependency_scanning\b/
+ except:
+ variables:
+ - $DEPENDENCY_SCANNING_DISABLED
diff --git a/lib/gitlab/ci/templates/Security/License-Management.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/License-Management.gitlab-ci.yml
new file mode 100644
index 00000000000..0208beb35b8
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/License-Management.gitlab-ci.yml
@@ -0,0 +1,27 @@
+# Read more about this feature here: https://docs.gitlab.com/ee/user/project/merge_requests/license_management.html
+
+variables:
+ LICENSE_MANAGEMENT_SETUP_CMD: '' # If needed, specify a command to setup your environment with a custom package manager.
+
+license_management:
+ stage: test
+ image:
+ name: "registry.gitlab.com/gitlab-org/security-products/license-management:$CI_SERVER_VERSION_MAJOR-$CI_SERVER_VERSION_MINOR-stable"
+ entrypoint: [""]
+ variables:
+ SETUP_CMD: $LICENSE_MANAGEMENT_SETUP_CMD
+ allow_failure: true
+ script:
+ - /run.sh analyze .
+ artifacts:
+ reports:
+ license_management: gl-license-management-report.json
+ dependencies: []
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\blicense_management\b/
+ except:
+ variables:
+ - $LICENSE_MANAGEMENT_DISABLED
diff --git a/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
new file mode 100644
index 00000000000..034fba5499c
--- /dev/null
+++ b/lib/gitlab/ci/templates/Security/SAST.gitlab-ci.yml
@@ -0,0 +1,43 @@
+# Read more about this feature here: https://docs.gitlab.com/ee/user/project/merge_requests/sast.html
+#
+# Configure the scanning tool through the environment variables.
+# List of the variables: https://gitlab.com/gitlab-org/security-products/sast#settings
+# How to set: https://docs.gitlab.com/ee/ci/yaml/#variables
+
+sast:
+ stage: test
+ image: docker:stable
+ variables:
+ DOCKER_DRIVER: overlay2
+ allow_failure: true
+ services:
+ - docker:stable-dind
+ script:
+ - export SAST_VERSION=${SP_VERSION:-$(echo "$CI_SERVER_VERSION" | sed 's/^\([0-9]*\)\.\([0-9]*\).*/\1-\2-stable/')}
+ - |
+ docker run \
+ --env SAST_ANALYZER_IMAGES \
+ --env SAST_ANALYZER_IMAGE_PREFIX \
+ --env SAST_ANALYZER_IMAGE_TAG \
+ --env SAST_DEFAULT_ANALYZERS \
+ --env SAST_BRAKEMAN_LEVEL \
+ --env SAST_GOSEC_LEVEL \
+ --env SAST_FLAWFINDER_LEVEL \
+ --env SAST_DOCKER_CLIENT_NEGOTIATION_TIMEOUT \
+ --env SAST_PULL_ANALYZER_IMAGE_TIMEOUT \
+ --env SAST_RUN_ANALYZER_TIMEOUT \
+ --volume "$PWD:/code" \
+ --volume /var/run/docker.sock:/var/run/docker.sock \
+ "registry.gitlab.com/gitlab-org/security-products/sast:$SAST_VERSION" /app/bin/run /code
+ artifacts:
+ reports:
+ sast: gl-sast-report.json
+ dependencies: []
+ only:
+ refs:
+ - branches
+ variables:
+ - $GITLAB_FEATURES =~ /\bsast\b/
+ except:
+ variables:
+ - $SAST_DISABLED
diff --git a/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml
new file mode 100644
index 00000000000..4f3d08d98fe
--- /dev/null
+++ b/lib/gitlab/ci/templates/Serverless.gitlab-ci.yml
@@ -0,0 +1,41 @@
+# GitLab Serverless template
+
+image: alpine:latest
+
+stages:
+ - build
+ - deploy
+
+.serverless:build:image:
+ variables:
+ DOCKERFILE: "Dockerfile"
+ stage: build
+ image:
+ name: gcr.io/kaniko-project/executor:debug
+ entrypoint: [""]
+ only:
+ refs:
+ - master
+ script:
+ - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
+ - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/$DOCKERFILE --destination $CI_REGISTRY_IMAGE
+
+.serverless:deploy:image:
+ stage: deploy
+ image: gcr.io/triggermesh/tm@sha256:e3ee74db94d215bd297738d93577481f3e4db38013326c90d57f873df7ab41d5
+ only:
+ refs:
+ - master
+ environment: development
+ script:
+ - echo "$CI_REGISTRY_IMAGE"
+ - tm -n "$KUBE_NAMESPACE" --config "$KUBECONFIG" deploy service "$CI_PROJECT_NAME" --from-image "$CI_REGISTRY_IMAGE" --wait
+
+.serverless:deploy:functions:
+ stage: deploy
+ environment: development
+ image: gcr.io/triggermesh/tm:v0.0.9
+ script:
+ - tm -n "$KUBE_NAMESPACE" set registry-auth gitlab-registry --registry "$CI_REGISTRY" --username "$CI_REGISTRY_USER" --password "$CI_JOB_TOKEN" --push
+ - tm -n "$KUBE_NAMESPACE" set registry-auth gitlab-registry --registry "$CI_REGISTRY" --username "$CI_DEPLOY_USER" --password "$CI_DEPLOY_PASSWORD" --pull
+ - tm -n "$KUBE_NAMESPACE" deploy --wait
diff --git a/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml
index 25a32ba0f74..5e128b793d0 100644
--- a/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml
+++ b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml
@@ -26,7 +26,6 @@ variables:
MSI_RELEASE_FOLDER: 'Setup\bin\Release'
TEST_FOLDER: 'Tests\bin\Release'
DEPLOY_FOLDER: 'P:\Projects\YourApp\Builds'
-
NUGET_PATH: 'C:\NuGet\nuget.exe'
MSBUILD_PATH: 'C:\Program Files (x86)\MSBuild\14.0\Bin\msbuild.exe'
NUNIT_PATH: 'C:\Program Files (x86)\NUnit.org\nunit-console\nunit3-console.exe'
@@ -84,4 +83,3 @@ deploy_job:
dependencies:
- build_job
- test_job
- \ No newline at end of file
diff --git a/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml b/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml
new file mode 100644
index 00000000000..df6ac4d340d
--- /dev/null
+++ b/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml
@@ -0,0 +1,28 @@
+# This is a very simple template that mainly relies on FastLane to build and distribute your app.
+# Read more about how to use this template on the blog post https://about.gitlab.com/2019/03/06/ios-publishing-with-gitlab-and-fastlane/
+# You will also need fastlane and signing configuration for this to work, along with a MacOS runner.
+# These details are provided in the blog post.
+
+# Note that when you're using the shell executor for MacOS builds, the
+# build and tests run as the identity of the runner logged in user, directly on
+# the build host. This is less secure than using container executors, so please
+# take a look at our security implications documentation at
+# https://docs.gitlab.com/runner/security/#usage-of-shell-executor for additional
+# detail on what to keep in mind in this scenario.
+
+stages:
+ - build
+
+variables:
+ LC_ALL: "en_US.UTF-8"
+ LANG: "en_US.UTF-8"
+ GIT_STRATEGY: clone
+
+build:
+ stage: build
+ script:
+ - bundle install
+ - bundle exec fastlane build
+ artifacts:
+ paths:
+ - ./*.ipa
diff --git a/lib/gitlab/ci/variables/collection.rb b/lib/gitlab/ci/variables/collection.rb
index a7b4e0348c2..f7bbb58df7e 100644
--- a/lib/gitlab/ci/variables/collection.rb
+++ b/lib/gitlab/ci/variables/collection.rb
@@ -17,6 +17,8 @@ module Gitlab
end
def concat(resources)
+ return self if resources.nil?
+
tap { resources.each { |variable| self.append(variable) } }
end
diff --git a/lib/gitlab/ci/variables/collection/item.rb b/lib/gitlab/ci/variables/collection/item.rb
index e3e4e62cc02..833aa75adb5 100644
--- a/lib/gitlab/ci/variables/collection/item.rb
+++ b/lib/gitlab/ci/variables/collection/item.rb
@@ -5,12 +5,12 @@ module Gitlab
module Variables
class Collection
class Item
- def initialize(key:, value:, public: true, file: false)
+ def initialize(key:, value:, public: true, file: false, masked: false)
raise ArgumentError, "`#{key}` must be of type String or nil value, while it was: #{value.class}" unless
value.is_a?(String) || value.nil?
@variable = {
- key: key, value: value, public: public, file: file
+ key: key, value: value, public: public, file: file, masked: masked
}
end
@@ -27,9 +27,13 @@ module Gitlab
# don't expose `file` attribute at all (stems from what the runner
# expects).
#
+ # If the `variable_masking` feature is enabled we expose the `masked`
+ # attribute, otherwise it's not exposed.
+ #
def to_runner_variable
@variable.reject do |hash_key, hash_value|
- hash_key == :file && hash_value == false
+ (hash_key == :file && hash_value == false) ||
+ (hash_key == :masked && !Feature.enabled?(:variable_masking))
end
end
diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb
index 5ed6427072a..f7d046600e8 100644
--- a/lib/gitlab/contributions_calendar.rb
+++ b/lib/gitlab/contributions_calendar.rb
@@ -49,6 +49,7 @@ module Gitlab
Event.contributions.where(author_id: contributor.id)
.where(created_at: date.beginning_of_day..date.end_of_day)
.where(project_id: projects)
+ .with_associations
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
index db8ac3becea..aeca9d00156 100644
--- a/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
+++ b/lib/gitlab/cycle_analytics/plan_event_fetcher.rb
@@ -40,11 +40,11 @@ module Gitlab
end
def first_time_reference_commit(event)
- return nil unless event && merge_request_diff_commits
+ return unless event && merge_request_diff_commits
commits = merge_request_diff_commits[event['id'].to_i]
- return nil if commits.blank?
+ return if commits.blank?
commits.find do |commit|
next unless commit[:committed_date] && event['first_mentioned_in_commit_at']
diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb
index 0f3f5cb3c08..ac65cf74808 100644
--- a/lib/gitlab/danger/helper.rb
+++ b/lib/gitlab/danger/helper.rb
@@ -106,13 +106,29 @@ module Gitlab
%r{\A(ee/)?app/(assets|views)/} => :frontend,
%r{\A(ee/)?public/} => :frontend,
- %r{\A(ee/)?spec/javascripts/} => :frontend,
+ %r{\A(ee/)?spec/(javascripts|frontend)/} => :frontend,
%r{\A(ee/)?vendor/assets/} => :frontend,
- %r{\A(jest\.config\.js|package\.json|yarn\.lock)\z} => :frontend,
+ %r{\Ascripts/frontend/} => :frontend,
+ %r{(\A|/)(
+ \.babelrc |
+ \.eslintignore |
+ \.eslintrc(\.yml)? |
+ \.nvmrc |
+ \.prettierignore |
+ \.prettierrc |
+ \.scss-lint.yml |
+ \.stylelintrc |
+ babel\.config\.js |
+ jest\.config\.js |
+ karma\.config\.js |
+ webpack\.config\.js |
+ package\.json |
+ yarn\.lock
+ )\z}x => :frontend,
%r{\A(ee/)?app/(?!assets|views)[^/]+} => :backend,
%r{\A(ee/)?(bin|config|danger|generator_templates|lib|rubocop|scripts)/} => :backend,
- %r{\A(ee/)?spec/(?!javascripts)[^/]+} => :backend,
+ %r{\A(ee/)?spec/(?!javascripts|frontend)[^/]+} => :backend,
%r{\A(ee/)?vendor/(?!assets)[^/]+} => :backend,
%r{\A(ee/)?vendor/(languages\.yml|licenses\.csv)\z} => :backend,
%r{\A(Dangerfile|Gemfile|Gemfile.lock|Procfile|Rakefile|\.gitlab-ci\.yml)\z} => :backend,
@@ -123,6 +139,7 @@ module Gitlab
# Files that don't fit into any category are marked with :none
%r{\A(ee/)?changelogs/} => :none,
+ %r{\Alocale/gitlab\.pot\z} => :none,
# Fallbacks in case the above patterns miss anything
%r{\.rb\z} => :backend,
diff --git a/lib/gitlab/danger/teammate.rb b/lib/gitlab/danger/teammate.rb
index 4b822aa86c5..bfada512727 100644
--- a/lib/gitlab/danger/teammate.rb
+++ b/lib/gitlab/danger/teammate.rb
@@ -21,21 +21,21 @@ module Gitlab
# Traintainers also count as reviewers
def reviewer?(project, category)
- capabilities(project) == "reviewer #{category}" || traintainer?(project, category)
+ capabilities(project).include?("reviewer #{category}") || traintainer?(project, category)
end
def traintainer?(project, category)
- capabilities(project) == "trainee_maintainer #{category}"
+ capabilities(project).include?("trainee_maintainer #{category}")
end
def maintainer?(project, category)
- capabilities(project) == "maintainer #{category}"
+ capabilities(project).include?("maintainer #{category}")
end
private
def capabilities(project)
- projects.fetch(project, '')
+ Array(projects.fetch(project, []))
end
end
end
diff --git a/lib/gitlab/database.rb b/lib/gitlab/database.rb
index b6ca777e029..8da98cc3909 100644
--- a/lib/gitlab/database.rb
+++ b/lib/gitlab/database.rb
@@ -27,6 +27,10 @@ module Gitlab
config['adapter']
end
+ def self.human_adapter_name
+ postgresql? ? 'PostgreSQL' : 'MySQL'
+ end
+
def self.mysql?
adapter_name.casecmp('mysql2').zero?
end
@@ -76,7 +80,7 @@ module Gitlab
postgresql? && version.to_f >= 9.4
end
- def self.pg_stat_wal_receiver_supported?
+ def self.postgresql_minimum_supported_version?
postgresql? && version.to_f >= 9.6
end
@@ -98,6 +102,10 @@ module Gitlab
Gitlab::Database.postgresql_9_or_less? ? 'pg_last_xlog_replay_location' : 'pg_last_wal_replay_lsn'
end
+ def self.pg_last_xact_replay_timestamp
+ 'pg_last_xact_replay_timestamp'
+ end
+
def self.nulls_last_order(field, direction = 'ASC')
order = "#{field} #{direction}"
diff --git a/lib/gitlab/database/count/reltuples_count_strategy.rb b/lib/gitlab/database/count/reltuples_count_strategy.rb
index c3a674aeb7e..695f6fa766e 100644
--- a/lib/gitlab/database/count/reltuples_count_strategy.rb
+++ b/lib/gitlab/database/count/reltuples_count_strategy.rb
@@ -37,6 +37,22 @@ module Gitlab
private
+ # Models using single-type inheritance (STI) don't work with
+ # reltuple count estimates. We just have to ignore them and
+ # use another strategy to compute them.
+ def non_sti_models
+ models.reject { |model| sti_model?(model) }
+ end
+
+ def non_sti_table_names
+ non_sti_models.map(&:table_name)
+ end
+
+ def sti_model?(model)
+ model.column_names.include?(model.inheritance_column) &&
+ model.base_class != model
+ end
+
def table_names
models.map(&:table_name)
end
@@ -47,7 +63,7 @@ module Gitlab
# Querying tuple stats only works on the primary. Due to load balancing, the
# easiest way to do this is to start a transaction.
ActiveRecord::Base.transaction do
- get_statistics(table_names, check_statistics: check_statistics).each_with_object({}) do |row, data|
+ get_statistics(non_sti_table_names, check_statistics: check_statistics).each_with_object({}) do |row, data|
model = table_to_model[row.table_name]
data[model] = row.estimate
end
diff --git a/lib/gitlab/database/count/tablesample_count_strategy.rb b/lib/gitlab/database/count/tablesample_count_strategy.rb
index cf1cf054dbf..7777f31f702 100644
--- a/lib/gitlab/database/count/tablesample_count_strategy.rb
+++ b/lib/gitlab/database/count/tablesample_count_strategy.rb
@@ -36,7 +36,7 @@ module Gitlab
def perform_count(model, estimate)
# If we estimate 0, we may not have statistics at all. Don't use them.
- return nil unless estimate && estimate > 0
+ return unless estimate && estimate > 0
if estimate < EXACT_COUNT_THRESHOLD
# The table is considered small, the assumption here is that
@@ -48,12 +48,21 @@ module Gitlab
end
end
+ def where_clause(model)
+ return unless sti_model?(model)
+
+ "WHERE #{model.inheritance_column} = '#{model.name}'"
+ end
+
def tablesample_count(model, estimate)
portion = (TABLESAMPLE_ROW_TARGET.to_f / estimate).round(4)
inverse = 1 / portion
query = <<~SQL
SELECT (COUNT(*)*#{inverse})::integer AS count
- FROM #{model.table_name} TABLESAMPLE SYSTEM (#{portion * 100})
+ FROM #{model.table_name}
+ TABLESAMPLE SYSTEM (#{portion * 100})
+ REPEATABLE (0)
+ #{where_clause(model)}
SQL
rows = ActiveRecord::Base.connection.select_all(query)
diff --git a/lib/gitlab/database/multi_threaded_migration.rb b/lib/gitlab/database/multi_threaded_migration.rb
index 1d39a3d0b57..65a6cb8e369 100644
--- a/lib/gitlab/database/multi_threaded_migration.rb
+++ b/lib/gitlab/database/multi_threaded_migration.rb
@@ -35,12 +35,10 @@ module Gitlab
threads = Array.new(thread_count) do
Thread.new do
pool.with_connection do |connection|
- begin
- Thread.current[MULTI_THREAD_AR_CONNECTION] = connection
- yield
- ensure
- Thread.current[MULTI_THREAD_AR_CONNECTION] = nil
- end
+ Thread.current[MULTI_THREAD_AR_CONNECTION] = connection
+ yield
+ ensure
+ Thread.current[MULTI_THREAD_AR_CONNECTION] = nil
end
end
end
diff --git a/lib/gitlab/database/sha_attribute.rb b/lib/gitlab/database/sha_attribute.rb
index 8d97adaff99..109ae7893da 100644
--- a/lib/gitlab/database/sha_attribute.rb
+++ b/lib/gitlab/database/sha_attribute.rb
@@ -22,7 +22,7 @@ module Gitlab
# Casts binary data to a SHA1 in hexadecimal.
def deserialize(value)
value = super(value)
- value ? value.unpack(PACK_FORMAT)[0] : nil
+ value ? value.unpack1(PACK_FORMAT) : nil
end
# Casts a SHA1 in hexadecimal to the proper binary format.
diff --git a/lib/gitlab/dependency_linker/base_linker.rb b/lib/gitlab/dependency_linker/base_linker.rb
index ac2efe598b4..ffad00fa7d7 100644
--- a/lib/gitlab/dependency_linker/base_linker.rb
+++ b/lib/gitlab/dependency_linker/base_linker.rb
@@ -4,6 +4,7 @@ module Gitlab
module DependencyLinker
class BaseLinker
URL_REGEX = %r{https?://[^'" ]+}.freeze
+ GIT_INVALID_URL_REGEX = /^git\+#{URL_REGEX}/.freeze
REPO_REGEX = %r{[^/'" ]+/[^/'" ]+}.freeze
class_attribute :file_type
@@ -29,8 +30,25 @@ module Gitlab
highlighted_lines.join.html_safe
end
+ def external_url(name, external_ref)
+ return if external_ref =~ GIT_INVALID_URL_REGEX
+
+ case external_ref
+ when /\A#{URL_REGEX}\z/
+ external_ref
+ when /\A#{REPO_REGEX}\z/
+ github_url(external_ref)
+ else
+ package_url(name)
+ end
+ end
+
private
+ def package_url(_name)
+ raise NotImplementedError
+ end
+
def link_dependencies
raise NotImplementedError
end
diff --git a/lib/gitlab/dependency_linker/composer_json_linker.rb b/lib/gitlab/dependency_linker/composer_json_linker.rb
index 22d2bead891..4b8862b31ee 100644
--- a/lib/gitlab/dependency_linker/composer_json_linker.rb
+++ b/lib/gitlab/dependency_linker/composer_json_linker.rb
@@ -8,8 +8,8 @@ module Gitlab
private
def link_packages
- link_packages_at_key("require", &method(:package_url))
- link_packages_at_key("require-dev", &method(:package_url))
+ link_packages_at_key("require")
+ link_packages_at_key("require-dev")
end
def package_url(name)
diff --git a/lib/gitlab/dependency_linker/gemfile_linker.rb b/lib/gitlab/dependency_linker/gemfile_linker.rb
index 8ab219c4962..c6e02248b0a 100644
--- a/lib/gitlab/dependency_linker/gemfile_linker.rb
+++ b/lib/gitlab/dependency_linker/gemfile_linker.rb
@@ -3,8 +3,14 @@
module Gitlab
module DependencyLinker
class GemfileLinker < MethodLinker
+ class_attribute :package_keyword
+
+ self.package_keyword = :gem
self.file_type = :gemfile
+ GITHUB_REGEX = /(github:|:github\s*=>)\s*['"](?<name>[^'"]+)['"]/.freeze
+ GIT_REGEX = /(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/.freeze
+
private
def link_dependencies
@@ -14,21 +20,35 @@ module Gitlab
def link_urls
# Link `github: "user/repo"` to https://github.com/user/repo
- link_regex(/(github:|:github\s*=>)\s*['"](?<name>[^'"]+)['"]/, &method(:github_url))
+ link_regex(GITHUB_REGEX, &method(:github_url))
# Link `git: "https://gitlab.example.com/user/repo"` to https://gitlab.example.com/user/repo
- link_regex(/(git:|:git\s*=>)\s*['"](?<name>#{URL_REGEX})['"]/, &:itself)
+ link_regex(GIT_REGEX, &:itself)
# Link `source "https://rubygems.org"` to https://rubygems.org
link_method_call('source', URL_REGEX, &:itself)
end
def link_packages
- # Link `gem "package_name"` to https://rubygems.org/gems/package_name
- link_method_call('gem') do |name|
- "https://rubygems.org/gems/#{name}"
+ packages = parse_packages
+
+ return if packages.blank?
+
+ packages.each do |package|
+ link_method_call('gem', package.name) do
+ external_url(package.name, package.external_ref)
+ end
end
end
+
+ def package_url(name)
+ "https://rubygems.org/gems/#{name}"
+ end
+
+ def parse_packages
+ parser = Gitlab::DependencyLinker::Parser::Gemfile.new(plain_text)
+ parser.parse(keyword: self.class.package_keyword)
+ end
end
end
end
diff --git a/lib/gitlab/dependency_linker/gemspec_linker.rb b/lib/gitlab/dependency_linker/gemspec_linker.rb
index b924ea86d89..94c2b375cf9 100644
--- a/lib/gitlab/dependency_linker/gemspec_linker.rb
+++ b/lib/gitlab/dependency_linker/gemspec_linker.rb
@@ -11,7 +11,7 @@ module Gitlab
link_method_call('homepage', URL_REGEX, &:itself)
link_method_call('license', &method(:license_url))
- link_method_call(%w[name add_dependency add_runtime_dependency add_development_dependency]) do |name|
+ link_method_call(%w[add_dependency add_runtime_dependency add_development_dependency]) do |name|
"https://rubygems.org/gems/#{name}"
end
end
diff --git a/lib/gitlab/dependency_linker/method_linker.rb b/lib/gitlab/dependency_linker/method_linker.rb
index d4d85bb3390..33899a931c6 100644
--- a/lib/gitlab/dependency_linker/method_linker.rb
+++ b/lib/gitlab/dependency_linker/method_linker.rb
@@ -23,18 +23,22 @@ module Gitlab
# link_method_call('name')
# # Will link `package` in `self.name = "package"`
def link_method_call(method_name, value = nil, &url_proc)
+ regex = method_call_regex(method_name, value)
+
+ link_regex(regex, &url_proc)
+ end
+
+ def method_call_regex(method_name, value = nil)
method_name = regexp_for_value(method_name)
value = regexp_for_value(value)
- regex = %r{
+ %r{
#{method_name} # Method name
\s* # Whitespace
[(=]? # Opening brace or equals sign
\s* # Whitespace
['"](?<name>#{value})['"] # Package name in quotes
}x
-
- link_regex(regex, &url_proc)
end
end
end
diff --git a/lib/gitlab/dependency_linker/package.rb b/lib/gitlab/dependency_linker/package.rb
new file mode 100644
index 00000000000..8a509bbd562
--- /dev/null
+++ b/lib/gitlab/dependency_linker/package.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module DependencyLinker
+ class Package
+ attr_reader :name, :git_ref, :github_ref
+
+ def initialize(name, git_ref, github_ref)
+ @name = name
+ @git_ref = git_ref
+ @github_ref = github_ref
+ end
+
+ def external_ref
+ @git_ref || @github_ref
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/dependency_linker/package_json_linker.rb b/lib/gitlab/dependency_linker/package_json_linker.rb
index 578e25f806a..6857f2a4fa2 100644
--- a/lib/gitlab/dependency_linker/package_json_linker.rb
+++ b/lib/gitlab/dependency_linker/package_json_linker.rb
@@ -8,7 +8,6 @@ module Gitlab
private
def link_dependencies
- link_json('name', json["name"], &method(:package_url))
link_json('license', &method(:license_url))
link_json(%w[homepage url], URL_REGEX, &:itself)
@@ -16,25 +15,19 @@ module Gitlab
end
def link_packages
- link_packages_at_key("dependencies", &method(:package_url))
- link_packages_at_key("devDependencies", &method(:package_url))
+ link_packages_at_key("dependencies")
+ link_packages_at_key("devDependencies")
end
- def link_packages_at_key(key, &url_proc)
+ def link_packages_at_key(key)
dependencies = json[key]
return unless dependencies
dependencies.each do |name, version|
- link_json(name, version, link: :key, &url_proc)
-
- link_json(name) do |value|
- case value
- when /\A#{URL_REGEX}\z/
- value
- when /\A#{REPO_REGEX}\z/
- github_url(value)
- end
- end
+ external_url = external_url(name, version)
+
+ link_json(name, version, link: :key) { external_url }
+ link_json(name) { external_url }
end
end
diff --git a/lib/gitlab/dependency_linker/parser/gemfile.rb b/lib/gitlab/dependency_linker/parser/gemfile.rb
new file mode 100644
index 00000000000..7f755375cea
--- /dev/null
+++ b/lib/gitlab/dependency_linker/parser/gemfile.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module DependencyLinker
+ module Parser
+ class Gemfile < MethodLinker
+ GIT_REGEX = Gitlab::DependencyLinker::GemfileLinker::GIT_REGEX
+ GITHUB_REGEX = Gitlab::DependencyLinker::GemfileLinker::GITHUB_REGEX
+
+ def initialize(plain_text)
+ @plain_text = plain_text
+ end
+
+ # Returns a list of Gitlab::DependencyLinker::Package
+ #
+ # keyword - The package definition keyword, e.g. `:gem` for
+ # Gemfile parsing, `:pod` for Podfile.
+ def parse(keyword:)
+ plain_lines.each_with_object([]) do |line, packages|
+ name = fetch(line, method_call_regex(keyword))
+
+ next unless name
+
+ git_ref = fetch(line, GIT_REGEX)
+ github_ref = fetch(line, GITHUB_REGEX)
+
+ packages << Gitlab::DependencyLinker::Package.new(name, git_ref, github_ref)
+ end
+ end
+
+ private
+
+ def fetch(line, regex, group: :name)
+ match = line.match(regex)
+ match[group] if match
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/dependency_linker/podfile_linker.rb b/lib/gitlab/dependency_linker/podfile_linker.rb
index def9b04cca9..a20d285da79 100644
--- a/lib/gitlab/dependency_linker/podfile_linker.rb
+++ b/lib/gitlab/dependency_linker/podfile_linker.rb
@@ -5,12 +5,21 @@ module Gitlab
class PodfileLinker < GemfileLinker
include Cocoapods
+ self.package_keyword = :pod
self.file_type = :podfile
private
def link_packages
- link_method_call('pod', &method(:package_url))
+ packages = parse_packages
+
+ return unless packages
+
+ packages.each do |package|
+ link_method_call('pod', package.name) do
+ external_url(package.name, package.external_ref)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/dependency_linker/podspec_linker.rb b/lib/gitlab/dependency_linker/podspec_linker.rb
index 6b1758c5a43..14abd3999c4 100644
--- a/lib/gitlab/dependency_linker/podspec_linker.rb
+++ b/lib/gitlab/dependency_linker/podspec_linker.rb
@@ -19,7 +19,7 @@ module Gitlab
link_method_call('license', &method(:license_url))
link_regex(/license\s*=\s*\{\s*(type:|:type\s*=>)\s*#{STRING_REGEX}/, &method(:license_url))
- link_method_call(%w[name dependency], &method(:package_url))
+ link_method_call('dependency', &method(:package_url))
end
end
end
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index c9d89d56884..dce80bf21de 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -75,7 +75,7 @@ module Gitlab
end
def line_for_position(pos)
- return nil unless pos.position_type == 'text'
+ return unless pos.position_type == 'text'
# This method is normally used to find which line the diff was
# commented on, and in this context, it's normally the raw diff persisted
@@ -158,7 +158,10 @@ module Gitlab
new_blob || old_blob
end
- attr_writer :highlighted_diff_lines
+ def highlighted_diff_lines=(value)
+ clear_memoization(:diff_lines_for_serializer)
+ @highlighted_diff_lines = value
+ end
# Array of Gitlab::Diff::Line objects
def diff_lines
@@ -314,19 +317,31 @@ module Gitlab
# This adds the bottom match line to the array if needed. It contains
# the data to load more context lines.
def diff_lines_for_serializer
- lines = highlighted_diff_lines
+ strong_memoize(:diff_lines_for_serializer) do
+ lines = highlighted_diff_lines
+
+ next if lines.empty?
+ next if blob.nil?
- return if lines.empty?
- return if blob.nil?
+ last_line = lines.last
- last_line = lines.last
+ 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
- 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)
+ lines
end
+ end
+
+ def fully_expanded?
+ return true if binary?
+
+ lines = diff_lines_for_serializer
+
+ return true if lines.nil?
- lines
+ lines.none? { |line| line.type.to_s == 'match' }
end
private
diff --git a/lib/gitlab/diff/suggestion.rb b/lib/gitlab/diff/suggestion.rb
new file mode 100644
index 00000000000..027c7a31bcf
--- /dev/null
+++ b/lib/gitlab/diff/suggestion.rb
@@ -0,0 +1,52 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class Suggestion
+ include Suggestible
+ include Gitlab::Utils::StrongMemoize
+
+ attr_reader :diff_file, :lines_above, :lines_below,
+ :target_line
+
+ def initialize(text, line:, above:, below:, diff_file:)
+ @text = text
+ @target_line = line
+ @lines_above = above.to_i
+ @lines_below = below.to_i
+ @diff_file = diff_file
+ end
+
+ def to_hash
+ {
+ from_content: from_content,
+ to_content: to_content,
+ lines_above: @lines_above,
+ lines_below: @lines_below
+ }
+ end
+
+ def from_content
+ strong_memoize(:from_content) do
+ fetch_from_content
+ end
+ end
+
+ def to_content
+ # The parsed suggestion doesn't have information about the correct
+ # ending characters (we may have a line break, or not), so we take
+ # this information from the last line being changed (last
+ # characters).
+ endline_chars = line_break_chars(from_content.lines.last)
+ "#{@text}#{endline_chars}"
+ end
+
+ private
+
+ def line_break_chars(line)
+ match = /\r\n|\r|\n/.match(line)
+ match[0] if match
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/suggestion_diff.rb b/lib/gitlab/diff/suggestion_diff.rb
new file mode 100644
index 00000000000..ee153c226b7
--- /dev/null
+++ b/lib/gitlab/diff/suggestion_diff.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class SuggestionDiff
+ include Gitlab::Utils::StrongMemoize
+
+ delegate :from_content, :to_content, :from_line, to: :@suggestible
+
+ def initialize(suggestible)
+ @suggestible = suggestible
+ end
+
+ def diff_lines
+ Gitlab::Diff::Parser.new.parse(raw_diff.each_line).to_a
+ end
+
+ private
+
+ def raw_diff
+ "#{diff_header}\n#{from_content_as_diff}#{to_content_as_diff}"
+ end
+
+ def diff_header
+ "@@ -#{from_line} +#{from_line}"
+ end
+
+ def from_content_as_diff
+ from_content.lines.map { |line| line.prepend('-') }.join
+ end
+
+ def to_content_as_diff
+ to_content.lines.map { |line| line.prepend('+') }.join
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/diff/suggestions_parser.rb b/lib/gitlab/diff/suggestions_parser.rb
new file mode 100644
index 00000000000..c8c03d5d001
--- /dev/null
+++ b/lib/gitlab/diff/suggestions_parser.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module Diff
+ class SuggestionsParser
+ # Matches for instance "-1", "+1" or "-1+2".
+ SUGGESTION_CONTEXT = /^(\-(?<above>\d+))?(\+(?<below>\d+))?$/.freeze
+
+ class << self
+ # Returns an array of Gitlab::Diff::Suggestion which represents each
+ # suggestion in the given text.
+ #
+ def parse(text, position:, project:)
+ return [] unless position.complete?
+
+ html = Banzai.render(text, project: nil, no_original_data: true)
+ doc = Nokogiri::HTML(html)
+ suggestion_nodes = doc.search('pre.suggestion')
+
+ return [] if suggestion_nodes.empty?
+
+ diff_file = position.diff_file(project.repository)
+
+ suggestion_nodes.map do |node|
+ lang_param = node['data-lang-params']
+
+ lines_above, lines_below = nil
+
+ if lang_param && suggestion_params = fetch_suggestion_params(lang_param)
+ lines_above, lines_below =
+ suggestion_params[:above],
+ suggestion_params[:below]
+ end
+
+ Gitlab::Diff::Suggestion.new(node.text,
+ line: position.new_line,
+ above: lines_above.to_i,
+ below: lines_below.to_i,
+ diff_file: diff_file)
+ end
+ end
+
+ private
+
+ def fetch_suggestion_params(lang_param)
+ lang_param.match(SUGGESTION_CONTEXT)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/email/reply_parser.rb b/lib/gitlab/email/reply_parser.rb
index 2743f011ca6..dc44e9d7481 100644
--- a/lib/gitlab/email/reply_parser.rb
+++ b/lib/gitlab/email/reply_parser.rb
@@ -61,7 +61,7 @@ module Gitlab
# Force encoding to UTF-8 on a Mail::Message or Mail::Part
def fix_charset(object)
- return nil if object.nil?
+ return if object.nil?
if object.charset
object.body.decoded.force_encoding(object.charset.gsub(/utf8/i, "UTF-8")).encode("UTF-8").to_s
diff --git a/lib/gitlab/fake_application_settings.rb b/lib/gitlab/fake_application_settings.rb
index bd806269bf0..77f7d9490f3 100644
--- a/lib/gitlab/fake_application_settings.rb
+++ b/lib/gitlab/fake_application_settings.rb
@@ -7,6 +7,8 @@
# column type without parsing db/schema.rb.
module Gitlab
class FakeApplicationSettings < OpenStruct
+ include ApplicationSettingImplementation
+
# Mimic ActiveRecord predicate methods for boolean values
def self.define_predicate_methods(options)
options.each do |key, value|
@@ -26,20 +28,7 @@ module Gitlab
FakeApplicationSettings.define_predicate_methods(options)
end
- def key_restriction_for(type)
- 0
- end
-
- def allowed_key_types
- ApplicationSetting::SUPPORTED_KEY_TYPES
- end
-
- def pick_repository_storage
- repository_storages.sample
- end
-
- def commit_email_hostname
- super.presence || ApplicationSetting.default_commit_email_hostname
- end
+ alias_method :read_attribute, :[]
+ alias_method :has_attribute?, :[]
end
end
diff --git a/lib/gitlab/favicon.rb b/lib/gitlab/favicon.rb
index 1ae2f9dfd93..6e31064f737 100644
--- a/lib/gitlab/favicon.rb
+++ b/lib/gitlab/favicon.rb
@@ -10,7 +10,7 @@ module Gitlab
elsif Gitlab::Utils.to_boolean(ENV['CANARY'])
'favicon-yellow.png'
elsif Rails.env.development?
- 'favicon-blue.png'
+ development_favicon
else
'favicon.png'
end
@@ -18,6 +18,12 @@ module Gitlab
ActionController::Base.helpers.image_path(image_name, host: host)
end
+ def development_favicon
+ # This is a separate method so that EE can return a different favicon
+ # for development environments.
+ 'favicon-blue.png'
+ end
+
def status_overlay(status_name)
path = File.join(
'ci_favicons',
diff --git a/lib/gitlab/fogbugz_import/importer.rb b/lib/gitlab/fogbugz_import/importer.rb
index 431911d1eee..2c53f9b026d 100644
--- a/lib/gitlab/fogbugz_import/importer.rb
+++ b/lib/gitlab/fogbugz_import/importer.rb
@@ -239,7 +239,7 @@ module Gitlab
res = ::Projects::DownloadService.new(project, link).execute
- return nil if res.nil?
+ return if res.nil?
res[:markdown]
end
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index 259a2b7911a..10df4ed72d9 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -23,6 +23,10 @@ module Gitlab
class << self
def find(repository, sha, path, limit: MAX_DATA_DISPLAY_SIZE)
+ tree_entry(repository, sha, path, limit)
+ end
+
+ def tree_entry(repository, sha, path, limit)
return unless path
path = path.sub(%r{\A/*}, '')
@@ -179,3 +183,5 @@ module Gitlab
end
end
end
+
+Gitlab::Git::Blob.singleton_class.prepend Gitlab::Git::RuggedImpl::Blob::ClassMethods
diff --git a/lib/gitlab/git/commit.rb b/lib/gitlab/git/commit.rb
index 5863815ca85..8fac3621df9 100644
--- a/lib/gitlab/git/commit.rb
+++ b/lib/gitlab/git/commit.rb
@@ -5,6 +5,7 @@ module Gitlab
module Git
class Commit
include Gitlab::EncodingHelper
+ prepend Gitlab::Git::RuggedImpl::Commit
extend Gitlab::Git::WrapsGitalyErrors
attr_accessor :raw_commit, :head
@@ -57,20 +58,24 @@ module Gitlab
return commit_id if commit_id.is_a?(Gitlab::Git::Commit)
# Some weird thing?
- return nil unless commit_id.is_a?(String)
+ return unless commit_id.is_a?(String)
# This saves us an RPC round trip.
- return nil if commit_id.include?(':')
+ return if commit_id.include?(':')
- commit = wrapped_gitaly_errors do
- repo.gitaly_commit_client.find_commit(commit_id)
- end
+ commit = find_commit(repo, commit_id)
decorate(repo, commit) if commit
rescue Gitlab::Git::CommandError, Gitlab::Git::Repository::NoRepository, ArgumentError
nil
end
+ def find_commit(repo, commit_id)
+ wrapped_gitaly_errors do
+ repo.gitaly_commit_client.find_commit(commit_id)
+ end
+ end
+
# Get last commit for HEAD
#
# Ex.
@@ -179,12 +184,17 @@ module Gitlab
end
end
- def initialize(repository, raw_commit, head = nil)
+ def initialize(repository, raw_commit, head = nil, lazy_load_parents: false)
raise "Nil as raw commit passed" unless raw_commit
@repository = repository
@head = head
+ @lazy_load_parents = lazy_load_parents
+
+ init_commit(raw_commit)
+ end
+ def init_commit(raw_commit)
case raw_commit
when Hash
init_from_hash(raw_commit)
@@ -216,6 +226,12 @@ module Gitlab
author_name != committer_name || author_email != committer_email
end
+ def parent_ids
+ return @parent_ids unless @lazy_load_parents
+
+ @parent_ids ||= @repository.commit(id).parent_ids
+ end
+
def parent_id
parent_ids.first
end
@@ -302,14 +318,23 @@ module Gitlab
parent_ids.size > 1
end
+ def gitaly_commit?
+ raw_commit.is_a?(Gitaly::GitCommit)
+ end
+
def tree_entry(path)
return unless path.present?
+ commit_tree_entry(path)
+ end
+
+ def commit_tree_entry(path)
# We're only interested in metadata, so limit actual data to 1 byte
# since Gitaly doesn't support "send no data" option.
entry = @repository.gitaly_commit_client.tree_entry(id, path, 1)
return unless entry
+ # To be compatible with the rugged format
entry = entry.to_h
entry.delete(:data)
entry[:name] = File.basename(path)
@@ -319,7 +344,7 @@ module Gitlab
end
def to_gitaly_commit
- return raw_commit if raw_commit.is_a?(Gitaly::GitCommit)
+ return raw_commit if gitaly_commit?
message_split = raw_commit.message.split("\n", 2)
Gitaly::GitCommit.new(
@@ -400,3 +425,5 @@ module Gitlab
end
end
end
+
+Gitlab::Git::Commit.singleton_class.prepend Gitlab::Git::RuggedImpl::Commit::ClassMethods
diff --git a/lib/gitlab/git/pre_receive_error.rb b/lib/gitlab/git/pre_receive_error.rb
index 03caace6fce..b46d4ba0b02 100644
--- a/lib/gitlab/git/pre_receive_error.rb
+++ b/lib/gitlab/git/pre_receive_error.rb
@@ -4,19 +4,38 @@ module Gitlab
module Git
#
# PreReceiveError is special because its message gets displayed to users
- # in the web UI. To prevent XSS we sanitize the message on
- # initialization.
+ # in the web UI. Because of this, we:
+ # - Only display errors that have been marked as safe with a prefix.
+ # This is to prevent leaking of stacktraces, or other sensitive info.
+ # - Sanitize the string of any XSS
class PreReceiveError < StandardError
- def initialize(msg = '')
- super(nlbr(msg))
+ SAFE_MESSAGE_PREFIXES = [
+ 'GitLab:', # Messages from gitlab-shell
+ 'GL-HOOK-ERR:' # Messages marked as safe by user
+ ].freeze
+
+ SAFE_MESSAGE_REGEX = /^(#{SAFE_MESSAGE_PREFIXES.join('|')})\s*(?<safe_message>.+)/
+
+ def initialize(message = '')
+ super(sanitize(message))
end
private
# In gitaly-ruby we override this method to do nothing, so that
# sanitization happens in gitlab-rails only.
- def nlbr(str)
- Gitlab::Utils.nlbr(str)
+ def sanitize(message)
+ return message if message.blank?
+
+ safe_messages = message.split("\n").map do |msg|
+ if (match = msg.match(SAFE_MESSAGE_REGEX))
+ match[:safe_message].presence
+ end
+ end
+
+ safe_messages = safe_messages.compact.join("\n")
+
+ Gitlab::Utils.nlbr(safe_messages)
end
end
end
diff --git a/lib/gitlab/git/ref.rb b/lib/gitlab/git/ref.rb
index eec91194949..47cfb483509 100644
--- a/lib/gitlab/git/ref.rb
+++ b/lib/gitlab/git/ref.rb
@@ -4,6 +4,7 @@ module Gitlab
module Git
class Ref
include Gitlab::EncodingHelper
+ include Gitlab::Git::RuggedImpl::Ref
# Branch or tag name
# without "refs/tags|heads" prefix
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 593a3676519..7d6851a4b8d 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -11,6 +11,7 @@ module Gitlab
include Gitlab::Git::WrapsGitalyErrors
include Gitlab::EncodingHelper
include Gitlab::Utils::StrongMemoize
+ prepend Gitlab::Git::RuggedImpl::Repository
SEARCH_CONTEXT_LINES = 3
REV_LIST_COMMIT_LIMIT = 2_000
@@ -275,7 +276,7 @@ module Gitlab
# senddata response.
def archive_file_path(storage_path, sha, name, format = "tar.gz")
# Build file path
- return nil unless name
+ return unless name
extension =
case format
@@ -343,12 +344,12 @@ module Gitlab
end
end
- def new_blobs(newrev)
+ def new_blobs(newrev, dynamic_timeout: nil)
return [] if newrev.blank? || newrev == ::Gitlab::Git::BLANK_SHA
strong_memoize("new_blobs_#{newrev}") do
wrapped_gitaly_errors do
- gitaly_ref_client.list_new_blobs(newrev, REV_LIST_COMMIT_LIMIT)
+ gitaly_ref_client.list_new_blobs(newrev, REV_LIST_COMMIT_LIMIT, dynamic_timeout: dynamic_timeout)
end
end
end
@@ -556,6 +557,12 @@ module Gitlab
tags.find { |tag| tag.name == name }
end
+ def merge_to_ref(user, source_sha, branch, target_ref, message)
+ wrapped_gitaly_errors do
+ gitaly_operation_client.user_merge_to_ref(user, source_sha, branch, target_ref, message)
+ end
+ end
+
def merge(user, source_sha, target_branch, message, &block)
wrapped_gitaly_errors do
gitaly_operation_client.user_merge_branch(user, source_sha, target_branch, message, &block)
@@ -846,17 +853,20 @@ module Gitlab
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)
+ start_branch_name: nil, start_repository: self,
+ force: false)
wrapped_gitaly_errors do
gitaly_operation_client.user_commit_files(user, branch_name,
message, actions, author_email, author_name,
- start_branch_name, start_repository)
+ start_branch_name, start_repository, force)
end
end
+ # rubocop:enable Metrics/ParameterLists
def write_config(full_path:)
return unless full_path.present?
diff --git a/lib/gitlab/git/rugged_impl/blob.rb b/lib/gitlab/git/rugged_impl/blob.rb
new file mode 100644
index 00000000000..11ee4ebda4b
--- /dev/null
+++ b/lib/gitlab/git/rugged_impl/blob.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+# NOTE: This code is legacy. Do not add/modify code here unless you have
+# discussed with the Gitaly team. See
+# https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code
+# for more details.
+
+module Gitlab
+ module Git
+ module RuggedImpl
+ module Blob
+ module ClassMethods
+ extend ::Gitlab::Utils::Override
+
+ override :tree_entry
+ def tree_entry(repository, sha, path, limit)
+ if Feature.enabled?(:rugged_tree_entry)
+ rugged_tree_entry(repository, sha, path, limit)
+ else
+ super
+ end
+ end
+
+ private
+
+ def rugged_tree_entry(repository, sha, path, limit)
+ return unless path
+
+ # Strip any leading / characters from the path
+ path = path.sub(%r{\A/*}, '')
+
+ rugged_commit = repository.lookup(sha)
+ root_tree = rugged_commit.tree
+
+ blob_entry = find_entry_by_path(repository, root_tree.oid, *path.split('/'))
+
+ return unless blob_entry
+
+ if blob_entry[:type] == :commit
+ submodule_blob(blob_entry, path, sha)
+ else
+ blob = repository.lookup(blob_entry[:oid])
+
+ if blob
+ new(
+ id: blob.oid,
+ name: blob_entry[:name],
+ size: blob.size,
+ # Rugged::Blob#content is expensive; don't call it if we don't have to.
+ data: limit.zero? ? '' : blob.content(limit),
+ mode: blob_entry[:filemode].to_s(8),
+ path: path,
+ commit_id: sha,
+ binary: blob.binary?
+ )
+ end
+ end
+ rescue Rugged::ReferenceError
+ nil
+ end
+
+ # 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 unless entry
+
+ if path_parts.size > 1
+ return 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
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/rugged_impl/commit.rb b/lib/gitlab/git/rugged_impl/commit.rb
new file mode 100644
index 00000000000..f6777dfa0c3
--- /dev/null
+++ b/lib/gitlab/git/rugged_impl/commit.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+
+# NOTE: This code is legacy. Do not add/modify code here unless you have
+# discussed with the Gitaly team. See
+# https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code
+# for more details.
+
+# rubocop:disable Gitlab/ModuleWithInstanceVariables
+module Gitlab
+ module Git
+ module RuggedImpl
+ module Commit
+ module ClassMethods
+ extend ::Gitlab::Utils::Override
+
+ def rugged_find(repo, commit_id)
+ obj = repo.rev_parse_target(commit_id)
+
+ obj.is_a?(::Rugged::Commit) ? obj : nil
+ rescue ::Rugged::Error
+ nil
+ end
+
+ override :find_commit
+ def find_commit(repo, commit_id)
+ if Feature.enabled?(:rugged_find_commit)
+ rugged_find(repo, commit_id)
+ else
+ super
+ end
+ end
+ end
+
+ extend ::Gitlab::Utils::Override
+
+ override :init_commit
+ def init_commit(raw_commit)
+ case raw_commit
+ when ::Rugged::Commit
+ init_from_rugged(raw_commit)
+ else
+ super
+ end
+ end
+
+ override :commit_tree_entry
+ def commit_tree_entry(path)
+ if Feature.enabled?(:rugged_commit_tree_entry)
+ rugged_tree_entry(path)
+ else
+ super
+ end
+ end
+
+ # Is this the same as Blob.find_entry_by_path ?
+ def rugged_tree_entry(path)
+ rugged_commit.tree.path(path)
+ rescue Rugged::TreeError
+ nil
+ end
+
+ def rugged_commit
+ @rugged_commit ||= if raw_commit.is_a?(Rugged::Commit)
+ raw_commit
+ else
+ @repository.rev_parse_target(id)
+ 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
+ end
+ end
+ end
+end
+# rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/lib/gitlab/git/rugged_impl/ref.rb b/lib/gitlab/git/rugged_impl/ref.rb
new file mode 100644
index 00000000000..b553e82dc47
--- /dev/null
+++ b/lib/gitlab/git/rugged_impl/ref.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+# NOTE: This code is legacy. Do not add/modify code here unless you have
+# discussed with the Gitaly team. See
+# https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code
+# for more details.
+
+module Gitlab
+ module Git
+ module RuggedImpl
+ module Ref
+ def self.dereference_object(object)
+ object = object.target while object.is_a?(::Rugged::Tag::Annotation)
+
+ object
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/rugged_impl/repository.rb b/lib/gitlab/git/rugged_impl/repository.rb
new file mode 100644
index 00000000000..c0a91f59ab9
--- /dev/null
+++ b/lib/gitlab/git/rugged_impl/repository.rb
@@ -0,0 +1,78 @@
+# frozen_string_literal: true
+
+# NOTE: This code is legacy. Do not add/modify code here unless you have
+# discussed with the Gitaly team. See
+# https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code
+# for more details.
+
+# rubocop:disable Gitlab/ModuleWithInstanceVariables
+module Gitlab
+ module Git
+ module RuggedImpl
+ module Repository
+ extend ::Gitlab::Utils::Override
+
+ FEATURE_FLAGS = %i(rugged_find_commit rugged_tree_entries rugged_tree_entry rugged_commit_is_ancestor rugged_commit_tree_entry).freeze
+
+ def alternate_object_directories
+ relative_object_directories.map { |d| File.join(path, d) }
+ end
+
+ ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES = %w[
+ GIT_OBJECT_DIRECTORY_RELATIVE
+ GIT_ALTERNATE_OBJECT_DIRECTORIES_RELATIVE
+ ].freeze
+
+ def relative_object_directories
+ Gitlab::Git::HookEnv.all(gl_repository).values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
+ end
+
+ def rugged
+ @rugged ||= ::Rugged::Repository.new(path, alternates: alternate_object_directories)
+ rescue ::Rugged::RepositoryError, ::Rugged::OSError
+ raise ::Gitlab::Git::Repository::NoRepository.new('no repository for such path')
+ end
+
+ def cleanup
+ @rugged&.close
+ 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
+
+ override :ancestor?
+ def ancestor?(from, to)
+ if Feature.enabled?(:rugged_commit_is_ancestor)
+ rugged_is_ancestor?(from, to)
+ else
+ super
+ end
+ end
+
+ def rugged_is_ancestor?(ancestor_id, descendant_id)
+ return false if ancestor_id.nil? || descendant_id.nil?
+
+ rugged_merge_base(ancestor_id, descendant_id) == ancestor_id
+ rescue Rugged::OdbError
+ false
+ end
+
+ def rugged_merge_base(from, to)
+ rugged.merge_base(from, to)
+ rescue Rugged::ReferenceError
+ nil
+ end
+
+ # Lookup for rugged object by oid or ref name
+ def lookup(oid_or_ref_name)
+ rugged.rev_parse(oid_or_ref_name)
+ end
+ end
+ end
+ end
+end
+# rubocop:enable Gitlab/ModuleWithInstanceVariables
diff --git a/lib/gitlab/git/rugged_impl/tree.rb b/lib/gitlab/git/rugged_impl/tree.rb
new file mode 100644
index 00000000000..0ebfd496695
--- /dev/null
+++ b/lib/gitlab/git/rugged_impl/tree.rb
@@ -0,0 +1,105 @@
+# frozen_string_literal: true
+
+# NOTE: This code is legacy. Do not add/modify code here unless you have
+# discussed with the Gitaly team. See
+# https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code
+# for more details.
+
+module Gitlab
+ module Git
+ module RuggedImpl
+ module Tree
+ module ClassMethods
+ extend ::Gitlab::Utils::Override
+
+ override :tree_entries
+ def tree_entries(repository, sha, path, recursive)
+ if Feature.enabled?(:rugged_tree_entries)
+ tree_entries_from_rugged(repository, sha, path, recursive)
+ else
+ super
+ end
+ end
+
+ def tree_entries_from_rugged(repository, sha, path, recursive)
+ current_path_entries = get_tree_entries_from_rugged(repository, sha, path)
+ ordered_entries = []
+
+ current_path_entries.each do |entry|
+ ordered_entries << entry
+
+ if recursive && entry.dir?
+ ordered_entries.concat(tree_entries_from_rugged(repository, sha, entry.path, true))
+ end
+ end
+
+ # This was an optimization to reduce N+1 queries for Gitaly
+ # (https://gitlab.com/gitlab-org/gitaly/issues/530). It
+ # used to be done lazily in the view via
+ # TreeHelper#flatten_tree, so it's possible there's a
+ # performance impact by loading this eagerly.
+ rugged_populate_flat_path(repository, sha, path, ordered_entries)
+ end
+
+ def rugged_populate_flat_path(repository, sha, path, entries)
+ entries.each do |entry|
+ entry.flat_path = entry.path
+
+ next unless entry.dir?
+
+ entry.flat_path =
+ if path
+ File.join(path, rugged_flatten_tree(repository, sha, entry, path))
+ else
+ rugged_flatten_tree(repository, sha, entry, path)
+ end
+ end
+ end
+
+ # Returns the relative path of the first subdir that doesn't have only one directory descendant
+ def rugged_flatten_tree(repository, sha, tree, root_path)
+ subtree = tree_entries_from_rugged(repository, sha, tree.path, false)
+
+ if subtree.count == 1 && subtree.first.dir?
+ File.join(tree.name, rugged_flatten_tree(repository, sha, subtree.first, root_path))
+ else
+ tree.name
+ end
+ end
+
+ def get_tree_entries_from_rugged(repository, sha, path)
+ commit = repository.lookup(sha)
+ root_tree = commit.tree
+
+ tree = if path
+ id = find_id_by_path(repository, root_tree.oid, path)
+ if id
+ repository.lookup(id)
+ else
+ []
+ end
+ else
+ root_tree
+ end
+
+ tree.map do |entry|
+ current_path = path ? File.join(path, entry[:name]) : entry[:name]
+
+ new(
+ id: entry[:oid],
+ root_id: root_tree.oid,
+ name: entry[:name],
+ type: entry[:type],
+ mode: entry[:filemode].to_s(8),
+ path: current_path,
+ commit_id: sha
+ )
+ end
+ rescue Rugged::ReferenceError
+ []
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/git/tree.rb b/lib/gitlab/git/tree.rb
index 51542bcaaa2..7e072c5db50 100644
--- a/lib/gitlab/git/tree.rb
+++ b/lib/gitlab/git/tree.rb
@@ -18,6 +18,10 @@ module Gitlab
def where(repository, sha, path = nil, recursive = false)
path = nil if path == '' || path == '/'
+ tree_entries(repository, sha, path, recursive)
+ end
+
+ def tree_entries(repository, sha, path, recursive)
wrapped_gitaly_errors do
repository.gitaly_commit_client.tree_entries(repository, sha, path, recursive)
end
@@ -44,7 +48,7 @@ module Gitlab
entry[:name] == path_arr[0] && entry[:type] == :tree
end
- return nil unless entry
+ return unless entry
if path_arr.size > 1
path_arr.shift
@@ -95,3 +99,5 @@ module Gitlab
end
end
end
+
+Gitlab::Git::Tree.singleton_class.prepend Gitlab::Git::RuggedImpl::Tree::ClassMethods
diff --git a/lib/gitlab/gitaly_client.rb b/lib/gitlab/gitaly_client.rb
index 0ab53f8f706..e3b9a7a1a89 100644
--- a/lib/gitlab/gitaly_client.rb
+++ b/lib/gitlab/gitaly_client.rb
@@ -28,7 +28,7 @@ module Gitlab
PEM_REGEX = /\-+BEGIN CERTIFICATE\-+.+?\-+END CERTIFICATE\-+/m
SERVER_VERSION_FILE = 'GITALY_SERVER_VERSION'
- MAXIMUM_GITALY_CALLS = 35
+ MAXIMUM_GITALY_CALLS = 30
CLIENT_NAME = (Sidekiq.server? ? 'gitlab-sidekiq' : 'gitlab-web').freeze
MUTEX = Mutex.new
@@ -75,13 +75,11 @@ module Gitlab
@certs = stub_cert_paths.flat_map do |cert_file|
File.read(cert_file).scan(PEM_REGEX).map do |cert|
- begin
- OpenSSL::X509::Certificate.new(cert).to_pem
- rescue OpenSSL::OpenSSLError => e
- Rails.logger.error "Could not load certificate #{cert_file} #{e}"
- Gitlab::Sentry.track_exception(e, extra: { cert_file: cert_file })
- nil
- end
+ OpenSSL::X509::Certificate.new(cert).to_pem
+ rescue OpenSSL::OpenSSLError => e
+ Rails.logger.error "Could not load certificate #{cert_file} #{e}"
+ Gitlab::Sentry.track_exception(e, extra: { cert_file: cert_file })
+ nil
end.compact
end.uniq.join("\n")
end
@@ -164,8 +162,6 @@ module Gitlab
kwargs = yield(kwargs) if block_given?
stub(service, storage).__send__(rpc, request, kwargs) # rubocop:disable GitlabSecurity/PublicSend
- rescue GRPC::Unavailable => ex
- handle_grpc_unavailable!(ex)
ensure
duration = Gitlab::Metrics::System.monotonic_time - start
@@ -178,27 +174,6 @@ module Gitlab
add_call_details(feature: "#{service}##{rpc}", duration: duration, request: request_hash, rpc: rpc)
end
- def self.handle_grpc_unavailable!(ex)
- status = ex.to_status
- raise ex unless status.details == 'Endpoint read failed'
-
- # There is a bug in grpc 1.8.x that causes a client process to get stuck
- # always raising '14:Endpoint read failed'. The only thing that we can
- # do to recover is to restart the process.
- #
- # See https://gitlab.com/gitlab-org/gitaly/issues/1029
-
- if Sidekiq.server?
- raise Gitlab::SidekiqMiddleware::Shutdown::WantShutdown.new(ex.to_s)
- else
- # SIGQUIT requests a Unicorn worker to shut down gracefully after the current request.
- Process.kill('QUIT', Process.pid)
- end
-
- raise ex
- end
- private_class_method :handle_grpc_unavailable!
-
def self.current_transaction_labels
Gitlab::Metrics::Transaction.current&.labels || {}
end
@@ -251,7 +226,7 @@ module Gitlab
result
end
- SERVER_FEATURE_FLAGS = %w[].freeze
+ SERVER_FEATURE_FLAGS = %w[go-find-all-tags].freeze
def self.server_feature_flags
SERVER_FEATURE_FLAGS.map do |f|
@@ -267,7 +242,9 @@ module Gitlab
end
def self.feature_enabled?(feature_name)
- Feature.enabled?("gitaly_#{feature_name}")
+ Feature::FlipperFeature.table_exists? && Feature.enabled?("gitaly_#{feature_name}")
+ rescue ActiveRecord::NoDatabaseError
+ false
end
# Ensures that Gitaly is not being abuse through n+1 misuse etc
@@ -278,8 +255,7 @@ module Gitlab
# This is this actual number of times this call was made. Used for information purposes only
actual_call_count = increment_call_count("gitaly_#{call_site}_actual")
- # Do no enforce limits in production
- return if Rails.env.production? || ENV["GITALY_DISABLE_REQUEST_LIMITS"]
+ return unless enforce_gitaly_request_limits?
# Check if this call is nested within a allow_n_plus_1_calls
# block and skip check if it is
@@ -296,6 +272,19 @@ module Gitlab
raise TooManyInvocationsError.new(call_site, actual_call_count, max_call_count, max_stacks)
end
+ def self.enforce_gitaly_request_limits?
+ # We typically don't want to enforce request limits in production
+ # However, we have some production-like test environments, i.e., ones
+ # where `Rails.env.production?` returns `true`. We do want to be able to
+ # check if the limit is being exceeded while testing in those environments
+ # In that case we can use a feature flag to indicate that we do want to
+ # enforce request limits.
+ return true if feature_enabled?('enforce_requests_limits')
+
+ !(Rails.env.production? || ENV["GITALY_DISABLE_REQUEST_LIMITS"])
+ end
+ private_class_method :enforce_gitaly_request_limits?
+
def self.allow_n_plus_1_calls
return yield unless Gitlab::SafeRequestStore.active?
@@ -407,13 +396,13 @@ module Gitlab
# Returns the stacks that calls Gitaly the most times. Used for n+1 detection
def self.max_stacks
- return nil unless Gitlab::SafeRequestStore.active?
+ return unless Gitlab::SafeRequestStore.active?
stack_counter = Gitlab::SafeRequestStore[:stack_counter]
- return nil unless stack_counter
+ return unless stack_counter
max = max_call_count
- return nil if max.zero?
+ return if max.zero?
stack_counter.select { |_, v| v == max }.keys
end
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index 39547328210..6b8e58e6199 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -27,7 +27,7 @@ module Gitlab
data << msg.data
end
- return nil if blob.oid.blank?
+ return if blob.oid.blank?
data = data.join
diff --git a/lib/gitlab/gitaly_client/blobs_stitcher.rb b/lib/gitlab/gitaly_client/blobs_stitcher.rb
index 01bab854082..f860d8ce517 100644
--- a/lib/gitlab/gitaly_client/blobs_stitcher.rb
+++ b/lib/gitlab/gitaly_client/blobs_stitcher.rb
@@ -13,17 +13,15 @@ module Gitlab
current_blob_data = nil
@rpc_response.each do |msg|
- begin
- if msg.oid.blank? && msg.data.blank?
- next
- elsif msg.oid.present?
- yield new_blob(current_blob_data) if current_blob_data
-
- current_blob_data = msg.to_h.slice(:oid, :path, :size, :revision, :mode)
- current_blob_data[:data] = msg.data.dup
- else
- current_blob_data[:data] << msg.data
- end
+ if msg.oid.blank? && msg.data.blank?
+ next
+ elsif msg.oid.present?
+ yield new_blob(current_blob_data) if current_blob_data
+
+ current_blob_data = msg.to_h.slice(:oid, :path, :size, :revision, :mode)
+ current_blob_data[:data] = msg.data.dup
+ else
+ current_blob_data[:data] << msg.data
end
end
diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb
index 22d2d149e65..2528208440e 100644
--- a/lib/gitlab/gitaly_client/operation_service.rb
+++ b/lib/gitlab/gitaly_client/operation_service.rb
@@ -62,7 +62,7 @@ module Gitlab
end
branch = response.branch
- return nil unless branch
+ return unless branch
target_commit = Gitlab::Git::Commit.decorate(@repository, branch.target_commit)
Gitlab::Git::Branch.new(@repository, branch.name, target_commit.id, target_commit)
@@ -100,6 +100,25 @@ module Gitlab
end
end
+ def user_merge_to_ref(user, source_sha, branch, target_ref, message)
+ request = Gitaly::UserMergeToRefRequest.new(
+ repository: @gitaly_repo,
+ source_sha: source_sha,
+ branch: encode_binary(branch),
+ target_ref: encode_binary(target_ref),
+ user: Gitlab::Git::User.from_gitlab(user).to_gitaly,
+ message: message
+ )
+
+ response = GitalyClient.call(@repository.storage, :operation_service, :user_merge_to_ref, request)
+
+ if pre_receive_error = response.pre_receive_error.presence
+ raise Gitlab::Git::PreReceiveError, pre_receive_error
+ end
+
+ response.commit_id
+ end
+
def user_merge_branch(user, source_sha, target_branch, message)
request_enum = QueueEnumerator.new
response_enum = GitalyClient.call(
@@ -258,14 +277,14 @@ module Gitlab
end
end
+ # rubocop:disable Metrics/ParameterLists
def user_commit_files(
user, branch_name, commit_message, actions, author_email, author_name,
- start_branch_name, start_repository)
-
+ start_branch_name, start_repository, force = false)
req_enum = Enumerator.new do |y|
header = user_commit_files_request_header(user, branch_name,
commit_message, actions, author_email, author_name,
- start_branch_name, start_repository)
+ start_branch_name, start_repository, force)
y.yield Gitaly::UserCommitFilesRequest.new(header: header)
@@ -300,6 +319,7 @@ module Gitlab
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
end
+ # rubocop:enable Metrics/ParameterLists
def user_commit_patches(user, branch_name, patches)
header = Gitaly::UserApplyPatchRequest::Header.new(
@@ -363,9 +383,10 @@ module Gitlab
Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update)
end
+ # rubocop:disable Metrics/ParameterLists
def user_commit_files_request_header(
user, branch_name, commit_message, actions, author_email, author_name,
- start_branch_name, start_repository)
+ start_branch_name, start_repository, force)
Gitaly::UserCommitFilesRequestHeader.new(
repository: @gitaly_repo,
@@ -375,9 +396,11 @@ module Gitlab
commit_author_name: encode_binary(author_name),
commit_author_email: encode_binary(author_email),
start_branch_name: encode_binary(start_branch_name),
- start_repository: start_repository.gitaly_repository
+ start_repository: start_repository.gitaly_repository,
+ force: force
)
end
+ # rubocop:enable Metrics/ParameterLists
def user_commit_files_action_header(action)
Gitaly::UserCommitFilesActionHeader.new(
diff --git a/lib/gitlab/gitaly_client/ref_service.rb b/lib/gitlab/gitaly_client/ref_service.rb
index d5633d167ac..6f6698607d9 100644
--- a/lib/gitlab/gitaly_client/ref_service.rb
+++ b/lib/gitlab/gitaly_client/ref_service.rb
@@ -84,15 +84,22 @@ module Gitlab
commits
end
- def list_new_blobs(newrev, limit = 0)
+ def list_new_blobs(newrev, limit = 0, dynamic_timeout: nil)
request = Gitaly::ListNewBlobsRequest.new(
repository: @gitaly_repo,
commit_id: newrev,
limit: limit
)
+ timeout =
+ if dynamic_timeout
+ [dynamic_timeout, GitalyClient.medium_timeout].min
+ else
+ GitalyClient.medium_timeout
+ end
+
response = GitalyClient
- .call(@storage, :ref_service, :list_new_blobs, request, timeout: GitalyClient.medium_timeout)
+ .call(@storage, :ref_service, :list_new_blobs, request, timeout: timeout)
response.flat_map do |msg|
# Returns an Array of Gitaly::NewBlobObject objects
diff --git a/lib/gitlab/gitaly_client/remote_service.rb b/lib/gitlab/gitaly_client/remote_service.rb
index 81fac37ee68..f3589fea39f 100644
--- a/lib/gitlab/gitaly_client/remote_service.rb
+++ b/lib/gitlab/gitaly_client/remote_service.rb
@@ -38,7 +38,7 @@ module Gitlab
def remove_remote(name)
request = Gitaly::RemoveRemoteRequest.new(repository: @gitaly_repo, name: name)
- response = GitalyClient.call(@storage, :remote_service, :remove_remote, request, timeout: GitalyClient.fast_timeout)
+ response = GitalyClient.call(@storage, :remote_service, :remove_remote, request)
response.result
end
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index a7e20d9429e..a08bfd0e25b 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -324,8 +324,8 @@ module Gitlab
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, chunked_response: true)
- request = Gitaly::SearchFilesByContentRequest.new(repository: @gitaly_repo, ref: ref, query: query, chunked_response: chunked_response)
+ def search_files_by_content(ref, query)
+ request = Gitaly::SearchFilesByContentRequest.new(repository: @gitaly_repo, ref: ref, query: query)
response = GitalyClient.call(@storage, :repository_service, :search_files_by_content, request)
search_results_from_response(response)
@@ -340,18 +340,11 @@ module Gitlab
gitaly_response.each do |message|
next if message.nil?
- # Old client will ignore :chunked_response flag
- # and return messages with `matches` key.
- # This code path will be removed post 12.0 release
- if message.matches.any?
- matches += message.matches
- else
- current_match << message.match_data
-
- if message.end_of_match
- matches << current_match
- current_match = +""
- end
+ current_match << message.match_data
+
+ if message.end_of_match
+ matches << current_match
+ current_match = +""
end
end
diff --git a/lib/gitlab/gitaly_client/storage_settings.rb b/lib/gitlab/gitaly_client/storage_settings.rb
index 754cccb6b3f..78ef6bfc0ec 100644
--- a/lib/gitlab/gitaly_client/storage_settings.rb
+++ b/lib/gitlab/gitaly_client/storage_settings.rb
@@ -32,11 +32,19 @@ module Gitlab
end
def self.disk_access_denied?
+ return false if rugged_enabled?
+
!temporarily_allowed?(ALLOW_KEY) && GitalyClient.feature_enabled?(DISK_ACCESS_DENIED_FLAG)
rescue
false # Err on the side of caution, don't break gitlab for people
end
+ def self.rugged_enabled?
+ Gitlab::Git::RuggedImpl::Repository::FEATURE_FLAGS.any? do |flag|
+ Feature.enabled?(flag)
+ end
+ end
+
def initialize(storage)
raise InvalidConfigurationError, "expected a Hash, got a #{storage.class.name}" unless storage.is_a?(Hash)
raise InvalidConfigurationError, INVALID_STORAGE_MESSAGE unless storage.has_key?('path')
diff --git a/lib/gitlab/github_import/importer/milestones_importer.rb b/lib/gitlab/github_import/importer/milestones_importer.rb
index 87cf2c8b598..71ff7465d9b 100644
--- a/lib/gitlab/github_import/importer/milestones_importer.rb
+++ b/lib/gitlab/github_import/importer/milestones_importer.rb
@@ -42,6 +42,7 @@ module Gitlab
description: milestone.description,
project_id: project.id,
state: state_for(milestone),
+ due_date: milestone.due_on&.to_date,
created_at: milestone.created_at,
updated_at: milestone.updated_at
}
diff --git a/lib/gitlab/github_import/importer/pull_request_importer.rb b/lib/gitlab/github_import/importer/pull_request_importer.rb
index e294173f992..1b293ddc7c7 100644
--- a/lib/gitlab/github_import/importer/pull_request_importer.rb
+++ b/lib/gitlab/github_import/importer/pull_request_importer.rb
@@ -89,7 +89,7 @@ module Gitlab
return if project.repository.branch_exists?(source_branch)
- project.repository.add_branch(merge_request.author, source_branch, pull_request.source_branch_sha)
+ project.repository.add_branch(project.creator, source_branch, pull_request.source_branch_sha)
rescue Gitlab::Git::CommandError => e
Gitlab::Sentry.track_acceptable_exception(e,
extra: {
diff --git a/lib/gitlab/github_import/importer/repository_importer.rb b/lib/gitlab/github_import/importer/repository_importer.rb
index e2dfb00dcc5..6d48c6a15b4 100644
--- a/lib/gitlab/github_import/importer/repository_importer.rb
+++ b/lib/gitlab/github_import/importer/repository_importer.rb
@@ -5,6 +5,7 @@ module Gitlab
module Importer
class RepositoryImporter
include Gitlab::ShellAdapter
+ include Gitlab::Utils::StrongMemoize
attr_reader :project, :client, :wiki_formatter
@@ -17,7 +18,7 @@ module Gitlab
# Returns true if we should import the wiki for the project.
# rubocop: disable CodeReuse/ActiveRecord
def import_wiki?
- client.repository(project.import_source)&.has_wiki &&
+ client_repository&.has_wiki &&
!project.wiki_repository_exists? &&
Gitlab::GitalyClient::RemoteService.exists?(wiki_url)
end
@@ -52,6 +53,7 @@ module Gitlab
refmap = Gitlab::GithubImport.refmap
project.repository.fetch_as_mirror(project.import_url, refmap: refmap, forced: true, remote_name: 'github')
+ project.change_head(default_branch) if default_branch
true
rescue Gitlab::Git::Repository::NoRepository, Gitlab::Shell::Error => e
fail_import("Failed to import the repository: #{e.message}")
@@ -82,6 +84,18 @@ module Gitlab
project.import_state.mark_as_failed(message)
false
end
+
+ private
+
+ def default_branch
+ client_repository&.default_branch
+ end
+
+ def client_repository
+ strong_memoize(:client_repository) do
+ client.repository(project.import_source)
+ end
+ end
end
end
end
diff --git a/lib/gitlab/gl_repository.rb b/lib/gitlab/gl_repository.rb
index 435b74806e7..c2be7f3d63a 100644
--- a/lib/gitlab/gl_repository.rb
+++ b/lib/gitlab/gl_repository.rb
@@ -2,23 +2,38 @@
module Gitlab
module GlRepository
- def self.gl_repository(project, is_wiki)
- "#{is_wiki ? 'wiki' : 'project'}-#{project.id}"
+ PROJECT = RepoType.new(
+ name: :project,
+ access_checker_class: Gitlab::GitAccess,
+ repository_accessor: -> (project) { project.repository }
+ ).freeze
+ WIKI = RepoType.new(
+ name: :wiki,
+ access_checker_class: Gitlab::GitAccessWiki,
+ repository_accessor: -> (project) { project.wiki.repository }
+ ).freeze
+
+ TYPES = {
+ PROJECT.name.to_s => PROJECT,
+ WIKI.name.to_s => WIKI
+ }.freeze
+
+ def self.types
+ TYPES
end
- # rubocop: disable CodeReuse/ActiveRecord
def self.parse(gl_repository)
- match_data = /\A(project|wiki)-([1-9][0-9]*)\z/.match(gl_repository)
- unless match_data
+ type_name, _id = gl_repository.split('-').first
+ type = types[type_name]
+ subject_id = type&.fetch_id(gl_repository)
+
+ unless subject_id
raise ArgumentError, "Invalid GL Repository \"#{gl_repository}\""
end
- type, id = match_data.captures
- project = Project.find_by(id: id)
- wiki = type == 'wiki'
+ project = Project.find_by_id(subject_id)
- [project, wiki]
+ [project, type]
end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
diff --git a/lib/gitlab/gl_repository/repo_type.rb b/lib/gitlab/gl_repository/repo_type.rb
new file mode 100644
index 00000000000..7abe6c29a25
--- /dev/null
+++ b/lib/gitlab/gl_repository/repo_type.rb
@@ -0,0 +1,42 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module GlRepository
+ class RepoType
+ attr_reader :name,
+ :access_checker_class,
+ :repository_accessor
+
+ def initialize(name:, access_checker_class:, repository_accessor:)
+ @name = name
+ @access_checker_class = access_checker_class
+ @repository_accessor = repository_accessor
+ end
+
+ def identifier_for_subject(subject)
+ "#{name}-#{subject.id}"
+ end
+
+ def fetch_id(identifier)
+ match = /\A#{name}-(?<id>\d+)\z/.match(identifier)
+ match[:id] if match
+ end
+
+ def wiki?
+ self == WIKI
+ end
+
+ def project?
+ self == PROJECT
+ end
+
+ def path_suffix
+ project? ? "" : ".#{name}"
+ end
+
+ def repository_for(subject)
+ repository_accessor.call(subject)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/gon_helper.rb b/lib/gitlab/gon_helper.rb
index 3235d3ccc4e..e00309e7946 100644
--- a/lib/gitlab/gon_helper.rb
+++ b/lib/gitlab/gon_helper.rb
@@ -25,6 +25,7 @@ module Gitlab
gon.test_env = Rails.env.test?
gon.suggested_label_colors = LabelsHelper.suggested_colors
gon.first_day_of_week = current_user&.first_day_of_week || Gitlab::CurrentSettings.first_day_of_week
+ gon.ee = Gitlab.ee?
if current_user
gon.current_user_id = current_user.id
diff --git a/lib/gitlab/graphql/authorize.rb b/lib/gitlab/graphql/authorize.rb
index 5e48bf9043d..f62813db82c 100644
--- a/lib/gitlab/graphql/authorize.rb
+++ b/lib/gitlab/graphql/authorize.rb
@@ -10,21 +10,6 @@ module Gitlab
def self.use(schema_definition)
schema_definition.instrument(:field, Instrumentation.new)
end
-
- def 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)
- required_permissions.concat(permissions)
- end
end
end
end
diff --git a/lib/gitlab/graphql/authorize/authorize_resource.rb b/lib/gitlab/graphql/authorize/authorize_resource.rb
index a56c4f6368d..b367a97105c 100644
--- a/lib/gitlab/graphql/authorize/authorize_resource.rb
+++ b/lib/gitlab/graphql/authorize/authorize_resource.rb
@@ -6,8 +6,21 @@ module Gitlab
module AuthorizeResource
extend ActiveSupport::Concern
- included do
- extend Gitlab::Graphql::Authorize
+ class_methods do
+ def 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)
+ required_permissions.concat(permissions)
+ end
end
def find_object(*args)
diff --git a/lib/gitlab/graphql/authorize/instrumentation.rb b/lib/gitlab/graphql/authorize/instrumentation.rb
index 2a3d790d67b..593da8471dd 100644
--- a/lib/gitlab/graphql/authorize/instrumentation.rb
+++ b/lib/gitlab/graphql/authorize/instrumentation.rb
@@ -6,19 +6,15 @@ module Gitlab
class Instrumentation
# Replace the resolver for the field with one that will only return the
# resolved object if the permissions check is successful.
- #
- # Collections are not supported. Apply permissions checks for those at the
- # database level instead, to avoid loading superfluous data from the DB
def instrument(_type, field)
- field_definition = field.metadata[:type_class]
- return field unless field_definition.respond_to?(:required_permissions)
- return field if field_definition.required_permissions.empty?
+ required_permissions = Array.wrap(field.metadata[:authorize])
+ return field if required_permissions.empty?
old_resolver = field.resolve_proc
new_resolver = -> (obj, args, ctx) do
resolved_obj = old_resolver.call(obj, args, ctx)
- checker = build_checker(ctx[:current_user], field_definition.required_permissions)
+ checker = build_checker(ctx[:current_user], required_permissions)
if resolved_obj.respond_to?(:then)
resolved_obj.then(&checker)
diff --git a/lib/gitlab/group_search_results.rb b/lib/gitlab/group_search_results.rb
new file mode 100644
index 00000000000..7255293b194
--- /dev/null
+++ b/lib/gitlab/group_search_results.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module Gitlab
+ class GroupSearchResults < SearchResults
+ def initialize(current_user, limit_projects, group, query, default_project_filter: false, per_page: 20)
+ super(current_user, limit_projects, query, default_project_filter: default_project_filter, per_page: per_page)
+
+ @group = group
+ end
+
+ # rubocop:disable CodeReuse/ActiveRecord
+ def users
+ # 1: get all groups the current user has access to
+ groups = GroupsFinder.new(current_user).execute.joins(:users)
+
+ # 2: Get the group's whole hierarchy
+ group_users = @group.direct_and_indirect_users
+
+ # 3: get all users the current user has access to (->
+ # `SearchResults#users`), which also applies the query.
+ users = super
+
+ # 4: filter for users that belong to the previously selected groups
+ users
+ .where(id: group_users.select('id'))
+ .where(id: groups.select('members.user_id'))
+ end
+ # rubocop:enable CodeReuse/ActiveRecord
+ end
+end
diff --git a/lib/gitlab/hashed_storage/migrator.rb b/lib/gitlab/hashed_storage/migrator.rb
index bf463077dcc..1f0deebea39 100644
--- a/lib/gitlab/hashed_storage/migrator.rb
+++ b/lib/gitlab/hashed_storage/migrator.rb
@@ -13,10 +13,18 @@ module Gitlab
#
# @param [Integer] start first project id for the range
# @param [Integer] finish last project id for the range
- def bulk_schedule(start:, finish:)
+ def bulk_schedule_migration(start:, finish:)
::HashedStorage::MigratorWorker.perform_async(start, finish)
end
+ # Schedule a range of projects to be bulk rolledback with #bulk_rollback asynchronously
+ #
+ # @param [Integer] start first project id for the range
+ # @param [Integer] finish last project id for the range
+ def bulk_schedule_rollback(start:, finish:)
+ ::HashedStorage::RollbackerWorker.perform_async(start, finish)
+ end
+
# Start migration of projects from specified range
#
# Flagging a project to be migrated is a synchronous action
@@ -34,6 +42,23 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
+ # Start rollback of projects from specified range
+ #
+ # Flagging a project to be rolled back is a synchronous action
+ # but the rollback runs through async jobs
+ #
+ # @param [Integer] start first project id for the range
+ # @param [Integer] finish last project id for the range
+ # rubocop: disable CodeReuse/ActiveRecord
+ def bulk_rollback(start:, finish:)
+ projects = build_relation(start, finish)
+
+ projects.with_route.find_each(batch_size: BATCH_SIZE) do |project|
+ rollback(project)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
# Flag a project to be migrated to Hashed Storage
#
# @param [Project] project that will be migrated
@@ -45,12 +70,37 @@ module Gitlab
Rails.logger.error("#{err.message} migrating storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
end
+ # Flag a project to be rolled-back to Legacy Storage
+ #
+ # @param [Project] project that will be rolled-back
def rollback(project)
- # TODO: implement rollback strategy
+ Rails.logger.info "Starting storage rollback of #{project.full_path} (ID=#{project.id})..."
+
+ project.rollback_to_legacy_storage!
+ rescue => err
+ Rails.logger.error("#{err.message} rolling-back storage of #{project.full_path} (ID=#{project.id}), trace - #{err.backtrace}")
+ end
+
+ # Returns whether we have any pending storage migration
+ #
+ def migration_pending?
+ any_non_empty_queue?(::HashedStorage::MigratorWorker, ::HashedStorage::ProjectMigrateWorker)
+ end
+
+ # Returns whether we have any pending storage rollback
+ #
+ def rollback_pending?
+ any_non_empty_queue?(::HashedStorage::RollbackerWorker, ::HashedStorage::ProjectRollbackWorker)
end
private
+ def any_non_empty_queue?(*workers)
+ workers.any? do |worker|
+ !Sidekiq::Queue.new(worker.queue).size.zero?
+ end
+ end
+
# rubocop: disable CodeReuse/ActiveRecord
def build_relation(start, finish)
relation = Project
diff --git a/lib/gitlab/hashed_storage/rake_helper.rb b/lib/gitlab/hashed_storage/rake_helper.rb
index 38f552fab03..87a31a37e3f 100644
--- a/lib/gitlab/hashed_storage/rake_helper.rb
+++ b/lib/gitlab/hashed_storage/rake_helper.rb
@@ -24,7 +24,7 @@ module Gitlab
end
# rubocop: disable CodeReuse/ActiveRecord
- def self.project_id_batches(&block)
+ def self.project_id_batches_migration(&block)
Project.with_unmigrated_storage.in_batches(of: batch_size, start: range_from, finish: range_to) do |relation| # rubocop: disable Cop/InBatches
ids = relation.pluck(:id)
@@ -34,6 +34,16 @@ module Gitlab
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
+ def self.project_id_batches_rollback(&block)
+ Project.with_storage_feature(:repository).in_batches(of: batch_size, start: range_from, finish: range_to) do |relation| # rubocop: disable Cop/InBatches
+ ids = relation.pluck(:id)
+
+ yield ids.min, ids.max
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ # rubocop: disable CodeReuse/ActiveRecord
def self.legacy_attachments_relation
Upload.joins(<<~SQL).where('projects.storage_version < :version OR projects.storage_version IS NULL', version: Project::HASHED_STORAGE_FEATURES[:attachments])
JOIN projects
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
index a4e60bbd828..381f1dd4e55 100644
--- a/lib/gitlab/highlight.rb
+++ b/lib/gitlab/highlight.rb
@@ -39,7 +39,7 @@ module Gitlab
private
def custom_language
- return nil unless @language
+ return unless @language
Rouge::Lexer.find_fancy(@language)
end
diff --git a/lib/gitlab/hook_data/issue_builder.rb b/lib/gitlab/hook_data/issue_builder.rb
index c99353b9d49..d39ff8c21cc 100644
--- a/lib/gitlab/hook_data/issue_builder.rb
+++ b/lib/gitlab/hook_data/issue_builder.rb
@@ -48,7 +48,7 @@ module Gitlab
}
issue.attributes.with_indifferent_access.slice(*self.class.safe_hook_attributes)
- .merge!(attrs)
+ .merge!(attrs)
end
end
end
diff --git a/lib/gitlab/hook_data/merge_request_builder.rb b/lib/gitlab/hook_data/merge_request_builder.rb
index ad38e26e40a..d77b1d04644 100644
--- a/lib/gitlab/hook_data/merge_request_builder.rb
+++ b/lib/gitlab/hook_data/merge_request_builder.rb
@@ -55,7 +55,7 @@ module Gitlab
}
merge_request.attributes.with_indifferent_access.slice(*self.class.safe_hook_attributes)
- .merge!(attrs)
+ .merge!(attrs)
end
end
end
diff --git a/lib/gitlab/i18n/metadata_entry.rb b/lib/gitlab/i18n/metadata_entry.rb
index 3764e379681..4facd10bfc8 100644
--- a/lib/gitlab/i18n/metadata_entry.rb
+++ b/lib/gitlab/i18n/metadata_entry.rb
@@ -15,7 +15,7 @@ module Gitlab
end
def expected_forms
- return nil unless plural_information
+ return unless plural_information
plural_information['nplurals'].to_i
end
diff --git a/lib/gitlab/import/merge_request_helpers.rb b/lib/gitlab/import/merge_request_helpers.rb
index fa3ff6c3f12..b3fe1fc0685 100644
--- a/lib/gitlab/import/merge_request_helpers.rb
+++ b/lib/gitlab/import/merge_request_helpers.rb
@@ -38,7 +38,6 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
- # rubocop: disable CodeReuse/ActiveRecord
def insert_or_replace_git_data(merge_request, source_branch_sha, target_branch_sha, already_exists = false)
# These fields are set so we can create the correct merge request
# diffs.
@@ -47,24 +46,21 @@ module Gitlab
merge_request.keep_around_commit
+ # We force to recreate all diffs to replace all existing data
+ # We use `.all` as otherwise `dependent: :nullify` (the default)
+ # takes an effect
+ merge_request.merge_request_diffs.all.delete_all if already_exists
+
# MR diffs normally use an "after_save" hook to pull data from Git.
# All of this happens in the transaction started by calling
# create/save/etc. This in turn can lead to these transactions being
# held open for much longer than necessary. To work around this we
# first save the diff, then populate it.
- diff =
- if already_exists
- merge_request.merge_request_diffs.take ||
- merge_request.merge_request_diffs.build
- else
- merge_request.merge_request_diffs.build
- end
-
+ diff = merge_request.merge_request_diffs.build
diff.importing = true
diff.save
diff.save_git_content
end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/import_export/import_export.yml b/lib/gitlab/import_export/import_export.yml
index 7f8c6d56627..89667976217 100644
--- a/lib/gitlab/import_export/import_export.yml
+++ b/lib/gitlab/import_export/import_export.yml
@@ -28,12 +28,12 @@ project_tree:
- notes:
:author
- releases:
- - :author
- :links
- project_members:
- :user
- merge_requests:
- :metrics
+ - :suggestions
- notes:
- :author
- events:
@@ -118,6 +118,7 @@ excluded_attributes:
- :description_html
- :repository_languages
- :bfg_object_map
+ - :tag_list
namespaces:
- :runners_token
- :runners_token_encrypted
diff --git a/lib/gitlab/import_export/json_hash_builder.rb b/lib/gitlab/import_export/json_hash_builder.rb
index 477499e1688..b145f37c052 100644
--- a/lib/gitlab/import_export/json_hash_builder.rb
+++ b/lib/gitlab/import_export/json_hash_builder.rb
@@ -67,7 +67,7 @@ module Gitlab
# +value+ existing model to be included in the hash
# +parsed_hash+ the original hash
def parse_hash(value)
- return nil if already_contains_methods?(value)
+ return if already_contains_methods?(value)
@attributes_finder.parse(value) do |hash|
{ include: hash_or_merge(value, hash) }
diff --git a/lib/gitlab/import_export/merge_request_parser.rb b/lib/gitlab/import_export/merge_request_parser.rb
index 040a70d6775..deb2f59f05f 100644
--- a/lib/gitlab/import_export/merge_request_parser.rb
+++ b/lib/gitlab/import_export/merge_request_parser.rb
@@ -20,6 +20,17 @@ module Gitlab
create_target_branch unless branch_exists?(@merge_request.target_branch)
end
+ # The merge_request_diff associated with the current @merge_request might
+ # be invalid. Than means, when the @merge_request object is saved, the
+ # @merge_request.merge_request_diff won't. This can leave the merge request
+ # in an invalid state, because a merge request must have an associated
+ # merge request diff.
+ # In this change, if the associated merge request diff is invalid, we set
+ # it to nil. This change, in association with the after callback
+ # :ensure_merge_request_diff in the MergeRequest class, makes that
+ # when the merge request is going to be created and it doesn't have
+ # one, a default one will be generated.
+ @merge_request.merge_request_diff = nil unless @merge_request.merge_request_diff&.valid?
@merge_request
end
diff --git a/lib/gitlab/import_export/relation_factory.rb b/lib/gitlab/import_export/relation_factory.rb
index 099b488f68e..61a1aa6da5a 100644
--- a/lib/gitlab/import_export/relation_factory.rb
+++ b/lib/gitlab/import_export/relation_factory.rb
@@ -75,7 +75,7 @@ module Gitlab
# the relation_hash, updating references with new object IDs, mapping users using
# the "members_mapper" object, also updating notes if required.
def create
- return nil if unknown_service?
+ return if unknown_service?
setup_models
diff --git a/lib/gitlab/incoming_email.rb b/lib/gitlab/incoming_email.rb
index cc0c633b943..8b346f6d7d2 100644
--- a/lib/gitlab/incoming_email.rb
+++ b/lib/gitlab/incoming_email.rb
@@ -57,7 +57,7 @@ module Gitlab
def address_regex
wildcard_address = config.address
- return nil unless wildcard_address
+ return unless wildcard_address
regex = Regexp.escape(wildcard_address)
regex = regex.sub(Regexp.escape(WILDCARD_PLACEHOLDER), '(.+)')
diff --git a/lib/gitlab/json_cache.rb b/lib/gitlab/json_cache.rb
index 1adf83739ad..e4bc437d787 100644
--- a/lib/gitlab/json_cache.rb
+++ b/lib/gitlab/json_cache.rb
@@ -71,7 +71,36 @@ module Gitlab
end
def parse_entry(raw, klass)
- klass.new(raw) if valid_entry?(raw, klass)
+ return unless valid_entry?(raw, klass)
+ return klass.new(raw) unless klass.ancestors.include?(ActiveRecord::Base)
+
+ # When the cached value is a persisted instance of ActiveRecord::Base in
+ # some cases a relation can return an empty collection becauses scope.none!
+ # is being applied on ActiveRecord::Associations::CollectionAssociation#scope
+ # when the new_record? method incorrectly returns false.
+ #
+ # See https://gitlab.com/gitlab-org/gitlab-ee/issues/9903#note_145329964
+ klass
+ .allocate
+ .init_with(
+ "attributes" => attributes_for(klass, raw),
+ "new_record" => new_record?(raw, klass)
+ )
+ end
+
+ def attributes_for(klass, raw)
+ # We have models that leave out some fields from the JSON export for
+ # security reasons, e.g. models that include the CacheMarkdownField.
+ # The ActiveRecord::AttributeSet we build from raw does know about
+ # these columns so we need manually set them.
+ missing_attributes = (klass.columns.map(&:name) - raw.keys)
+ missing_attributes.each { |column| raw[column] = nil }
+
+ klass.attributes_builder.build_from_database(raw, {})
+ end
+
+ def new_record?(raw, klass)
+ raw.fetch(klass.primary_key, nil).blank?
end
def valid_entry?(raw, klass)
diff --git a/lib/gitlab/kubernetes.rb b/lib/gitlab/kubernetes.rb
index a9957a85d48..d46b5e3aee3 100644
--- a/lib/gitlab/kubernetes.rb
+++ b/lib/gitlab/kubernetes.rb
@@ -24,6 +24,30 @@ module Gitlab
end
end
+ # Filters an array of pods (as returned by the kubernetes API) by their annotations
+ def filter_by_annotation(items, annotations = {})
+ items.select do |item|
+ metadata = item.fetch("metadata", {})
+ item_annotations = metadata.fetch("annotations", nil)
+ next unless item_annotations
+
+ annotations.all? { |k, v| item_annotations[k.to_s] == v }
+ end
+ end
+
+ # Filters an array of pods (as returned by the kubernetes API) by their project and environment
+ def filter_by_project_environment(items, app, env)
+ pods = filter_by_annotation(items, {
+ 'app.gitlab.com/app' => app,
+ 'app.gitlab.com/env' => env
+ })
+ return pods unless pods.empty?
+
+ filter_by_label(items, {
+ 'app' => env, # deprecated: replaced by app.gitlab.com/env
+ })
+ end
+
# Converts a pod (as returned by the kubernetes API) into a terminal
def terminals_for_pod(api_url, namespace, pod)
metadata = pod.fetch("metadata", {})
diff --git a/lib/gitlab/kubernetes/helm.rb b/lib/gitlab/kubernetes/helm.rb
index bbac15c7710..42c4745ff98 100644
--- a/lib/gitlab/kubernetes/helm.rb
+++ b/lib/gitlab/kubernetes/helm.rb
@@ -3,8 +3,8 @@
module Gitlab
module Kubernetes
module Helm
- HELM_VERSION = '2.12.2'.freeze
- KUBECTL_VERSION = '1.11.0'.freeze
+ HELM_VERSION = '2.12.3'.freeze
+ KUBECTL_VERSION = '1.11.7'.freeze
NAMESPACE = 'gitlab-managed-apps'.freeze
SERVICE_ACCOUNT = 'tiller'.freeze
CLUSTER_ROLE_BINDING = 'tiller-admin'.freeze
diff --git a/lib/gitlab/kubernetes/helm/install_command.rb b/lib/gitlab/kubernetes/helm/install_command.rb
index f931248b747..e33ba9305ce 100644
--- a/lib/gitlab/kubernetes/helm/install_command.rb
+++ b/lib/gitlab/kubernetes/helm/install_command.rb
@@ -7,7 +7,8 @@ module Gitlab
include BaseCommand
include ClientCommand
- attr_reader :name, :files, :chart, :version, :repository, :preinstall, :postinstall
+ attr_reader :name, :files, :chart, :repository, :preinstall, :postinstall
+ attr_accessor :version
def initialize(name:, chart:, files:, rbac:, version: nil, repository: nil, preinstall: nil, postinstall: nil)
@name = name
diff --git a/lib/gitlab/kubernetes/kube_client.rb b/lib/gitlab/kubernetes/kube_client.rb
index 624c2c67551..de14df56555 100644
--- a/lib/gitlab/kubernetes/kube_client.rb
+++ b/lib/gitlab/kubernetes/kube_client.rb
@@ -82,6 +82,8 @@ module Gitlab
def initialize(api_prefix, **kubeclient_options)
@api_prefix = api_prefix
@kubeclient_options = kubeclient_options.merge(http_max_redirects: 0)
+
+ validate_url!
end
def create_or_update_cluster_role_binding(resource)
@@ -118,6 +120,12 @@ module Gitlab
private
+ def validate_url!
+ return if Gitlab::CurrentSettings.allow_local_requests_from_hooks_and_services?
+
+ Gitlab::UrlBlocker.validate!(api_prefix, allow_local_network: false)
+ end
+
def cluster_role_binding_exists?(resource)
get_cluster_role_binding(resource.metadata.name)
rescue ::Kubeclient::ResourceNotFoundError
diff --git a/lib/gitlab/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb
index bc952147667..bbdd094e33b 100644
--- a/lib/gitlab/legacy_github_import/client.rb
+++ b/lib/gitlab/legacy_github_import/client.rb
@@ -68,7 +68,7 @@ module Gitlab
end
def user(login)
- return nil unless login.present?
+ return unless login.present?
return @users[login] if @users.key?(login)
@users[login] = api.user(login)
diff --git a/lib/gitlab/legacy_github_import/importer.rb b/lib/gitlab/legacy_github_import/importer.rb
index f3323c98af2..70b18221a66 100644
--- a/lib/gitlab/legacy_github_import/importer.rb
+++ b/lib/gitlab/legacy_github_import/importer.rb
@@ -89,12 +89,10 @@ module Gitlab
def import_labels
fetch_resources(:labels, repo, per_page: 100) do |labels|
labels.each do |raw|
- begin
- gh_label = LabelFormatter.new(project, raw)
- gh_label.create!
- rescue => e
- errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(gh_label.url), errors: e.message }
- end
+ gh_label = LabelFormatter.new(project, raw)
+ gh_label.create!
+ rescue => e
+ errors << { type: :label, url: Gitlab::UrlSanitizer.sanitize(gh_label.url), errors: e.message }
end
end
@@ -104,12 +102,10 @@ module Gitlab
def import_milestones
fetch_resources(:milestones, repo, state: :all, per_page: 100) do |milestones|
milestones.each do |raw|
- begin
- gh_milestone = MilestoneFormatter.new(project, raw)
- gh_milestone.create!
- rescue => e
- errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(gh_milestone.url), errors: e.message }
- end
+ gh_milestone = MilestoneFormatter.new(project, raw)
+ gh_milestone.create!
+ rescue => e
+ errors << { type: :milestone, url: Gitlab::UrlSanitizer.sanitize(gh_milestone.url), errors: e.message }
end
end
end
@@ -223,24 +219,22 @@ module Gitlab
def create_comments(comments)
ActiveRecord::Base.no_touching do
comments.each do |raw|
- begin
- comment = CommentFormatter.new(project, raw, client)
+ comment = CommentFormatter.new(project, raw, client)
- # GH does not return info about comment's parent, so we guess it by checking its URL!
- *_, parent, iid = URI(raw.html_url).path.split('/')
+ # GH does not return info about comment's parent, so we guess it by checking its URL!
+ *_, parent, iid = URI(raw.html_url).path.split('/')
- issuable = if parent == 'issues'
- Issue.find_by(project_id: project.id, iid: iid)
- else
- MergeRequest.find_by(target_project_id: project.id, iid: iid)
- end
+ issuable = if parent == 'issues'
+ Issue.find_by(project_id: project.id, iid: iid)
+ else
+ MergeRequest.find_by(target_project_id: project.id, iid: iid)
+ end
- next unless issuable
+ next unless issuable
- issuable.notes.create!(comment.attributes)
- rescue => e
- errors << { type: :comment, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
- end
+ issuable.notes.create!(comment.attributes)
+ rescue => e
+ errors << { type: :comment, url: Gitlab::UrlSanitizer.sanitize(raw.url), errors: e.message }
end
end
end
@@ -281,12 +275,10 @@ module Gitlab
def import_releases
fetch_resources(:releases, repo, per_page: 100) do |releases|
releases.each do |raw|
- begin
- gh_release = ReleaseFormatter.new(project, raw)
- gh_release.create! if gh_release.valid?
- rescue => e
- errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(gh_release.url), errors: e.message }
- end
+ gh_release = ReleaseFormatter.new(project, raw)
+ gh_release.create! if gh_release.valid?
+ rescue => e
+ errors << { type: :release, url: Gitlab::UrlSanitizer.sanitize(gh_release.url), errors: e.message }
end
end
end
diff --git a/lib/gitlab/legacy_github_import/user_formatter.rb b/lib/gitlab/legacy_github_import/user_formatter.rb
index ec0e221b1ff..889e6aaa968 100644
--- a/lib/gitlab/legacy_github_import/user_formatter.rb
+++ b/lib/gitlab/legacy_github_import/user_formatter.rb
@@ -25,7 +25,7 @@ module Gitlab
end
def find_by_email
- return nil unless email
+ return unless email
User.find_by_any_email(email)
.try(:id)
@@ -33,7 +33,7 @@ module Gitlab
# rubocop: disable CodeReuse/ActiveRecord
def find_by_external_uid
- return nil unless id
+ return unless id
identities = ::Identity.arel_table
diff --git a/lib/gitlab/metrics/influx_db.rb b/lib/gitlab/metrics/influx_db.rb
index 0b04340fbb5..269d90fa971 100644
--- a/lib/gitlab/metrics/influx_db.rb
+++ b/lib/gitlab/metrics/influx_db.rb
@@ -52,10 +52,8 @@ module Gitlab
pool&.with do |connection|
prepared.each_slice(settings[:packet_size]) do |slice|
- begin
- connection.write_points(slice)
- rescue StandardError
- end
+ connection.write_points(slice)
+ rescue StandardError
end
end
rescue Errno::EADDRNOTAVAIL, SocketError => ex
diff --git a/lib/gitlab/middleware/basic_health_check.rb b/lib/gitlab/middleware/basic_health_check.rb
index acf8c301b8f..84e49805428 100644
--- a/lib/gitlab/middleware/basic_health_check.rb
+++ b/lib/gitlab/middleware/basic_health_check.rb
@@ -24,7 +24,13 @@ module Gitlab
def call(env)
return @app.call(env) unless env['PATH_INFO'] == HEALTH_PATH
- request = ActionDispatch::Request.new(env)
+ # We should be using ActionDispatch::Request instead of
+ # Rack::Request to be consistent with Rails, but due to a Rails
+ # bug described in
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58573#note_149799010
+ # hosts behind a load balancer will only see 127.0.0.1 for the
+ # load balancer's IP.
+ request = Rack::Request.new(env)
return OK_RESPONSE if client_ip_whitelisted?(request)
diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb
index a68f8801c2a..58f06b6708c 100644
--- a/lib/gitlab/project_search_results.rb
+++ b/lib/gitlab/project_search_results.rb
@@ -22,11 +22,17 @@ module Gitlab
paginated_blobs(wiki_blobs, page)
when 'commits'
Kaminari.paginate_array(commits).page(page).per(per_page)
+ when 'users'
+ users.page(page).per(per_page)
else
super(scope, page, false)
end
end
+ def users
+ super.where(id: @project.team.members) # rubocop:disable CodeReuse/ActiveRecord
+ end
+
def blobs_count
@blobs_count ||= blobs.count
end
diff --git a/lib/gitlab/project_template.rb b/lib/gitlab/project_template.rb
index ef656e5b2ce..99885be8755 100644
--- a/lib/gitlab/project_template.rb
+++ b/lib/gitlab/project_template.rb
@@ -28,11 +28,20 @@ module Gitlab
ProjectTemplate.new('rails', 'Ruby on Rails', _('Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/rails', 'illustrations/logos/rails.svg'),
ProjectTemplate.new('spring', 'Spring', _('Includes an MVC structure, mvnw and pom.xml to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/spring', 'illustrations/logos/spring.svg'),
ProjectTemplate.new('express', 'NodeJS Express', _('Includes an MVC structure to help you get started.'), 'https://gitlab.com/gitlab-org/project-templates/express', 'illustrations/logos/express.svg'),
+ ProjectTemplate.new('iosswift', 'iOS (Swift)', _('A ready-to-go template for use with iOS Swift apps.'), 'https://gitlab.com/gitlab-org/project-templates/iosswift'),
+ ProjectTemplate.new('dotnetcore', '.NET Core', _('A .NET Core console application template, customizable for any .NET Core project'), 'https://gitlab.com/gitlab-org/project-templates/dotnetcore', 'illustrations/logos/dotnet.svg'),
+ ProjectTemplate.new('android', 'Android', _('A ready-to-go template for use with Android apps.'), 'https://gitlab.com/gitlab-org/project-templates/android', 'illustrations/logos/android.svg'),
+ ProjectTemplate.new('gomicro', 'Go Micro', _('Go Micro is a framework for micro service development.'), 'https://gitlab.com/gitlab-org/project-templates/go-micro'),
ProjectTemplate.new('hugo', 'Pages/Hugo', _('Everything you need to create a GitLab Pages site using Hugo.'), 'https://gitlab.com/pages/hugo'),
ProjectTemplate.new('jekyll', 'Pages/Jekyll', _('Everything you need to create a GitLab Pages site using Jekyll.'), 'https://gitlab.com/pages/jekyll'),
ProjectTemplate.new('plainhtml', 'Pages/Plain HTML', _('Everything you need to create a GitLab Pages site using plain HTML.'), 'https://gitlab.com/pages/plain-html'),
ProjectTemplate.new('gitbook', 'Pages/GitBook', _('Everything you need to create a GitLab Pages site using GitBook.'), 'https://gitlab.com/pages/gitbook'),
- ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo')
+ ProjectTemplate.new('hexo', 'Pages/Hexo', _('Everything you need to create a GitLab Pages site using Hexo.'), 'https://gitlab.com/pages/hexo'),
+ ProjectTemplate.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook', 'illustrations/logos/netlify.svg'),
+ ProjectTemplate.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo', 'illustrations/logos/netlify.svg')
].freeze
class << self
diff --git a/lib/gitlab/quick_actions/command_definition.rb b/lib/gitlab/quick_actions/command_definition.rb
index 259345b8a9a..e7bfcb16582 100644
--- a/lib/gitlab/quick_actions/command_definition.rb
+++ b/lib/gitlab/quick_actions/command_definition.rb
@@ -48,6 +48,8 @@ module Gitlab
def execute(context, arg)
return if noop? || !available?(context)
+ count_commands_executed_in(context)
+
execute_block(action_block, context, arg)
end
@@ -73,6 +75,13 @@ module Gitlab
private
+ def count_commands_executed_in(context)
+ return unless context.respond_to?(:commands_executed_count=)
+
+ context.commands_executed_count ||= 0
+ context.commands_executed_count += 1
+ end
+
def execute_block(block, context, arg)
if arg.present?
parsed = parse_params(arg, context)
diff --git a/lib/gitlab/recaptcha.rb b/lib/gitlab/recaptcha.rb
index 6559c3e3c57..772d743c9b0 100644
--- a/lib/gitlab/recaptcha.rb
+++ b/lib/gitlab/recaptcha.rb
@@ -5,8 +5,8 @@ module Gitlab
def self.load_configurations!
if Gitlab::CurrentSettings.recaptcha_enabled
::Recaptcha.configure do |config|
- config.public_key = Gitlab::CurrentSettings.recaptcha_site_key
- config.private_key = Gitlab::CurrentSettings.recaptcha_private_key
+ config.site_key = Gitlab::CurrentSettings.recaptcha_site_key
+ config.secret_key = Gitlab::CurrentSettings.recaptcha_private_key
end
true
diff --git a/lib/gitlab/repo_path.rb b/lib/gitlab/repo_path.rb
index 202d310e237..207a80b7db2 100644
--- a/lib/gitlab/repo_path.rb
+++ b/lib/gitlab/repo_path.rb
@@ -5,19 +5,26 @@ module Gitlab
NotFoundError = Class.new(StandardError)
def self.parse(repo_path)
- wiki = false
project_path = repo_path.sub(/\.git\z/, '').sub(%r{\A/}, '')
- project, was_redirected = find_project(project_path)
-
- if project_path.end_with?('.wiki') && project.nil?
- project, was_redirected = find_project(project_path.chomp('.wiki'))
- wiki = true
+ # Detect the repo type based on the path, the first one tried is the project
+ # type, which does not have a suffix.
+ Gitlab::GlRepository.types.each do |_name, type|
+ # If the project path does not end with the defined suffix, try the next
+ # type.
+ # We'll always try to find a project with an empty suffix (for the
+ # `Gitlab::GlRepository::PROJECT` type.
+ next unless project_path.end_with?(type.path_suffix)
+
+ project, was_redirected = find_project(project_path.chomp(type.path_suffix))
+ redirected_path = project_path if was_redirected
+
+ # If we found a matching project, then the type was matched, no need to
+ # continue looking.
+ return [project, type, redirected_path] if project
end
- redirected_path = project_path if was_redirected
-
- [project, wiki, redirected_path]
+ nil
end
def self.find_project(project_path)
diff --git a/lib/gitlab/request_context.rb b/lib/gitlab/request_context.rb
index d9811e036d3..f6d289476c5 100644
--- a/lib/gitlab/request_context.rb
+++ b/lib/gitlab/request_context.rb
@@ -13,7 +13,13 @@ module Gitlab
end
def call(env)
- req = ActionDispatch::Request.new(env)
+ # We should be using ActionDispatch::Request instead of
+ # Rack::Request to be consistent with Rails, but due to a Rails
+ # bug described in
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58573#note_149799010
+ # hosts behind a load balancer will only see 127.0.0.1 for the
+ # load balancer's IP.
+ req = Rack::Request.new(env)
Gitlab::SafeRequestStore[:client_ip] = req.ip
diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb
index 491148ec1a6..8988b9ad7be 100644
--- a/lib/gitlab/search_results.rb
+++ b/lib/gitlab/search_results.rb
@@ -32,6 +32,8 @@ module Gitlab
merge_requests.page(page).per(per_page)
when 'milestones'
milestones.page(page).per(per_page)
+ when 'users'
+ users.page(page).per(per_page)
else
Kaminari.paginate_array([]).page(page).per(per_page)
end
@@ -71,6 +73,12 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
+ # rubocop:disable CodeReuse/ActiveRecord
+ def limited_users_count
+ @limited_users_count ||= users.limit(count_limit).count
+ end
+ # rubocop:enable CodeReuse/ActiveRecord
+
def single_commit_result?
false
end
@@ -79,6 +87,12 @@ module Gitlab
1001
end
+ def users
+ return User.none unless Ability.allowed?(current_user, :read_users_list)
+
+ UsersFinder.new(current_user, search: query).execute
+ end
+
private
def projects
diff --git a/lib/gitlab/shell.rb b/lib/gitlab/shell.rb
index 1153e69d3de..93182607616 100644
--- a/lib/gitlab/shell.rb
+++ b/lib/gitlab/shell.rb
@@ -10,18 +10,6 @@ module Gitlab
Error = Class.new(StandardError)
- KeyAdder = Struct.new(:io) do
- def add_key(id, key)
- key = Gitlab::Shell.strip_key(key)
- # Newline and tab are part of the 'protocol' used to transmit id+key to the other end
- if key.include?("\t") || key.include?("\n")
- raise Error.new("Invalid key: #{key.inspect}")
- end
-
- io.puts("#{id}\t#{key}")
- end
- end
-
class << self
def secret_token
@secret_token ||= begin
@@ -40,10 +28,6 @@ module Gitlab
.join('GITLAB_SHELL_VERSION')).strip
end
- def strip_key(key)
- key.split(/[ ]+/)[0, 2].join(' ')
- end
-
private
# Create (if necessary) and link the secret token file
@@ -173,7 +157,7 @@ module Gitlab
false
end
- # Add new key to gitlab-shell
+ # Add new key to authorized_keys
#
# Ex.
# add_key("key-42", "sha-rsa ...")
@@ -181,33 +165,53 @@ module Gitlab
def add_key(key_id, key_content)
return unless self.authorized_keys_enabled?
- gitlab_shell_fast_execute([gitlab_shell_keys_path,
- 'add-key', key_id, self.class.strip_key(key_content)])
+ if shell_out_for_gitlab_keys?
+ gitlab_shell_fast_execute([
+ gitlab_shell_keys_path,
+ 'add-key',
+ key_id,
+ strip_key(key_content)
+ ])
+ else
+ gitlab_authorized_keys.add_key(key_id, key_content)
+ end
end
# Batch-add keys to authorized_keys
#
# Ex.
- # batch_add_keys { |adder| adder.add_key("key-42", "sha-rsa ...") }
- def batch_add_keys(&block)
+ # batch_add_keys(Key.all)
+ def batch_add_keys(keys)
return unless self.authorized_keys_enabled?
- IO.popen(%W(#{gitlab_shell_path}/bin/gitlab-keys batch-add-keys), 'w') do |io|
- yield(KeyAdder.new(io))
+ if shell_out_for_gitlab_keys?
+ begin
+ IO.popen("#{gitlab_shell_keys_path} batch-add-keys", 'w') do |io|
+ add_keys_to_io(keys, io)
+ end
+
+ $?.success?
+ rescue Error
+ false
+ end
+ else
+ gitlab_authorized_keys.batch_add_keys(keys)
end
end
- # Remove ssh key from gitlab shell
+ # Remove ssh key from authorized_keys
#
# Ex.
- # remove_key("key-342", "sha-rsa ...")
+ # remove_key("key-342")
#
- def remove_key(key_id, key_content = nil)
+ def remove_key(id, _ = nil)
return unless self.authorized_keys_enabled?
- args = [gitlab_shell_keys_path, 'rm-key', key_id]
- args << key_content if key_content
- gitlab_shell_fast_execute(args)
+ if shell_out_for_gitlab_keys?
+ gitlab_shell_fast_execute([gitlab_shell_keys_path, 'rm-key', id])
+ else
+ gitlab_authorized_keys.rm_key(id)
+ end
end
# Remove all ssh keys from gitlab shell
@@ -218,7 +222,11 @@ module Gitlab
def remove_all_keys
return unless self.authorized_keys_enabled?
- gitlab_shell_fast_execute([gitlab_shell_keys_path, 'clear'])
+ if shell_out_for_gitlab_keys?
+ gitlab_shell_fast_execute([gitlab_shell_keys_path, 'clear'])
+ else
+ gitlab_authorized_keys.clear
+ end
end
# Remove ssh keys from gitlab shell that are not in the DB
@@ -247,40 +255,16 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
- # Iterate over all ssh key IDs from gitlab shell, in batches
- #
- # Ex.
- # batch_read_key_ids { |batch| keys = Key.where(id: batch) }
- #
- def batch_read_key_ids(batch_size: 100, &block)
- return unless self.authorized_keys_enabled?
-
- list_key_ids do |key_id_stream|
- key_id_stream.lazy.each_slice(batch_size) do |lines|
- key_ids = lines.map { |l| l.chomp.to_i }
- yield(key_ids)
- end
- end
- end
-
- # Stream all ssh key IDs from gitlab shell, separated by newlines
- #
- # Ex.
- # list_key_ids
- #
- def list_key_ids(&block)
- return unless self.authorized_keys_enabled?
-
- IO.popen(%W(#{gitlab_shell_path}/bin/gitlab-keys list-key-ids), &block)
- end
-
# Add empty directory for storing repositories
#
# Ex.
# add_namespace("default", "gitlab")
#
def add_namespace(storage, name)
- Gitlab::GitalyClient::NamespaceService.new(storage).add(name)
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58012
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ Gitlab::GitalyClient::NamespaceService.new(storage).add(name)
+ end
rescue GRPC::InvalidArgument => e
raise ArgumentError, e.message
end
@@ -337,16 +321,16 @@ module Gitlab
end
# rubocop: enable CodeReuse/ActiveRecord
+ def hooks_path
+ File.join(gitlab_shell_path, 'hooks')
+ end
+
protected
def gitlab_shell_path
File.expand_path(Gitlab.config.gitlab_shell.path)
end
- def gitlab_shell_hooks_path
- File.expand_path(Gitlab.config.gitlab_shell.hooks_path)
- end
-
def gitlab_shell_user_home
File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}")
end
@@ -375,6 +359,10 @@ module Gitlab
private
+ def shell_out_for_gitlab_keys?
+ Gitlab.config.gitlab_shell.authorized_keys_file.blank?
+ end
+
def gitlab_shell_fast_execute(cmd)
output, status = gitlab_shell_fast_execute_helper(cmd)
@@ -412,6 +400,40 @@ module Gitlab
raise Error, e
end
+ def gitlab_authorized_keys
+ @gitlab_authorized_keys ||= Gitlab::AuthorizedKeys.new
+ end
+
+ def batch_read_key_ids(batch_size: 100, &block)
+ return unless self.authorized_keys_enabled?
+
+ if shell_out_for_gitlab_keys?
+ IO.popen("#{gitlab_shell_keys_path} list-key-ids") do |key_id_stream|
+ key_id_stream.lazy.each_slice(batch_size) do |lines|
+ yield(lines.map { |l| l.chomp.to_i })
+ end
+ end
+ else
+ gitlab_authorized_keys.list_key_ids.lazy.each_slice(batch_size) do |key_ids|
+ yield(key_ids)
+ end
+ end
+ end
+
+ def strip_key(key)
+ key.split(/[ ]+/)[0, 2].join(' ')
+ end
+
+ def add_keys_to_io(keys, io)
+ keys.each do |k|
+ key = strip_key(k.key)
+
+ raise Error.new("Invalid key: #{key.inspect}") if key.include?("\t") || key.include?("\n")
+
+ io.puts("#{k.shell_id}\t#{key}")
+ end
+ end
+
class GitalyGitlabProjects
attr_reader :shard_name, :repository_relative_path, :output, :gl_project_path
diff --git a/lib/gitlab/sidekiq_config.rb b/lib/gitlab/sidekiq_config.rb
index 3b8de64913b..fb303e3fb0c 100644
--- a/lib/gitlab/sidekiq_config.rb
+++ b/lib/gitlab/sidekiq_config.rb
@@ -48,7 +48,9 @@ module Gitlab
end
def self.workers
- @workers ||= find_workers(Rails.root.join('app', 'workers'))
+ @workers ||=
+ find_workers(Rails.root.join('app', 'workers')) +
+ find_workers(Rails.root.join('ee', 'app', 'workers'))
end
def self.find_workers(root)
diff --git a/lib/gitlab/sidekiq_middleware/memory_killer.rb b/lib/gitlab/sidekiq_middleware/memory_killer.rb
new file mode 100644
index 00000000000..671d795ec33
--- /dev/null
+++ b/lib/gitlab/sidekiq_middleware/memory_killer.rb
@@ -0,0 +1,82 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SidekiqMiddleware
+ class MemoryKiller
+ # Default the RSS limit to 0, meaning the MemoryKiller is disabled
+ MAX_RSS = (ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] || 0).to_s.to_i
+ # Give Sidekiq 15 minutes of grace time after exceeding the RSS limit
+ GRACE_TIME = (ENV['SIDEKIQ_MEMORY_KILLER_GRACE_TIME'] || 15 * 60).to_s.to_i
+ # Wait 30 seconds for running jobs to finish during graceful shutdown
+ SHUTDOWN_WAIT = (ENV['SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT'] || 30).to_s.to_i
+
+ # Create a mutex used to ensure there will be only one thread waiting to
+ # shut Sidekiq down
+ MUTEX = Mutex.new
+
+ def call(worker, job, queue)
+ yield
+
+ current_rss = get_rss
+
+ return unless MAX_RSS > 0 && current_rss > MAX_RSS
+
+ Thread.new do
+ # Return if another thread is already waiting to shut Sidekiq down
+ next unless MUTEX.try_lock
+
+ Sidekiq.logger.warn "Sidekiq worker PID-#{pid} current RSS #{current_rss}"\
+ " exceeds maximum RSS #{MAX_RSS} after finishing job #{worker.class} JID-#{job['jid']}"
+ Sidekiq.logger.warn "Sidekiq worker PID-#{pid} will stop fetching new jobs in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later"
+
+ # Wait `GRACE_TIME` to give the memory intensive job time to finish.
+ # Then, tell Sidekiq to stop fetching new jobs.
+ wait_and_signal(GRACE_TIME, 'SIGTSTP', 'stop fetching new jobs')
+
+ # Wait `SHUTDOWN_WAIT` to give already fetched jobs time to finish.
+ # Then, tell Sidekiq to gracefully shut down by giving jobs a few more
+ # moments to finish, killing and requeuing them if they didn't, and
+ # then terminating itself. Sidekiq will replicate the TERM to all its
+ # children if it can.
+ wait_and_signal(SHUTDOWN_WAIT, 'SIGTERM', 'gracefully shut down')
+
+ # Wait for Sidekiq to shutdown gracefully, and kill it if it didn't.
+ # Kill the whole pgroup, so we can be sure no children are left behind
+ wait_and_signal_pgroup(Sidekiq.options[:timeout] + 2, 'SIGKILL', 'die')
+ end
+ end
+
+ private
+
+ def get_rss
+ output, status = Gitlab::Popen.popen(%W(ps -o rss= -p #{pid}), Rails.root.to_s)
+ return 0 unless status.zero?
+
+ output.to_i
+ end
+
+ # If this sidekiq process is pgroup leader, signal to the whole pgroup
+ def wait_and_signal_pgroup(time, signal, explanation)
+ return wait_and_signal(time, signal, explanation) unless Process.getpgrp == pid
+
+ Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})"
+ sleep(time)
+
+ Sidekiq.logger.warn "sending Sidekiq worker PGRP-#{pid} #{signal} (#{explanation})"
+ Process.kill(signal, 0)
+ end
+
+ def wait_and_signal(time, signal, explanation)
+ Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})"
+ sleep(time)
+
+ Sidekiq.logger.warn "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})"
+ Process.kill(signal, pid)
+ end
+
+ def pid
+ Process.pid
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sidekiq_middleware/shutdown.rb b/lib/gitlab/sidekiq_middleware/shutdown.rb
deleted file mode 100644
index 19f3be83bce..00000000000
--- a/lib/gitlab/sidekiq_middleware/shutdown.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-# frozen_string_literal: true
-
-require 'mutex_m'
-
-module Gitlab
- module SidekiqMiddleware
- class Shutdown
- extend Mutex_m
-
- # Default the RSS limit to 0, meaning the MemoryKiller is disabled
- MAX_RSS = (ENV['SIDEKIQ_MEMORY_KILLER_MAX_RSS'] || 0).to_s.to_i
- # Give Sidekiq 15 minutes of grace time after exceeding the RSS limit
- GRACE_TIME = (ENV['SIDEKIQ_MEMORY_KILLER_GRACE_TIME'] || 15 * 60).to_s.to_i
- # Wait 30 seconds for running jobs to finish during graceful shutdown
- SHUTDOWN_WAIT = (ENV['SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT'] || 30).to_s.to_i
-
- # This exception can be used to request that the middleware start shutting down Sidekiq
- WantShutdown = Class.new(StandardError)
-
- ShutdownWithoutRaise = Class.new(WantShutdown)
- private_constant :ShutdownWithoutRaise
-
- # For testing only, to avoid race conditions (?) in Rspec mocks.
- attr_reader :trace
-
- # We store the shutdown thread in a class variable to ensure that there
- # can be only one shutdown thread in the process.
- def self.create_shutdown_thread
- mu_synchronize do
- break unless @shutdown_thread.nil?
-
- @shutdown_thread = Thread.new { yield }
- end
- end
-
- # For testing only: so we can wait for the shutdown thread to finish.
- def self.shutdown_thread
- mu_synchronize { @shutdown_thread }
- end
-
- # For testing only: so that we can reset the global state before each test.
- def self.clear_shutdown_thread
- mu_synchronize { @shutdown_thread = nil }
- end
-
- def initialize
- @trace = Queue.new if Rails.env.test?
- end
-
- def call(worker, job, queue)
- shutdown_exception = nil
-
- begin
- yield
- check_rss!
- rescue WantShutdown => ex
- shutdown_exception = ex
- end
-
- return unless shutdown_exception
-
- self.class.create_shutdown_thread do
- do_shutdown(worker, job, shutdown_exception)
- end
-
- raise shutdown_exception unless shutdown_exception.is_a?(ShutdownWithoutRaise)
- end
-
- private
-
- def do_shutdown(worker, job, shutdown_exception)
- Sidekiq.logger.warn "Sidekiq worker PID-#{pid} shutting down because of #{shutdown_exception} after job "\
- "#{worker.class} JID-#{job['jid']}"
- Sidekiq.logger.warn "Sidekiq worker PID-#{pid} will stop fetching new jobs in #{GRACE_TIME} seconds, and will be shut down #{SHUTDOWN_WAIT} seconds later"
-
- # Wait `GRACE_TIME` to give the memory intensive job time to finish.
- # Then, tell Sidekiq to stop fetching new jobs.
- wait_and_signal(GRACE_TIME, 'SIGTSTP', 'stop fetching new jobs')
-
- # Wait `SHUTDOWN_WAIT` to give already fetched jobs time to finish.
- # Then, tell Sidekiq to gracefully shut down by giving jobs a few more
- # moments to finish, killing and requeuing them if they didn't, and
- # then terminating itself.
- wait_and_signal(SHUTDOWN_WAIT, 'SIGTERM', 'gracefully shut down')
-
- # Wait for Sidekiq to shutdown gracefully, and kill it if it didn't.
- wait_and_signal(Sidekiq.options[:timeout] + 2, 'SIGKILL', 'die')
- end
-
- def check_rss!
- return unless MAX_RSS > 0
-
- current_rss = get_rss
- return unless current_rss > MAX_RSS
-
- raise ShutdownWithoutRaise.new("current RSS #{current_rss} exceeds maximum RSS #{MAX_RSS}")
- end
-
- def get_rss
- output, status = Gitlab::Popen.popen(%W(ps -o rss= -p #{pid}), Rails.root.to_s)
- return 0 unless status.zero?
-
- output.to_i
- end
-
- def wait_and_signal(time, signal, explanation)
- Sidekiq.logger.warn "waiting #{time} seconds before sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})"
- sleep(time)
-
- Sidekiq.logger.warn "sending Sidekiq worker PID-#{pid} #{signal} (#{explanation})"
- kill(signal, pid)
- end
-
- def pid
- Process.pid
- end
-
- def sleep(time)
- if Rails.env.test?
- @trace << [:sleep, time]
- else
- Kernel.sleep(time)
- end
- end
-
- def kill(signal, pid)
- if Rails.env.test?
- @trace << [:kill, signal, pid]
- else
- Process.kill(signal, pid)
- end
- end
- end
- end
-end
diff --git a/lib/gitlab/sidekiq_signals.rb b/lib/gitlab/sidekiq_signals.rb
new file mode 100644
index 00000000000..82462544d07
--- /dev/null
+++ b/lib/gitlab/sidekiq_signals.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Gitlab
+ # As a process group leader, we can ensure that children of sidekiq are killed
+ # at the same time as sidekiq itself, to stop long-lived children from being
+ # reparented to init and "escaping". To do this, we override the default
+ # handlers used by sidekiq for INT and TERM signals
+ module SidekiqSignals
+ REPLACE_SIGNALS = %w[INT TERM].freeze
+
+ SIDEKIQ_CHANGED_MESSAGE =
+ "Intercepting signal handlers: #{REPLACE_SIGNALS.join(", ")} failed. " \
+ "Sidekiq should have registered them, but appears not to have done so."
+
+ def self.install!(sidekiq_handlers)
+ # This only works if we're process group leader
+ return unless Process.getpgrp == Process.pid
+
+ raise SIDEKIQ_CHANGED_MESSAGE unless
+ REPLACE_SIGNALS == sidekiq_handlers.keys & REPLACE_SIGNALS
+
+ REPLACE_SIGNALS.each do |signal|
+ old_handler = sidekiq_handlers[signal]
+ sidekiq_handlers[signal] = ->(cli) do
+ blindly_signal_pgroup!(signal)
+ old_handler.call(cli)
+ end
+ end
+ end
+
+ # The process group leader can forward INT and TERM signals to the whole
+ # group. However, the forwarded signal is *also* received by the leader,
+ # which could lead to an infinite loop. We can avoid this by temporarily
+ # ignoring the forwarded signal. This may cause us to miss some repeated
+ # signals from outside the process group, but that isn't fatal.
+ def self.blindly_signal_pgroup!(signal)
+ old_trap = trap(signal, 'IGNORE')
+ begin
+ Process.kill(signal, 0)
+ ensure
+ trap(signal, old_trap)
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/application_help.rb b/lib/gitlab/slash_commands/application_help.rb
new file mode 100644
index 00000000000..0ea7554ba64
--- /dev/null
+++ b/lib/gitlab/slash_commands/application_help.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SlashCommands
+ class ApplicationHelp < BaseCommand
+ def initialize(params)
+ @params = params
+ end
+
+ def execute
+ Gitlab::SlashCommands::Presenters::Help.new(commands).present(trigger, params[:text])
+ end
+
+ private
+
+ def trigger
+ "#{params[:command]} [project name or alias]"
+ end
+
+ def commands
+ Gitlab::SlashCommands::Command.commands
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/command.rb b/lib/gitlab/slash_commands/command.rb
index 474c09b9c4d..7c963fcf38a 100644
--- a/lib/gitlab/slash_commands/command.rb
+++ b/lib/gitlab/slash_commands/command.rb
@@ -9,7 +9,8 @@ module Gitlab
Gitlab::SlashCommands::IssueNew,
Gitlab::SlashCommands::IssueSearch,
Gitlab::SlashCommands::IssueMove,
- Gitlab::SlashCommands::Deploy
+ Gitlab::SlashCommands::Deploy,
+ Gitlab::SlashCommands::Run
]
end
diff --git a/lib/gitlab/slash_commands/presenters/error.rb b/lib/gitlab/slash_commands/presenters/error.rb
new file mode 100644
index 00000000000..442f8796338
--- /dev/null
+++ b/lib/gitlab/slash_commands/presenters/error.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SlashCommands
+ module Presenters
+ class Error < Presenters::Base
+ def initialize(message)
+ @message = message
+ end
+
+ def message
+ ephemeral_response(text: @message)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/presenters/run.rb b/lib/gitlab/slash_commands/presenters/run.rb
new file mode 100644
index 00000000000..c4bbc231464
--- /dev/null
+++ b/lib/gitlab/slash_commands/presenters/run.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SlashCommands
+ module Presenters
+ class Run < Presenters::Base
+ # rubocop: disable CodeReuse/ActiveRecord
+ def present(pipeline)
+ build = pipeline.builds.take
+
+ if build && (responder = Chat::Responder.responder_for(build))
+ in_channel_response(responder.scheduled_output)
+ else
+ unsupported_chat_service
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ def unsupported_chat_service
+ ephemeral_response(text: 'Sorry, this chat service is currently not supported by GitLab ChatOps.')
+ end
+
+ def failed_to_schedule(command)
+ ephemeral_response(
+ text: 'The command could not be scheduled. Make sure that your ' \
+ 'project has a .gitlab-ci.yml that defines a job with the ' \
+ "name #{command.inspect}"
+ )
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/slash_commands/run.rb b/lib/gitlab/slash_commands/run.rb
new file mode 100644
index 00000000000..10a545e28ac
--- /dev/null
+++ b/lib/gitlab/slash_commands/run.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module SlashCommands
+ # Slash command for triggering chatops jobs.
+ class Run < BaseCommand
+ def self.match(text)
+ /\Arun\s+(?<command>\S+)(\s+(?<arguments>.+))?\z/.match(text)
+ end
+
+ def self.help_message
+ 'run <command> <arguments>'
+ end
+
+ def self.available?(project)
+ Chat.available? && project.builds_enabled?
+ end
+
+ def self.allowed?(project, user)
+ can?(user, :create_pipeline, project)
+ end
+
+ def execute(match)
+ command = Chat::Command.new(
+ project: project,
+ chat_name: chat_name,
+ name: match[:command],
+ arguments: match[:arguments],
+ channel: params[:channel_id],
+ response_url: params[:response_url]
+ )
+
+ presenter = Gitlab::SlashCommands::Presenters::Run.new
+ pipeline = command.try_create_pipeline
+
+ if pipeline&.persisted?
+ presenter.present(pipeline)
+ else
+ presenter.failed_to_schedule(command.name)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/sql/pattern.rb b/lib/gitlab/sql/pattern.rb
index 92388262035..b698391c8bd 100644
--- a/lib/gitlab/sql/pattern.rb
+++ b/lib/gitlab/sql/pattern.rb
@@ -23,8 +23,12 @@ module Gitlab
end
end
+ def min_chars_for_partial_matching
+ MIN_CHARS_FOR_PARTIAL_MATCHING
+ end
+
def partial_matching?(query)
- query.length >= MIN_CHARS_FOR_PARTIAL_MATCHING
+ query.length >= min_chars_for_partial_matching
end
# column - The column name to search in.
@@ -33,7 +37,7 @@ module Gitlab
# `LOWER(column) = query` instead of using `ILIKE`.
def fuzzy_arel_match(column, query, lower_exact_match: false)
query = query.squish
- return nil unless query.present?
+ return unless query.present?
words = select_fuzzy_words(query)
diff --git a/lib/gitlab/template/gitlab_ci_yml_template.rb b/lib/gitlab/template/gitlab_ci_yml_template.rb
index fbefb5f7f0e..3e2bb11c35f 100644
--- a/lib/gitlab/template/gitlab_ci_yml_template.rb
+++ b/lib/gitlab/template/gitlab_ci_yml_template.rb
@@ -28,11 +28,6 @@ module Gitlab
def finder(project = nil)
Gitlab::Template::Finders::GlobalTemplateFinder.new(self.base_dir, self.extension, self.categories)
end
-
- def dropdown_names(context)
- categories = context == 'autodeploy' ? ['Auto deploy'] : %w(General Pages)
- super().slice(*categories)
- end
end
end
end
diff --git a/lib/gitlab/tracing.rb b/lib/gitlab/tracing.rb
index 0d9b0be1c8e..29517591c51 100644
--- a/lib/gitlab/tracing.rb
+++ b/lib/gitlab/tracing.rb
@@ -27,10 +27,11 @@ module Gitlab
def self.tracing_url
return unless tracing_url_enabled?
- tracing_url_template % {
- correlation_id: Gitlab::CorrelationId.current_id.to_s,
- service: Gitlab.process_name
- }
+ # Avoid using `format` since it can throw TypeErrors
+ # which we want to avoid on unsanitised env var input
+ tracing_url_template.to_s
+ .gsub(/\{\{\s*correlation_id\s*\}\}/, Gitlab::CorrelationId.current_id.to_s)
+ .gsub(/\{\{\s*service\s*\}\}/, Gitlab.process_name)
end
end
end
diff --git a/lib/gitlab/tracing/jaeger_factory.rb b/lib/gitlab/tracing/jaeger_factory.rb
index 2682007302a..93520d5667b 100644
--- a/lib/gitlab/tracing/jaeger_factory.rb
+++ b/lib/gitlab/tracing/jaeger_factory.rb
@@ -60,7 +60,7 @@ module Gitlab
elsif udp_endpoint.present?
sender = get_udp_sender(encoder, udp_endpoint)
else
- return nil
+ return
end
Jaeger::Reporters::RemoteReporter.new(
diff --git a/lib/gitlab/tree_summary.rb b/lib/gitlab/tree_summary.rb
index 453d78e2f7b..8518a13cd1c 100644
--- a/lib/gitlab/tree_summary.rb
+++ b/lib/gitlab/tree_summary.rb
@@ -95,7 +95,7 @@ module Gitlab
end
def cache_commit(commit)
- return nil unless commit.present?
+ return unless commit.present?
resolved_commits[commit.id] ||= commit
end
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index 0101ccc046a..75477c7cf18 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -54,6 +54,8 @@ module Gitlab
auto_devops_disabled: count(::ProjectAutoDevops.disabled),
deploy_keys: count(DeployKey),
deployments: count(Deployment),
+ successful_deployments: count(Deployment.success),
+ failed_deployments: count(Deployment.failed),
environments: count(::Environment),
clusters: count(::Clusters::Cluster),
clusters_enabled: count(::Clusters::Cluster.enabled),
@@ -82,6 +84,7 @@ module Gitlab
projects: count(Project),
projects_imported_from_github: count(Project.where(import_type: 'github')),
projects_with_repositories_enabled: count(ProjectFeature.where('repository_access_level > ?', ProjectFeature::DISABLED)),
+ projects_with_error_tracking_enabled: count(::ErrorTracking::ProjectErrorTrackingSetting.where(enabled: true)),
protected_branches: count(ProtectedBranch),
releases: count(Release),
remote_mirrors: count(RemoteMirror),
diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb
index 980a8014409..9ef23cf849f 100644
--- a/lib/gitlab/user_access.rb
+++ b/lib/gitlab/user_access.rb
@@ -118,8 +118,8 @@ module Gitlab
protected_refs: project.protected_tags)
end
- request_cache def protected?(kind, project, ref)
- kind.protected?(project, ref)
+ request_cache def protected?(kind, project, refs)
+ kind.protected?(project, refs)
end
end
end
diff --git a/lib/gitlab/user_extractor.rb b/lib/gitlab/user_extractor.rb
index 874599688bb..f0557f6ad68 100644
--- a/lib/gitlab/user_extractor.rb
+++ b/lib/gitlab/user_extractor.rb
@@ -11,11 +11,14 @@ module Gitlab
USERNAME_REGEXP = User.reference_pattern
def initialize(text)
- @text = text
+ # EE passes an Array to `text` in a few places, so we want to support both
+ # here.
+ @text = Array(text).join(' ')
end
def users
return User.none unless @text.present?
+ return User.none if references.empty?
@users ||= User.from_union(union_relations)
end
diff --git a/lib/gitlab/utils.rb b/lib/gitlab/utils.rb
index 99fa65e0e90..16ec8a8bb28 100644
--- a/lib/gitlab/utils.rb
+++ b/lib/gitlab/utils.rb
@@ -104,6 +104,12 @@ module Gitlab
nil
end
+ def try_megabytes_to_bytes(size)
+ Integer(size).megabytes
+ rescue ArgumentError
+ size
+ end
+
def bytes_to_megabytes(bytes)
bytes.to_f / Numeric::MEGABYTE
end
diff --git a/lib/gitlab/workhorse.rb b/lib/gitlab/workhorse.rb
index 265f6213a99..5d5a867c9ab 100644
--- a/lib/gitlab/workhorse.rb
+++ b/lib/gitlab/workhorse.rb
@@ -20,14 +20,14 @@ module Gitlab
SECRET_LENGTH = 32
class << self
- def git_http_ok(repository, is_wiki, user, action, show_all_refs: false)
+ def git_http_ok(repository, repo_type, user, action, show_all_refs: false)
raise "Unsupported action: #{action}" unless ALLOWED_GIT_HTTP_ACTIONS.include?(action.to_s)
project = repository.project
attrs = {
GL_ID: Gitlab::GlId.gl_id(user),
- GL_REPOSITORY: Gitlab::GlRepository.gl_repository(project, is_wiki),
+ GL_REPOSITORY: repo_type.identifier_for_subject(project),
GL_USERNAME: user&.username,
ShowAllRefs: show_all_refs,
Repository: repository.gitaly_repository.to_h,
diff --git a/lib/google_api/cloud_platform/client.rb b/lib/google_api/cloud_platform/client.rb
index e74ff6a9129..b5f99ea012b 100644
--- a/lib/google_api/cloud_platform/client.rb
+++ b/lib/google_api/cloud_platform/client.rb
@@ -10,6 +10,7 @@ module GoogleApi
class Client < GoogleApi::Auth
SCOPE = 'https://www.googleapis.com/auth/cloud-platform'.freeze
LEAST_TOKEN_LIFE_TIME = 10.minutes
+ CLUSTER_MASTER_AUTH_USERNAME = 'admin'.freeze
class << self
def session_key_for_token
@@ -64,6 +65,12 @@ module GoogleApi
"node_config": {
"machine_type": machine_type
},
+ "master_auth": {
+ "username": CLUSTER_MASTER_AUTH_USERNAME,
+ "client_certificate_config": {
+ issue_client_certificate: true
+ }
+ },
"legacy_abac": {
"enabled": legacy_abac
}
diff --git a/lib/sentry/client.rb b/lib/sentry/client.rb
index 4187014d49e..5e0c9101de5 100644
--- a/lib/sentry/client.rb
+++ b/lib/sentry/client.rb
@@ -54,10 +54,10 @@ module Sentry
def handle_response(response)
unless response.code == 200
- raise Client::Error, "Sentry response error: #{response.code}"
+ raise Client::Error, "Sentry response status code: #{response.code}"
end
- response.as_json
+ response
end
def projects_api_url
diff --git a/lib/system_check/app/git_user_default_ssh_config_check.rb b/lib/system_check/app/git_user_default_ssh_config_check.rb
index 6cd53779bfd..a331f88873b 100644
--- a/lib/system_check/app/git_user_default_ssh_config_check.rb
+++ b/lib/system_check/app/git_user_default_ssh_config_check.rb
@@ -53,7 +53,7 @@ module SystemCheck
end
def ssh_dir
- return nil unless home_dir
+ return unless home_dir
File.join(home_dir, '.ssh')
end
diff --git a/lib/tasks/gemojione.rake b/lib/tasks/gemojione.rake
index 560a52053d8..c24207b134a 100644
--- a/lib/tasks/gemojione.rake
+++ b/lib/tasks/gemojione.rake
@@ -30,33 +30,28 @@ namespace :gemojione do
# We don't have `node_modules` available in built versions of GitLab
FileUtils.cp_r(Rails.root.join('node_modules', 'emoji-unicode-version', 'emoji-unicode-version-map.json'), File.join(Rails.root, 'fixtures', 'emojis'))
- dir = Gemojione.images_path
resultant_emoji_map = {}
Gitlab::Emoji.emojis.each do |name, emoji_hash|
# Ignore aliases
unless Gitlab::Emoji.emojis_aliases.key?(name)
- fpath = File.join(dir, "#{emoji_hash['unicode']}.png")
- hash_digest = Digest::SHA256.file(fpath).hexdigest
-
category = emoji_hash['category']
if name == 'gay_pride_flag'
category = 'flags'
end
entry = {
- category: category,
- moji: emoji_hash['moji'],
- description: emoji_hash['description'],
- unicodeVersion: Gitlab::Emoji.emoji_unicode_version(name),
- digest: hash_digest
+ c: category,
+ e: emoji_hash['moji'],
+ d: emoji_hash['description'],
+ u: Gitlab::Emoji.emoji_unicode_version(name)
}
resultant_emoji_map[name] = entry
end
end
- out = File.join(Rails.root, 'fixtures', 'emojis', 'digests.json')
+ out = File.join(Rails.root, 'public', '-', 'emojis', '1', 'emojis.json')
File.open(out, 'w') do |handle|
handle.write(JSON.pretty_generate(resultant_emoji_map))
end
diff --git a/lib/tasks/gitlab/artifacts/migrate.rake b/lib/tasks/gitlab/artifacts/migrate.rake
index e7634d2ed4f..9012e55a70c 100644
--- a/lib/tasks/gitlab/artifacts/migrate.rake
+++ b/lib/tasks/gitlab/artifacts/migrate.rake
@@ -11,14 +11,13 @@ namespace :gitlab do
Ci::Build.joins(:project)
.with_artifacts_stored_locally
.find_each(batch_size: 10) do |build|
- begin
- build.artifacts_file.migrate!(ObjectStorage::Store::REMOTE)
- build.artifacts_metadata.migrate!(ObjectStorage::Store::REMOTE)
- logger.info("Transferred artifact ID #{build.id} with size #{build.artifacts_size} to object storage")
- rescue => e
- logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
- end
+ build.artifacts_file.migrate!(ObjectStorage::Store::REMOTE)
+ build.artifacts_metadata.migrate!(ObjectStorage::Store::REMOTE)
+
+ logger.info("Transferred artifact ID #{build.id} with size #{build.artifacts_size} to object storage")
+ rescue => e
+ logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
end
end
end
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index 3a1a36e36ce..3977fc7ad8c 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -61,7 +61,7 @@ namespace :gitlab do
Rake::Task['gitlab:backup:uploads:restore'].invoke unless backup.skipped?('uploads')
Rake::Task['gitlab:backup:builds:restore'].invoke unless backup.skipped?('builds')
Rake::Task['gitlab:backup:artifacts:restore'].invoke unless backup.skipped?('artifacts')
- Rake::Task["gitlab:backup:pages:restore"].invoke unless backup.skipped?('pages')
+ Rake::Task['gitlab:backup:pages:restore'].invoke unless backup.skipped?('pages')
Rake::Task['gitlab:backup:lfs:restore'].invoke unless backup.skipped?('lfs')
Rake::Task['gitlab:backup:registry:restore'].invoke unless backup.skipped?('registry')
Rake::Task['gitlab:shell:setup'].invoke
diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake
index 451ba651674..760331620ef 100644
--- a/lib/tasks/gitlab/cleanup.rake
+++ b/lib/tasks/gitlab/cleanup.rake
@@ -60,7 +60,7 @@ namespace :gitlab do
.new(server.storage)
.rename(path, new_path)
rescue StandardError => e
- puts "Error occured while moving the repository: #{e.message}".color(:red)
+ puts "Error occurred while moving the repository: #{e.message}".color(:red)
end
end
end
diff --git a/lib/tasks/gitlab/features.rake b/lib/tasks/gitlab/features.rake
new file mode 100644
index 00000000000..d115961108e
--- /dev/null
+++ b/lib/tasks/gitlab/features.rake
@@ -0,0 +1,24 @@
+namespace :gitlab do
+ namespace :features do
+ desc 'GitLab | Features | Enable direct Git access via Rugged for NFS'
+ task enable_rugged: :environment do
+ set_rugged_feature_flags(true)
+ puts 'All Rugged feature flags were enabled.'
+ end
+
+ task disable_rugged: :environment do
+ set_rugged_feature_flags(false)
+ puts 'All Rugged feature flags were disabled.'
+ end
+ end
+
+ def set_rugged_feature_flags(status)
+ Gitlab::Git::RuggedImpl::Repository::FEATURE_FLAGS.each do |flag|
+ if status
+ Feature.enable(flag)
+ else
+ Feature.disable(flag)
+ end
+ end
+ end
+end
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index e97d77d20e0..7872e5b08c0 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -34,9 +34,6 @@ namespace :gitlab do
puts "Sidekiq Version:#{Sidekiq::VERSION}"
puts "Go Version:\t#{go_version[1] || "unknown".color(:red)}"
- # check database adapter
- database_adapter = ActiveRecord::Base.connection.adapter_name.downcase
-
project = Group.new(path: "some-group").projects.build(path: "some-project")
# construct clone URLs
http_clone_url = project.http_url_to_repo
@@ -49,7 +46,8 @@ namespace :gitlab do
puts "Version:\t#{Gitlab::VERSION}"
puts "Revision:\t#{Gitlab.revision}"
puts "Directory:\t#{Rails.root}"
- puts "DB Adapter:\t#{database_adapter}"
+ puts "DB Adapter:\t#{Gitlab::Database.human_adapter_name}"
+ puts "DB Version:\t#{Gitlab::Database.version}"
puts "URL:\t\t#{Gitlab.config.gitlab.url}"
puts "HTTP Clone URL:\t#{http_clone_url}"
puts "SSH Clone URL:\t#{ssh_clone_url}"
@@ -58,7 +56,7 @@ namespace :gitlab do
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"
+ gitlab_shell_version_file = "#{Gitlab.config.gitlab_shell.path}/VERSION"
if File.readable?(gitlab_shell_version_file)
gitlab_shell_version = File.read(gitlab_shell_version_file)
end
@@ -72,7 +70,7 @@ namespace :gitlab do
puts "- #{name}: \t#{repository_storage.legacy_disk_path}"
end
end
- puts "Hooks:\t\t#{Gitlab.config.gitlab_shell.hooks_path}"
+ puts "GitLab Shell path:\t\t#{Gitlab.config.gitlab_shell.path}"
puts "Git:\t\t#{Gitlab.config.git.bin_path}"
end
end
diff --git a/lib/tasks/gitlab/lfs/migrate.rake b/lib/tasks/gitlab/lfs/migrate.rake
index a45e5ca91e0..97c15175a23 100644
--- a/lib/tasks/gitlab/lfs/migrate.rake
+++ b/lib/tasks/gitlab/lfs/migrate.rake
@@ -9,13 +9,12 @@ namespace :gitlab do
LfsObject.with_files_stored_locally
.find_each(batch_size: 10) do |lfs_object|
- begin
- lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
- logger.info("Transferred LFS object #{lfs_object.oid} of size #{lfs_object.size.to_i.bytes} to object storage")
- rescue => e
- logger.error("Failed to transfer LFS object #{lfs_object.oid} with error: #{e.message}")
- end
+ lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
+
+ logger.info("Transferred LFS object #{lfs_object.oid} of size #{lfs_object.size.to_i.bytes} to object storage")
+ rescue => e
+ logger.error("Failed to transfer LFS object #{lfs_object.oid} with error: #{e.message}")
end
end
end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 0ebc6f00793..ee3ef9dad6e 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -103,19 +103,12 @@ namespace :gitlab do
Gitlab::Shell.new.remove_all_keys
- Gitlab::Shell.new.batch_add_keys do |adder|
- Key.find_each(batch_size: 1000) do |key|
- adder.add_key(key.shell_id, key.key)
- print '.'
+ Key.find_in_batches(batch_size: 1000) do |keys|
+ unless Gitlab::Shell.new.batch_add_keys(keys)
+ puts "Failed to add keys...".color(:red)
+ exit 1
end
end
- puts ""
-
- unless $?.success?
- puts "Failed to add keys...".color(:red)
- exit 1
- end
-
rescue Gitlab::TaskAbortedByUserError
puts "Quitting...".color(:red)
exit 1
diff --git a/lib/tasks/gitlab/storage.rake b/lib/tasks/gitlab/storage.rake
index f9ce3e1d338..954f827f716 100644
--- a/lib/tasks/gitlab/storage.rake
+++ b/lib/tasks/gitlab/storage.rake
@@ -11,6 +11,13 @@ namespace :gitlab do
storage_migrator = Gitlab::HashedStorage::Migrator.new
helper = Gitlab::HashedStorage::RakeHelper
+ if storage_migrator.rollback_pending?
+ warn "There is already a rollback operation in progress, " \
+ "running a migration at the same time may have unexpected consequences."
+
+ next
+ end
+
if helper.range_single_item?
project = Project.with_unmigrated_storage.find_by(id: helper.range_from)
@@ -36,8 +43,60 @@ namespace :gitlab do
print "Enqueuing migration of #{legacy_projects_count} projects in batches of #{helper.batch_size}"
- helper.project_id_batches do |start, finish|
- storage_migrator.bulk_schedule(start: start, finish: finish)
+ helper.project_id_batches_migration do |start, finish|
+ storage_migrator.bulk_schedule_migration(start: start, finish: finish)
+
+ print '.'
+ end
+
+ puts ' Done!'
+ end
+
+ desc 'GitLab | Storage | Rollback existing projects to Legacy Storage'
+ task rollback_to_legacy: :environment do
+ if Gitlab::Database.read_only?
+ warn 'This task requires database write access. Exiting.'
+
+ next
+ end
+
+ storage_migrator = Gitlab::HashedStorage::Migrator.new
+ helper = Gitlab::HashedStorage::RakeHelper
+
+ if storage_migrator.migration_pending?
+ warn "There is already a migration operation in progress, " \
+ "running a rollback at the same time may have unexpected consequences."
+
+ next
+ end
+
+ if helper.range_single_item?
+ project = Project.with_storage_feature(:repository).find_by(id: helper.range_from)
+
+ unless project
+ warn "There are no projects that can be rolledback with ID=#{helper.range_from}"
+
+ next
+ end
+
+ puts "Enqueueing storage rollback of #{project.full_path} (ID=#{project.id})..."
+ storage_migrator.rollback(project)
+
+ next
+ end
+
+ hashed_projects_count = Project.with_storage_feature(:repository).count
+
+ if hashed_projects_count == 0
+ warn 'There are no projects that can have storage rolledback. Nothing to do!'
+
+ next
+ end
+
+ print "Enqueuing rollback of #{hashed_projects_count} projects in batches of #{helper.batch_size}"
+
+ helper.project_id_batches_rollback do |start, finish|
+ storage_migrator.bulk_schedule_rollback(start: start, finish: finish)
print '.'
end
diff --git a/lib/tasks/gitlab/traces.rake b/lib/tasks/gitlab/traces.rake
index 5a232091a7e..5e1ec481ece 100644
--- a/lib/tasks/gitlab/traces.rake
+++ b/lib/tasks/gitlab/traces.rake
@@ -26,13 +26,12 @@ namespace :gitlab do
Ci::Build.joins(:project)
.with_archived_trace_stored_locally
.find_each(batch_size: 10) do |build|
- begin
- build.job_artifacts_trace.file.migrate!(ObjectStorage::Store::REMOTE)
- logger.info("Transferred job trace of #{build.id} to object storage")
- rescue => e
- logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
- end
+ build.job_artifacts_trace.file.migrate!(ObjectStorage::Store::REMOTE)
+
+ logger.info("Transferred job trace of #{build.id} to object storage")
+ rescue => e
+ logger.error("Failed to transfer artifacts of #{build.id} with error: #{e.message}")
end
end
end
diff --git a/lib/tasks/karma.rake b/lib/tasks/karma.rake
index 62a12174efa..02987f2beef 100644
--- a/lib/tasks/karma.rake
+++ b/lib/tasks/karma.rake
@@ -1,13 +1,24 @@
unless Rails.env.production?
namespace :karma do
desc 'GitLab | Karma | Generate fixtures for JavaScript tests'
- RSpec::Core::RakeTask.new(:fixtures, [:pattern]) do |t, args|
- args.with_defaults(pattern: 'spec/javascripts/fixtures/*.rb')
+ task fixtures: ['karma:copy_emojis_from_public_folder', 'karma:rspec_fixtures']
+
+ desc 'GitLab | Karma | Generate fixtures using RSpec'
+ RSpec::Core::RakeTask.new(:rspec_fixtures, [:pattern]) do |t, args|
+ args.with_defaults(pattern: '{spec,ee/spec}/javascripts/fixtures/*.rb')
ENV['NO_KNAPSACK'] = 'true'
t.pattern = args[:pattern]
t.rspec_opts = '--format documentation'
end
+ desc 'GitLab | Karma | Copy emojis file'
+ task :copy_emojis_from_public_folder do
+ # Copying the emojis.json from the public folder
+ fixture_file_name = Rails.root.join('spec/javascripts/fixtures/emojis/emojis.json')
+ FileUtils.mkdir_p(File.dirname(fixture_file_name))
+ FileUtils.cp(Rails.root.join('public/-/emojis/1/emojis.json'), fixture_file_name)
+ end
+
desc 'GitLab | Karma | Run JavaScript tests'
task tests: ['yarn:check'] do
sh "yarn run karma" do |ok, res|
diff --git a/lib/tasks/lint.rake b/lib/tasks/lint.rake
index 5d673a1a285..c5d0f2c292f 100644
--- a/lib/tasks/lint.rake
+++ b/lib/tasks/lint.rake
@@ -19,11 +19,9 @@ unless Rails.env.production?
desc "GitLab | lint | Lint HAML files"
task :haml do
- begin
- Rake::Task['haml_lint'].invoke
- rescue RuntimeError # The haml_lint tasks raise a RuntimeError
- exit(1)
- end
+ Rake::Task['haml_lint'].invoke
+ rescue RuntimeError # The haml_lint tasks raise a RuntimeError
+ exit(1)
end
desc "GitLab | lint | Run several lint checks"
diff --git a/lib/tasks/migrate/migrate_iids.rake b/lib/tasks/migrate/migrate_iids.rake
index aa2d01730d7..cb7c496c31c 100644
--- a/lib/tasks/migrate/migrate_iids.rake
+++ b/lib/tasks/migrate/migrate_iids.rake
@@ -2,49 +2,43 @@ desc "GitLab | Build internal ids for issues and merge requests"
task migrate_iids: :environment do
puts 'Issues'.color(:yellow)
Issue.where(iid: nil).find_each(batch_size: 100) do |issue|
- begin
- issue.set_iid
+ issue.set_iid
- if issue.update_attribute(:iid, issue.iid)
- print '.'
- else
- print 'F'
- end
- rescue
+ if issue.update_attribute(:iid, issue.iid)
+ print '.'
+ else
print 'F'
end
+ rescue
+ print 'F'
end
puts 'done'
puts 'Merge Requests'.color(:yellow)
MergeRequest.where(iid: nil).find_each(batch_size: 100) do |mr|
- begin
- mr.set_iid
+ mr.set_iid
- if mr.update_attribute(:iid, mr.iid)
- print '.'
- else
- print 'F'
- end
- rescue
+ if mr.update_attribute(:iid, mr.iid)
+ print '.'
+ else
print 'F'
end
+ rescue
+ print 'F'
end
puts 'done'
puts 'Milestones'.color(:yellow)
Milestone.where(iid: nil).find_each(batch_size: 100) do |m|
- begin
- m.set_iid
+ m.set_iid
- if m.update_attribute(:iid, m.iid)
- print '.'
- else
- print 'F'
- end
- rescue
+ if m.update_attribute(:iid, m.iid)
+ print '.'
+ else
print 'F'
end
+ rescue
+ print 'F'
end
puts 'done'
diff --git a/lib/unfold_form.rb b/lib/unfold_form.rb
deleted file mode 100644
index 05bb3ed7f1c..00000000000
--- a/lib/unfold_form.rb
+++ /dev/null
@@ -1,14 +0,0 @@
-# frozen_string_literal: true
-
-require_relative 'gt_one_coercion'
-
-class UnfoldForm
- include Virtus.model
-
- attribute :since, GtOneCoercion
- attribute :to, GtOneCoercion
- attribute :bottom, Boolean
- attribute :unfold, Boolean, default: true
- attribute :offset, Integer
- attribute :indent, Integer, default: 0
-end
diff --git a/locale/ar_SA/gitlab.po b/locale/ar_SA/gitlab.po
index 71f89268a38..af98becc19e 100644
--- a/locale/ar_SA/gitlab.po
+++ b/locale/ar_SA/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ar\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:11\n"
+"PO-Revision-Date: 2019-03-06 15:38\n"
msgid " Status"
msgstr ""
@@ -51,6 +51,15 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -186,12 +195,30 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -216,15 +243,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -312,6 +339,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -342,6 +372,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -480,6 +519,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -495,6 +549,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -579,24 +636,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -765,6 +849,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -780,6 +867,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -849,7 +939,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -861,6 +951,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -873,6 +972,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -909,6 +1011,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -966,12 +1071,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -1062,6 +1173,60 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1104,9 +1269,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1191,6 +1362,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1206,9 +1380,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1767,6 +1938,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1803,6 +1977,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1815,6 +1992,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1881,9 +2061,6 @@ 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 ""
@@ -2043,6 +2220,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -2058,9 +2238,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2106,10 +2283,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2148,6 +2325,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2244,7 +2424,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2259,9 +2439,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2307,9 +2484,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2547,15 +2721,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2859,6 +3045,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3351,6 +3540,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3429,6 +3621,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3600,6 +3795,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3636,6 +3834,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3654,9 +3855,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3681,6 +3879,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3732,15 +3936,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3753,6 +3975,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3804,6 +4038,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3873,6 +4110,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3951,6 +4215,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -4038,7 +4305,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -4104,9 +4371,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4164,9 +4428,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4257,9 +4518,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4374,7 +4632,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4404,9 +4662,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4839,15 +5103,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4905,6 +5178,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5277,9 +5553,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5331,6 +5622,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5388,6 +5685,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5406,6 +5706,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5448,7 +5751,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5637,6 +5940,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5724,6 +6030,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5901,6 +6210,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5946,6 +6264,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5973,36 +6294,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -6057,6 +6348,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6207,9 +6501,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6354,6 +6645,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6435,13 +6729,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6504,9 +6798,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6534,13 +6834,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6636,6 +6939,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6843,6 +7149,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6852,6 +7161,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -7044,6 +7356,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -7059,6 +7377,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7179,9 +7500,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7191,9 +7524,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7218,6 +7560,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7416,9 +7761,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7518,6 +7860,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7560,6 +7905,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7659,6 +8007,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -8022,6 +8373,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8157,6 +8514,27 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "Resend invite"
msgstr ""
@@ -8319,6 +8697,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8349,6 +8730,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8382,6 +8766,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8442,6 +8829,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8700,6 +9090,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8724,6 +9117,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8781,9 +9177,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8886,6 +9288,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9156,6 +9576,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9366,6 +9792,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9555,6 +9984,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9678,6 +10110,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9723,6 +10158,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9744,6 +10194,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9828,6 +10281,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10395,6 +10854,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10404,6 +10866,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10632,6 +11097,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10641,6 +11109,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10944,6 +11415,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -11049,6 +11523,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -11109,6 +11586,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11202,6 +11682,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11301,6 +11784,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11403,9 +11889,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11445,6 +11928,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11553,9 +12039,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11682,6 +12165,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11754,6 +12240,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11823,6 +12312,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/bg/gitlab.po b/locale/bg/gitlab.po
index ed642e63cdf..cf0ef977e47 100644
--- a/locale/bg/gitlab.po
+++ b/locale/bg/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: bg\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:12\n"
+"PO-Revision-Date: 2019-03-06 15:39\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d подаване"
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Ðабор от графики отноÑно непрекъÑнатата интеграциÑ"
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr "ОтмÑна"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr "Графики"
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "ТърÑене по път"
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr "ВнаÑÑне на хранилище"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr "ПредÑтавÑме Ви анализа на циклите"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Ðаучете повече в"
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,15 +6537,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Ðов план за Ñхема"
msgid "New Snippet"
msgstr ""
-msgid "New Snippets"
-msgstr ""
-
msgid "New branch"
msgstr "Ðов клон"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr "ÐÑма доÑтатъчно данни"
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr "Променливи"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "ÑобÑтвен"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "Схеми"
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr "Връзката към изнеÑените данни на проекÑ
msgid "Project export started. A download link will be sent by email."
msgstr "ИзнаÑÑнето на проекта започна. Ще получите връзка към данните по е-поща."
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "Планиране на Ñхемите"
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr "Преминаване към клон/етикет"
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr "Това означава, че нÑма да можете да изпр
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "ОттеглÑне на заÑвката за доÑтъп"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Ðе можете да Ñъздавате повече проекти"
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/ca_ES/gitlab.po b/locale/ca_ES/gitlab.po
index a6bc33b3974..e70b8c43f9e 100644
--- a/locale/ca_ES/gitlab.po
+++ b/locale/ca_ES/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ca\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:12\n"
+"PO-Revision-Date: 2019-03-06 15:47\n"
msgid " Status"
msgstr " Estat"
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d comissió"
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "Afegeix una taula"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "Afegeix el comentari"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "Afegeix un comentari a la imatge"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "Afegeix una llicència"
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr "Configuració avançada"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr "Tots"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr "ag."
msgid "August"
msgstr "agost"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr "Diagrames"
msgid "Chat"
msgstr "Xat"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr "Tancades"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "Incidències tancades"
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr "Descripció:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr "Introduïu el títol de la petició de fusió"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "Mostra-ho tot"
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr "Nova"
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr "Desa els canvis"
-
msgid "FeatureFlags|Status"
msgstr "Estat"
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr "Filtra..."
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "Claus GPG"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "General"
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr "Enrere"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "Enrere"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr "Vés a"
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr "Informació del grup:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "Petició de fusió"
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr "Sistema"
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr "Més informació"
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/cs_CZ/gitlab.po b/locale/cs_CZ/gitlab.po
index c71b768ece2..97c0e8046a1 100644
--- a/locale/cs_CZ/gitlab.po
+++ b/locale/cs_CZ/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: cs\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:13\n"
+"PO-Revision-Date: 2019-03-06 15:52\n"
msgid " Status"
msgstr ""
@@ -47,6 +47,13 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -156,12 +163,28 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -182,15 +205,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -270,6 +293,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -296,6 +322,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -416,6 +449,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -431,6 +479,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -515,24 +566,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -701,6 +779,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -714,6 +795,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -783,7 +867,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -795,6 +879,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -807,6 +900,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -843,6 +939,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -900,12 +999,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -996,6 +1101,52 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1038,9 +1189,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1125,6 +1282,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1140,9 +1300,6 @@ msgstr "Srp"
msgid "August"
msgstr "Srpen"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1701,6 +1858,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1737,6 +1897,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1749,6 +1912,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1815,9 +1981,6 @@ 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 ""
@@ -1977,6 +2140,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1992,9 +2158,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2040,10 +2203,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2082,6 +2245,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2178,7 +2344,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2193,9 +2359,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2241,9 +2404,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2481,15 +2641,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2791,6 +2963,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3281,6 +3456,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3359,6 +3537,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3530,6 +3711,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3566,6 +3750,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3584,9 +3771,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3611,6 +3795,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3662,15 +3852,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3683,6 +3891,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3734,6 +3954,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3803,6 +4026,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3881,6 +4131,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3968,7 +4221,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -4034,9 +4287,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4094,9 +4344,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4185,9 +4432,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4302,7 +4546,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4332,9 +4576,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4767,15 +5017,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4833,6 +5092,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5203,9 +5465,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5257,6 +5534,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5312,6 +5595,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5330,6 +5616,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5372,7 +5661,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5561,6 +5850,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5646,6 +5938,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5821,6 +6116,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5866,6 +6170,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5893,36 +6200,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5977,6 +6254,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6127,9 +6407,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6274,6 +6551,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6353,13 +6633,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6422,9 +6702,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6452,13 +6738,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6554,6 +6843,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6759,6 +7051,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6768,6 +7063,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6960,6 +7258,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6975,6 +7279,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7095,9 +7402,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7107,9 +7426,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7134,6 +7462,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7332,9 +7663,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7434,6 +7762,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7476,6 +7807,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7575,6 +7909,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7936,6 +8273,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8071,6 +8414,23 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Resend invite"
msgstr ""
@@ -8231,6 +8591,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8261,6 +8624,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8294,6 +8660,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8354,6 +8723,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8612,6 +8984,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8636,6 +9011,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8693,9 +9071,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8796,6 +9180,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9066,6 +9468,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9276,6 +9684,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9465,6 +9876,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9588,6 +10002,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9633,6 +10050,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9654,6 +10086,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9738,6 +10173,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10301,6 +10742,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10310,6 +10754,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10538,6 +10985,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10547,6 +10997,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10850,6 +11303,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10955,6 +11411,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -11015,6 +11474,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11108,6 +11570,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11199,6 +11664,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11297,9 +11765,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11339,6 +11804,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11439,9 +11907,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11564,6 +12029,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11636,6 +12104,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11701,6 +12172,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/cy_GB/gitlab.po b/locale/cy_GB/gitlab.po
index 174737050e6..1eabb89a94c 100644
--- a/locale/cy_GB/gitlab.po
+++ b/locale/cy_GB/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: cy\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:16\n"
+"PO-Revision-Date: 2019-03-06 15:53\n"
msgid " Status"
msgstr ""
@@ -51,6 +51,15 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -186,12 +195,30 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -216,15 +243,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -312,6 +339,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -342,6 +372,15 @@ msgstr[3] ""
msgstr[4] ""
msgstr[5] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -480,6 +519,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -495,6 +549,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -579,24 +636,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -765,6 +849,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -780,6 +867,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -849,7 +939,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -861,6 +951,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -873,6 +972,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -909,6 +1011,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -966,12 +1071,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -1062,6 +1173,60 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1104,9 +1269,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1191,6 +1362,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1206,9 +1380,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1767,6 +1938,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1803,6 +1977,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1815,6 +1992,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1881,9 +2061,6 @@ 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 ""
@@ -2043,6 +2220,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -2058,9 +2238,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2106,10 +2283,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2148,6 +2325,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2244,7 +2424,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2259,9 +2439,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2307,9 +2484,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2547,15 +2721,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2859,6 +3045,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3351,6 +3540,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3429,6 +3621,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3600,6 +3795,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3636,6 +3834,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3654,9 +3855,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3681,6 +3879,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3732,15 +3936,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3753,6 +3975,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3804,6 +4038,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3873,6 +4110,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3951,6 +4215,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -4038,7 +4305,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -4104,9 +4371,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4164,9 +4428,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4257,9 +4518,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4374,7 +4632,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4404,9 +4662,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4839,15 +5103,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4905,6 +5178,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5277,9 +5553,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5331,6 +5622,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5388,6 +5685,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5406,6 +5706,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5448,7 +5751,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5637,6 +5940,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5724,6 +6030,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5901,6 +6210,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5946,6 +6264,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5973,36 +6294,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -6057,6 +6348,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6207,9 +6501,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6354,6 +6645,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6435,13 +6729,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6504,9 +6798,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6534,13 +6834,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6636,6 +6939,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6843,6 +7149,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6852,6 +7161,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -7044,6 +7356,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -7059,6 +7377,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7179,9 +7500,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7191,9 +7524,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7218,6 +7560,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7416,9 +7761,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7518,6 +7860,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7560,6 +7905,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7659,6 +8007,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -8022,6 +8373,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8157,6 +8514,27 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
msgid "Resend invite"
msgstr ""
@@ -8319,6 +8697,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8349,6 +8730,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8382,6 +8766,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8442,6 +8829,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8700,6 +9090,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8724,6 +9117,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8781,9 +9177,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8886,6 +9288,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9156,6 +9576,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9366,6 +9792,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9555,6 +9984,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9678,6 +10110,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9723,6 +10158,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9744,6 +10194,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9828,6 +10281,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10395,6 +10854,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10404,6 +10866,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10632,6 +11097,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10641,6 +11109,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10944,6 +11415,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -11049,6 +11523,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -11109,6 +11586,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11202,6 +11682,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11301,6 +11784,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11403,9 +11889,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11445,6 +11928,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11553,9 +12039,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11682,6 +12165,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11754,6 +12240,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11823,6 +12312,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/da_DK/gitlab.po b/locale/da_DK/gitlab.po
index ee02b31f2b0..231c3569bb2 100644
--- a/locale/da_DK/gitlab.po
+++ b/locale/da_DK/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: da\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:13\n"
+"PO-Revision-Date: 2019-03-06 15:52\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/de/gitlab.po b/locale/de/gitlab.po
index f77e2599726..cc544400449 100644
--- a/locale/de/gitlab.po
+++ b/locale/de/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: de\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:15\n"
+"PO-Revision-Date: 2019-03-06 15:39\n"
msgid " Status"
msgstr " Status"
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr "\"%{query}\" in Projekten"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d Commit"
@@ -126,12 +131,26 @@ msgstr "%{counter_storage} (%{counter_repositories} Repositories, %{counter_buil
msgid "%{count} %{alerts}"
msgstr "%{count} %{alerts}"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr "%{count} weitere Zugewiesene"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} Teilnehmer"
@@ -148,15 +167,15 @@ msgstr "%{filePath} gelöscht"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} +%{labelCount} mehr"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{group_docs_link_start}Gruppen%{group_docs_link_end} ermöglichen dir die Verwaltung und die Zusammenarbeit über mehrere Projekte hinweg. Mitglieder einer Gruppe haben Zugriff auf alle Projekte darin."
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} wird entfernt! Bist du sicher?"
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr "+ %{count} weitere"
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} weitere"
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] "1 %{type} Änderung"
msgstr[1] "%{count} %{type} Änderungen"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "1 geschlossenes Ticket"
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "Ein 'Runner' ist ein Prozess, welcher ein Job ausführt. Du kannst so viele Runner erstellen wie du benötigst."
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Eine Sammlung von Graphen bezüglich kontinuierlicher Integration"
@@ -367,6 +409,9 @@ msgstr "Ein Mitglied des GitLab-Missbrauchsteams wird deinen Bericht so schnell
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "Ein neuer Branch wird in deinem Fork erzeugt und ein neuer Merge-Request wird gestartet."
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr "In einem Projekt speicherst du deine Dateien (Repository), planst deine Arbeit (Tickets) und veröffentlichst deine Dokumentation (Wiki), %{among_other_things_link}."
@@ -451,24 +496,51 @@ msgstr "Kubernetes-Cluster hinzufügen"
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "Füge zu deinem Wiki eine Startseite mit Informationen zu deinem GitLab-Project hinzu und GitLab wird sie hier anstatt dieser Meldung anzeigen."
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "Tabelle hinzufügen"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "Füge zusätzlichen Text hinzu, der in jeglicher E-Mail-Kommunikation angezeigt wird. Maximal %{character_limit} Zeichen"
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "Jetzt kommentieren"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "Bildkommentar hinzufügen"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "Lizenz hinzufügen"
@@ -637,6 +709,9 @@ msgstr "Erweiterte Berechtigungen, Large File Storage und Einstellungen zur Zwei
msgid "Advanced settings"
msgstr "Erweiterte Einstellungen"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "Alarm"
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr "Alle"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr "Alle Änderungen sind committed"
@@ -717,7 +795,7 @@ msgstr "Bei einem leeren GitLab-Benutzerfeld wird der vollständigen Namen des F
msgid "An error has occurred"
msgstr "Es ist ein Fehler aufgetreten"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr "Beim Hinzufügen eines neuen Entwurfs ist ein Fehler aufgetreten."
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr "Bei der Vorschau des Blobs ist ein Fehler aufgetreten"
@@ -741,6 +828,9 @@ msgstr "Beim Aktualisieren der Ticket-Gewichtung ist ein Fehler aufgetreten"
msgid "An error occurred while adding approver"
msgstr "Beim Hinzufügen eines Genehmigungsberechtigten ist ein Fehler aufgetreten"
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "Beim Löschen des Kommentars ist ein Fehler aufgetreten"
@@ -777,6 +867,9 @@ msgstr "Beim Abrufen der Jobs ist ein Fehler aufgetreten."
msgid "An error occurred while fetching the pipeline."
msgstr "Beim Abrufen der Pipeline ist ein Fehler aufgetreten."
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "Beim Abrufen der Projekte ist ein Fehler aufgetreten"
@@ -834,12 +927,18 @@ msgstr "Beim Speichern des LDAP-Ãœberschreibungsstatus ist ein Fehler aufgetrete
msgid "An error occurred while saving assignees"
msgstr "Beim Speichern der Zuweisungen ist ein Fehler aufgetreten"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr "Beim Abonnieren von Benachrichtigungen ist ein Fehler aufgetreten."
msgid "An error occurred while unsubscribing to notifications."
msgstr "Beim Abbestellen der Benachrichtigungen ist ein Fehler aufgetreten."
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "Beim Aktualisieren des Kommentars ist ein Fehler aufgetreten"
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr "Möchtest du den öffentlichen Schlüssel wirklich neu generieren? Du mu
msgid "Are you sure you want to remove %{group_name}?"
msgstr "Bist du sicher, dass du %{group_name} wirklich entfernen willst?"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr "Zuweisungslisten zeigen alle dem ausgewählten Benutzer zugewiesenen Tic
msgid "Assignee(s)"
msgstr "Zugewiesene Personen"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr "Aug"
msgid "August"
msgstr "August"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "Authentifizierungsprotokoll"
@@ -1635,6 +1778,9 @@ msgstr "Kann nicht automatisch zusammengeführt werden"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "Kann verwalteten Kubernetes-Cluster nicht ändern"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr "Wiederherstellen "
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "Dies wird einen neuen Commit erzeugen, um die vorhandenen Änderungen rückgängig zu machen."
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "Änderungen werden angezeigt, als ob die <b>Quell</b>-Revision in die <b>Ziel</b>-Revision gemerged wurde."
@@ -1683,6 +1832,9 @@ msgstr "Diagramme"
msgid "Chat"
msgstr "Chat"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Lies die %{docs_link_start}Dokumentation%{docs_link_end}."
@@ -1749,9 +1901,6 @@ msgstr "Wähle die Gruppen aus, die du mit diesem sekundären Knoten synchronisi
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr "Wähle welche Repositories du verbinden und die CI/CD-Pipelines ausführen möchtest."
-msgid "Choose which repositories you want to import."
-msgstr "Wähle aus, welche Repositories du importieren möchtest."
-
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr "Wähle die Fragmente aus, die du mit diesem sekundären Knoten synchronisieren möchtest."
@@ -1911,6 +2060,9 @@ msgstr "Repository klonen"
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr "Geschlossen"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "Geschlossene Tickets"
@@ -1974,12 +2123,12 @@ msgstr "Nachdem Ingress installiert wurde, solltest du deine Wildcard-DNS an die
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "Beim Abrufen der Projektzonen ist ein Fehler aufgetreten: %{error}"
-
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "Ein Fehler ist bei dem Verbindungsversuch mit der Google Cloud API aufgetreten. Bitte versuche es später erneut."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr ""
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
msgstr ""
@@ -2016,6 +2165,9 @@ msgstr "Wähle, welche Anwendungen auf deinem Kubernetes Cluster installiert wer
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "Wähle, welche deiner Umgebungen du für dieses Cluster verwenden willst."
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,8 +2264,8 @@ msgstr "Ausblenden"
msgid "ClusterIntegration|If you are setting up multiple clusters and are using Auto DevOps, %{help_link_start}read this first%{help_link_end}."
msgstr "Wenn du mehrere Cluster einrichtest und Auto-DevOps verwendest, lies zuerst %{help_link_start}dies%{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 "Um den Zustand des Clusters anzuzeigen, müssen wir dein Cluster mit Prometheus bereitstellen, um die erforderlichen Daten zu sammeln."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
+msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
@@ -2127,9 +2279,6 @@ msgstr "Ingress bietet die Möglichkeit, Dienste-Anfragen je nach Anfragehost od
msgid "ClusterIntegration|Install"
msgstr "Installieren"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr "Prometheus installieren"
-
msgid "ClusterIntegration|Installed"
msgstr "Installiert"
@@ -2175,9 +2324,6 @@ msgstr "Kubernetes-Cluster"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Kubernetes-Cluster-Details"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Zustand des Kubernetes-Cluster"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes-Cluster wird mit der Google Kubernetes-Engine erstellt..."
@@ -2415,15 +2561,27 @@ msgstr "Registrierung"
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr "Code-Besitzer(innen)"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr "Kohorten"
msgid "Collapse"
msgstr "Reduzieren"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "Seitenleiste einklappen"
@@ -2723,6 +2881,9 @@ msgstr "Kopiere %{protocol} clone-URL"
msgid "Copy ID to clipboard"
msgstr "ID in Zwischenablage kopieren"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr "Kopiere SSH clone-URL"
@@ -3211,6 +3372,9 @@ msgstr "Beschreibungsvorlagen ermöglichen dir, kontextspezifische Vorlagen für
msgid "Description:"
msgstr "Beschreibung:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr "Löschen"
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr "Verwerfen"
@@ -3460,6 +3627,9 @@ msgstr "Für dieses Projekt aktivieren"
msgid "Enable group Runners"
msgstr "Gruppen-Runner aktivieren"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr "Aktiviere oder deaktiviere die Pseudonymizer-Datensammlung."
@@ -3496,6 +3666,9 @@ msgstr "Endet am (UTC)"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr "Gib die Beschreibung des Merge-Requests ein"
msgid "Enter the merge request title"
msgstr "Gib den Titel des Merge-Requests ein"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr "Beim Abrufen der Umgebungen ist ein Fehler aufgetreten."
msgid "Environments|An error occurred while making the request."
msgstr "Während der Anfrage ist ein Fehler aufgetreten."
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr "Beim Anhalten der Umgebung ist ein Fehler aufgetreten. Bitte versuche es erneut"
@@ -3592,15 +3768,33 @@ msgstr "Öffne Live-Umgebung"
msgid "Environments|Pod logs from"
msgstr "Pod-Protokolle von"
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+msgstr ""
+
msgid "Environments|Re-deploy to environment"
msgstr "Erneute Bereitstellung in der Umgebung"
msgid "Environments|Read more about environments"
msgstr "Lies mehr über Umgebungen"
+msgid "Environments|Rollback"
+msgstr ""
+
msgid "Environments|Rollback environment"
msgstr "Umgebung wiederherstellen"
+msgid "Environments|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "Alle anzeigen"
@@ -3613,6 +3807,18 @@ msgstr "Umgebung stoppen"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Aktualisiert"
@@ -3664,6 +3870,9 @@ msgstr "Fehlerberichterstattung und Protokollierung"
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr "Fehler beim Erstellen des Epics"
@@ -3733,6 +3942,33 @@ msgstr "Fehler beim Laden des Merge-Requests. Bitte versuche es erneut."
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr "Aufklappen"
msgid "Expand all"
msgstr "Alle erweitern"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "Menüleiste ausklappen"
@@ -3898,7 +4137,7 @@ msgstr "Bereitstellung fehlgeschlagen für"
msgid "Failed to load emoji list."
msgstr "Fehler beim Laden der Emoji-Liste."
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr "Beschreibung"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr "%{feature_flag_name} bearbeiten"
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr "Feature-Flag bearbeiten"
@@ -4024,9 +4260,6 @@ msgstr "Neu"
msgid "FeatureFlags|New Feature Flag"
msgstr "Neues Feature-Flag"
-msgid "FeatureFlags|Save changes"
-msgstr "Änderungen speichern"
-
msgid "FeatureFlags|Status"
msgstr "Status"
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr "Filter..."
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "Finde über den Pfad"
@@ -4230,8 +4460,8 @@ msgstr "Fehler in deiner .gitlab-ci.yml gefunden:"
msgid "Free Trial of GitLab.com Gold"
msgstr "Kostenlose Testversion von GitLab.com Gold"
-msgid "From %{provider_title}"
-msgstr "Von %{provider_title}"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "Von Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr "Von Meilensteinen:"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Installiere den Runner aus der Anwendungsliste in der Kubernetes-Cluster Detailansicht"
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "GPG-Schlüssel"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "Allgemein"
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr "Zurück"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "Zurück"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr "Gehe zu"
msgid "Go to %{link_to_google_takeout}."
msgstr "Gehe zu %{link_to_google_takeout}."
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr "Google Code-Import"
@@ -4761,6 +5006,9 @@ msgstr "Gruppeninfo:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "Gruppenbetreuer können Gruppen-Runner unter %{link} registrieren"
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "Gruppenname"
@@ -5129,9 +5377,24 @@ msgstr "Repository importieren"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr "Verbinde Repositories von"
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr "Verbessere Ticketboards mit der GitLab Enterprise Edition."
@@ -5183,6 +5446,12 @@ msgstr "Gib die Host-Schlüssel manuell ein"
msgid "Input your repository URL"
msgstr "Gib deine Repository-URL ein"
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr "Arbeitsablaufsanalysen vorgestellt"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr "Ticket"
@@ -5296,8 +5571,8 @@ msgstr "Tickets können Bugs, Tasks oder zu diskutierende Ideen sein. Tickets si
msgid "Issues closed"
msgstr "Geschlossene Tickets"
-msgid "Issues, merge requests, pushes and comments."
-msgstr "Tickets, Merge-Requests, Pushes und Kommentare."
+msgid "Issues, merge requests, pushes, and comments."
+msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
msgstr ""
@@ -5485,6 +5760,9 @@ msgstr "Label hochstufen"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr "%{labelTitle} hochzustufen macht es für alle Projekte in %{groupName} verfügbar. Existierende Projektlabels mit dem selben Titel werden zusammengeführt. Diese Aktion kann nicht rückgängig gemacht werden."
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr "Large File Storage"
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr "Erfahre mehr über geschützte Branches"
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Erfahre mehr in den"
@@ -5741,6 +6022,15 @@ msgstr "Mit Smartcard einloggen"
msgid "Logs"
msgstr "Protokolle"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 "Mache alle in deinem Team unabhängig von ihrem Standort produktiver. GitLab Geo erstellt schreibgeschützte Mirror deiner GitLab-Instanz, sodass du die Zeit zum Klonen und Abrufen großer Repos reduzieren kannst."
@@ -5786,6 +6076,9 @@ msgstr "Manifest"
msgid "Manifest file import"
msgstr "Manifestdateiimport"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Ordne eine FogBugz-Konto-ID einem/einer GitLab-Benutzer(in) zu"
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr "Markdown aktiviert"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr "Eine Punktliste hinzufügen"
-
-msgid "MarkdownToolbar|Add a link"
-msgstr "Einen Link hinzufügen"
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr "Eine nummerierte Liste hinzufügen"
-
-msgid "MarkdownToolbar|Add a table"
-msgstr "Eine Tabelle hinzufügen"
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr "Eine Aufgabenliste hinzufügen"
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr "Einen fetten Text hinzufügen"
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr "Einen kursiven Text hinzufügen"
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr "Gehe in den Vollbildmodus"
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr "Füge ein Zitat ein"
-
-msgid "MarkdownToolbar|Insert code"
-msgstr "Füge Code ein"
-
msgid "Maven Metadata"
msgstr "Maven-Metadaten"
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "Merge-Request"
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr "Prometheus Query-Dokumentation"
-msgid "Metrics|System"
-msgstr "System"
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr "Beim Abrufen der Umgebungsdaten trat ein Fehler auf, bitte versuche es erneut"
@@ -6194,6 +6457,9 @@ msgstr "Mehr Informationen"
msgid "More information is available|here"
msgstr "hier"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr "Die meisten Sterne"
@@ -6271,15 +6537,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Neuer Pipeline Zeitplan"
msgid "New Snippet"
msgstr "Neuer Codeausschnitt"
-msgid "New Snippets"
-msgstr "Neue Codeausschnitte"
-
msgid "New branch"
msgstr "Neuer Branch"
@@ -6340,9 +6606,15 @@ msgstr "Neu..."
msgid "No"
msgstr "Nein"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr "Kein Label"
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr "Keine Beiträge gefunden"
msgid "No credit card required."
msgstr "Keine Kreditkarte erforderlich."
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr "Kein Fälligkeitsdatum"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr "Nicht genügend Daten"
msgid "Not now"
msgstr "Nicht jetzt"
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Beachte, dass der master-Branch automatisch geschützt ist. %{link_to_protected_branches}"
@@ -6675,6 +6953,9 @@ msgstr "Vorgänge"
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr "Ein Projekt zum Dashboard hinzufügen"
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "Optional kannst du %{link_to_customize}, wie FogBugz E-Mail-Adressen und Benutzernamen in GitLab importiert werden."
@@ -6876,6 +7160,12 @@ msgstr "Variablen"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Benutzerdefiniert"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "Pipelines"
@@ -6891,6 +7181,9 @@ msgstr "Pipelines der letzten Woche"
msgid "Pipelines for last year"
msgstr "Pipelines des letzten Jahres"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr "Baue mit Vertrauen"
@@ -7011,9 +7304,21 @@ msgstr "Bitte wandle sie in %{link_to_git} um und durchlaufe erneut die %{link_t
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr "Bitte wandle sie in Git auf Google Code um und durchlaufe erneut die %{link_to_import_flow}."
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr "Bitte gib einen beschreibenden Namen für deine Gruppe ein."
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr "Bitte beachte, dass diese Anwendung nicht von GitLab bereitgestellt wird. Du solltest daher die Authentizität überprüfen, bevor du den Zugriff erlaubst."
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr "Bitte wähle zumindest einen Filter um Ergebnisse zu sehen"
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "Bitte löse das reCAPTCHA"
@@ -7050,6 +7364,9 @@ msgstr "Einstellungen"
msgid "Preferences|Navigation theme"
msgstr "Navigationsthema"
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr "Drücke Eingabe oder klicke um zu suchen"
@@ -7248,9 +7565,6 @@ msgstr "Diese E-Mail-Adresse wird für webbasierte Vorgänge wie Bearbeitungen u
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr "Dieses Emoji und diese Nachricht werden in deinem Profil und auf der gesamten Benutzeroberfläche angezeigt."
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr "Fortschritt"
msgid "Project"
msgstr "Projekt"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Projekt \"%{project_name}\" wird gerade gelöscht."
@@ -7392,6 +7709,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 has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr "Benutzer(innen) können nur Commits in dieses Repository pushen die mit
msgid "Projects"
msgstr "Projekte"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr "Geteilte Projekte mit %{group_name}"
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr "Avatar entfernen"
@@ -7985,6 +8314,19 @@ msgstr "Alle Benutzer(innen) dieser Gruppe müssen die Zwei-Faktor-Authentifizie
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "Fordere alle Benutzer(innen) auf, die Nutzungsbedingungen und Datenschutzrichtlinien zu akzeptieren, wenn sie auf GitLab zugreifen."
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr "Du hast alle deine Pipeline-Minuten für geteilte Runner verbraucht."
msgid "Running"
msgstr "Wird ausgeführt"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "SAML SSO"
@@ -8173,6 +8518,9 @@ msgstr "Öffentlicher SSH-Schlüssel"
msgid "SSL Verification"
msgstr "SSL-Verifizierung"
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "Speichern"
@@ -8206,6 +8554,9 @@ msgstr "Geplant"
msgid "Schedules"
msgstr "Zeitpläne"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "Pipelines planen"
@@ -8266,6 +8617,9 @@ msgstr "Projekte suchen"
msgid "Search users"
msgstr "Suche Benutzer(innen)"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr "Deine Projekte durchsuchen"
@@ -8524,6 +8878,9 @@ msgstr "Lege ein Instanz-weites Vorlagen-Repository"
msgid "Set max session time for web terminal."
msgstr "Lege die maximale Sitzungszeit für das Web-Terminal fest."
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "Benachrichtigungs-E-Mail für Missbrauchsberichte festlegen."
@@ -8548,6 +8905,9 @@ msgstr "Richte notwendige Angaben (E-Mail, Vorname, Nachname) und NameID gemäß
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "Richte dein Projekt so ein, dass Änderungen automatisch an ein anderes Repository gesendet bzw. von diesem abgerufen werden. Branches, Tags und Commits werden automatisch synchronisiert."
@@ -8605,9 +8965,15 @@ msgstr "Verwendete Pipeline-Minuten zurücksetzen"
msgid "Sherlock Transactions"
msgstr "Sherlock-Transaktionen"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "Befehl anzeigen"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "Zeige komplettes Rohprotokoll"
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr "Codeausschnitte"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr "Aktivität markierter Projekte"
msgid "Starred projects"
msgstr "Markierte Projekte"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr "Zu Branch/Tag wechseln"
msgid "Sync information"
msgstr "Informationen synchronisieren"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "System-Hooks"
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr "Die maximal zulässige Dateigröße beträgt 200 KB."
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "Das Kennwort, das zum Entschlüsseln des privaten Schlüssels erforderlich ist. Dies ist optional und der Wert wird im Ruhezustand verschlüsselt."
@@ -9498,6 +9894,9 @@ msgstr "Beim Löschen des To-dos ist ein Fehler aufgetreten."
msgid "There was an error loading users activity calendar."
msgstr "Beim Laden des Benutzeraktivitäts-Kalenders ist ein Fehler aufgetreten."
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr "Beim Speichern deiner Benachrichtigungseinstellungen ist ein Fehler aufgetreten."
@@ -9543,6 +9942,21 @@ msgstr "Der Umfang dieses Boards ist reduziert"
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr "Dieser Branch wurde verändert, seit du begonnen hast ihn zu bearbeiten. Möchtest du einen neuen Branch anlegen?"
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr "Diese Container-Registry wurde zur Löschung vorgesehen."
@@ -9564,6 +9978,9 @@ msgstr "Dieses Verzeichnis"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr "Diese Gruppe"
@@ -9648,6 +10065,12 @@ msgstr "Dies bedeutet, dass du keinen Code pushen kannst, bevor du kein leeres R
msgid "This merge request is locked."
msgstr "Dieser Merge-Request ist gesperrt."
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr "Diese Option wurde deaktiviert, da du keine Schreibrechte für diesen Branch hast"
@@ -10207,6 +10630,9 @@ msgstr "Demnächst"
msgid "Update"
msgstr "Aktualisieren"
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr "Jetzt updaten"
msgid "Update your group name, description, avatar, and visibility."
msgstr "Deine Gruppenbezeichnung, Beschreibung, Avatar und Sichtbarkeit anpassen."
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "Aktualisiere"
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr "Dokumentation anzeigen"
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr "Epic-Liste anzeigen"
@@ -10453,6 +10885,9 @@ msgstr "Zeige Datei @ "
msgid "View group labels"
msgstr "Gruppenlabels ansehen"
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "Ticket anzeigen"
@@ -10756,6 +11191,9 @@ msgstr "Mit der Beitragsanalyse hast du einen Überblick über die Aktivitäten
msgid "Withdraw Access Request"
msgstr "Zugriffsanfrage widerrufen"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr "Du hast keine Berechtigungen"
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Du hast die Projektbegrenzung erreicht"
@@ -10921,6 +11362,9 @@ msgstr "Du musst einen deinem Profil einen SSH-Schlüssel hinzufügen, bevor du
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Du musst unterschiedliche Branchnamen verwenden, um einen gültigen Vergleich zu erhalten."
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr "Du erhältst diese E-Mail, weil %{reason}."
@@ -11014,6 +11458,9 @@ msgstr "selber zuweisen"
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr "Branch-Name"
@@ -11097,6 +11544,9 @@ msgstr "Code-Qualität"
msgid "ciReport|Confidence"
msgstr "Vertrauen"
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr "Container-Scan"
@@ -11191,9 +11641,6 @@ msgstr "Keine Änderungen an den Leistungsmetriken"
msgid "ciReport|Performance metrics"
msgstr "Leistungsmetriken"
-msgid "ciReport|Revert dismissal"
-msgstr "Ausblenden rückgängig machen"
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11233,6 +11680,9 @@ msgstr "Fehler beim Laden des Abhängigkeitsprüfungs-Berichtes"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "Fehler beim Wiedereinblenden. Bitte versuche es erneut."
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "Upgrade %{name} von %{version} zu %{fixed}."
@@ -11325,9 +11775,6 @@ msgstr "Hilfe"
msgid "here"
msgstr "hier"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://dein-bitbucket-server"
@@ -11446,6 +11893,9 @@ msgstr "Während des Sendens deiner Genehmigung trat ein Fehler auf."
msgid "mrWidget|Approve"
msgstr "Genehmigen"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "Genehmigt von"
@@ -11518,6 +11968,9 @@ msgstr "Lokal mergen"
msgid "mrWidget|Merge request approved"
msgstr "Merge-Request genehmigt"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr "Merge-Request genehmigt; du kannst zusätzlich genehmigen"
@@ -11579,6 +12032,9 @@ msgstr "Zurücksetzen"
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr "Diesen Merge-Request in einem neuen Merge-Request rückgängig machen"
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "Festlegt von"
diff --git a/locale/el_GR/gitlab.po b/locale/el_GR/gitlab.po
index 68aebe5b9f2..e8c50215d7e 100644
--- a/locale/el_GR/gitlab.po
+++ b/locale/el_GR/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: el\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:15\n"
+"PO-Revision-Date: 2019-03-06 15:50\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/eo/gitlab.po b/locale/eo/gitlab.po
index fde168f06af..8704cc351fc 100644
--- a/locale/eo/gitlab.po
+++ b/locale/eo/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: eo\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:13\n"
+"PO-Revision-Date: 2019-03-06 15:51\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d enmetado"
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Aro da diagramoj pri la seninterrompa integrado"
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr "Malfari"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr "Diagramoj"
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "Trovi per dosierindiko"
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr "Enporti deponejon"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr "Ni prezentas al vi la ciklan analizon"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Lernu pli en la"
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,15 +6537,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Nova ĉenstabla plano"
msgid "New Snippet"
msgstr ""
-msgid "New Snippets"
-msgstr ""
-
msgid "New branch"
msgstr "Nova branĉo"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr "Ne estas sufiĉe da datenoj"
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr "Variabloj"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Propra"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "Ĉenstabloj"
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,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 has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "Planado de la ĉenstabloj"
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr "Iri al branĉo/etikedo"
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ 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 merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "Nuligi la peton pri atingeblo"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Vi ne povas krei pliajn projektojn"
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/es/gitlab.po b/locale/es/gitlab.po
index ae1db647187..7ac4b7c7eb1 100644
--- a/locale/es/gitlab.po
+++ b/locale/es/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: es-ES\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 10:21\n"
+"PO-Revision-Date: 2019-03-06 15:19\n"
msgid " Status"
msgstr " Estado"
@@ -32,17 +32,22 @@ msgstr[0] " mejorado en %d punto"
msgstr[1] " mejorado en %d puntos"
msgid " or "
-msgstr ""
+msgstr " o "
msgid " or <#epic id>"
-msgstr ""
+msgstr " o <#epic id>"
msgid " or <#issue id>"
-msgstr ""
+msgstr " o <#issue id>"
msgid "\"%{query}\" in projects"
msgstr "\"%{query}\" en proyectos"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d cambio"
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr "%{count} %{alerts}"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} participante"
@@ -148,15 +167,15 @@ msgstr "%{filePath} eliminado"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} +%{labelCount} más"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr "¡Va a eliminar %{issuableType}! ¿Está seguro de que desea realizar esta acción?"
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,9 +247,12 @@ msgstr "+ %{count} más"
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} más"
-msgid ", or "
+msgid "+%{extraOptionCount} more"
msgstr ""
+msgid ", or "
+msgstr ", o "
+
msgid "- Runner is active and can process any new jobs"
msgstr "- El ejecutor está activo y puede procesar cualquier trabajo nuevo"
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] "1 %{type} modificación"
msgstr[1] "%{count} %{type} modificaciones"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "1 incidencia cerrada"
@@ -299,7 +326,7 @@ msgid "1st contribution!"
msgstr "¡1ra contribución!"
msgid "2FA"
-msgstr ""
+msgstr "2FA"
msgid "2FA enabled"
msgstr "Verificación en 2 pasos habilitada"
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Una colección de gráficos sobre Integración Continua"
@@ -367,6 +409,9 @@ msgstr "Un miembro del equipo de GitLab revisará tus comentarios tan pronto com
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -434,7 +479,7 @@ msgid "Add"
msgstr "Añadir"
msgid "Add CHANGELOG"
-msgstr ""
+msgstr "Añadir CHANGELOG"
msgid "Add CONTRIBUTING"
msgstr ""
@@ -451,24 +496,51 @@ msgstr "Añadir clúster de Kubernetes"
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "Añadir tabla"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "Añadir comentario ahora"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "Añadir comentario a la imagen"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "Añadir licencia"
@@ -637,17 +709,23 @@ msgstr "Permisos avanzados, almacenamiento de grandes archivos y configuraciones
msgid "Advanced settings"
msgstr "Configuración avanzada"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "Alerta"
msgstr[1] "Alertas"
msgid "Alerts"
-msgstr ""
+msgstr "Alertas"
msgid "All"
msgstr "Todos"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr "Se ha producido un error"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr "Se ha producido un error al añadir un nuevo borrador."
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr "Ha ocurrido un error visualizando el blob"
@@ -741,6 +828,9 @@ msgstr "Se ha producido un error al actualizar el tamaño de la incidencia"
msgid "An error occurred while adding approver"
msgstr "Se ha producido un error al añadir el aprobador."
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "Se ha producido un error al eliminar el comentario"
@@ -777,6 +867,9 @@ msgstr "Se ha producido un error al obtener los trabajos."
msgid "An error occurred while fetching the pipeline."
msgstr "Se ha producido un error mientras se obtenía el pipeline."
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "Se ha producido un error al obtener los proyectos"
@@ -834,12 +927,18 @@ msgstr "Se ha producido un error al guardar el estado de LDAP. Por favor, intén
msgid "An error occurred while saving assignees"
msgstr "Se ha producido un error al guardar las asignaciones"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr "Se ha producido un error al suscribirse a las notificaciones."
msgid "An error occurred while unsubscribing to notifications."
msgstr "Se ha producido un error al cancelar la suscripción a las notificaciones."
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "Se ha producido un error al actualizar el comentario"
@@ -925,14 +1024,52 @@ msgid "Applications"
msgstr "Aplicaciones"
msgid "Applied"
-msgstr ""
+msgstr "Aplicado"
msgid "Apply suggestion"
+msgstr "Aplicar sugerencia"
+
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
msgstr ""
-msgid "Approvals"
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
msgstr ""
+msgid "Approvals"
+msgstr "Aprobaciones"
+
msgid "Approvals required"
msgstr ""
@@ -952,7 +1089,7 @@ msgid "Archived projects"
msgstr "Proyectos archivados"
msgid "Are you sure"
-msgstr ""
+msgstr "¿Está seguro?"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "¿Estás seguro que deseas eliminar esta programación del pipeline?"
@@ -972,9 +1109,15 @@ msgstr "¿Seguro que quiere regenerar la clave pública? Deberá actualizar la c
msgid "Are you sure you want to remove %{group_name}?"
msgstr "¿Está seguro que desea eliminar %{group_name}?"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr "Asignado(s)"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr "Ago"
msgid "August"
msgstr "Agosto"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "Registro de Autenticación"
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr "No se puede modificar el clúster de Kubernetes gestionado"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr "Revertir"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr "Gráficos"
msgid "Chat"
msgstr "Chat"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ msgstr ""
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr ""
-msgid "Choose which repositories you want to import."
-msgstr "Seleccione que repositorios desea importar."
-
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr ""
@@ -1911,6 +2060,9 @@ msgstr "Clonar repositorio"
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr "Cerrado"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "Incidencias cerradas"
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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 ""
-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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr "Instalar"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr "Instalado"
@@ -2175,9 +2324,6 @@ msgstr "cluster de Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Detalles del cluster de Kubernetes"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2413,31 +2559,43 @@ msgid "ClusterIntegration|sign up"
msgstr "regístrese"
msgid "Code"
+msgstr "Código"
+
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
msgstr ""
msgid "Code owners"
msgstr "Propietarios del código"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr "Contraer"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "Contraer la barra lateral"
msgid "Command line instructions"
-msgstr ""
+msgstr "Instrucciones de la línea de comando"
msgid "Comment"
msgstr "Comentar"
msgid "Comment & close %{noteable_name}"
-msgstr ""
+msgstr "Comentar y cerrar %{noteable_name}"
msgid "Comment & reopen %{noteable_name}"
-msgstr ""
+msgstr "Comentar y volver a abrir %{noteable_name}"
msgid "Comment & resolve discussion"
msgstr "Comentar y resolver la discusión"
@@ -2457,7 +2615,7 @@ msgstr[0] "Cambio"
msgstr[1] "Cambios"
msgid "Commit %{commit_id}"
-msgstr ""
+msgstr "Commit %{commit_id}"
msgid "Commit Message"
msgstr "Mensaje del commit"
@@ -2529,7 +2687,7 @@ msgid "Compare Revisions"
msgstr "Comparar revisiones"
msgid "Compare changes"
-msgstr ""
+msgstr "Comparar cambios"
msgid "Compare changes with the last commit"
msgstr "Comparar los cambios con el último commit"
@@ -2723,11 +2881,14 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr "Copiar el ID a portapapeles"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
msgid "Copy SSH public key"
-msgstr ""
+msgstr "Copiar la clave pública SSH"
msgid "Copy SSH public key to clipboard"
msgstr "Copiar la clave pública SSH al portapapeles"
@@ -2775,7 +2936,7 @@ msgid "Create New Directory"
msgstr "Crear Nuevo Directorio"
msgid "Create New Domain"
-msgstr ""
+msgstr "Crear un nuevo dominio"
msgid "Create a new branch"
msgstr "Crear un nuevo 'branch'"
@@ -2787,7 +2948,7 @@ msgid "Create a new issue"
msgstr "Crear una nueva incidencia"
msgid "Create a new repository"
-msgstr ""
+msgstr "Crear un nuevo repositorio"
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}."
@@ -2961,7 +3122,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "Pruebas"
msgid "DNS"
-msgstr ""
+msgstr "DNS"
msgid "Dashboard"
msgstr "Panel de control"
@@ -2973,10 +3134,10 @@ msgid "DashboardProjects|Personal"
msgstr "Personales"
msgid "Data is still calculating..."
-msgstr ""
+msgstr "Los datos aún se están calculando..."
msgid "Date picker"
-msgstr ""
+msgstr "Selector de fecha"
msgid "Debug"
msgstr "Depurar"
@@ -2988,16 +3149,16 @@ msgid "December"
msgstr "Diciembre"
msgid "Decline"
-msgstr ""
+msgstr "Declinar"
msgid "Decline and sign out"
msgstr "Rechazar y cerrar sesión"
msgid "Default Branch"
-msgstr ""
+msgstr "Branch por defecto"
msgid "Default classification label"
-msgstr ""
+msgstr "Etiqueta de clasificación por defecto"
msgid "Default first day of the week"
msgstr ""
@@ -3006,7 +3167,7 @@ msgid "Default first day of the week in calendars and date pickers."
msgstr ""
msgid "Default: Directly import the Google Code email address or username"
-msgstr ""
+msgstr "Por defecto: Importar directamente el nombre de usuario o la dirección de correo electrónico de Google Code"
msgid "Default: Map a FogBugz account ID to a full name"
msgstr ""
@@ -3200,7 +3361,7 @@ msgid "Deprioritize label"
msgstr ""
msgid "Descending"
-msgstr ""
+msgstr "Descendente"
msgid "Description"
msgstr "Descripción"
@@ -3209,6 +3370,9 @@ msgid "Description templates allow you to define context-specific templates for
msgstr ""
msgid "Description:"
+msgstr "Descripción:"
+
+msgid "Designs"
msgstr ""
msgid "Destroy"
@@ -3266,22 +3430,22 @@ msgid "Discard all unstaged changes?"
msgstr ""
msgid "Discard changes"
-msgstr ""
+msgstr "Descartar los cambios"
msgid "Discard changes to %{path}?"
-msgstr ""
+msgstr "¿Descartar los cambios en %{path}?"
msgid "Discard draft"
-msgstr ""
+msgstr "Descartar borrador"
msgid "Discard review"
-msgstr ""
+msgstr "Descartar la revisión"
msgid "Discover GitLab Geo"
msgstr ""
msgid "Discover projects, groups and snippets. Share your projects with others"
-msgstr ""
+msgstr "Descubra proyectos, grupos y fragmentos. Comparta sus proyectos con otros"
msgid "Discuss a specific suggestion or question"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr "Descartar"
@@ -3371,13 +3538,13 @@ msgid "Edit"
msgstr "Editar"
msgid "Edit %{name}"
-msgstr ""
+msgstr "Editar %{name}"
msgid "Edit Label"
msgstr "Editar etiqueta"
msgid "Edit Milestone"
-msgstr ""
+msgstr "Editar hito"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Editar Programación del Pipeline %{id}"
@@ -3389,10 +3556,10 @@ msgid "Edit application"
msgstr "Editar aplicación"
msgid "Edit comment"
-msgstr ""
+msgstr "Editar comentario"
msgid "Edit environment"
-msgstr ""
+msgstr "Editar el entorno"
msgid "Edit files in the editor and commit changes here"
msgstr "Editar los archivos en el editor y confirmar los cambios aquí"
@@ -3458,6 +3625,9 @@ msgid "Enable for this project"
msgstr "Habilitar para este proyecto"
msgid "Enable group Runners"
+msgstr "Habilitar el grupo de ejecutores"
+
+msgid "Enable header and footer in emails"
msgstr ""
msgid "Enable or disable the Pseudonymizer data collection."
@@ -3496,6 +3666,9 @@ msgstr "Finaliza a las (UTC)"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,11 +3687,8 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
-msgstr ""
+msgstr "Variables de entorno"
msgid "Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use environment variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr "Se ha producido un error al recuperar los entornos."
msgid "Environments|An error occurred while making the request."
msgstr "Se ha producido un error al realizar la solicitud."
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,27 +3768,57 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
-msgid "Environments|Re-deploy to environment"
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
msgstr ""
+msgid "Environments|Re-deploy environment %{name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy to environment"
+msgstr "Re-desplegar al entorno"
+
msgid "Environments|Read more about environments"
+msgstr "Obtenga más información sobre los entornos"
+
+msgid "Environments|Rollback"
msgstr ""
msgid "Environments|Rollback environment"
+msgstr "Deshacer entorno"
+
+msgid "Environments|Rollback environment %{environment_name}?"
msgstr ""
-msgid "Environments|Show all"
+msgid "Environments|Rollback environment %{name}?"
msgstr ""
+msgid "Environments|Show all"
+msgstr "Mostrar todo"
+
msgid "Environments|Stop"
-msgstr ""
+msgstr "Detener"
msgid "Environments|Stop environment"
-msgstr ""
+msgstr "Detener entorno"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Actualizado"
@@ -3620,7 +3826,7 @@ msgid "Environments|You don't have any environments right now"
msgstr ""
msgid "Environments|protected"
-msgstr ""
+msgstr "protegido"
msgid "Epic"
msgstr "Épica"
@@ -3638,10 +3844,10 @@ msgid "Epics|An error occurred while saving the %{epicDateType} date"
msgstr ""
msgid "Epics|How can I solve this?"
-msgstr ""
+msgstr "¿Cómo puedo resolver esto?"
msgid "Epics|More information"
-msgstr ""
+msgstr "Más información"
msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
msgstr ""
@@ -3653,7 +3859,7 @@ msgid "Epics|due"
msgstr ""
msgid "Epics|start"
-msgstr ""
+msgstr "Inicio"
msgid "Error"
msgstr "Error"
@@ -3664,6 +3870,9 @@ msgstr "Informe y registro de errores"
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr "Error al crear la tarea épica"
@@ -3731,11 +3940,38 @@ msgid "Error while loading the merge request. Please try again."
msgstr ""
msgid "Error:"
+msgstr "Error:"
+
+msgid "ErrorTracking|Active"
msgstr ""
-msgid "Errors"
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
msgstr ""
+msgid "Errors"
+msgstr "Errores"
+
msgid "Estimated"
msgstr "Estimado"
@@ -3758,7 +3994,7 @@ msgid "EventFilterBy|Filter by team"
msgstr "Filtrar por equipo"
msgid "Events"
-msgstr ""
+msgstr "Eventos"
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
msgstr ""
@@ -3773,7 +4009,7 @@ msgid "Every week (Sundays at 4:00am)"
msgstr "Todas las semanas (domingos a las 4:00 am)"
msgid "Everyone"
-msgstr ""
+msgstr "Todo el mundo"
msgid "Everyone can contribute"
msgstr "Todo el mundo puede colaborar"
@@ -3797,7 +4033,7 @@ msgid "Except policy:"
msgstr ""
msgid "Existing Git repository"
-msgstr ""
+msgstr "Repositorio Git existente"
msgid "Existing folder"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr "Expandir"
msgid "Expand all"
msgstr "Expandir todo"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "Expandir barra lateral"
@@ -3898,7 +4137,7 @@ msgstr "Se ha producido un error al desplegar a"
msgid "Failed to load emoji list."
msgstr "Se ha producido un error al cargar la lista de emojis."
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3962,10 +4201,7 @@ msgid "FeatureFlags|Delete feature flag"
msgstr ""
msgid "FeatureFlags|Description"
-msgstr ""
-
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
+msgstr "Descripción"
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -3995,7 +4231,7 @@ msgid "FeatureFlags|Get started with Feature Flags"
msgstr ""
msgid "FeatureFlags|Inactive"
-msgstr ""
+msgstr "Inactivo"
msgid "FeatureFlags|Inactive flag for %{scope}"
msgstr ""
@@ -4016,19 +4252,16 @@ msgid "FeatureFlags|More information"
msgstr ""
msgid "FeatureFlags|Name"
-msgstr ""
+msgstr "Nombre"
msgid "FeatureFlags|New"
-msgstr ""
+msgstr "Nuevo"
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
-msgstr ""
+msgstr "Estado"
msgid "FeatureFlags|Target environments"
msgstr ""
@@ -4075,7 +4308,7 @@ msgid "File moved"
msgstr ""
msgid "File templates"
-msgstr ""
+msgstr "Plantillas de archivos"
msgid "File upload error."
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr "Filtrar..."
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "Buscar por ruta"
@@ -4156,13 +4386,13 @@ msgid "Fixed date"
msgstr "Fecha fija"
msgid "Fixed due date"
-msgstr ""
+msgstr "Fecha de finalicación fija"
msgid "Fixed start date"
-msgstr ""
+msgstr "Fecha de inicio fija"
msgid "Fixed:"
-msgstr ""
+msgstr "Arreglado:"
msgid "FogBugz Email"
msgstr "Correo electrónico de FogBugz"
@@ -4177,10 +4407,10 @@ msgid "FogBugz URL"
msgstr "URL de FogBugz"
msgid "FogBugz import"
-msgstr ""
+msgstr "Importar desde FogBugz"
msgid "Follow the steps below to export your Google Code project data."
-msgstr ""
+msgstr "Siga los pasos a continuación para exportar sus datos de proyecto de Google Code."
msgid "Font Color"
msgstr "Color de la fuente"
@@ -4230,8 +4460,8 @@ msgstr "Se han encontrado errores en su fichero .gitlab-ci.yml:"
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
-msgstr "De %{provider_title}"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "Desde Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr "De los hitos:"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Desde la vista de detalles del clúster de Kubernetes, seleccione instalar 'runner' desde la lista de aplicaciones"
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "Llaves GPG"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "General"
@@ -4273,7 +4509,7 @@ msgid "Generate a default set of labels"
msgstr "Generar un conjunto predeterminado de etiquetas"
msgid "Generate key"
-msgstr ""
+msgstr "Generar clave"
msgid "Geo"
msgstr "Geo"
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr "Volver"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "Volver"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr "Ir a"
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr "Importar desde Google Code"
@@ -4761,6 +5006,9 @@ msgstr "Información del grupo:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "Nombre del grupo"
@@ -4768,7 +5016,7 @@ msgid "Group overview content"
msgstr ""
msgid "Group:"
-msgstr ""
+msgstr "Grupo:"
msgid "Group: %{group_name}"
msgstr "Grupo: %{group_name}"
@@ -5064,7 +5312,7 @@ msgid "Import"
msgstr "Importar"
msgid "Import CSV"
-msgstr ""
+msgstr "Importar CSV"
msgid "Import Projects from Gitea"
msgstr "Importar proyectos desde Gitea"
@@ -5085,13 +5333,13 @@ msgid "Import in progress"
msgstr "Importación en progreso"
msgid "Import issues"
-msgstr ""
+msgstr "Importar incidencias"
msgid "Import members"
-msgstr ""
+msgstr "Importar miembros"
msgid "Import members from another project"
-msgstr ""
+msgstr "Importar miembros desde otro proyecto"
msgid "Import multiple repositories by uploading a manifest file."
msgstr "Importar varios repositorios subiendo un archivo manifiesto."
@@ -5100,7 +5348,7 @@ msgid "Import project"
msgstr "Importar proyecto"
msgid "Import project members"
-msgstr ""
+msgstr "Importar miembros del proyecto"
msgid "Import projects from Bitbucket"
msgstr "Importar proyectos desde Bitbucket"
@@ -5129,9 +5377,24 @@ msgstr "Importar repositorio"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr "Conectar repositorios desde"
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,9 +5446,15 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
-msgid "Insert suggestion"
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
msgstr ""
+msgid "Insert suggestion"
+msgstr "Insertar sugerencia"
+
msgid "Install GitLab Runner"
msgstr "Instalar GitLab Runner"
@@ -5216,7 +5485,7 @@ msgid "Interested parties can even contribute by pushing commits if they want to
msgstr ""
msgid "Internal"
-msgstr ""
+msgstr "Interno"
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "Interno - cualquier usuario que haya iniciado sesión puede ver el grupo y cualquier proyecto interno."
@@ -5236,6 +5505,9 @@ msgstr "Introducción a Cycle Analytics"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5243,10 +5515,10 @@ msgid "Invite"
msgstr "Invitar"
msgid "Invite group"
-msgstr ""
+msgstr "Invitar al grupo"
msgid "Invite member"
-msgstr ""
+msgstr "Invitar al miembro"
msgid "Invoke Count"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr "Incidencia"
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr "Incidencias cerradas"
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Más información en la"
@@ -5703,10 +5984,10 @@ msgid "Loading..."
msgstr "Cargando..."
msgid "Loading…"
-msgstr ""
+msgstr "Cargando…"
msgid "Localization"
-msgstr ""
+msgstr "Localización"
msgid "Lock"
msgstr "Bloquear"
@@ -5741,6 +6022,15 @@ msgstr "Iniciar sesión con una tarjeta inteligente"
msgid "Logs"
msgstr "Logs"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 "Haga que todos en su equipo sean más productivos, independientemente de su ubicación. GitLab Geo crea réplicas de solo lectura de su instancia de GitLab para que pueda reducir el tiempo que lleva clonar y obtener grandes repositorios."
@@ -5775,7 +6065,7 @@ msgid "Manage project labels"
msgstr "Administrar etiquetas de proyectos"
msgid "Manage two-factor authentication"
-msgstr ""
+msgstr "Administrar autenticación de dos factores"
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr ""
@@ -5786,6 +6076,9 @@ msgstr "Manifiesto"
msgid "Manifest file import"
msgstr "Importar fichero de manifiesto"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Asigne una ID de cuenta de FogBugz a un usuario de GitLab"
@@ -5808,41 +6101,11 @@ msgid "Mark todo as done"
msgstr "Marcar tarea pendiente como completada"
msgid "Markdown"
-msgstr ""
+msgstr "Markdown"
msgid "Markdown enabled"
msgstr "Markdown habilitado"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr "Metadatos de Maven"
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "Merge request"
@@ -5982,7 +6248,7 @@ msgid "MergeRequest|Search files"
msgstr ""
msgid "Merged"
-msgstr "Mergeado"
+msgstr ""
msgid "Messages"
msgstr "Mensajes"
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr "Documentción sobre las consultas de Prometheus"
-msgid "Metrics|System"
-msgstr "Sistema"
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr "Se ha producido un error al recuperar los datos de los distintos entornos, por favor inténtelo de nuevo"
@@ -6168,7 +6431,7 @@ msgid "Modify merge commit"
msgstr ""
msgid "Monday"
-msgstr ""
+msgstr "Lunes"
msgid "Monitor your errors by integrating with Sentry"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr "Más información"
msgid "More information is available|here"
msgstr "Hay más información disponible | aquí"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr "Más estrellas"
@@ -6271,15 +6537,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Nueva Programación del Pipeline"
msgid "New Snippet"
msgstr "Nuevo fragmento de código"
-msgid "New Snippets"
-msgstr "Nuevo fragmento de código"
-
msgid "New branch"
msgstr "Nueva rama"
@@ -6340,12 +6606,18 @@ msgstr "Nuevo..."
msgid "No"
msgstr "No"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr "Sin etiqueta"
-msgid "No activities found"
+msgid "No Tag"
msgstr ""
+msgid "No activities found"
+msgstr "No se han encontrado actividades"
+
msgid "No assignee"
msgstr "Sin asignar"
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr "No se necesita tarjeta de crédito."
-msgid "No details available"
+msgid "No designs found."
msgstr ""
+msgid "No details available"
+msgstr "No hay detalles disponibles"
+
msgid "No due date"
msgstr "Sin fecha límite"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6386,7 +6661,7 @@ msgid "No file chosen"
msgstr "No se ha seleccionado nignun archivo"
msgid "No file selected"
-msgstr ""
+msgstr "No ha seleccionado ningún archivo"
msgid "No files found."
msgstr "No se han encontrado archivos."
@@ -6401,7 +6676,7 @@ msgid "No license. All rights reserved"
msgstr "Sin licencia. Todos los derechos reservados"
msgid "No matching results"
-msgstr ""
+msgstr "No se han encontrado resultados"
msgid "No merge requests for the selected time period."
msgstr ""
@@ -6413,13 +6688,13 @@ msgid "No messages were logged"
msgstr "No se registraron mensajes"
msgid "No milestones to show"
-msgstr ""
+msgstr "No hay archivos para mostrar"
msgid "No other labels with such name or description"
msgstr ""
msgid "No preview for this file type"
-msgstr ""
+msgstr "No hay vista previa para este tipo de archivo"
msgid "No prioritised labels with such name or description"
msgstr ""
@@ -6440,7 +6715,7 @@ msgid "No schedules"
msgstr "No hay programaciones"
msgid "No start date"
-msgstr ""
+msgstr "Sin fecha de inicio"
msgid "No, directly import the existing email addresses and usernames."
msgstr ""
@@ -6472,6 +6747,9 @@ msgstr "No hay suficientes datos"
msgid "Not now"
msgstr "Ahora no"
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Tenga en cuenta que la rama principal está protegida automáticamente. %{link_to_protected_branches}"
@@ -6640,7 +6918,7 @@ msgid "Open comment type dropdown"
msgstr ""
msgid "Open errors"
-msgstr ""
+msgstr "Errores abiertos"
msgid "Open in Xcode"
msgstr "Abrir en Xcode"
@@ -6675,6 +6953,9 @@ msgstr "Operaciones"
msgid "Operations Dashboard"
msgstr "Panel de operaciones"
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr "Agregar un proyecto al panel"
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6748,7 +7032,7 @@ msgid "Pagination|« First"
msgstr ""
msgid "Parameter"
-msgstr ""
+msgstr "Parámetro"
msgid "Parent epic"
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr "Variables"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Personalizado"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "Pipelines"
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr "Preferencias"
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr "Presione Enter o haga clic para buscar"
@@ -7078,7 +7395,7 @@ msgid "Prioritized label"
msgstr "Etiquetas priorizada"
msgid "Private"
-msgstr ""
+msgstr "Privado"
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."
@@ -7248,9 +7565,6 @@ msgstr "Este correo electrónico se utilizará para operaciones basadas en la we
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr "Progreso"
msgid "Project"
msgstr "Proyecto"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "El proyecto '%{project_name}' está en proceso de ser eliminado."
@@ -7392,6 +7709,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 has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr "Proyectos"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr "Eliminar avatar"
@@ -7881,13 +8210,13 @@ msgid "Reopen epic"
msgstr ""
msgid "Reopen milestone"
-msgstr ""
+msgstr "Reabrir hito"
msgid "Repair authentication"
msgstr "Reparar la autenticación"
msgid "Reply to comment"
-msgstr ""
+msgstr "Responder a este comentario"
msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr "Responda a este correo directamente o %{view_it_on_gitlab}."
@@ -7950,7 +8279,7 @@ msgid "Repository URL"
msgstr "URL del repositorio"
msgid "Repository cleanup"
-msgstr ""
+msgstr "Limpieza del repositorio"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
msgstr ""
@@ -7985,20 +8314,33 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
-msgid "Resend invite"
+msgid "Require approval from code owners"
msgstr ""
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Resend invite"
+msgstr "Reenviar invitación"
+
msgid "Reset authorization key"
-msgstr ""
+msgstr "Reiniciar la clave de autorización"
msgid "Reset authorization key?"
-msgstr ""
+msgstr "¿Reiniciar la clave de autorización?"
msgid "Reset health check access token"
msgstr ""
msgid "Reset key"
-msgstr ""
+msgstr "Reiniciar la clave"
msgid "Reset runners registration token"
msgstr "Reinicializar el token de registro del runner"
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr "En ejecución"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "SAML SSO"
@@ -8173,6 +8518,9 @@ msgstr "Clave pública SSH"
msgid "SSL Verification"
msgstr "Verificación SSL"
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "Guardar"
@@ -8206,6 +8554,9 @@ msgstr "Programado"
msgid "Schedules"
msgstr "Programaciones"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "Programación de Pipelines"
@@ -8266,6 +8617,9 @@ msgstr "Buscar proyectos"
msgid "Search users"
msgstr "Buscar usuarios"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr "Buscar tus proyectos"
@@ -8438,7 +8792,7 @@ msgid "Send email"
msgstr "Enviar correo electrónico"
msgid "Send report"
-msgstr ""
+msgstr "Enviar informe"
msgid "Send usage data"
msgstr "Enviar estadísticas de uso"
@@ -8456,7 +8810,7 @@ msgid "Server version"
msgstr "Versión del servidor"
msgid "Serverless"
-msgstr ""
+msgstr "Serverless"
msgid "ServerlessDetails|Kubernetes Pods"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr "Configure las afirmaciones/atributos/reclamaciones (correo electrónico,
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr "Transacciones de Sherlock"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "Mostrar comando"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "Mostrar el registro completo sin procesar"
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr "Fragmentos de código"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr "Proyectos favoritos"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9172,10 +9562,10 @@ msgid "SubscriptionTable|Usage count is performed once a day at 12:00 PM."
msgstr ""
msgid "Suggested change"
-msgstr ""
+msgstr "Cambio sugerido"
msgid "Sunday"
-msgstr ""
+msgstr "Domingo"
msgid "Support for custom certificates is disabled. Ask your system's administrator to enable it."
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr "Cambiar rama/etiqueta"
msgid "Sync information"
msgstr "Sincronizar información"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "Hooks de sistema"
@@ -9205,10 +9598,10 @@ msgid "System metrics (Kubernetes)"
msgstr "Métricas del sistema (Kubernetes)"
msgid "Tag"
-msgstr ""
+msgstr "Etiqueta"
msgid "Tag list:"
-msgstr ""
+msgstr "Listas de etiquetas:"
msgid "Tags"
msgstr "Etiquetas"
@@ -9304,10 +9697,10 @@ msgid "Templates"
msgstr "Plantillas"
msgid "Terminal"
-msgstr ""
+msgstr "Terminal"
msgid "Terminal for environment"
-msgstr ""
+msgstr "Terminal para el entorno"
msgid "Terms of Service Agreement and Privacy Policy"
msgstr "Términos del acuerdo de servicio y de la política de privacidad"
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr "Se ha producido un error al cargar el calendario de actividades de los usuarios."
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr "El alcance de este tablero es limitado"
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr "Esta rama ha cambiado desde que se comenzó a editar. ¿Le gustaría crear una nueva rama?"
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr "Este directorio"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr "Este grupo"
@@ -9648,6 +10065,12 @@ msgstr "Esto significa que no puede enviar código hasta que cree un repositorio
msgid "This merge request is locked."
msgstr "Este merge request está bloqueado."
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -9902,7 +10325,7 @@ msgid "Title"
msgstr "Título"
msgid "Titles and Filenames"
-msgstr ""
+msgstr "Títulos y nombres de archivos"
msgid "To %{link_to_help} of your domain, add the above key to a TXT record within to your DNS configuration."
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr "Actualizar"
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr "Actualizar ahora"
msgid "Update your group name, description, avatar, and visibility."
msgstr "Actualice el nombre de su grupo, su descripción, su avatar y su visibilidad."
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "Actualizando"
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr "Ver documentación"
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr "Ver lista de tareas épicas"
@@ -10453,6 +10885,9 @@ msgstr "Ver archivo @ "
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "Ver incidencia"
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "Retirar Solicitud de Acceso"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Has alcanzado el límite de tu proyecto"
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr "asignar a uno mismo"
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr "nombre de la rama"
@@ -11097,6 +11544,9 @@ msgstr "Calidad del código"
msgid "ciReport|Confidence"
msgstr "Confianza"
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr "Análisis de contenedores"
@@ -11191,9 +11641,6 @@ msgstr "No hay cambios en las métricas de rendimiento"
msgid "ciReport|Performance metrics"
msgstr "Métricas de rendimiento"
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr "ayuda"
msgid "here"
msgstr "aquí"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://tu-servidor-bitbucket"
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr "Aprobar"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "Aprobado por"
@@ -11518,6 +11968,9 @@ msgstr "Merge local"
msgid "mrWidget|Merge request approved"
msgstr "Solicitud de merge request aprobada"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "Establecido por"
diff --git a/locale/et_EE/gitlab.po b/locale/et_EE/gitlab.po
index ee015172f6d..16c4951d8a7 100644
--- a/locale/et_EE/gitlab.po
+++ b/locale/et_EE/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: et\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:14\n"
+"PO-Revision-Date: 2019-03-06 15:50\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/fil_PH/gitlab.po b/locale/fil_PH/gitlab.po
index c943b2814a3..271ba2587b6 100644
--- a/locale/fil_PH/gitlab.po
+++ b/locale/fil_PH/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: fil\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:14\n"
+"PO-Revision-Date: 2019-03-06 15:50\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/fr/gitlab.po b/locale/fr/gitlab.po
index 83e62a5f9de..81d25244a74 100644
--- a/locale/fr/gitlab.po
+++ b/locale/fr/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: fr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:14\n"
+"PO-Revision-Date: 2019-03-06 15:50\n"
msgid " Status"
msgstr " Statut"
@@ -32,17 +32,22 @@ msgstr[0] " amélioré sur %d point"
msgstr[1] " amélioré sur %d points"
msgid " or "
-msgstr ""
+msgstr " ou "
msgid " or <#epic id>"
-msgstr ""
+msgstr " ou <#epic id>"
msgid " or <#issue id>"
-msgstr ""
+msgstr " ou <#issue id>"
msgid "\"%{query}\" in projects"
msgstr "« %{query} » dans les projets"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -54,7 +59,7 @@ msgstr[0] "%d commit de retard"
msgstr[1] "%d commits de retard"
msgid "%d commits"
-msgstr ""
+msgstr "%d commits"
msgid "%d exporter"
msgid_plural "%d exporters"
@@ -78,8 +83,8 @@ msgstr[1] "%d tickets"
msgid "%d issue selected"
msgid_plural "%d issues selected"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d ticket sélectionné"
+msgstr[1] "%d tickets sélectionnés"
msgid "%d layer"
msgid_plural "%d layers"
@@ -124,12 +129,26 @@ msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build
msgstr "%{counter_storage} (%{counter_repositories} dépôts, %{counter_build_artifacts} artefacts construits, %{counter_lfs_objects} LFS)"
msgid "%{count} %{alerts}"
+msgstr "%{count} %{alerts}"
+
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
msgstr ""
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
+msgstr "%{count} autres personnes assignées"
+
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
msgstr ""
msgid "%{count} participant"
@@ -146,10 +165,7 @@ msgid "%{filePath} deleted"
msgstr "%{filePath} supprimé"
msgid "%{firstLabel} +%{labelCount} more"
-msgstr ""
-
-msgid "%{firstOption} +%{extraOptionCount} more"
-msgstr ""
+msgstr "%{firstLabel} et %{labelCount} de plus"
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."
@@ -157,6 +173,9 @@ msgstr "Les %{group_docs_link_start}groupes%{group_docs_link_end} vous permetten
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} sera supprimé ! Êtesâ€vous sûr ?"
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -217,7 +236,7 @@ msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what infor
msgstr "%{usage_ping_link_start}En savoir plus%{usage_ping_link_end} sur les informations partagées avec GitLab Inc."
msgid "%{user_name} profile page"
-msgstr ""
+msgstr "Profil de %{user_name}"
msgid "(external source)"
msgstr ""
@@ -228,9 +247,12 @@ msgstr "+ %{count} de plus"
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} de plus"
-msgid ", or "
+msgid "+%{extraOptionCount} more"
msgstr ""
+msgid ", or "
+msgstr ", ou "
+
msgid "- Runner is active and can process any new jobs"
msgstr "- l’exécuteur est actif et peut traiter de nouvelles tâches"
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] "une modification de %{type}"
msgstr[1] "%{count} modifications de %{type}"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "un ticket fermé"
@@ -305,7 +332,7 @@ msgid "2FA enabled"
msgstr "2FA activé"
msgid "403|Please contact your GitLab administrator to get permission."
-msgstr ""
+msgstr "Veuillez contacter votre administrateur·rice 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."
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up 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 .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Un ensemble de graphiques concernant l’intégration continue (CI)"
@@ -367,6 +409,9 @@ msgstr "Un membre de l’équipe de vérification des abus de GitLab examinera v
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 dépôt divergent (fork) et une nouvelle demande de fusion sera lancée."
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A 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}."
@@ -395,7 +440,7 @@ msgid "Abuse reports"
msgstr "Rapports d’abus"
msgid "Accept invitation"
-msgstr ""
+msgstr "Accepter l’invitation"
msgid "Accept terms"
msgstr "Accepter les conditions"
@@ -451,24 +496,51 @@ msgstr "Ajouter une grappe de serveurs Kubernetes"
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "Ajoutez une page d’accueil à votre wiki contenant des informations sur votre projet. GitLab l’affichera ici à la place de ce message."
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "Ajouter un tableau"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "Ajouter un texte apparaissant dans toutes communications par courriel (%{character_limit} caractères maximum)"
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "Ajouter un commentaire"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "Ajouter un commentaire à l’image"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "Ajouter une licence"
@@ -485,7 +557,7 @@ msgid "Add reaction"
msgstr "Ajouter une réaction"
msgid "Add to project"
-msgstr ""
+msgstr "Ajouter au projet"
msgid "Add to review"
msgstr "Ajouter à la revue de code"
@@ -637,6 +709,9 @@ msgstr "Autorisations avancées, stockage de fichiers volumineux et paramètres
msgid "Advanced settings"
msgstr "Paramètres avancés"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "Alerte"
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr "Tous"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr "Toutes les modifications sont validées"
@@ -717,7 +795,7 @@ msgstr "Un champ utilisateur Gitlab vide ajoutera le nom complet de l’utilisat
msgid "An error has occurred"
msgstr "Une erreur est survenue"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr "Une erreur est survenue lors de l’ajout d’un nouveau brouillon."
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr "Une erreur s’est produite lors de la prévisualisation du blob"
@@ -741,6 +828,9 @@ 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’approba·teur·trice"
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "Une erreur est survenue lors de la suppression du commentaire"
@@ -777,6 +867,9 @@ msgstr "Une erreur est survenue pendant la récupération des tâches."
msgid "An error occurred while fetching the pipeline."
msgstr "Une erreur est survenue pendant la récupération du pipeline."
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "Une erreur s’est produite lors de la récupération des projets"
@@ -834,12 +927,18 @@ msgstr "Une erreur est survenue lors de l’enregistrement du statut d’outrepa
msgid "An error occurred while saving assignees"
msgstr "Une erreur s’est produite lors de l’enregistrement des destinataires"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr "Une erreur est survenue lors de l’abonnement aux notifications."
msgid "An error occurred while unsubscribing to notifications."
msgstr "Une erreur est survenue lors du désabonnement aux notifications."
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "Une erreur est survenue lors de la mise à jour du commentaire"
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr "Êtesâ€vous sûr de vouloir générer une nouvelle paire de clefs ? V
msgid "Are you sure you want to remove %{group_name}?"
msgstr "Voulezâ€vous vraiment supprimer %{group_name} ?"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr "Les listes d’assignation montrent tous les bogues assignés à l’uti
msgid "Assignee(s)"
msgstr "Assigné·e(s)"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr "août"
msgid "August"
msgstr "août"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "Journal d’authentification"
@@ -1635,6 +1778,9 @@ msgstr "Ne peut être fusionnée automatiquement"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "Impossible de modifier la grappe de serveurs gérée par Kubernetes"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr "Défaire"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "Cela va créer un nouveau commit afin de défaire les modifications existantes."
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "Les modifications sont affichées comme si la révision <b>source</b> était fusionnée dans la révision<b>cible</b>."
@@ -1683,6 +1832,9 @@ msgstr "Statistiques"
msgid "Chat"
msgstr "Discussion"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Veuillez vérifier la %{docs_link_start}documentation%{docs_link_end}."
@@ -1749,9 +1901,6 @@ msgstr "Choisissez les groupes que vous souhaitez synchroniser avec ce nœud sec
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
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 fragments que vous souhaitez synchroniser avec ce nœud secondaire."
@@ -1911,6 +2060,9 @@ msgstr "Cloner le dépôt"
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr "Fermé(e)"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "Tickets clos"
@@ -1974,12 +2123,12 @@ msgstr "Après avoir installé Ingress, vous devrez faire pointer votre entrée
msgid "ClusterIntegration|Alternatively"
msgstr ""
-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 occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "Une erreur est survenue lors de la tentative de contact de l’API Google Cloud. Veuillez réessayer plus tard."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr ""
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
msgstr ""
@@ -2016,6 +2165,9 @@ msgstr "Choisissez les applications à installer sur votre grappe de serveurs Ku
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "Choisissez lequel de vos environnements utilisera cette grappe de serveurs."
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,8 +2264,8 @@ 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 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|In order to view the health of your cluster, you must first install Prometheus below."
+msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
@@ -2127,9 +2279,6 @@ msgstr "Ingress vous permet de router les requêtes vers des services en fonctio
msgid "ClusterIntegration|Install"
msgstr "Installer"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr "Installer Prometheus"
-
msgid "ClusterIntegration|Installed"
msgstr "Installé"
@@ -2175,9 +2324,6 @@ msgstr "Grappe de serveurs Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Détails de la grappe de serveurs Kubernetes"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "État de santé de la grappe de serveurs Kubernetes"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "La grappe de serveurs Kubernetes est en cours de création sur Google Kubernetes Engine…"
@@ -2415,15 +2561,27 @@ msgstr "s’inscrire"
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr "Propriétaires du code"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr "Cohortes"
msgid "Collapse"
msgstr "Réduire"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "Masquer la barre latérale"
@@ -2723,6 +2881,9 @@ msgstr "Copier l’URL %{protocol} de clonage"
msgid "Copy ID to clipboard"
msgstr "Copier l’identifiant dans le presseâ€papiers"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr "Copier l’URL SSH de clonage"
@@ -3211,6 +3372,9 @@ msgstr "Les modèles de description permettent de définir des modèles spécifi
msgid "Description:"
msgstr "Description :"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr "Détruire"
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr "Rejeter"
@@ -3460,6 +3627,9 @@ msgstr "Activer pour ce projet"
msgid "Enable group Runners"
msgstr "Activer les exécuteurs de groupe"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr "Activer ou désactiver la collecte de données Pseudonymizer."
@@ -3496,6 +3666,9 @@ msgstr "Se termine à (UTC)"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr "Entrez la description de la demande de fusion"
msgid "Enter the merge request title"
msgstr "Entrez l’intitulé de la demande de fusion"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ 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 re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr "Une erreur s’est produite lors de l’arrêt de l’environnement. Veuillez réessayer"
@@ -3592,15 +3768,33 @@ msgstr "Ouvrir l’environnement en cours"
msgid "Environments|Pod logs from"
msgstr "Journaux du pod depuis"
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+msgstr ""
+
msgid "Environments|Re-deploy to environment"
msgstr "Redéployer dans l’environnement"
msgid "Environments|Read more about environments"
msgstr "En savoir plus sur les environnements"
+msgid "Environments|Rollback"
+msgstr ""
+
msgid "Environments|Rollback environment"
msgstr "Restaurer l’environnement"
+msgid "Environments|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "Tout afficher"
@@ -3613,6 +3807,18 @@ msgstr "Arrêter l’environnement"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Mis à jour"
@@ -3664,6 +3870,9 @@ msgstr "Rapport d’erreur et journalisation"
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr "Erreur lors de la création de l’épopée"
@@ -3733,6 +3942,33 @@ msgstr "Erreur lors du chargement de la demande de fusion. Veuillez réessayer."
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr "Étendre"
msgid "Expand all"
msgstr "Tout étendre"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "Étendre la barre latérale"
@@ -3898,7 +4137,7 @@ msgstr "Échec du déploiement sur"
msgid "Failed to load emoji list."
msgstr "Impossible de charger la liste des émojis."
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr "Description"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr "Modifier %{feature_flag_name}"
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr "Modifier l’indicateur de fonctionnalité"
@@ -4024,9 +4260,6 @@ msgstr "Nouveau"
msgid "FeatureFlags|New Feature Flag"
msgstr "Nouvel indicateur de fonctionnalité"
-msgid "FeatureFlags|Save changes"
-msgstr "Enregistrer les modifications"
-
msgid "FeatureFlags|Status"
msgstr "État"
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr "Filtrer…"
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "Rechercher par chemin d’accès"
@@ -4230,8 +4460,8 @@ msgstr "Erreurs trouvées dans votre fichier .gitlab-ci.yml :"
msgid "Free Trial of GitLab.com Gold"
msgstr "Essai gratuit de GitLab.com Gold"
-msgid "From %{provider_title}"
-msgstr "De %{provider_title}"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "Depuis Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr "À partir des jalons :"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
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 Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "Clefs GPG"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "Général"
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr "Retour"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "Retour"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr "Aller vers"
msgid "Go to %{link_to_google_takeout}."
msgstr "Consultez le site de %{link_to_google_takeout}."
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr "Importation depuis Google Code"
@@ -4761,6 +5006,9 @@ msgstr "Informations du groupe :"
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 managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "Nom du groupe"
@@ -5129,9 +5377,24 @@ msgstr "Importer un dépôt"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr "Connecter des dépôts provenant de"
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr "Améliorez le tableau des tickets avec Gitlab Entreprise Edition."
@@ -5183,6 +5446,12 @@ msgstr "Entrer les clefs d’hôte manuellement"
msgid "Input your repository URL"
msgstr "Entrez l’URL de votre dépôt"
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr "Introduction à l’analyseur de cycle"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr "Ticket"
@@ -5296,8 +5571,8 @@ msgstr "Les tickets peuvent être des bogues, des tâches ou des sujets de discu
msgid "Issues closed"
msgstr "Tickets clos"
-msgid "Issues, merge requests, pushes and comments."
-msgstr "Tickets, demandes de fusion, poussées Git et commentaires."
+msgid "Issues, merge requests, pushes, and comments."
+msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
msgstr "Une fois que vous aurez commencé à créer des tickets d’incident en relation avec vos projets, nous pourrons commencer à en effectuer le suivi et afficher des statistiques les concernant"
@@ -5485,6 +5760,9 @@ msgstr "Promouvoir l’étiquette"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr "La promotion de l’étiquette « %{labelTitle} » va la rendre disponible pour tous les projets du groupe %{groupName}. Les étiquettes de projet ayant le même intitulé seront fusionnées. Cette action est irréversible."
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr "Stockage de fichiers volumineux"
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr "En savoir plus sur les branches protégées"
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Apprenezâ€en plus dans la"
@@ -5741,6 +6022,15 @@ msgstr "Se connecter avec une carte à puce"
msgid "Logs"
msgstr "Journaux"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 chaque membre de votre équipe plus productif, quel que soit l’endroit où il se situe. 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."
@@ -5786,6 +6076,9 @@ msgstr "Manifeste"
msgid "Manifest file import"
msgstr "Importation de fichier manifeste"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Associer un identifiant de compte FogBugz à un utilisateur GitLab"
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr "Markdown activé"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr "Ajouter une liste à puces"
-
-msgid "MarkdownToolbar|Add a link"
-msgstr "Ajouter un lien"
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr "Ajouter une liste numérotée"
-
-msgid "MarkdownToolbar|Add a table"
-msgstr "Ajouter un tableau"
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr "Ajouter une liste de tâches"
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr "Ajouter du texte en gras"
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr "Ajouter du texte en italique"
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr "Afficher en plein écran"
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr "Insérer une citation"
-
-msgid "MarkdownToolbar|Insert code"
-msgstr "Insérer du code"
-
msgid "Maven Metadata"
msgstr "Métadonnées Maven"
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "Demande de fusion"
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr "Documentation des requêtes Prometheus"
-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."
@@ -6194,6 +6457,9 @@ msgstr "Plus d’informations"
msgid "More information is available|here"
msgstr "ici"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr "Les plus étoilés"
@@ -6271,15 +6537,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
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"
@@ -6340,9 +6606,15 @@ msgstr "Nouveau…"
msgid "No"
msgstr "Non"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr "Aucune étiquette"
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr "Aucune contribution n’a été trouvée"
msgid "No credit card required."
msgstr "Aucune carte de crédit n’est nécessaire."
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr "Aucune date d’échéance"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr "Données insuffisantes"
msgid "Not now"
msgstr "Pas maintenant"
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Notez que la branche principale « master » est automatiquement protégée. %{link_to_protected_branches}"
@@ -6675,6 +6953,9 @@ msgstr "Opérations"
msgid "Operations Dashboard"
msgstr "Tableau de bord des opérations"
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr "Ajouter un projet au tableau de bord"
@@ -6684,6 +6965,9 @@ msgstr "Le tableau de bord des opérations fournit un résumé de l’état de s
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "Vous pouvez éventuellement %{link_to_customize} la manière dont les adresses de courriel et les noms d’utilisateur issus de FogBugz sont importés dans GitLab."
@@ -6876,6 +7160,12 @@ msgstr "Variables"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Personnalisé"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "Pipelines"
@@ -6891,6 +7181,9 @@ msgstr "Pipelines de la semaine dernière"
msgid "Pipelines for last year"
msgstr "Pipelines de l’année dernière"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr "Construire en toute confiance"
@@ -7011,9 +7304,21 @@ msgstr "Veuillez les convertir en %{link_to_git} et repasser par %{link_to_impor
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr "Veuillez les convertir en dépôts Git sur Google Code et repasser par %{link_to_import_flow}."
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr "Veuillez saisir un nom descriptif pour votre groupe."
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr "Veuillez noter que cette application n’est pas fournie par GitLab, vous devriez vérifier son authenticité avant d’autoriser son accès."
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+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 set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "Veuillez résoudre le reCAPTCHA"
@@ -7050,6 +7364,9 @@ msgstr "Préférences"
msgid "Preferences|Navigation theme"
msgstr "Thème de navigation"
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr "Appuyez sur Entrée ou cliquez pour rechercher"
@@ -7248,9 +7565,6 @@ msgstr "Cette adresse de courriel sera utilisée pour les opérations Web, telle
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr "Cet émoji et ce message apparaîtront sur votre profil et partout dans l’interface."
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr "Progression"
msgid "Project"
msgstr "Projet"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Le projet « %{project_name} » est en cours de suppression."
@@ -7392,6 +7709,9 @@ msgstr "Le lien de l’exportation du projet a expiré. Merci de générer une n
msgid "Project export started. A download link will be sent by email."
msgstr "L’exportation du projet a débuté. Un lien de téléchargement sera envoyé par courriel."
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr "Les utilisateurs et utilisatrices ne peuvent uniquement pousser sur ce d
msgid "Projects"
msgstr "Projets"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr "Projets partagés avec %{group_name}"
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr "Supprimer l’avatar"
@@ -7985,6 +8314,19 @@ msgstr "Exiger de tous les utilisateurs de ce groupe la configuration de l’aut
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 "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr "Vous avez utilisé tout le temps de pipeline partagé de vos exécuteurs
msgid "Running"
msgstr "En cours d’exécution"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "Authentification unique SAML"
@@ -8173,6 +8518,9 @@ msgstr "Clef SSH publique"
msgid "SSL Verification"
msgstr "Vérification SSL"
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "Enregistrer"
@@ -8206,6 +8554,9 @@ msgstr "Planifié"
msgid "Schedules"
msgstr "Planifications"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "Planification des pipelines"
@@ -8266,6 +8617,9 @@ msgstr "Rechercher des projets"
msgid "Search users"
msgstr "Rechercher des utilisateurs et utilisatrices"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr "Rechercher dans vos projets"
@@ -8524,6 +8878,9 @@ msgstr "Définir un dépôt de modèles au niveau de l’instance"
msgid "Set max session time for web terminal."
msgstr "Définissez le temps maximal de la session pour le terminal Web."
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "Définissez un courriel de notification pour les rapports d’abus."
@@ -8548,6 +8905,9 @@ msgstr "Configure les assertions, attributs et revendications (courriel, prénom
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "Configurez votre projet afin de pouvoir pousser et/ou récupérer automatiquement les modifications vers ou depuis un autre dépôt. Les branches, les étiquetets et les commits seront automatiquement synchronisés."
@@ -8605,9 +8965,15 @@ msgstr "Réinitialiser les minutes du pipeline utilisées"
msgid "Sherlock Transactions"
msgstr "Transactions Sherlock"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "Afficher la commande"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "Afficher le journal brut complet"
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr "Extraits de code"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr "Activité des projets favoris"
msgid "Starred projects"
msgstr "Projets favoris"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr "Changer de branche ou d’étiquette"
msgid "Sync information"
msgstr "Synchroniser les informations"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "« Hooks » système"
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr "La taille maximale autorisée pour un fichier est de 200 Kio."
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "La phrase de passe permettant de déchiffrer la clef privée. Ceci est facultatif et la valeur est chiffrée au repos."
@@ -9498,6 +9894,9 @@ msgstr "Une erreur est survenue lors de la suppression de la tâche à accomplir
msgid "There was an error loading users activity calendar."
msgstr "Une erreur s’est produite lors du chargement du calendrier d’activité des utilisateurs."
+msgid "There was an error saving your changes."
+msgstr ""
+
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."
@@ -9543,6 +9942,21 @@ msgstr "La portée de ce tableau est réduite"
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr "Cette branche a changé depuis que vous y avez apporté des modifications. Souhaitezâ€vous créer une nouvelle branche ?"
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr "Ce registre de conteneur a été programmé pour suppression."
@@ -9564,6 +9978,9 @@ msgstr "Ce répertoire"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr "Ce groupe"
@@ -9648,6 +10065,12 @@ msgstr "Cela signifie que vous ne pouvez pas pousser du code tant que vous n’a
msgid "This merge request is locked."
msgstr "Cette demande de fusion est verrouillée."
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr "Cette option est désactivée car vous n’avez pas les droits d’écriture sur la branche actuelle"
@@ -10207,6 +10630,9 @@ msgstr "À venir"
msgid "Update"
msgstr "Mettre à jour"
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr "Mettre à jour maintenant"
msgid "Update your group name, description, avatar, and visibility."
msgstr "Modifiez le nom du groupe, sa description, son avatar et sa visibilité."
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "Mise à jour en cours"
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr "Voir la documentation"
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr "Afficher la liste des épopées"
@@ -10453,6 +10885,9 @@ msgstr "Voir le fichier @ "
msgid "View group labels"
msgstr "Afficher les labels de groupe"
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "Afficher le ticket"
@@ -10756,6 +11191,9 @@ msgstr "Grâce à l’analyse des contributions, vous pouvez avoir une vue d’e
msgid "Withdraw Access Request"
msgstr "Retirer la demande d’accès"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr "Vous n’avez pas les autorisations"
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Vous avez atteint votre limite de projet"
@@ -10921,6 +11362,9 @@ msgstr "Vous ne pourrez pas récupérer ou pousser de code via 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 only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr "Vous recevez ce courriel parce que %{reason}."
@@ -11014,6 +11458,9 @@ msgstr "assignez vous"
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr "nom de la branche"
@@ -11097,6 +11544,9 @@ msgstr "Qualité du code"
msgid "ciReport|Confidence"
msgstr "Niveau de confiance"
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr "Analyse du conteneur"
@@ -11191,9 +11641,6 @@ msgstr "Aucun changement dans les indicateurs de performance"
msgid "ciReport|Performance metrics"
msgstr "Indicateurs de performance"
-msgid "ciReport|Revert dismissal"
-msgstr "Annuler le rejet"
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11233,6 +11680,9 @@ msgstr "Une erreur s’est produite lors du chargement du rapport d’analyse de
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "Une erreur s’est produite lors de l’annulation du rejet. Veuillez réessayer."
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "Mise à niveau de %{name} de %{version} à %{fixed}."
@@ -11325,9 +11775,6 @@ msgstr "aide"
msgid "here"
msgstr "ici"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://votre-serveur-bitbucket"
@@ -11446,6 +11893,9 @@ msgstr "Une erreur est survenue lors de l’envoi de votre approbation."
msgid "mrWidget|Approve"
msgstr "Approuver"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "Approuvée par"
@@ -11518,6 +11968,9 @@ msgstr "Fusionner localement"
msgid "mrWidget|Merge request approved"
msgstr "Demande de fusion approuvée"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr "Demande de fusion approuvée ; vous pouvez ajouter votre approbation"
@@ -11579,6 +12032,9 @@ msgstr "Défaire"
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr "Défaire cette demande de fusion dans une nouvelle demande de fusion"
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "Marqué par"
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 7f6ca12aebf..c7755c5c7e2 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -19,6 +19,9 @@ msgstr ""
msgid " Status"
msgstr ""
+msgid " You need to do this before %{grace_period_deadline}."
+msgstr ""
+
msgid " or "
msgstr ""
@@ -111,6 +114,9 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
+msgid "%{gitlab_ci_yml} not found in this commit"
+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 ""
@@ -138,6 +144,15 @@ msgstr ""
msgid "%{percent}%% complete"
msgstr ""
+msgid "%{service_title} activated."
+msgstr ""
+
+msgid "%{service_title} settings saved, but not activated."
+msgstr ""
+
+msgid "%{spammable_titlecase} was submitted to Akismet successfully."
+msgstr ""
+
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
msgstr[0] ""
@@ -285,6 +300,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -300,9 +330,21 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A new impersonation token has been created."
+msgstr ""
+
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
+msgid "A ready-to-go template for use with Android apps."
+msgstr ""
+
+msgid "A ready-to-go template for use with iOS Swift apps."
+msgstr ""
+
msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
msgstr ""
@@ -339,6 +381,9 @@ msgstr ""
msgid "Access expiration date"
msgstr ""
+msgid "Access forbidden. Check your access level."
+msgstr ""
+
msgid "Account"
msgstr ""
@@ -366,18 +411,39 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -402,6 +468,9 @@ msgstr ""
msgid "Add users to group"
msgstr ""
+msgid "Added at"
+msgstr ""
+
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr ""
@@ -447,6 +516,9 @@ msgstr ""
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
+msgid "AdminSettings|Enable shared runners for new projects"
+msgstr ""
+
msgid "AdminSettings|Environment variables are protected by default"
msgstr ""
@@ -525,6 +597,9 @@ msgstr ""
msgid "AdminUsers|Without projects"
msgstr ""
+msgid "Advanced"
+msgstr ""
+
msgid "Advanced permissions, Large File Storage and Two-Factor authentication settings."
msgstr ""
@@ -546,12 +621,27 @@ msgstr ""
msgid "All issues for this milestone are closed. You may close this milestone now."
msgstr ""
+msgid "All merge conflicts were resolved. The merge request can now be merged."
+msgstr ""
+
+msgid "All todos were marked as done."
+msgstr ""
+
msgid "All users"
msgstr ""
+msgid "All users must have a name."
+msgstr ""
+
msgid "Allow commits from members who can merge to the target branch."
msgstr ""
+msgid "Allow mirrors to be set up for projects"
+msgstr ""
+
+msgid "Allow only the selected protocols to be used for Git access."
+msgstr ""
+
msgid "Allow projects within this group to use Git LFS"
msgstr ""
@@ -564,6 +654,12 @@ msgstr ""
msgid "Allow requests to the local network from hooks and services."
msgstr ""
+msgid "Allow this key to push to repository as well? (Default only allows pull access.)"
+msgstr ""
+
+msgid "Allow users to register any application to use GitLab as an OAuth provider"
+msgstr ""
+
msgid "Allow users to request access"
msgstr ""
@@ -576,6 +672,9 @@ msgstr ""
msgid "Allows you to add and manage Kubernetes clusters."
msgstr ""
+msgid "Alternate support URL for help page"
+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 ""
@@ -594,10 +693,10 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occurred creating the new branch."
msgstr ""
-msgid "An error occurred creating the new branch."
+msgid "An error occurred fetching the dropdown data."
msgstr ""
msgid "An error occurred previewing the blob"
@@ -636,6 +735,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -720,12 +822,27 @@ msgstr ""
msgid "Appearance"
msgstr ""
+msgid "Appearance was successfully created."
+msgstr ""
+
+msgid "Appearance was successfully updated."
+msgstr ""
+
msgid "Application"
msgstr ""
msgid "Application ID"
msgstr ""
+msgid "Application settings saved successfully"
+msgstr ""
+
+msgid "Application was successfully destroyed."
+msgstr ""
+
+msgid "Application was successfully updated."
+msgstr ""
+
msgid "Application: %{name}"
msgstr ""
@@ -744,6 +861,9 @@ msgstr ""
msgid "April"
msgstr ""
+msgid "Archive jobs"
+msgstr ""
+
msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
@@ -846,9 +966,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -858,6 +975,9 @@ msgstr ""
msgid "Authentication method"
msgstr ""
+msgid "Authentication via U2F device failed."
+msgstr ""
+
msgid "Author"
msgstr ""
@@ -912,15 +1032,6 @@ msgstr ""
msgid "AutoDevOps|The Auto DevOps pipeline has been enabled and will be used if no alternative CI configuration file is found. %{more_information_link}"
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 "Automatically marked as default internal user"
msgstr ""
@@ -951,6 +1062,9 @@ msgstr ""
msgid "Average per day: %{average}"
msgstr ""
+msgid "Background Color"
+msgstr ""
+
msgid "Background Jobs"
msgstr ""
@@ -1197,6 +1311,12 @@ msgstr ""
msgid "Branches|protected"
msgstr ""
+msgid "Broadcast Message was successfully created."
+msgstr ""
+
+msgid "Broadcast Message was successfully updated."
+msgstr ""
+
msgid "Browse Directory"
msgstr ""
@@ -1215,6 +1335,9 @@ msgstr ""
msgid "By %{user_name}"
msgstr ""
+msgid "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."
+msgstr ""
+
msgid "ByAuthor|by"
msgstr ""
@@ -1260,6 +1383,9 @@ msgstr ""
msgid "CICD|Default to Auto DevOps pipeline"
msgstr ""
+msgid "CICD|Default to Auto DevOps pipeline for all projects"
+msgstr ""
+
msgid "CICD|Deployment strategy"
msgstr ""
@@ -1275,6 +1401,9 @@ msgstr ""
msgid "CICD|You must add a %{kubernetes_cluster_start}Kubernetes cluster integration%{kubernetes_cluster_end} to this project with a domain in order for your deployment strategy to work correctly."
msgstr ""
+msgid "CICD|group enabled"
+msgstr ""
+
msgid "CICD|instance enabled"
msgstr ""
@@ -1299,6 +1428,12 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
+msgid "Cannot skip two factor authentication setup"
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1344,6 +1479,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1398,6 +1536,12 @@ msgstr ""
msgid "Choose the top-level group for your repository imports."
msgstr ""
+msgid "Choose visibility level, enable/disable project features (issues, repository, wiki, snippets) and set permissions."
+msgstr ""
+
+msgid "Choose your merge method, set up a default merge request description template."
+msgstr ""
+
msgid "CiStatusLabel|canceled"
msgstr ""
@@ -1422,6 +1566,9 @@ msgstr ""
msgid "CiStatusLabel|pending"
msgstr ""
+msgid "CiStatusLabel|preparing"
+msgstr ""
+
msgid "CiStatusLabel|skipped"
msgstr ""
@@ -1455,6 +1602,9 @@ msgstr ""
msgid "CiStatusText|pending"
msgstr ""
+msgid "CiStatusText|preparing"
+msgstr ""
+
msgid "CiStatusText|skipped"
msgstr ""
@@ -1542,9 +1692,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
msgstr ""
@@ -1557,9 +1704,6 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr ""
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
-msgstr ""
-
msgid "ClusterIntegration|%{title} upgraded successfully."
msgstr ""
@@ -1581,16 +1725,13 @@ msgstr ""
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr ""
-msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
-msgstr ""
-
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -1638,13 +1779,13 @@ msgstr ""
msgid "ClusterIntegration|Copy CA Certificate"
msgstr ""
-msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
+msgid "ClusterIntegration|Copy Ingress Endpoint to clipboard"
msgstr ""
msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
msgstr ""
-msgid "ClusterIntegration|Copy Knative IP Address to clipboard"
+msgid "ClusterIntegration|Copy Knative Endpoint to clipboard"
msgstr ""
msgid "ClusterIntegration|Copy Kubernetes cluster name"
@@ -1698,7 +1839,7 @@ msgstr ""
msgid "ClusterIntegration|GitLab Runner"
msgstr ""
-msgid "ClusterIntegration|GitLab Runner connects to this project's repository and executes CI/CD jobs, pushing results back and deploying, applications to production."
+msgid "ClusterIntegration|GitLab Runner connects to the repository and executes CI/CD jobs, pushing results back and deploying applications to production."
msgstr ""
msgid "ClusterIntegration|Google Cloud Platform project"
@@ -1725,7 +1866,7 @@ msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr ""
-msgid "ClusterIntegration|Ingress IP Address"
+msgid "ClusterIntegration|Ingress Endpoint"
msgstr ""
msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
@@ -1740,6 +1881,12 @@ msgstr ""
msgid "ClusterIntegration|Installing"
msgstr ""
+msgid "ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|Installing Knative may incur additional costs. Learn more about %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
msgstr ""
@@ -1767,7 +1914,7 @@ msgstr ""
msgid "ClusterIntegration|Knative Domain Name:"
msgstr ""
-msgid "ClusterIntegration|Knative IP Address:"
+msgid "ClusterIntegration|Knative Endpoint:"
msgstr ""
msgid "ClusterIntegration|Knative extends Kubernetes to provide a set of middleware components that are essential to build modern, source-centric, and container-based applications that can run anywhere: on premises, in the cloud, or even in a third-party data center."
@@ -1821,9 +1968,6 @@ msgstr ""
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr ""
-msgid "ClusterIntegration|More information"
-msgstr ""
-
msgid "ClusterIntegration|No machine types matched your search"
msgstr ""
@@ -1836,9 +1980,6 @@ msgstr ""
msgid "ClusterIntegration|No zones matched your search"
msgstr ""
-msgid "ClusterIntegration|Note:"
-msgstr ""
-
msgid "ClusterIntegration|Number of nodes"
msgstr ""
@@ -1848,7 +1989,7 @@ msgstr ""
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr ""
-msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
+msgid "ClusterIntegration|Point a wildcard DNS to this generated endpoint in order to access your application after it has been deployed."
msgstr ""
msgid "ClusterIntegration|Project cluster"
@@ -1887,7 +2028,7 @@ msgstr ""
msgid "ClusterIntegration|Request to begin installing failed"
msgstr ""
-msgid "ClusterIntegration|Retry upgrade"
+msgid "ClusterIntegration|Retry update"
msgstr ""
msgid "ClusterIntegration|Save changes"
@@ -1932,9 +2073,6 @@ msgstr ""
msgid "ClusterIntegration|Something went wrong on our end."
msgstr ""
-msgid "ClusterIntegration|Something went wrong when upgrading %{title}. Please check the logs and try again."
-msgstr ""
-
msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr ""
@@ -1944,7 +2082,7 @@ msgstr ""
msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain."
msgstr ""
-msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgid "ClusterIntegration|The endpoint is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
@@ -1953,12 +2091,21 @@ msgstr ""
msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
msgstr ""
+msgid "ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr ""
msgid "ClusterIntegration|Token"
msgstr ""
+msgid "ClusterIntegration|Update failed. Please check the logs and try again."
+msgstr ""
+
+msgid "ClusterIntegration|Updating"
+msgstr ""
+
msgid "ClusterIntegration|Upgrade"
msgstr ""
@@ -1995,9 +2142,6 @@ msgstr ""
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr ""
-msgid "ClusterIntegration|check the pricing here"
-msgstr ""
-
msgid "ClusterIntegration|documentation"
msgstr ""
@@ -2007,6 +2151,9 @@ msgstr ""
msgid "ClusterIntegration|meets the requirements"
msgstr ""
+msgid "ClusterIntegration|pricing"
+msgstr ""
+
msgid "ClusterIntegration|properly configured"
msgstr ""
@@ -2246,6 +2393,9 @@ msgstr ""
msgid "Contribution"
msgstr ""
+msgid "Contribution Analytics"
+msgstr ""
+
msgid "Contribution Charts"
msgstr ""
@@ -2321,9 +2471,24 @@ msgstr ""
msgid "Copy token to clipboard"
msgstr ""
+msgid "Could not connect to FogBugz, check your URL"
+msgstr ""
+
+msgid "Could not create Wiki Repository at this time. Please try again later."
+msgstr ""
+
+msgid "Could not remove the trigger."
+msgstr ""
+
msgid "Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}"
msgstr ""
+msgid "Could not revoke impersonation token %{token_name}."
+msgstr ""
+
+msgid "Coverage"
+msgstr ""
+
msgid "Create"
msgstr ""
@@ -2453,6 +2618,9 @@ 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 ""
@@ -2528,6 +2696,9 @@ msgstr ""
msgid "Default Branch"
msgstr ""
+msgid "Default artifacts expiration"
+msgstr ""
+
msgid "Default first day of the week"
msgstr ""
@@ -2593,6 +2764,9 @@ msgstr[1] ""
msgid "Deploy Keys"
msgstr ""
+msgid "Deploy key was successfully updated."
+msgstr ""
+
msgid "DeployKeys|+%{count} others"
msgstr ""
@@ -2866,6 +3040,9 @@ msgstr ""
msgid "Edit"
msgstr ""
+msgid "Edit Deploy Key"
+msgstr ""
+
msgid "Edit Label"
msgstr ""
@@ -2887,6 +3064,9 @@ msgstr ""
msgid "Edit environment"
msgstr ""
+msgid "Edit file"
+msgstr ""
+
msgid "Edit files in the editor and commit changes here"
msgstr ""
@@ -2899,6 +3079,9 @@ msgstr ""
msgid "Edit issues"
msgstr ""
+msgid "Edit public deploy key"
+msgstr ""
+
msgid "Email"
msgstr ""
@@ -2911,6 +3094,9 @@ msgstr ""
msgid "Embed"
msgstr ""
+msgid "Emojis|Something went wrong while loading emojis."
+msgstr ""
+
msgid "Empty file"
msgstr ""
@@ -2920,6 +3106,9 @@ msgstr ""
msgid "Enable Auto DevOps"
msgstr ""
+msgid "Enable HTML emails"
+msgstr ""
+
msgid "Enable Sentry for error reporting and logging."
msgstr ""
@@ -2938,6 +3127,12 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
+msgid "Enable mirror configuration"
+msgstr ""
+
msgid "Enable or disable version check and usage ping."
msgstr ""
@@ -2962,6 +3157,12 @@ msgstr ""
msgid "Enabled"
msgstr ""
+msgid "Enabled Git access protocols"
+msgstr ""
+
+msgid "Enabled sources for code import during project creation. OmniAuth must be configured for GitHub"
+msgstr ""
+
msgid "Ends at (UTC)"
msgstr ""
@@ -2980,9 +3181,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3007,6 +3205,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3052,15 +3256,33 @@ msgstr ""
msgid "Environments|Open live environment"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3073,12 +3295,27 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
msgid "Environments|You don't have any environments right now"
msgstr ""
+msgid "Environments|protected"
+msgstr ""
+
msgid "Epic"
msgstr ""
@@ -3091,6 +3328,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error deleting %{issuableType}"
msgstr ""
@@ -3136,6 +3376,18 @@ msgstr ""
msgid "Error occurred when toggling the notification subscription"
msgstr ""
+msgid "Error occurred. User was not blocked"
+msgstr ""
+
+msgid "Error occurred. User was not confirmed"
+msgstr ""
+
+msgid "Error occurred. User was not unblocked"
+msgstr ""
+
+msgid "Error occurred. User was not unlocked"
+msgstr ""
+
msgid "Error rendering markdown preview"
msgstr ""
@@ -3151,12 +3403,45 @@ msgstr ""
msgid "Error updating todo status."
msgstr ""
+msgid "Error uploading file"
+msgstr ""
+
msgid "Error while loading the merge request. Please try again."
msgstr ""
+msgid "Error with Akismet. Please check the logs for more info."
+msgstr ""
+
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3220,12 +3505,6 @@ msgstr ""
msgid "Except policy:"
msgstr ""
-msgid "Existing Git repository"
-msgstr ""
-
-msgid "Existing folder"
-msgstr ""
-
msgid "Existing members and groups"
msgstr ""
@@ -3295,7 +3574,10 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
+msgstr ""
+
+msgid "Failed to promote label due to internal error. Please contact administrators."
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3307,6 +3589,12 @@ msgstr ""
msgid "Failed to remove the pipeline schedule"
msgstr ""
+msgid "Failed to remove user identity."
+msgstr ""
+
+msgid "Failed to remove user key."
+msgstr ""
+
msgid "Failed to update issues, please try again."
msgstr ""
@@ -3322,6 +3610,9 @@ msgstr ""
msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
msgstr ""
+msgid "Favicon was successfully removed."
+msgstr ""
+
msgid "Feb"
msgstr ""
@@ -3381,9 +3672,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -3399,6 +3687,9 @@ msgstr ""
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
+msgid "Fingerprint"
+msgstr ""
+
msgid "Fingerprints"
msgstr ""
@@ -3435,6 +3726,12 @@ 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 ""
@@ -3462,6 +3759,9 @@ msgstr ""
msgid "Format"
msgstr ""
+msgid "Found errors in your %{gitlab_ci_yml}:"
+msgstr ""
+
msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
@@ -3504,6 +3804,9 @@ msgstr ""
msgid "General"
msgstr ""
+msgid "General Settings"
+msgstr ""
+
msgid "General pipelines"
msgstr ""
@@ -3522,6 +3825,9 @@ msgstr ""
msgid "Git"
msgstr ""
+msgid "Git LFS is not enabled on this GitLab server, contact your admin."
+msgstr ""
+
msgid "Git global setup"
msgstr ""
@@ -3558,6 +3864,9 @@ msgstr ""
msgid "GitLab project export"
msgstr ""
+msgid "GitLab restart is required to apply changes"
+msgstr ""
+
msgid "GitLab.com import"
msgstr ""
@@ -3585,9 +3894,15 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
@@ -3615,9 +3930,18 @@ msgstr ""
msgid "Graph"
msgstr ""
+msgid "Gravatar enabled"
+msgstr ""
+
msgid "Group"
msgstr ""
+msgid "Group %{group_name} was scheduled for deletion."
+msgstr ""
+
+msgid "Group %{group_name} was successfully created."
+msgstr ""
+
msgid "Group CI/CD settings"
msgstr ""
@@ -3627,6 +3951,9 @@ msgstr ""
msgid "Group ID"
msgstr ""
+msgid "Group ID: %{group_id}"
+msgstr ""
+
msgid "Group Runners"
msgstr ""
@@ -3654,24 +3981,45 @@ msgstr ""
msgid "Group name"
msgstr ""
+msgid "Group was successfully updated."
+msgstr ""
+
msgid "Group:"
msgstr ""
msgid "Group: %{group_name}"
msgstr ""
+msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
+msgstr ""
+
+msgid "GroupSettings|Auto DevOps will automatically build, test and deploy your application based on a predefined Continuous Integration and Delivery configuration. %{auto_devops_start}Learn more about Auto DevOps%{auto_devops_end}"
+msgstr ""
+
msgid "GroupSettings|Badges"
msgstr ""
msgid "GroupSettings|Customize your group badges."
msgstr ""
+msgid "GroupSettings|Default to Auto DevOps pipeline for all projects within this group"
+msgstr ""
+
msgid "GroupSettings|Learn more about badges."
msgstr ""
+msgid "GroupSettings|New runners registration token has been generated!"
+msgstr ""
+
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr ""
+msgid "GroupSettings|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
+msgstr ""
+
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
msgstr ""
@@ -3762,6 +4110,12 @@ msgstr ""
msgid "GroupsTree|Search by name"
msgstr ""
+msgid "Header logo was successfully removed."
+msgstr ""
+
+msgid "Header message"
+msgstr ""
+
msgid "Health Check"
msgstr ""
@@ -3798,6 +4152,9 @@ msgstr ""
msgid "Hide host keys manual input"
msgstr ""
+msgid "Hide marketing-related entries from help"
+msgstr ""
+
msgid "Hide payload"
msgstr ""
@@ -3809,12 +4166,24 @@ msgstr[1] ""
msgid "Hide values"
msgstr ""
+msgid "Highest role:"
+msgstr ""
+
msgid "History"
msgstr ""
+msgid "Hook was successfully created."
+msgstr ""
+
+msgid "Hook was successfully updated."
+msgstr ""
+
msgid "Housekeeping successfully started"
msgstr ""
+msgid "Housekeeping, export, path, transfer, remove, archive."
+msgstr ""
+
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
msgstr ""
@@ -3875,13 +4244,13 @@ msgstr ""
msgid "If any job surpasses this timeout threshold, it will be marked as failed. Human readable time input language is accepted like \"1 hour\". Values without specification represent seconds."
msgstr ""
-msgid "If disabled, the access level will depend on the user's permissions in the project."
+msgid "If disabled, only admins will be able to set up mirrors in projects."
msgstr ""
-msgid "If enabled"
+msgid "If disabled, the access level will depend on the user's permissions in the project."
msgstr ""
-msgid "If you already have files you can push them using the %{link_to_cli} below."
+msgid "If enabled"
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>."
@@ -3995,6 +4364,9 @@ msgstr ""
msgid "Include a Terms of Service agreement and Privacy Policy that all users must accept."
msgstr ""
+msgid "Include author name in notification email body"
+msgstr ""
+
msgid "Include merge request description"
msgstr ""
@@ -4016,6 +4388,9 @@ msgstr ""
msgid "Indicates whether this runner can pick jobs without tags"
msgstr ""
+msgid "Inform users without uploaded SSH keys that they can't push over SSH until one is added"
+msgstr ""
+
msgid "Information about additional Pages templates and how to install them can be found in our %{pages_getting_started_guide}."
msgstr ""
@@ -4028,6 +4403,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -4076,9 +4457,21 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid Login or password"
+msgstr ""
+
+msgid "Invalid file."
+msgstr ""
+
msgid "Invalid input, please avoid emojis"
msgstr ""
+msgid "Invalid pin code"
+msgstr ""
+
+msgid "Invalid two-factor code."
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -4097,6 +4490,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -4115,7 +4511,7 @@ msgstr ""
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "It must have a header row and at least two columns: the first column is the issue title and the second column is the issue description. The separator is automatically detected."
@@ -4133,9 +4529,15 @@ msgstr ""
msgid "Job"
msgstr ""
+msgid "Job ID"
+msgstr ""
+
msgid "Job has been erased"
msgstr ""
+msgid "Job has been successfully erased!"
+msgstr ""
+
msgid "Job is stuck. Check runners."
msgstr ""
@@ -4169,6 +4571,9 @@ msgstr ""
msgid "Job|Keep"
msgstr ""
+msgid "Job|Pipeline"
+msgstr ""
+
msgid "Job|Scroll to bottom"
msgstr ""
@@ -4187,6 +4592,15 @@ msgstr ""
msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it."
msgstr ""
+msgid "Job|for"
+msgstr ""
+
+msgid "Job|into"
+msgstr ""
+
+msgid "Job|with"
+msgstr ""
+
msgid "Jul"
msgstr ""
@@ -4244,6 +4658,15 @@ msgstr ""
msgid "Label actions dropdown"
msgstr ""
+msgid "Label was created"
+msgstr ""
+
+msgid "Label was removed"
+msgstr ""
+
+msgid "Label was successfully updated."
+msgstr ""
+
msgid "LabelSelect|%{firstLabelName} +%{remainingLabelCount} more"
msgstr ""
@@ -4271,6 +4694,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -4336,9 +4762,6 @@ msgstr ""
msgid "Learn more about Kubernetes"
msgstr ""
-msgid "Learn more about protected branches"
-msgstr ""
-
msgid "Learn more about signing commits"
msgstr ""
@@ -4422,9 +4845,18 @@ msgstr ""
msgid "Locked to current projects"
msgstr ""
+msgid "Logo was successfully removed."
+msgstr ""
+
msgid "Logs"
msgstr ""
+msgid "MRDiff|Show changes only"
+msgstr ""
+
+msgid "MRDiff|Show full file"
+msgstr ""
+
msgid "Make sure you're logged into the account that owns the projects you'd like to import."
msgstr ""
@@ -4494,40 +4926,19 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
+msgid "Max access level"
msgstr ""
-msgid "MarkdownToolbar|Insert a quote"
+msgid "Maximum artifacts size (MB)"
msgstr ""
-msgid "MarkdownToolbar|Insert code"
+msgid "Maximum attachment size (MB)"
msgstr ""
-msgid "Max access level"
+msgid "Maximum job timeout"
msgstr ""
-msgid "Maximum job timeout"
+msgid "Maximum push size (MB)"
msgstr ""
msgid "May"
@@ -4623,16 +5034,19 @@ msgstr ""
msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
msgstr ""
+msgid "MergeRequest|Error loading full diff. Please try again."
+msgstr ""
+
msgid "MergeRequest|Filter files"
msgstr ""
msgid "MergeRequest|No files found"
msgstr ""
-msgid "MergeRequest|Search files"
+msgid "Merged"
msgstr ""
-msgid "Merged"
+msgid "Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes."
msgstr ""
msgid "Messages"
@@ -4728,6 +5142,9 @@ msgstr ""
msgid "Mirroring repositories"
msgstr ""
+msgid "Mirroring settings were successfully updated."
+msgstr ""
+
msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
@@ -4737,6 +5154,9 @@ msgstr ""
msgid "Modal|Close"
msgstr ""
+msgid "Modify commit message"
+msgstr ""
+
msgid "Modify commit messages"
msgstr ""
@@ -4785,6 +5205,9 @@ msgstr ""
msgid "Name:"
msgstr ""
+msgid "Naming, tags, avatar"
+msgstr ""
+
msgid "Naming, visibility"
msgstr ""
@@ -4844,15 +5267,15 @@ msgstr ""
msgid "New Snippet"
msgstr ""
-msgid "New Snippets"
-msgstr ""
-
msgid "New branch"
msgstr ""
msgid "New branch unavailable"
msgstr ""
+msgid "New deploy key"
+msgstr ""
+
msgid "New directory"
msgstr ""
@@ -4865,6 +5288,9 @@ msgstr ""
msgid "New group"
msgstr ""
+msgid "New health check access token has been generated!"
+msgstr ""
+
msgid "New identity"
msgstr ""
@@ -4886,6 +5312,9 @@ msgstr ""
msgid "New project"
msgstr ""
+msgid "New runners registration token has been generated!"
+msgstr ""
+
msgid "New schedule"
msgstr ""
@@ -4898,15 +5327,24 @@ msgstr ""
msgid "New tag"
msgstr ""
+msgid "New users set to external"
+msgstr ""
+
msgid "New..."
msgstr ""
+msgid "Newly registered users will by default be external"
+msgstr ""
+
msgid "No"
msgstr ""
msgid "No %{providerTitle} repositories available to import"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -4937,7 +5375,7 @@ msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -5012,13 +5450,13 @@ msgstr ""
msgid "Not enough data"
msgstr ""
-msgid "Not now"
+msgid "Not found."
msgstr ""
-msgid "Not started"
+msgid "Not now"
msgstr ""
-msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
+msgid "Not started"
msgstr ""
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
@@ -5120,6 +5558,9 @@ msgstr ""
msgid "November"
msgstr ""
+msgid "Object does not exist on the server or you don't have permissions to access it"
+msgstr ""
+
msgid "Oct"
msgstr ""
@@ -5194,6 +5635,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -5209,9 +5653,6 @@ msgstr ""
msgid "Other Labels"
msgstr ""
-msgid "Otherwise it is recommended you start with one of the options below."
-msgstr ""
-
msgid "Outbound requests"
msgstr ""
@@ -5221,6 +5662,12 @@ msgstr ""
msgid "Owner"
msgstr ""
+msgid "Page not found"
+msgstr ""
+
+msgid "Page was successfully deleted"
+msgstr ""
+
msgid "Pages"
msgstr ""
@@ -5257,6 +5704,9 @@ msgstr ""
msgid "Past due"
msgstr ""
+msgid "Paste a machine public key here. Read more about how to generate it %{link_start}here%{link_end}"
+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 ""
@@ -5302,9 +5752,6 @@ msgstr ""
msgid "Pipeline"
msgstr ""
-msgid "Pipeline Health"
-msgstr ""
-
msgid "Pipeline Schedule"
msgstr ""
@@ -5362,6 +5809,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -5377,6 +5830,12 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines page"
+msgstr ""
+
+msgid "Pipelines settings for '%{project_name}' were successfully updated."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -5419,6 +5878,9 @@ msgstr ""
msgid "Pipeline|Commit"
msgstr ""
+msgid "Pipeline|Coverage"
+msgstr ""
+
msgid "Pipeline|Create for"
msgstr ""
@@ -5464,9 +5926,21 @@ msgstr ""
msgid "Pipeline|all"
msgstr ""
+msgid "Pipeline|for"
+msgstr ""
+
+msgid "Pipeline|into"
+msgstr ""
+
+msgid "Pipeline|on"
+msgstr ""
+
msgid "Pipeline|success"
msgstr ""
+msgid "Pipeline|with"
+msgstr ""
+
msgid "Pipeline|with stage"
msgstr ""
@@ -5506,6 +5980,9 @@ 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 a group."
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
@@ -5530,6 +6007,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -5716,15 +6196,12 @@ msgstr ""
msgid "Profiles|This email will be displayed on your public profile"
msgstr ""
-msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}"
+msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{commit_email_link_start}Learn more%{commit_email_link_end}"
msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -5824,6 +6301,15 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
+msgid "Project %{project_repo} could not be found"
+msgstr ""
+
+msgid "Project '%{project_name}' is being imported."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -5857,6 +6343,9 @@ msgstr ""
msgid "Project export could not be deleted."
msgstr ""
+msgid "Project export enabled"
+msgstr ""
+
msgid "Project export has been deleted."
msgstr ""
@@ -5929,12 +6418,18 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
msgid "Projects that belong to a group are prefixed with the group namespace. Existing projects may be moved into a group."
msgstr ""
+msgid "Projects with write access"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr ""
@@ -6028,6 +6523,9 @@ msgstr ""
msgid "Promote to group label"
msgstr ""
+msgid "Prompt users to upload SSH keys"
+msgstr ""
+
msgid "Protected"
msgstr ""
@@ -6046,12 +6544,21 @@ msgstr ""
msgid "Public - The project can be accessed without any authentication."
msgstr ""
+msgid "Public deploy keys (%{deploy_keys_count})"
+msgstr ""
+
msgid "Public pipelines"
msgstr ""
msgid "Push"
msgstr ""
+msgid "Push an existing Git repository"
+msgstr ""
+
+msgid "Push an existing folder"
+msgstr ""
+
msgid "Push events"
msgstr ""
@@ -6079,6 +6586,9 @@ msgstr ""
msgid "Real-time features"
msgstr ""
+msgid "Recent Project Activity"
+msgstr ""
+
msgid "Recent searches"
msgstr ""
@@ -6225,6 +6735,9 @@ msgstr ""
msgid "Repository URL"
msgstr ""
+msgid "Repository check was triggered."
+msgstr ""
+
msgid "Repository cleanup"
msgstr ""
@@ -6276,6 +6789,12 @@ msgstr ""
msgid "Resolved"
msgstr ""
+msgid "Resolved 1 discussion."
+msgstr ""
+
+msgid "Resolved all discussions."
+msgstr ""
+
msgid "Response metrics (AWS ELB)"
msgstr ""
@@ -6332,6 +6851,9 @@ msgstr ""
msgid "Revoke"
msgstr ""
+msgid "Revoked impersonation token %{token_name}!"
+msgstr ""
+
msgid "Run untagged jobs"
msgstr ""
@@ -6350,6 +6872,12 @@ msgstr ""
msgid "Runner token"
msgstr ""
+msgid "Runner was not updated."
+msgstr ""
+
+msgid "Runner was successfully updated."
+msgstr ""
+
msgid "Runner will not receive any new jobs"
msgstr ""
@@ -6392,6 +6920,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -6431,6 +6962,9 @@ msgstr ""
msgid "Scope"
msgstr ""
+msgid "Scope not supported with disabled 'users_search' feature!"
+msgstr ""
+
msgid "Scroll down to <strong>Google Code Project Hosting</strong> and enable the switch on the right."
msgstr ""
@@ -6560,6 +7094,9 @@ msgstr ""
msgid "Select the branch you want to set as the default for this project. All merge requests and commits will automatically be made against this branch unless you specify a different one."
msgstr ""
+msgid "Selected levels cannot be used by non-admin users for groups, projects or snippets. If the public level is restricted, user profiles are only visible to logged in users."
+msgstr ""
+
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 ""
@@ -6581,6 +7118,9 @@ msgstr ""
msgid "September"
msgstr ""
+msgid "Server supports batch API only, please update your Git LFS client to version 1.0.1 and up."
+msgstr ""
+
msgid "Server version"
msgstr ""
@@ -6629,6 +7169,9 @@ msgstr ""
msgid "Service Templates"
msgstr ""
+msgid "Session duration (minutes)"
+msgstr ""
+
msgid "Session expiration, projects limit and attachment size."
msgstr ""
@@ -6650,6 +7193,15 @@ msgstr ""
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
msgstr ""
+msgid "Set the default expiration time for each job's artifacts. 0 for unlimited. The default unit is in seconds, but you can define an alternative. For example: <code>4 mins 2 sec</code>, <code>2h42min</code>."
+msgstr ""
+
+msgid "Set the duration for which the jobs will be considered as old and expired. Once that time passes, the jobs will be archived and no longer able to be retried. Make it empty to never expire jobs. It has to be no less than 1 day, for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>."
+msgstr ""
+
+msgid "Set the maximum file size for each job's artifacts"
+msgstr ""
+
msgid "Set up CI/CD"
msgstr ""
@@ -6710,9 +7262,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -6772,6 +7330,27 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
+msgid "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."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -6955,6 +7534,9 @@ msgstr ""
msgid "Spam and Anti-bot Protection"
msgstr ""
+msgid "Spam log successfully submitted as ham."
+msgstr ""
+
msgid "Specific Runners"
msgstr ""
@@ -7003,6 +7585,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -7036,12 +7624,18 @@ msgstr ""
msgid "Started %{startsIn}"
msgstr ""
+msgid "Started asynchronous removal of all repository check states."
+msgstr ""
+
msgid "Starts %{startsIn}"
msgstr ""
msgid "Starts at (UTC)"
msgstr ""
+msgid "State your message to activate"
+msgstr ""
+
msgid "Status"
msgstr ""
@@ -7102,6 +7696,24 @@ msgstr ""
msgid "Subscribed"
msgstr ""
+msgid "Successfully blocked"
+msgstr ""
+
+msgid "Successfully confirmed"
+msgstr ""
+
+msgid "Successfully removed email."
+msgstr ""
+
+msgid "Successfully scheduled a pipeline to run. Go to the %{link_to_pipelines} for details."
+msgstr ""
+
+msgid "Successfully unblocked"
+msgstr ""
+
+msgid "Successfully unlocked"
+msgstr ""
+
msgid "Suggested change"
msgstr ""
@@ -7111,6 +7723,9 @@ msgstr ""
msgid "Support for custom certificates is disabled. Ask your system's administrator to enable it."
msgstr ""
+msgid "Support page URL"
+msgstr ""
+
msgid "Switch branch/tag"
msgstr ""
@@ -7123,6 +7738,12 @@ msgstr ""
msgid "System default (%{default})"
msgstr ""
+msgid "System header and footer"
+msgstr ""
+
+msgid "System hook was successfully updated."
+msgstr ""
+
msgid "System metrics (Custom)"
msgstr ""
@@ -7240,6 +7861,9 @@ msgstr ""
msgid "Test coverage parsing"
msgstr ""
+msgid "Test failed."
+msgstr ""
+
msgid "The Git LFS objects will <strong>not</strong> be synced."
msgstr ""
@@ -7258,15 +7882,42 @@ msgstr ""
msgid "The collection of events added to the data gathered for that stage."
msgstr ""
+msgid "The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository."
+msgstr ""
+
msgid "The deployment of this job to %{environmentLink} did not succeed."
msgstr ""
+msgid "The directory has been successfully created."
+msgstr ""
+
+msgid "The entered user map is not a valid JSON user map."
+msgstr ""
+
+msgid "The file has been successfully created."
+msgstr ""
+
+msgid "The file has been successfully deleted."
+msgstr ""
+
msgid "The fork relationship has been removed."
msgstr ""
+msgid "The global settings require you to enable Two-Factor Authentication for your account."
+msgstr ""
+
+msgid "The group settings for %{group_links} require you to enable Two-Factor Authentication for your account. You can %{leave_group_links}."
+msgstr ""
+
msgid "The import will time out after %{timeout}. For repositories that take longer, use a clone/push combination."
msgstr ""
+msgid "The invitation has already been accepted."
+msgstr ""
+
+msgid "The invitation was successfully resent."
+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 ""
@@ -7276,6 +7927,18 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The merge conflicts for this merge request cannot be resolved through GitLab. Please try to resolve them locally."
+msgstr ""
+
+msgid "The merge conflicts for this merge request have already been resolved."
+msgstr ""
+
+msgid "The merge conflicts for this merge request have already been resolved. Please return to the merge request."
+msgstr ""
+
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The path to CI config file. Defaults to <code>.gitlab-ci.yml</code>"
msgstr ""
@@ -7297,6 +7960,15 @@ msgstr ""
msgid "The project can be accessed without any authentication."
msgstr ""
+msgid "The project was successfully forked."
+msgstr ""
+
+msgid "The project was successfully imported."
+msgstr ""
+
+msgid "The remote repository is being updated..."
+msgstr ""
+
msgid "The repository for this project does not exist."
msgstr ""
@@ -7327,9 +7999,18 @@ msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr ""
+msgid "The uploaded file is not a valid Google Takeout archive."
+msgstr ""
+
msgid "The usage ping is disabled, and cannot be configured through this form."
msgstr ""
+msgid "The user is being deleted."
+msgstr ""
+
+msgid "The user map has been saved. Continue by selecting the projects you want to import."
+msgstr ""
+
msgid "The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side."
msgstr ""
@@ -7372,6 +8053,12 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error removing the e-mail."
+msgstr ""
+
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -7387,6 +8074,9 @@ msgstr ""
msgid "There was an error when unsubscribing from this label."
msgstr ""
+msgid "There was an error with the reCAPTCHA. Please solve the reCAPTCHA again."
+msgstr ""
+
msgid "These existing issues have a similar title. It might be better to comment there instead of creating another similar issue."
msgstr ""
@@ -7510,6 +8200,12 @@ msgstr ""
msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr ""
+msgid "This job is performing tasks that must complete before it can start"
+msgstr ""
+
+msgid "This job is preparing to start"
+msgstr ""
+
msgid "This job is stuck because you don't have any active runners online with any of these tags assigned to them:"
msgstr ""
@@ -7531,6 +8227,9 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This namespace has already been taken! Please choose another one."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -7543,7 +8242,7 @@ msgstr ""
msgid "This page will be removed in a future release."
msgstr ""
-msgid "This pipeline is run in a merge request context"
+msgid "This pipeline is run on the source branch"
msgstr ""
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{strongStart}Auto DevOps.%{strongEnd}"
@@ -7567,6 +8266,9 @@ msgstr ""
msgid "This repository"
msgstr ""
+msgid "This repository is currently empty. A new Auto DevOps pipeline will be created after a new file has been pushed to a branch."
+msgstr ""
+
msgid "This runner will only run on pipelines triggered on protected branches"
msgstr ""
@@ -7582,6 +8284,9 @@ msgstr ""
msgid "This timeout will take precedence when lower than project-defined timeout and accepts a human readable time input language like \"1 hour\". Values without specification represent seconds."
msgstr ""
+msgid "This user cannot be unlocked manually from GitLab"
+msgstr ""
+
msgid "This user has no identities"
msgstr ""
@@ -7850,6 +8555,9 @@ msgstr ""
msgid "Todo"
msgstr ""
+msgid "Todo was successfully marked as done."
+msgstr ""
+
msgid "Todos"
msgstr ""
@@ -7907,6 +8615,9 @@ msgstr ""
msgid "Trending"
msgstr ""
+msgid "Trigger removed."
+msgstr ""
+
msgid "Trigger this manual action"
msgstr ""
@@ -7916,6 +8627,15 @@ msgstr ""
msgid "Trigger variables:"
msgstr ""
+msgid "Trigger was created successfully."
+msgstr ""
+
+msgid "Trigger was re-assigned."
+msgstr ""
+
+msgid "Trigger was successfully updated."
+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 ""
@@ -7931,15 +8651,24 @@ msgstr ""
msgid "Twitter"
msgstr ""
+msgid "Two-factor Authentication has been disabled for this user"
+msgstr ""
+
msgid "Two-factor authentication"
msgstr ""
msgid "Type"
msgstr ""
+msgid "Unable to connect to server: %{error}"
+msgstr ""
+
msgid "Unable to load the diff. %{button_try_again}"
msgstr ""
+msgid "Unable to schedule a pipeline to run immediately"
+msgstr ""
+
msgid "Unblock"
msgstr ""
@@ -8015,9 +8744,15 @@ msgstr ""
msgid "Update now"
msgstr ""
+msgid "Update your bookmarked URLs as filtered/sorted branches URL has been changed."
+msgstr ""
+
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Update your project name, tags, description and avatar."
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -8072,18 +8807,51 @@ msgstr ""
msgid "Use your global notification setting"
msgstr ""
+msgid "User %{current_user_username} has started impersonating %{username}"
+msgstr ""
+
+msgid "User %{username} was successfully removed."
+msgstr ""
+
msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
msgstr ""
+msgid "User OAuth applications"
+msgstr ""
+
msgid "User Settings"
msgstr ""
msgid "User and IP Rate Limits"
msgstr ""
+msgid "User identity was successfully created."
+msgstr ""
+
+msgid "User identity was successfully removed."
+msgstr ""
+
+msgid "User identity was successfully updated."
+msgstr ""
+
+msgid "User key was successfully removed."
+msgstr ""
+
msgid "User map"
msgstr ""
+msgid "User was successfully created."
+msgstr ""
+
+msgid "User was successfully removed from group and any subresources."
+msgstr ""
+
+msgid "User was successfully removed from project."
+msgstr ""
+
+msgid "User was successfully updated."
+msgstr ""
+
msgid "UserProfile|Activity"
msgstr ""
@@ -8165,12 +8933,18 @@ msgstr ""
msgid "Users requesting access to"
msgstr ""
+msgid "Users were successfully added."
+msgstr ""
+
msgid "Validate"
msgstr ""
msgid "Validate your GitLab CI configuration file"
msgstr ""
+msgid "Validations failed."
+msgstr ""
+
msgid "Value"
msgstr ""
@@ -8210,6 +8984,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View it on GitLab"
msgstr ""
@@ -8246,6 +9023,9 @@ msgstr ""
msgid "Visibility level:"
msgstr ""
+msgid "Visibility, project features, permissions"
+msgstr ""
+
msgid "Visibility:"
msgstr ""
@@ -8303,6 +9083,9 @@ msgstr ""
msgid "Wiki"
msgstr ""
+msgid "Wiki was successfully updated."
+msgstr ""
+
msgid "WikiClone|Clone your wiki"
msgstr ""
@@ -8441,9 +9224,15 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
+msgid "Write access allowed"
+msgstr ""
+
msgid "Write milestone description..."
msgstr ""
@@ -8477,6 +9266,9 @@ msgstr ""
msgid "You are going to transfer %{project_full_name} to another owner. Are you ABSOLUTELY sure?"
msgstr ""
+msgid "You are now impersonating %{username}"
+msgstr ""
+
msgid "You are on a read-only GitLab instance."
msgstr ""
@@ -8492,6 +9284,15 @@ msgstr ""
msgid "You can also star a label to make it a priority label."
msgstr ""
+msgid "You can also test your %{gitlab_ci_yml} in %{lint_link_start}CI Lint%{lint_link_end}"
+msgstr ""
+
+msgid "You can also upload existing files from your computer using the instructions below."
+msgstr ""
+
+msgid "You can create files directly in GitLab using one of the following options."
+msgstr ""
+
msgid "You can easily contribute to them by requesting to join these groups."
msgstr ""
@@ -8504,6 +9305,12 @@ msgstr ""
msgid "You can move around the graph by using the arrow keys."
msgstr ""
+msgid "You can now submit a merge request to get this change into the original branch."
+msgstr ""
+
+msgid "You can now submit a merge request to get this change into the original project."
+msgstr ""
+
msgid "You can only add files when you are on a branch"
msgstr ""
@@ -8522,9 +9329,27 @@ msgstr ""
msgid "You can test your .gitlab-ci.yml in %{linkStart}CI Lint%{linkEnd}."
msgstr ""
+msgid "You cannot impersonate a blocked user"
+msgstr ""
+
+msgid "You cannot impersonate a user who cannot log in"
+msgstr ""
+
+msgid "You cannot impersonate an internal user"
+msgstr ""
+
+msgid "You cannot play this scheduled pipeline at the moment. Please wait a minute."
+msgstr ""
+
msgid "You cannot write to this read-only GitLab instance."
msgstr ""
+msgid "You could not create a new trigger."
+msgstr ""
+
+msgid "You could not take ownership of trigger."
+msgstr ""
+
msgid "You do not have any subscriptions yet"
msgstr ""
@@ -8543,6 +9368,9 @@ msgstr ""
msgid "You have reached your project limit"
msgstr ""
+msgid "You left the \"%{membershipable_human_name}\" %{source_type}."
+msgstr ""
+
msgid "You may also add variables that are made available to the running application by prepending the variable key with <code>K8S_SECRET_</code>."
msgstr ""
@@ -8558,6 +9386,15 @@ msgstr ""
msgid "You need to register a two-factor authentication app before you can set up a U2F device."
msgstr ""
+msgid "You need to specify both an Access Token and a Host URL."
+msgstr ""
+
+msgid "You need to upload a GitLab project export archive (ending in .gz)."
+msgstr ""
+
+msgid "You need to upload a Google Takeout archive."
+msgstr ""
+
msgid "You will lose all changes you've made to this file. This action cannot be undone."
msgstr ""
@@ -8591,6 +9428,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -8624,6 +9464,15 @@ msgstr ""
msgid "Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left."
msgstr ""
+msgid "Your U2F device was registered!"
+msgstr ""
+
+msgid "Your access request to the %{source_type} has been withdrawn."
+msgstr ""
+
+msgid "Your account uses dedicated credentials for the \"%{group_name}\" group and can only be updated through SSO."
+msgstr ""
+
msgid "Your applications (%{size})"
msgstr ""
@@ -8642,6 +9491,9 @@ msgstr ""
msgid "Your changes have been saved"
msgstr ""
+msgid "Your changes have been successfully committed."
+msgstr ""
+
msgid "Your comment will not be visible to the public."
msgstr ""
@@ -8666,9 +9518,15 @@ msgstr ""
msgid "Your projects"
msgstr ""
+msgid "Your request for access has been queued for review."
+msgstr ""
+
msgid "a deleted user"
msgstr ""
+msgid "added %{created_at_timeago}"
+msgstr ""
+
msgid "ago"
msgstr ""
@@ -8690,9 +9548,6 @@ msgstr ""
msgid "branch name"
msgstr ""
-msgid "command line instructions"
-msgstr ""
-
msgid "commented on %{link_to_project}"
msgstr ""
@@ -8719,6 +9574,9 @@ msgstr ""
msgid "deploy token"
msgstr ""
+msgid "detached"
+msgstr ""
+
msgid "disabled"
msgstr ""
@@ -8742,6 +9600,21 @@ msgstr ""
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr ""
+msgid "failed"
+msgstr ""
+
+msgid "for %{link_to_merge_request} with %{link_to_merge_request_source_branch}"
+msgstr ""
+
+msgid "for %{link_to_merge_request} with %{link_to_merge_request_source_branch} into %{link_to_merge_request_target_branch}"
+msgstr ""
+
+msgid "for %{link_to_pipeline_ref}"
+msgstr ""
+
+msgid "for %{ref}"
+msgstr ""
+
msgid "for this project"
msgstr ""
@@ -8754,9 +9627,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -8799,6 +9669,9 @@ msgstr ""
msgid "latest version"
msgstr ""
+msgid "leave %{group_name}"
+msgstr ""
+
msgid "manual"
msgstr ""
@@ -8813,6 +9686,9 @@ msgstr ""
msgid "mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}."
msgstr ""
+msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
+msgstr ""
+
msgid "mrWidgetCommitsAdded|1 merge commit"
msgstr ""
@@ -9104,6 +9980,9 @@ msgstr ""
msgid "stuck"
msgstr ""
+msgid "success"
+msgstr ""
+
msgid "syntax is correct"
msgstr ""
diff --git a/locale/gl_ES/gitlab.po b/locale/gl_ES/gitlab.po
index a931df046da..962e96995b6 100644
--- a/locale/gl_ES/gitlab.po
+++ b/locale/gl_ES/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: gl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:15\n"
+"PO-Revision-Date: 2019-03-06 15:38\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/he_IL/gitlab.po b/locale/he_IL/gitlab.po
index 0c3654b1266..0819ef9afee 100644
--- a/locale/he_IL/gitlab.po
+++ b/locale/he_IL/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: he\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:15\n"
+"PO-Revision-Date: 2019-03-06 15:49\n"
msgid " Status"
msgstr ""
@@ -47,6 +47,13 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -156,12 +163,28 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -182,15 +205,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -270,6 +293,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -296,6 +322,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -416,6 +449,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -431,6 +479,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -515,24 +566,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -701,6 +779,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -714,6 +795,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -783,7 +867,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -795,6 +879,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -807,6 +900,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -843,6 +939,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -900,12 +999,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -996,6 +1101,52 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1038,9 +1189,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1125,6 +1282,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1140,9 +1300,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1701,6 +1858,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1737,6 +1897,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1749,6 +1912,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1815,9 +1981,6 @@ 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 ""
@@ -1977,6 +2140,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1992,9 +2158,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2040,10 +2203,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2082,6 +2245,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2178,7 +2344,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2193,9 +2359,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2241,9 +2404,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2481,15 +2641,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2791,6 +2963,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3281,6 +3456,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3359,6 +3537,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3530,6 +3711,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3566,6 +3750,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3584,9 +3771,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3611,6 +3795,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3662,15 +3852,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3683,6 +3891,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3734,6 +3954,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3803,6 +4026,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3881,6 +4131,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3968,7 +4221,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -4034,9 +4287,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4094,9 +4344,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4185,9 +4432,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4302,7 +4546,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4332,9 +4576,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4767,15 +5017,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4833,6 +5092,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5203,9 +5465,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5257,6 +5534,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5312,6 +5595,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5330,6 +5616,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5372,7 +5661,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5561,6 +5850,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5646,6 +5938,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5821,6 +6116,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5866,6 +6170,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5893,36 +6200,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5977,6 +6254,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6127,9 +6407,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6274,6 +6551,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6353,13 +6633,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6422,9 +6702,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6452,13 +6738,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6554,6 +6843,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6759,6 +7051,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6768,6 +7063,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6960,6 +7258,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6975,6 +7279,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7095,9 +7402,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7107,9 +7426,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7134,6 +7462,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7332,9 +7663,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7434,6 +7762,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7476,6 +7807,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7575,6 +7909,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7936,6 +8273,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8071,6 +8414,23 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Resend invite"
msgstr ""
@@ -8231,6 +8591,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8261,6 +8624,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8294,6 +8660,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8354,6 +8723,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8612,6 +8984,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8636,6 +9011,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8693,9 +9071,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8796,6 +9180,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9066,6 +9468,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9276,6 +9684,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9465,6 +9876,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9588,6 +10002,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9633,6 +10050,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9654,6 +10086,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9738,6 +10173,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10301,6 +10742,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10310,6 +10754,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10538,6 +10985,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10547,6 +10997,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10850,6 +11303,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10955,6 +11411,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -11015,6 +11474,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11108,6 +11570,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11199,6 +11664,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11297,9 +11765,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11339,6 +11804,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11439,9 +11907,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11564,6 +12029,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11636,6 +12104,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11701,6 +12172,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/hi_IN/gitlab.po b/locale/hi_IN/gitlab.po
index 8a3603ef135..779c2496e2e 100644
--- a/locale/hi_IN/gitlab.po
+++ b/locale/hi_IN/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: hi\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:15\n"
+"PO-Revision-Date: 2019-03-06 15:49\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/hr_HR/gitlab.po b/locale/hr_HR/gitlab.po
index 5267aa42b8d..912929a31c4 100644
--- a/locale/hr_HR/gitlab.po
+++ b/locale/hr_HR/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: hr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:13\n"
+"PO-Revision-Date: 2019-03-06 15:52\n"
msgid " Status"
msgstr ""
@@ -45,6 +45,12 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -141,12 +147,27 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -165,15 +186,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -249,6 +270,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -273,6 +297,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -384,6 +414,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -399,6 +444,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -483,24 +531,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -669,6 +744,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -681,6 +759,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -750,7 +831,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -762,6 +843,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -774,6 +864,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -810,6 +903,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -867,12 +963,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -963,6 +1065,48 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1005,9 +1149,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1092,6 +1242,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1107,9 +1260,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1668,6 +1818,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1704,6 +1857,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1716,6 +1872,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1782,9 +1941,6 @@ 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 ""
@@ -1944,6 +2100,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1959,9 +2118,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2007,10 +2163,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2049,6 +2205,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2145,7 +2304,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2160,9 +2319,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2208,9 +2364,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2448,15 +2601,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2757,6 +2922,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3246,6 +3414,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3324,6 +3495,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3495,6 +3669,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3531,6 +3708,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3549,9 +3729,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3576,6 +3753,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3627,15 +3810,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3648,6 +3849,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3699,6 +3912,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3768,6 +3984,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3846,6 +4089,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3933,7 +4179,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3999,9 +4245,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4059,9 +4302,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4149,9 +4389,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4266,7 +4503,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4296,9 +4533,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4731,15 +4974,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4797,6 +5049,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5166,9 +5421,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5220,6 +5490,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5274,6 +5550,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5292,6 +5571,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5334,7 +5616,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5523,6 +5805,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5607,6 +5892,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5781,6 +6069,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5826,6 +6123,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5853,36 +6153,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5937,6 +6207,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6087,9 +6360,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6234,6 +6504,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6312,13 +6585,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6381,9 +6654,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6411,13 +6690,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6513,6 +6795,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6717,6 +7002,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6726,6 +7014,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6918,6 +7209,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6933,6 +7230,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7053,9 +7353,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7065,9 +7377,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7092,6 +7413,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7290,9 +7614,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7392,6 +7713,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7434,6 +7758,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7533,6 +7860,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7893,6 +8223,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8028,6 +8364,21 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Resend invite"
msgstr ""
@@ -8187,6 +8538,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8217,6 +8571,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8250,6 +8607,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8310,6 +8670,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8568,6 +8931,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8592,6 +8958,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8649,9 +9018,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8751,6 +9126,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9021,6 +9414,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9231,6 +9630,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9420,6 +9822,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9543,6 +9948,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9588,6 +9996,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9609,6 +10032,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9693,6 +10119,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10254,6 +10686,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10263,6 +10698,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10491,6 +10929,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10500,6 +10941,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10803,6 +11247,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10908,6 +11355,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10968,6 +11418,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11061,6 +11514,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11148,6 +11604,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11244,9 +11703,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11286,6 +11742,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11382,9 +11841,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11505,6 +11961,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11577,6 +12036,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11640,6 +12102,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/hu_HU/gitlab.po b/locale/hu_HU/gitlab.po
index bc8e95021c1..5fe58919064 100644
--- a/locale/hu_HU/gitlab.po
+++ b/locale/hu_HU/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: hu\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:14\n"
+"PO-Revision-Date: 2019-03-06 15:49\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/id_ID/gitlab.po b/locale/id_ID/gitlab.po
index 79a8b5aa073..9c9444e6d46 100644
--- a/locale/id_ID/gitlab.po
+++ b/locale/id_ID/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: id\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:11\n"
+"PO-Revision-Date: 2019-03-06 15:48\n"
msgid " Status"
msgstr ""
@@ -41,6 +41,10 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -111,12 +115,25 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -131,15 +148,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -207,6 +224,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -227,6 +247,10 @@ msgid "1 %{type} modification"
msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -320,6 +344,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -335,6 +374,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -419,24 +461,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -605,6 +674,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -615,6 +687,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -684,7 +759,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -696,6 +771,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -708,6 +792,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -744,6 +831,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -801,12 +891,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -897,6 +993,40 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -939,9 +1069,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1026,6 +1162,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1041,9 +1180,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1602,6 +1738,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1638,6 +1777,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1650,6 +1792,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1716,9 +1861,6 @@ 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 ""
@@ -1878,6 +2020,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1893,9 +2038,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1941,10 +2083,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -1983,6 +2125,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2079,7 +2224,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2094,9 +2239,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2142,9 +2284,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2382,15 +2521,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2689,6 +2840,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3176,6 +3330,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3254,6 +3411,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3425,6 +3585,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3461,6 +3624,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3479,9 +3645,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3506,6 +3669,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3557,15 +3726,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3578,6 +3765,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3629,6 +3828,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3698,6 +3900,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3776,6 +4005,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3863,7 +4095,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3929,9 +4161,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -3989,9 +4218,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4077,9 +4303,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4194,7 +4417,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4224,9 +4447,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4659,15 +4888,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4725,6 +4963,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5092,9 +5333,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5146,6 +5402,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5198,6 +5460,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5216,6 +5481,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5258,7 +5526,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5447,6 +5715,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5529,6 +5800,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5701,6 +5975,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5746,6 +6029,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5773,36 +6059,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5857,6 +6113,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6007,9 +6266,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6154,6 +6410,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6230,13 +6489,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6299,9 +6558,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6329,13 +6594,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6431,6 +6699,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6633,6 +6904,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6642,6 +6916,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6834,6 +7111,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6849,6 +7132,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -6969,9 +7255,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -6981,9 +7279,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7008,6 +7315,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7206,9 +7516,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7308,6 +7615,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7350,6 +7660,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7449,6 +7762,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7807,6 +8123,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7942,6 +8264,17 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+
msgid "Resend invite"
msgstr ""
@@ -8099,6 +8432,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8129,6 +8465,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8162,6 +8501,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8222,6 +8564,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8480,6 +8825,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8504,6 +8852,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8561,9 +8912,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8661,6 +9018,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8931,6 +9306,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9141,6 +9522,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9330,6 +9714,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9453,6 +9840,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9498,6 +9888,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9519,6 +9924,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9603,6 +10011,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10160,6 +10574,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10169,6 +10586,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10397,6 +10817,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10406,6 +10829,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10709,6 +11135,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10814,6 +11243,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10874,6 +11306,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -10967,6 +11402,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11046,6 +11484,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11138,9 +11579,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11180,6 +11618,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11268,9 +11709,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11387,6 +11825,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11459,6 +11900,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11518,6 +11962,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/it/gitlab.po b/locale/it/gitlab.po
index a7dd46e61dd..b63371c5616 100644
--- a/locale/it/gitlab.po
+++ b/locale/it/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: it\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:09\n"
+"PO-Revision-Date: 2019-03-06 15:18\n"
msgid " Status"
msgstr " Stato"
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr "\"%{query}\" nei progetti"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr "%{count}%{alerts}"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr "%{count} altri assegnatari"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} partecipante"
@@ -148,15 +167,15 @@ msgstr "%{filePath} eliminato"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} + %{labelCount} più"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "I %{group_docs_link_start}Gruppi%{group_docs_link_end} consentono di gestire e collaborare in vari progetti. I membri di un gruppo hanno accesso a tutti i suoi progetti."
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} sarà rimosso! Sei sicuro?"
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} più"
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Un insieme di grafici riguardo la Continuous Integration"
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr "Impostazioni Avanzate"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr "Tutto"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr "Ago"
msgid "August"
msgstr "Agosto"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "Log di autenticazione"
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr "Ripristina"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr "Grafici"
msgid "Chat"
msgstr "Chat"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr "Clona repository"
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr "Installa"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr "Installato"
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ 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 re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+msgstr ""
+
msgid "Environments|Re-deploy to environment"
msgstr ""
msgid "Environments|Read more about environments"
msgstr "Leggi di più sugli ambienti"
+msgid "Environments|Rollback"
+msgstr ""
+
msgid "Environments|Rollback environment"
msgstr ""
+msgid "Environments|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "Mostra tutti"
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Aggiornato"
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "Trova in percorso"
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "Chiavi GPG"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr "Importa repository"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr "Introduzione delle Analisi Cicliche"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Leggi di più su"
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "Richiesta di merge"
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr "Ulteriori informazioni sono disponibili | qui"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,15 +6537,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Nuova pianificazione Pipeline"
msgid "New Snippet"
msgstr ""
-msgid "New Snippets"
-msgstr ""
-
msgid "New branch"
msgstr "Nuova Branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr "Dati insufficienti "
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr "Variabili"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Personalizzato"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "Pipeline"
@@ -6891,6 +7181,9 @@ msgstr "Pipeline per la settimana scorsa"
msgid "Pipelines for last year"
msgstr "Pipeline per l'ultimo anno"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr "Preferenze"
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Il progetto '%{project_name}' è in fase di eliminazione."
@@ -7392,6 +7709,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 has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr "Progetti"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "Pianificazione pipelines"
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr "Snippet"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr "Cambia branch/tag"
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr "Questo significa che non è possibile effettuare push di codice fino a c
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "Ritira richiesta d'accesso"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Hai raggiunto il tuo limite di progetto"
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/ja/gitlab.po b/locale/ja/gitlab.po
index 8998c660e18..f8654721e31 100644
--- a/locale/ja/gitlab.po
+++ b/locale/ja/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ja\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 12:12\n"
+"PO-Revision-Date: 2019-03-06 17:47\n"
msgid " Status"
msgstr " ステータス"
@@ -30,10 +30,10 @@ msgid_plural " improved on %d points"
msgstr[0] " %dãƒã‚¤ãƒ³ãƒˆã§æ”¹å–„"
msgid " or "
-msgstr ""
+msgstr " ã¾ãŸã¯ "
msgid " or <#epic id>"
-msgstr ""
+msgstr " ã¾ãŸã¯ <#エピックID>"
msgid " or <#issue id>"
msgstr ""
@@ -41,6 +41,10 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr "プロジェクト内㮠\"%{query}\""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d個ã®ã‚³ãƒŸãƒƒãƒˆ"
@@ -50,7 +54,7 @@ msgid_plural "%d commits behind"
msgstr[0] "%d個ã®ã‚³ãƒŸãƒƒãƒˆå¾…ã¡"
msgid "%d commits"
-msgstr ""
+msgstr "%d個ã®ã‚³ãƒŸãƒƒãƒˆ"
msgid "%d exporter"
msgid_plural "%d exporters"
@@ -70,7 +74,7 @@ msgstr[0] "%d個ã®èª²é¡Œ"
msgid "%d issue selected"
msgid_plural "%d issues selected"
-msgstr[0] ""
+msgstr[0] "%d個ã®èª²é¡Œã‚’é¸æŠžæ¸ˆã¿"
msgid "%d layer"
msgid_plural "%d layers"
@@ -111,12 +115,25 @@ msgstr "%{counter_storage} (%{counter_repositories} リãƒã‚¸ãƒˆãƒªã€%{counter_
msgid "%{count} %{alerts}"
msgstr "%{count} %{alerts}"
-msgid "%{count} more"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+
+msgid "%{count} approvals from %{name}"
msgstr ""
+msgid "%{count} more"
+msgstr "%{count} 以上"
+
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} 人ã®å‚加者"
@@ -131,18 +148,18 @@ msgstr "%{filePath} ãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} +%{labelCount} ã®è©³ç´°"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{group_docs_link_start}グループ%{group_docs_link_end}を使用ã™ã‚‹ã¨ã€è¤‡æ•°ã®ãƒ—ロジェクトを管ç†ã—ã¦å…±åŒä½œæ¥­ã‚’è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚グループã®ãƒ¡ãƒ³ãƒãƒ¼ã¯ã€æ‰€å±žã™ã‚‹ãƒ—ロジェクトã®ã™ã¹ã¦ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚"
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType}を削除ã—ã¾ã™ï¼ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
-msgid "%{link_start}Read more%{link_end} about role permissions"
+msgid "%{label_for_message} unavailable"
msgstr ""
+msgid "%{link_start}Read more%{link_end} about role permissions"
+msgstr "ロールã®æ¨©é™ã«ã¤ã„ã¦%{link_start}ã‚‚ã£ã¨èª­ã‚€%{link_end}"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 開始"
@@ -162,22 +179,22 @@ msgid "%{percent}%% complete"
msgstr "%{percent}%% 完了"
msgid "%{state} epics"
-msgstr ""
+msgstr "%{state}エピック"
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{branch_count}%{strong_end} ブランãƒ"
msgid "%{strong_start}%{commit_count}%{strong_end} Commit"
msgid_plural "%{strong_start}%{commit_count}%{strong_end} Commits"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{commit_count}%{strong_end} コミット"
msgid "%{strong_start}%{human_size}%{strong_end} Files"
-msgstr ""
+msgstr "%{strong_start}%{human_size}%{strong_end} ファイル"
msgid "%{strong_start}%{tag_count}%{strong_end} Tag"
msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{tag_count}%{strong_end} ã‚¿ã‚°"
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
@@ -196,20 +213,23 @@ msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what infor
msgstr "GitLab Inc. ã¨ã©ã®ã‚ˆã†ãªæƒ…報を共有ã™ã‚‹ã‹ã«ã¤ã„ã¦ã¯ %{usage_ping_link_start} ã“ã¡ã‚‰%{usage_ping_link_end} ã‚’å‚ç…§ã—ã¦ãã ã•ã„。"
msgid "%{user_name} profile page"
-msgstr ""
+msgstr "%{user_name} プロフィールページ"
msgid "(external source)"
-msgstr ""
+msgstr "(外部ソース)"
msgid "+ %{count} more"
-msgstr ""
+msgstr "%{count} 以上"
msgid "+ %{moreCount} more"
msgstr "+ 他 %{moreCount} 件"
-msgid ", or "
+msgid "+%{extraOptionCount} more"
msgstr ""
+msgid ", or "
+msgstr "ã€ã¾ãŸã¯"
+
msgid "- Runner is active and can process any new jobs"
msgstr "- RunnerãŒã‚¢ã‚¯ãƒ†ã‚£ãƒ–ã§æ–°ã—ã„ジョブを処ç†ã§ãã¾ã™"
@@ -227,6 +247,10 @@ msgid "1 %{type} modification"
msgid_plural "%{count} %{type} modifications"
msgstr[0] "%{count} 件 %{type} ã®ä¿®æ­£"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "%d件ã®ã‚¯ãƒ­ãƒ¼ã‚ºã•ã‚ŒãŸèª²é¡Œ"
@@ -257,7 +281,7 @@ msgstr[0] "%d 個ã®ãƒ‘イプライン"
msgid "1 role"
msgid_plural "%d roles"
-msgstr[0] ""
+msgstr[0] "役割 %d 件"
msgid "1 user"
msgid_plural "%d users"
@@ -267,13 +291,13 @@ msgid "1st contribution!"
msgstr "最åˆã®è²¢çŒ®!"
msgid "2FA"
-msgstr ""
+msgstr "2FA"
msgid "2FA enabled"
msgstr "2段階èªè¨¼ãŒæœ‰åŠ¹"
msgid "403|Please contact your GitLab administrator to get permission."
-msgstr ""
+msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã®é–²è¦§ã«ã¯æ¨©é™ãŒå¿…è¦ã§ã™ã€‚GitLab 管ç†è€…ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。"
msgid "403|You don't have the permission to access this page."
msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹æ¨©é™ãŒã‚ã‚Šã¾ã›ã‚“。"
@@ -315,11 +339,26 @@ msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong>
msgstr "<strong>%{pushes}</strong>回ã®ãƒ—ッシュã€<strong>%{commits}</strong>回以上ã®ã‚³ãƒŸãƒƒãƒˆãŒè²¢çŒ®è€…<strong>%{people}</strong>ã«ã‚ˆã£ã¦è¡Œã‚ã‚Œã¾ã—ãŸã€‚"
msgid "<strong>Deletes</strong> source branch"
-msgstr ""
+msgstr "ソースブランãƒã‚’<strong>削除</strong>"
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "「Runnerã€ã¯ã‚¸ãƒ§ãƒ–を実行ã™ã‚‹ãƒ—ロセスã§ã™ã€‚å¿…è¦ãªæ•°ã® Runner ã‚’ä»»æ„ã«ã‚»ãƒƒãƒˆã‚¢ãƒƒãƒ—ã§ãã¾ã™ã€‚"
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "CIã«ã¤ã„ã¦ã®ã‚°ãƒ©ãƒ•"
@@ -335,6 +374,9 @@ msgstr "ãŸã ã¡ã«é–‹ç™ºãƒ¡ãƒ³ãƒãƒ¼ã§ã„ãŸã ã„ãŸãƒ¬ãƒãƒ¼ãƒˆã‚’æ‹èª­ã—
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "フォークã«æ–°ã—ã„ブランãƒãŒä½œæˆã•ã‚Œã€æ–°ã—ã„マージリクエストãŒé–‹å§‹ã—ã¾ã™ã€‚"
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A 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}"
@@ -363,7 +405,7 @@ msgid "Abuse reports"
msgstr "迷惑行為レãƒãƒ¼ãƒˆ"
msgid "Accept invitation"
-msgstr ""
+msgstr "招待をå—ã‘入れる"
msgid "Accept terms"
msgstr "利用è¦ç´„ã«åŒæ„ã™ã‚‹"
@@ -417,6 +459,9 @@ msgid "Add Kubernetes cluster"
msgstr "Kubernetes クラスターを追加"
msgid "Add README"
+msgstr "README を追加"
+
+msgid "Add a bullet list"
msgstr ""
msgid "Add a general comment to this %{noteable_name}."
@@ -425,18 +470,42 @@ msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "ã‚ãªãŸã® Wiki ã«ãƒ—ロジェクトã«é–¢ã™ã‚‹æƒ…報をå«ã‚€ãƒ›ãƒ¼ãƒ ãƒšãƒ¼ã‚¸ã‚’追加ã™ã‚‹ã¨ã€GitLab ã¯ã“ã®ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã®ä»£ã‚ã‚Šã«ãれをã“ã“ã«è¡¨ç¤ºã—ã¾ã™ã€‚"
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "テーブルを追加ã™ã‚‹"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "ã™ã¹ã¦ã®ãƒ¡ãƒ¼ãƒ«ã«è¡¨ç¤ºã™ã‚‹ãƒ†ã‚­ã‚¹ãƒˆã‚’追加ã—ã¾ã™ã€‚ ãŸã ã—ã€%{character_limit} 文字ã®åˆ¶é™ãŒã‚ã‚Šã¾ã™ã€‚"
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "ç”»åƒã‚³ãƒ¡ãƒ³ãƒˆã‚’追加"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "ライセンスを追加"
@@ -453,7 +522,7 @@ msgid "Add reaction"
msgstr "リアクションã®è¿½åŠ "
msgid "Add to project"
-msgstr ""
+msgstr "プロジェクトã«è¿½åŠ "
msgid "Add to review"
msgstr "レビュー追加"
@@ -516,40 +585,40 @@ msgid "AdminProjects|Delete project"
msgstr "プロジェクトã®å‰Šé™¤"
msgid "AdminSettings|Auto DevOps domain"
-msgstr ""
+msgstr "Auto DevOps ドメイン"
msgid "AdminSettings|Environment variables are protected by default"
-msgstr ""
+msgstr "環境変数ã¯ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã§ä¿è­·ã•ã‚Œã¦ã„ã¾ã™"
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクトã®Auto Review AppsãŠã‚ˆã³Auto Deployステージã§ä½¿ç”¨ã™ã‚‹ã€ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®ãƒ‰ãƒ¡ã‚¤ãƒ³ã‚’指定ã—ã¾ã™ã€‚"
msgid "AdminSettings|When creating a new environment variable it will be protected by default."
-msgstr ""
+msgstr "環境変数ã®æ–°è¦ä½œæˆæ™‚ã«ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã§ä¿è­·ã•ã‚Œã¾ã™"
msgid "AdminUsers|2FA Disabled"
-msgstr ""
+msgstr "2FA 無効"
msgid "AdminUsers|2FA Enabled"
-msgstr ""
+msgstr "2FA 有効"
msgid "AdminUsers|Active"
-msgstr ""
+msgstr "アクティブ"
msgid "AdminUsers|Admin"
-msgstr ""
+msgstr "管ç†è€…"
msgid "AdminUsers|Admins"
-msgstr ""
+msgstr "管ç†è€…"
msgid "AdminUsers|Block user"
msgstr "ブロックユーザー"
msgid "AdminUsers|Blocked"
-msgstr ""
+msgstr "ブロック済ã¿"
msgid "AdminUsers|Cannot unblock LDAP blocked users"
-msgstr ""
+msgstr "LDAP ã§ãƒ–ロックã•ã‚ŒãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’ブロック解除ã§ãã¾ã›ã‚“"
msgid "AdminUsers|Delete User %{username} and contributions?"
msgstr "ユーザー %{username} ã¨è²¢çŒ®åº¦ã‚’削除ã—ã¾ã™ã‹?"
@@ -564,28 +633,28 @@ msgid "AdminUsers|Delete user and contributions"
msgstr "ユーザーã¨è²¢çŒ®åº¦ã®å‰Šé™¤"
msgid "AdminUsers|External"
-msgstr ""
+msgstr "外部"
msgid "AdminUsers|It's you!"
-msgstr ""
+msgstr "ã‚ãªãŸã§ã™ï¼"
msgid "AdminUsers|New user"
-msgstr ""
+msgstr "æ–°ã—ã„ユーザー"
msgid "AdminUsers|No users found"
-msgstr ""
+msgstr "ユーザーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
msgid "AdminUsers|Search by name, email or username"
-msgstr ""
+msgstr "åå‰ã€ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼åã§æ¤œç´¢"
msgid "AdminUsers|Search users"
-msgstr ""
+msgstr "ユーザーを検索"
msgid "AdminUsers|Send email to users"
-msgstr ""
+msgstr "ユーザーã«Eメールをé€ä¿¡ã™ã‚‹"
msgid "AdminUsers|Sort by"
-msgstr ""
+msgstr "並ã³æ›¿ãˆ"
msgid "AdminUsers|To confirm, type %{projectName}"
msgstr "確èªã®ãŸã‚ã€%{projectName} を入力ã—ã¦ãã ã•ã„"
@@ -594,10 +663,10 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr "確èªã®ãŸã‚ã€%{username} を入力ã—ã¦ãã ã•ã„"
msgid "AdminUsers|User will be blocked"
-msgstr ""
+msgstr "ユーザーã¯ãƒ–ロックã•ã‚Œã¾ã™"
msgid "AdminUsers|Without projects"
-msgstr ""
+msgstr "プロジェクトãªã—"
msgid "Advanced permissions, Large File Storage and Two-Factor authentication settings."
msgstr ""
@@ -605,16 +674,22 @@ msgstr ""
msgid "Advanced settings"
msgstr "高度ãªè¨­å®š"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "アラート"
msgid "Alerts"
-msgstr ""
+msgstr "アラート"
msgid "All"
msgstr "ã™ã¹ã¦"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr "ã™ã¹ã¦ã®å¤‰æ›´ãŒã‚³ãƒŸãƒƒãƒˆã•ã‚Œã¦ã„ã¾ã™"
@@ -622,7 +697,7 @@ msgid "All features are enabled for blank projects, from templates, or when impo
msgstr "空ã®ãƒ—ロジェクトã€ãƒ†ãƒ³ãƒ—レートã‹ã‚‰ã€ã¾ãŸã¯ã‚¤ãƒ³ãƒãƒ¼ãƒˆæ™‚ã«ã™ã¹ã¦ã®æ©Ÿèƒ½ãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã¾ã™ãŒã€å¾Œã§ãƒ—ロジェクト設定ã§ç„¡åŠ¹ã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "All issues for this milestone are closed. You may close this milestone now."
-msgstr ""
+msgstr "ã“ã®ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã«é–¢ã™ã‚‹èª²é¡Œã¯ã™ã¹ã¦è§£æ±ºã•ã‚Œã¾ã—ãŸã€‚ã“ã®ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã‚’クローズã§ãã¾ã™ã€‚"
msgid "All users"
msgstr "ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼"
@@ -649,7 +724,7 @@ msgid "Allow users to request access"
msgstr "ユーザーãŒã‚¢ã‚¯ã‚»ã‚¹ã‚’è¦æ±‚ã§ãるよã†ã«ã™ã‚‹"
msgid "Allow users to request access if visibility is public or internal."
-msgstr ""
+msgstr "å¯è¦–性ãŒå…¬é–‹ã¾ãŸã¯å†…部ã®å ´åˆã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒã‚¢ã‚¯ã‚»ã‚¹ã‚’è¦æ±‚ã§ãるよã†ã«ã—ã¾ã™ã€‚"
msgid "Allowed to fail"
msgstr ""
@@ -684,7 +759,7 @@ msgstr "GitLab ユーザフィールドãŒç©ºã®å ´åˆã€ã™ã¹ã¦ã®å•é¡Œã¨ã‚
msgid "An error has occurred"
msgstr "エラーãŒç™ºç”Ÿã—ã¾ã—ãŸ"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -694,6 +769,15 @@ msgid "An error occurred adding a new draft."
msgstr "æ–°ã—ã„ドラフトã®è¿½åŠ ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred creating the new branch."
+msgstr "æ–°ã—ã„ブランãƒã®ä½œæˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
msgstr ""
msgid "An error occurred previewing the blob"
@@ -708,6 +792,9 @@ msgstr "課題ã®ã‚¦ã‚¨ã‚¤ãƒˆæ›´æ–°æ™‚ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while adding approver"
msgstr "承èªè€…ã®è¿½åŠ ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "コメントã®å‰Šé™¤ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -744,6 +831,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr "パイプラインã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "プロジェクトã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -778,10 +868,10 @@ msgid "An error occurred while removing approver"
msgstr "承èªè€…ã®å‰Šé™¤ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred while removing epics."
-msgstr ""
+msgstr "エピックã®å‰Šé™¤ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while removing issues."
-msgstr ""
+msgstr "課題ã®å‰Šé™¤ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while rendering KaTeX"
msgstr "KaTeXã®ãƒ¬ãƒ³ãƒ€ãƒªãƒ³ã‚°ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
@@ -801,12 +891,18 @@ msgstr "LDAP ã®ã‚ªãƒ¼ãƒãƒ¼ãƒ©ã‚¤ãƒ‰çŠ¶æ…‹ã‚’ä¿å­˜ã™ã‚‹ã¨ãã«ã‚¨ãƒ©ãƒ¼ãŒç
msgid "An error occurred while saving assignees"
msgstr "担当者ã®ç™»éŒ²ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr "通知ã®è³¼èª­ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred while unsubscribing to notifications."
msgstr "通知ã®è³¼èª­ã‚’解除中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "コメントを更新中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -814,19 +910,19 @@ msgid "An error occurred while validating username"
msgstr "ユーザåã®æ¤œè¨¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ"
msgid "An error occurred whilst committing your changes."
-msgstr ""
+msgstr "変更ã®ã‚³ãƒŸãƒƒãƒˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred whilst fetching the job trace."
msgstr ""
msgid "An error occurred whilst fetching the latest pipeline."
-msgstr ""
+msgstr "最新ã®ãƒ‘イプラインã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred whilst loading all the files."
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã®èª­è¾¼ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred whilst loading the file content."
-msgstr ""
+msgstr "ファイルã®å†…容を読込中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An error occurred whilst loading the file."
msgstr ""
@@ -853,13 +949,13 @@ msgid "An unexpected error occurred while checking the project runners."
msgstr ""
msgid "An unexpected error occurred while communicating with the Web Terminal."
-msgstr ""
+msgstr "Web ターミナルã¨ã®é€šä¿¡ä¸­ã«äºˆæœŸã—ãªã„エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An unexpected error occurred while starting the Web Terminal."
-msgstr ""
+msgstr "Web ターミナルã®èµ·å‹•ä¸­ã«äºˆæœŸã—ãªã„エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "An unexpected error occurred while stopping the Web Terminal."
-msgstr ""
+msgstr "Web ターミナルã®åœæ­¢ä¸­ã«äºˆæœŸã—ãªã„エラーãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Analytics"
msgstr "アクセス解æž"
@@ -892,20 +988,54 @@ msgid "Applications"
msgstr "アプリケーション"
msgid "Applied"
-msgstr ""
+msgstr "é©ç”¨æ¸ˆã¿"
msgid "Apply suggestion"
msgstr ""
-msgid "Approvals"
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
msgstr ""
-msgid "Approvals required"
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
msgstr ""
-msgid "Approvers"
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
msgstr ""
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
+msgid "Approvals"
+msgstr "承èª"
+
+msgid "Approvals required"
+msgstr "承èªãŒå¿…è¦"
+
+msgid "Approvers"
+msgstr "承èªè€…"
+
msgid "Apr"
msgstr "4月"
@@ -919,7 +1049,7 @@ msgid "Archived projects"
msgstr "アーカイブã•ã‚ŒãŸãƒ—ロジェクト"
msgid "Are you sure"
-msgstr ""
+msgstr "よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "ã“ã®ãƒ‘イプラインスケジュールを削除ã—ã¾ã™ã‹ï¼Ÿ"
@@ -934,14 +1064,20 @@ msgid "Are you sure you want to lose your issue information?"
msgstr ""
msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
-msgstr ""
+msgstr "公開éµã‚’å†ç”Ÿæˆã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹? ミラーリングを行ã†å‰ã«ãƒªãƒ¢ãƒ¼ãƒˆã‚µãƒ¼ãƒãƒ¼ã®å…¬é–‹éµã‚’æ›´æ–°ã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“。"
msgid "Are you sure you want to remove %{group_name}?"
msgstr "ã“ã® %{group_name} を削除ã—ã¦ã‚ˆã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -985,7 +1121,7 @@ msgid "Assertion consumer service URL"
msgstr "アサーション コンシューマー サービス URL"
msgid "Assets"
-msgstr ""
+msgstr "アセット"
msgid "Assign custom color like #FF0000"
msgstr "#FF0000ã®ã‚ˆã†ãªã‚«ã‚¹ã‚¿ãƒ ã‚«ãƒ©ãƒ¼ã‚’割り当ã¦ã‚‹"
@@ -1026,9 +1162,12 @@ msgstr "担当者一覧ã«ã¯ã€é¸æŠžã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚Œã¦
msgid "Assignee(s)"
msgstr "担当者"
-msgid "Attach a file"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
msgstr ""
+msgid "Attach a file"
+msgstr "ファイルを添付"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "ドラッグ&ドロップã¾ãŸã¯ %{upload_link} ã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’添付"
@@ -1041,9 +1180,6 @@ msgstr "8月"
msgid "August"
msgstr "8月"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "èªè¨¼ãƒ­ã‚°"
@@ -1060,7 +1196,7 @@ msgid "Authorization code:"
msgstr "èªè¨¼ã‚³ãƒ¼ãƒ‰:"
msgid "Authorization key"
-msgstr ""
+msgstr "èªè¨¼ã‚­ãƒ¼"
msgid "Authorization was granted by entering your username and password in the application."
msgstr "ã“ã®ã‚¢ãƒ—リケーションã«ã‚ãªãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¨ãƒ‘スワードãŒå…¥åŠ›ã•ã‚ŒãŸã®ã§ã€æ‰¿èªãŒè¨±å¯ã•ã‚Œã¾ã—ãŸã€‚"
@@ -1327,10 +1463,10 @@ msgid "Bitbucket import"
msgstr "Bitbucket インãƒãƒ¼ãƒˆ"
msgid "Block"
-msgstr ""
+msgstr "ブロック"
msgid "Blocked"
-msgstr ""
+msgstr "ブロック中"
msgid "Blog"
msgstr "ブログ"
@@ -1495,10 +1631,10 @@ msgid "Browse files"
msgstr "ファイルを表示"
msgid "Built-in"
-msgstr ""
+msgstr "ビルトイン"
msgid "Business"
-msgstr ""
+msgstr "ビジãƒã‚¹"
msgid "Business metrics (Custom)"
msgstr "ビジãƒã‚¹ãƒ¡ãƒˆãƒªã‚¯ã‚¹ï¼ˆã‚«ã‚¹ã‚¿ãƒ ï¼‰"
@@ -1510,19 +1646,19 @@ msgid "ByAuthor|by"
msgstr "作者"
msgid "CHANGELOG"
-msgstr ""
+msgstr "変更履歴"
msgid "CI / CD"
msgstr "CI / CD"
msgid "CI / CD Charts"
-msgstr ""
+msgstr "CI / CD ãƒãƒ£ãƒ¼ãƒˆ"
msgid "CI / CD Settings"
msgstr "CI / CD 設定"
msgid "CI Lint"
-msgstr ""
+msgstr "CI Lint"
msgid "CI will run using the credentials assigned above."
msgstr "CI ã¯ä¸Šè¨˜ã§å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸè³‡æ ¼æƒ…報を元ã«å®Ÿè¡Œã—ã¾ã™ã€‚"
@@ -1552,7 +1688,7 @@ msgid "CICD|Continuous deployment to production"
msgstr "本番環境ã¸ã®ç¶™ç¶šçš„デプロイ"
msgid "CICD|Continuous deployment to production using timed incremental rollout"
-msgstr ""
+msgstr "タイム・インクリメンタル・ロールアウトを用ã„ãŸæœ¬ç•ªç’°å¢ƒã¸ã®ç¶™ç¶šçš„デプロイ"
msgid "CICD|Default to Auto DevOps pipeline"
msgstr "デフォルト㮠Auto DevOps パイプライン"
@@ -1602,11 +1738,14 @@ msgstr "自動的ã«ãƒžãƒ¼ã‚¸ã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "管ç†ä¸‹ã® Kubernetes クラスターを変更ã§ãã¾ã›ã‚“"
-msgid "Certificate"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
msgstr ""
+msgid "Certificate"
+msgstr "証明書"
+
msgid "Certificate (PEM)"
-msgstr ""
+msgstr "証明書 (PEM)"
msgid "Certificate fingerprint"
msgstr "証明書ã®ãƒ•ã‚£ãƒ³ã‚¬ãƒ¼ プリント"
@@ -1615,7 +1754,7 @@ msgid "Change Weight"
msgstr "ウェイトを変更ã™ã‚‹"
msgid "Change permissions"
-msgstr ""
+msgstr "アクセス権é™ã‚’変更"
msgid "Change template"
msgstr "テンプレートを変更"
@@ -1638,6 +1777,9 @@ msgstr "リãƒãƒ¼ãƒˆ"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "コミット済ã®å¤‰æ›´ã‚’ revert ã™ã‚‹ãŸã‚ã«æ–°ã—ã„コミットを作æˆã—ã¾ã™"
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "<b>source</b>リビジョンãŒ<b>target</b>リビジョン内ã«å–ã‚Šè¾¼ã¾ã‚Œã¦ã„るよã†ãªå¤‰æ›´ã¨ã—ã¦è¡¨ç¤ºã•ã‚Œã¾ã™"
@@ -1650,11 +1792,14 @@ msgstr "ãƒãƒ£ãƒ¼ãƒˆ"
msgid "Chat"
msgstr "ãƒãƒ£ãƒƒãƒˆ"
-msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
+msgid "Check again"
msgstr ""
+msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
+msgstr "%{docs_link_start}ドキュメント%{docs_link_end}を確èª"
+
msgid "Check your .gitlab-ci.yml"
-msgstr ""
+msgstr ".gitlab-ci.yml を確èªã—ã¦ãã ã•ã„"
msgid "Checking %{text} availability…"
msgstr "%{text} ãŒåˆ©ç”¨å¯èƒ½ã‹ç¢ºèªã—ã¦ã„ã¾ã™â€¦"
@@ -1684,7 +1829,7 @@ msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to s
msgstr "変更内容を確èªã—ãŸã‚Šãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’作æˆã™ã‚‹ãŸã‚ã«ã€Branch/tag (例: %{master}) ã‚’é¸æŠžã™ã‚‹ã‹ã€ã‚³ãƒŸãƒƒãƒˆID(例: %{sha})を入力ã—ã¦ãã ã•ã„。"
msgid "Choose a file"
-msgstr ""
+msgstr "ファイルをé¸æŠžã—ã¦ãã ã•ã„"
msgid "Choose a role permission"
msgstr ""
@@ -1693,7 +1838,7 @@ msgid "Choose a template..."
msgstr ""
msgid "Choose a type..."
-msgstr ""
+msgstr "タイプをé¸æŠžã—ã¦ãã ã•ã„..."
msgid "Choose any color."
msgstr "カラーをé¸æŠžã—ã¦ãã ã•ã„。"
@@ -1705,7 +1850,7 @@ msgid "Choose file..."
msgstr "ファイルをé¸æŠž..."
msgid "Choose the top-level group for your repository imports."
-msgstr ""
+msgstr "インãƒãƒ¼ãƒˆãƒªãƒã‚¸ãƒˆãƒªã®ãƒˆãƒƒãƒ—レベルã®ã‚°ãƒ«ãƒ¼ãƒ—ã‚’é¸æŠžã—ã¦ãã ã•ã„。"
msgid "Choose what content you want to see on a group’s overview page"
msgstr ""
@@ -1716,9 +1861,6 @@ msgstr "åŒæœŸã•ã›ãŸã„セカンダリノードã®ã‚°ãƒ«ãƒ¼ãƒ—ã‚’é¸æŠžã—ã¦
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr "CI/CD パイプラインを実行ã—ãŸã„リãƒã‚¸ãƒˆãƒªã‚’é¸æŠžã—ã¦ãã ã•ã„。"
-msgid "Choose which repositories you want to import."
-msgstr "インãƒãƒ¼ãƒˆã—ãŸã„リãƒã‚¸ãƒˆãƒªã‚’é¸æŠžã—ã¦ãã ã•ã„。"
-
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr "セカンダリノードã¨åŒæœŸã•ã›ãŸã„シャードをé¸æŠžã—ã¦ãã ã•ã„。"
@@ -1825,10 +1967,10 @@ msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "使用ã§ãã¾ã›ã‚“:%{reason}"
msgid "Clear"
-msgstr ""
+msgstr "クリア"
msgid "Clear input"
-msgstr ""
+msgstr "入力をクリア"
msgid "Clear search"
msgstr "検索をクリア"
@@ -1870,16 +2012,19 @@ msgid "Clients"
msgstr "クライアント"
msgid "Clone"
-msgstr ""
+msgstr "クローン"
msgid "Clone repository"
msgstr "リãƒã‚¸ãƒˆãƒªã‚’クローン"
msgid "Clone with %{http_label}"
+msgstr "%{http_label} ã§ã‚¯ãƒ­ãƒ¼ãƒ³"
+
+msgid "Clone with KRB5"
msgstr ""
msgid "Clone with SSH"
-msgstr ""
+msgstr "SSH ã§ã‚¯ãƒ­ãƒ¼ãƒ³"
msgid "Close"
msgstr "クローズã™ã‚‹"
@@ -1893,9 +2038,6 @@ msgstr ""
msgid "Closed"
msgstr "クローズ"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "クローズã—ãŸèª²é¡Œ"
@@ -1936,17 +2078,17 @@ msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integrat
msgstr "ã“ã® Kubernetes クラスター統åˆã«é–¢ã™ã‚‹è©³ç´°ã‚ªãƒ—ション"
msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
-msgstr ""
+msgstr "Ingress をインストールã—ãŸå¾Œã€ã‚¢ãƒ—リケーションをデプロイã—ãŸå¾Œã«è¡¨ç¤ºã™ã‚‹ãŸã‚ã«ã€ç”Ÿæˆã•ã‚ŒãŸå¤–部 IP アドレス ã«ãƒ¯ã‚¤ãƒ«ãƒ‰ã‚«ãƒ¼ãƒ‰ã§ DNS を指定ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚ %{ingressHelpLink}"
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "プロジェクトゾーンã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸ: %{error}"
-
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "Google Cloud API ã¸æŽ¥ç¶šã‚’試ã¿ãŸéš›ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚後ã§ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ãã ã•ã„。"
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr ""
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
msgstr ""
@@ -1983,6 +2125,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2014,7 +2159,7 @@ msgid "ClusterIntegration|Did you know?"
msgstr "ã”存知ã§ã™ã‹ï¼Ÿ"
msgid "ClusterIntegration|Enable or disable GitLab's connection to your Kubernetes cluster."
-msgstr ""
+msgstr "Kubernetes クラスターã¸ã® GitLab 接続を有効ã¾ãŸã¯ç„¡åŠ¹ã«ã—ã¾ã™ã€‚"
msgid "ClusterIntegration|Enable this setting if using role-based access control (RBAC)."
msgstr ""
@@ -2079,8 +2224,8 @@ 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 を設置ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
+msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
@@ -2094,9 +2239,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr "インストール"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr "Prometheus をインストール"
-
msgid "ClusterIntegration|Installed"
msgstr "インストール済ã¿"
@@ -2142,9 +2284,6 @@ msgstr "Kubernetes クラスター"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Kubernetes クラスターã®è©³ç´°"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Kubernetes クラスターã®ç¨¼åƒçŠ¶æ…‹"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes クラスターを Google Kubernetes Engine 上ã«ä½œæˆã—ã¦ã„ã¾ã™..."
@@ -2215,7 +2354,7 @@ msgid "ClusterIntegration|Please make sure that your Google account meets the fo
msgstr "Google アカウントãŒæ¬¡ã®è¦ä»¶ã‚’満ãŸã—ã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
-msgstr ""
+msgstr "デプロイ完了後ã«ã‚ãªãŸã®ã‚¢ãƒ—リケーションã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ç‚ºã«ã€ç”Ÿæˆã•ã‚ŒãŸ IP アドレスをワイルドカード DNS ã«æŒ‡å®šã—ã¾ã™ã€‚"
msgid "ClusterIntegration|Project cluster"
msgstr "プロジェクトクラスター"
@@ -2311,13 +2450,13 @@ msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review
msgstr ""
msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
-msgstr ""
+msgstr "ã“ã® I Pアドレスã¯å‰²ã‚Šå½“ã¦ãƒ—ロセス実施中ã§ã™ã€‚ã‚‚ã—処ç†æ™‚é–“ãŒé•·ã„å ´åˆã€ã‚ãªãŸã® Kubernetes クラスターã¾ãŸã¯ Google Kubernetes Engine ã®ã‚¯ã‚©ãƒ¼ã‚¿ã‚’確èªã—ã¦ãã ã•ã„。"
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ %{link_to_container_project} 㧠Kubernetes クラスターを作æˆã™ã‚‹ã®ã«ä»¥ä¸‹ã®æ¨©é™ãŒå¿…è¦ã§ã™"
msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
-msgstr ""
+msgstr "ã“ã®ã‚ªãƒ—ションを使用ã™ã‚‹ã¨ã€ã‚ãªãŸã¯ã‚¢ãƒ—リケーションを RBAC クラスター上ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã§ãã¾ã™ã€‚"
msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr "Kubernetes クラスターを切り替ãˆ"
@@ -2341,13 +2480,13 @@ msgid "ClusterIntegration|Validating project billing status"
msgstr "プロジェクトã®è«‹æ±‚ステータスを検証ã—ã¦ã„ã¾ã™"
msgid "ClusterIntegration|We could not verify that one of your projects on GCP has billing enabled. Please try again."
-msgstr ""
+msgstr "GCP 上ã®ãƒ—ロジェクトã®è¦æ±‚ãŒæœ‰åŠ¹ã«ãªã£ã¦ã„ã‚‹ã“ã¨ã‚’確èªã§ãã¾ã›ã‚“ã§ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
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 ã®ä½¿ç”¨ã€ã‚¢ãƒ—リケーションã®ãƒ‡ãƒ—ロイã€ãƒ‘イプラインã®å®Ÿè¡Œãªã©ã‚’ç°¡å˜ã«è¡Œã†ã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "ClusterIntegration|You must first install Helm Tiller before installing the applications below"
-msgstr ""
+msgstr "次ã®ã‚¢ãƒ—リケーションをインストールã™ã‚‹å‰ã«ã€Helm Tiller をインストールã—ãªã‘ã‚Œã°ãªã‚Šã¾ã›ã‚“"
msgid "ClusterIntegration|You must have an RBAC-enabled cluster to install Knative."
msgstr ""
@@ -2380,17 +2519,29 @@ msgid "ClusterIntegration|sign up"
msgstr "æ–°è¦ç™»éŒ²"
msgid "Code"
+msgstr "コード"
+
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
msgstr ""
msgid "Code owners"
msgstr "コードオーナー"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr "折りãŸãŸã‚€"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "サイドãƒãƒ¼ã‚’éš ã™"
@@ -2423,13 +2574,13 @@ msgid_plural "Commits"
msgstr[0] "コミット"
msgid "Commit %{commit_id}"
-msgstr ""
+msgstr "コミット %{commit_id}"
msgid "Commit Message"
msgstr "コミットメッセージ"
msgid "Commit deleted"
-msgstr ""
+msgstr "コミットを削除ã—ã¾ã—ãŸ"
msgid "Commit duration in minutes for last 30 commits"
msgstr "ç›´è¿‘30コミットã®æ‰€è¦æ™‚é–“(分)"
@@ -2495,7 +2646,7 @@ msgid "Compare Revisions"
msgstr "リビジョンを比較"
msgid "Compare changes"
-msgstr ""
+msgstr "変更を比較"
msgid "Compare changes with the last commit"
msgstr "最後ã®ã‚³ãƒŸãƒƒãƒˆã¨å¤‰æ›´ã‚’比較"
@@ -2546,10 +2697,10 @@ msgid "Configure push mirrors."
msgstr "プッシュミラーを構æˆã—ã¾ã™ã€‚"
msgid "Configure storage path settings."
-msgstr ""
+msgstr "ストレージパス設定を構æˆã—ã¾ã™ã€‚"
msgid "Configure the %{link} integration."
-msgstr ""
+msgstr "%{link} ã®çµ±åˆã‚’設定ã—ã¾ã™ 。"
msgid "Configure the way a user creates a new account."
msgstr "ユーザーãŒæ–°ã—ã„アカウントを作æˆã™ã‚‹æ–¹æ³•ã‚’設定ã—ã¾ã™ã€‚"
@@ -2642,7 +2793,7 @@ msgid "Contribution Charts"
msgstr ""
msgid "Contributions for <strong>%{calendar_date}</strong>"
-msgstr ""
+msgstr "<strong>%{calendar_date}</strong>ã®è²¢çŒ®"
msgid "Contributions per group member"
msgstr "グループメンãƒãƒ¼ã®è²¢çŒ®åº¦"
@@ -2663,7 +2814,7 @@ msgid "ContributorsPage|Please wait a moment, this page will automatically refre
msgstr "ã—ã°ã‚‰ããŠå¾…ã¡ãã ã•ã„ã€ã“ã®ãƒšãƒ¼ã‚¸ã¯æº–å‚™ãŒæ•´ã†ã¨è‡ªå‹•çš„ã«æ›´æ–°ã•ã‚Œã¾ã™ã€‚"
msgid "Control the display of third party offers."
-msgstr ""
+msgstr "サードパーティã®ã‚ªãƒ•ã‚¡ãƒ¼ã®è¡¨ç¤ºã‚’管ç†ã—ã¾ã™ã€‚"
msgid "Control the maximum concurrency of LFS/attachment backfill for this secondary node"
msgstr "ã“ã®ã‚»ã‚«ãƒ³ãƒ€ãƒªãƒŽãƒ¼ãƒ‰ã® LFS / attachment ãƒãƒƒã‚¯ãƒ•ã‚£ãƒ«ã®æœ€å¤§ä¸¦è¡Œæ€§ã‚’制御ã™ã‚‹"
@@ -2681,7 +2832,7 @@ msgid "ConvDev Index"
msgstr "ConvDev インデックス"
msgid "Copy %{http_label} clone URL"
-msgstr ""
+msgstr "%{http_label} クローン URL をコピー"
msgid "Copy %{protocol} clone URL"
msgstr "%{protocol} クローン URL をコピー"
@@ -2689,11 +2840,14 @@ msgstr "%{protocol} クローン URL をコピー"
msgid "Copy ID to clipboard"
msgstr "クリップボード㫠ID をコピー"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr "SSH 用クローン URL をコピー"
msgid "Copy SSH public key"
-msgstr ""
+msgstr "SSH 公開éµã‚’コピー"
msgid "Copy SSH public key to clipboard"
msgstr "SSH 公開éµã‚’クリップボードã«ã‚³ãƒ”ー"
@@ -2741,7 +2895,7 @@ msgid "Create New Directory"
msgstr "æ–°è¦ãƒ‡ã‚£ãƒ¬ã‚¯ãƒˆãƒªã‚’作æˆ"
msgid "Create New Domain"
-msgstr ""
+msgstr "æ–°ã—ã„ドメインを作æˆ"
msgid "Create a new branch"
msgstr "æ–°ã—ã„ブランãƒã‚’作æˆ"
@@ -2753,7 +2907,7 @@ msgid "Create a new issue"
msgstr "課題ã®æ–°è¦ä½œæˆ"
msgid "Create a new repository"
-msgstr ""
+msgstr "æ–°ã—ã„リãƒã‚¸ãƒˆãƒªã‚’作æˆ"
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "%{protocol} ã§ãƒ—ッシュやプルã™ã‚‹ãŸã‚ã®ã‚ãªãŸå€‹äººç”¨ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’作æˆ"
@@ -2795,7 +2949,7 @@ msgid "Create merge request and branch"
msgstr "マージリクエストã¨ãƒ–ランãƒã‚’作æˆ"
msgid "Create milestone"
-msgstr ""
+msgstr "マイルストーンを作æˆ"
msgid "Create new branch"
msgstr "æ–°ã—ã„ブランãƒã‚’作æˆ"
@@ -2831,7 +2985,7 @@ msgid "Created"
msgstr "作æˆæ¸ˆã¿"
msgid "Created At"
-msgstr ""
+msgstr "作æˆæ—¥"
msgid "Created by me"
msgstr "自分ãŒä½œæˆ"
@@ -2867,7 +3021,7 @@ msgid "Custom CI config path"
msgstr "カスタム CI config パス"
msgid "Custom hostname (for private commit emails)"
-msgstr ""
+msgstr "カスタムホストå (プライベートコミット用メールアドレス)"
msgid "Custom notification events"
msgstr "カスタム通知設定"
@@ -2927,7 +3081,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "テスト"
msgid "DNS"
-msgstr ""
+msgstr "DNS"
msgid "Dashboard"
msgstr "ダッシュボード"
@@ -2942,7 +3096,7 @@ msgid "Data is still calculating..."
msgstr ""
msgid "Date picker"
-msgstr ""
+msgstr "日付é¸æŠž"
msgid "Debug"
msgstr "デãƒãƒƒã‚°"
@@ -2987,10 +3141,10 @@ msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwis
msgstr ""
msgid "DelayedJobs|Are you sure you want to run %{job_name} immediately? This job will run automatically after it's timer finishes."
-msgstr ""
+msgstr "%{job_name} ã‚’ã™ãã«å®Ÿè¡Œã—ã¦ã‚‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿã“ã®ã‚¸ãƒ§ãƒ–ã¯ã‚¿ã‚¤ãƒžãƒ¼çµ‚了後自動的ã«å®Ÿè¡Œã•ã‚Œã¾ã™ã€‚"
msgid "DelayedJobs|Start now"
-msgstr ""
+msgstr "今ã™ã始ã‚ã‚‹"
msgid "DelayedJobs|Unschedule"
msgstr ""
@@ -3002,7 +3156,7 @@ msgid "Delete"
msgstr "削除"
msgid "Delete Package"
-msgstr ""
+msgstr "パッケージを削除"
msgid "Delete Snippet"
msgstr "スニペットを削除"
@@ -3014,10 +3168,10 @@ msgid "Delete list"
msgstr "リストを削除ã™ã‚‹"
msgid "Delete source branch"
-msgstr ""
+msgstr "ソースブランãƒã‚’削除"
msgid "Delete this attachment"
-msgstr ""
+msgstr "ã“ã®æ·»ä»˜ãƒ•ã‚¡ã‚¤ãƒ«ã‚’削除"
msgid "Deleted"
msgstr "削除完了"
@@ -3156,10 +3310,10 @@ msgid "Deployed"
msgstr ""
msgid "Deployed to"
-msgstr ""
+msgstr "デプロイ先"
msgid "Deploying to"
-msgstr ""
+msgstr "デプロイ先"
msgid "Deprioritize label"
msgstr "éžå„ªå…ˆãƒ©ãƒ™ãƒ«"
@@ -3176,6 +3330,9 @@ msgstr "Description テンプレートを使用ã™ã‚‹ã¨ã€ãƒ—ロジェクトã®
msgid "Description:"
msgstr "説明:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr "破棄"
@@ -3183,10 +3340,10 @@ msgid "Details"
msgstr "詳細"
msgid "Details (default)"
-msgstr ""
+msgstr "詳細 (デフォルト)"
msgid "Detect host keys"
-msgstr ""
+msgstr "ホストキーã®æ¤œå‡º"
msgid "Diff content limits"
msgstr ""
@@ -3219,16 +3376,16 @@ msgid "Disable shared Runners"
msgstr ""
msgid "Disabled"
-msgstr ""
+msgstr "無効"
msgid "Discard"
msgstr "破棄"
msgid "Discard all changes"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®å¤‰æ›´ã‚’破棄"
msgid "Discard all unstaged changes?"
-msgstr ""
+msgstr "ステージã•ã‚Œã¦ã„ãªã„ã™ã¹ã¦ã®å¤‰æ›´ã‚’破棄ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Discard changes"
msgstr "変更を破棄ã™ã‚‹"
@@ -3240,7 +3397,7 @@ msgid "Discard draft"
msgstr "下書ãを破棄"
msgid "Discard review"
-msgstr ""
+msgstr "レビューを破棄"
msgid "Discover GitLab Geo"
msgstr ""
@@ -3254,6 +3411,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr "解除"
@@ -3264,7 +3424,7 @@ msgid "Dismiss Cycle Analytics introduction box"
msgstr "サイクル分æžã®ç´¹ä»‹ã‚’é–‰ã˜ã‚‹"
msgid "Dismiss Merge Request promotion"
-msgstr ""
+msgstr "マージリクエストã®æ˜‡æ ¼ã‚’å´ä¸‹ã—ã¾ã™"
msgid "Dismiss trial promotion"
msgstr ""
@@ -3291,7 +3451,7 @@ msgid "Download artifacts"
msgstr ""
msgid "Download asset"
-msgstr ""
+msgstr "アセットをダウンロード"
msgid "Download tar"
msgstr "tarå½¢å¼ã§ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰"
@@ -3318,7 +3478,7 @@ msgid "DownloadSource|Download"
msgstr "ダウンロード"
msgid "Downstream"
-msgstr ""
+msgstr "ダウンストリーム"
msgid "Downvotes"
msgstr "イマイãƒ"
@@ -3336,13 +3496,13 @@ msgid "Edit"
msgstr "編集"
msgid "Edit %{name}"
-msgstr ""
+msgstr "%{name} を編集"
msgid "Edit Label"
msgstr "ラベルã®ç·¨é›†"
msgid "Edit Milestone"
-msgstr ""
+msgstr "マイルストーンを編集"
msgid "Edit Pipeline Schedule %{id}"
msgstr "パイプラインスケジュール %{id} を編集"
@@ -3354,10 +3514,10 @@ msgid "Edit application"
msgstr "アプリケーションã®ç·¨é›†"
msgid "Edit comment"
-msgstr ""
+msgstr "コメントを編集"
msgid "Edit environment"
-msgstr ""
+msgstr "環境を編集"
msgid "Edit files in the editor and commit changes here"
msgstr "エディターã§ãƒ•ã‚¡ã‚¤ãƒ«ã‚’編集ã—ã€ã“ã“ã§å¤‰æ›´ã‚’コミットã—ã¾ã™"
@@ -3369,7 +3529,7 @@ msgid "Edit identity for %{user_name}"
msgstr "%{user_name} ã® ID を編集ã™ã‚‹"
msgid "Edit issues"
-msgstr ""
+msgstr "課題を編集"
msgid "Elasticsearch"
msgstr "Elasticsearch"
@@ -3390,7 +3550,7 @@ msgid "Embed"
msgstr "埋ã‚è¾¼ã¿"
msgid "Empty file"
-msgstr ""
+msgstr "空ã®ãƒ•ã‚¡ã‚¤ãƒ«"
msgid "Enable"
msgstr "有効化ã™ã‚‹"
@@ -3417,7 +3577,7 @@ msgid "Enable classification control using an external service"
msgstr "外部サービスを使用ã—ã¦ã€åˆ†é¡žåˆ¶å¾¡ã‚’有効ã«ã™ã‚‹ã€‚"
msgid "Enable error tracking"
-msgstr ""
+msgstr "エラートラッキングを有効ã«ã™ã‚‹"
msgid "Enable for this project"
msgstr "ã“ã®ãƒ—ロジェクトã§ã¯æœ‰åŠ¹ã«ã™ã‚‹"
@@ -3425,6 +3585,9 @@ msgstr "ã“ã®ãƒ—ロジェクトã§ã¯æœ‰åŠ¹ã«ã™ã‚‹"
msgid "Enable group Runners"
msgstr "グループ Runner を有効ã«ã™ã‚‹"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3450,7 +3613,7 @@ msgid "Enable usage ping"
msgstr "利用状æ³ã®é€ä¿¡ã‚’有効ã«ã™ã‚‹"
msgid "Enable usage ping to get an overview of how you are using GitLab from a feature perspective."
-msgstr ""
+msgstr "Ping ã®ä½¿ç”¨ã‚’有効ã«ã™ã‚‹ã¨ã€æ©Ÿèƒ½ãƒ‘ースペクティブã‹ã‚‰ GitLab ã‚’ã©ã®ã‚ˆã†ã«ä½¿ç”¨ã—ã¦ã„ã‚‹ã‹ã‚’知るã“ã¨ãŒã§ãã¾ã™ã€‚"
msgid "Enabled"
msgstr "有効"
@@ -3461,6 +3624,9 @@ msgstr "終了時刻 (UTC)"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3471,19 +3637,16 @@ msgid "Enter the issue description"
msgstr "課題ã®èª¬æ˜Žã‚’入力ã—ã¦ãã ã•ã„"
msgid "Enter the issue title"
-msgstr ""
+msgstr "課題ã®ã‚¿ã‚¤ãƒˆãƒ«ã‚’入力ã—ã¦ãã ã•ã„"
msgid "Enter the merge request description"
-msgstr ""
+msgstr "マージリクエストã®èª¬æ˜Žã‚’入力ã—ã¦ãã ã•ã„"
msgid "Enter the merge request title"
-msgstr ""
-
-msgid "Enter your Sentry API URL"
-msgstr ""
+msgstr "マージリクエストã®ã‚¿ã‚¤ãƒˆãƒ«ã‚’入力ã—ã¦ãã ã•ã„"
msgid "Environment variables"
-msgstr ""
+msgstr "環境変数"
msgid "Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use environment variables for passwords, secret keys, or whatever you want."
msgstr ""
@@ -3492,7 +3655,7 @@ msgid "Environment variables are configured by your administrator to be %{link_s
msgstr ""
msgid "Environment:"
-msgstr ""
+msgstr "環境:"
msgid "Environments"
msgstr "環境"
@@ -3506,17 +3669,23 @@ msgstr "環境をå–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Environments|An error occurred while making the request."
msgstr "リクエスト作æˆä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
-msgid "Environments|An error occurred while stopping the environment, please try again"
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
msgstr ""
-msgid "Environments|Are you sure you want to stop this environment?"
+msgid "Environments|An error occurred while rolling back the environment, please try again"
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 ""
+msgstr "デプロイã—ã¾ã™"
msgid "Environments|Deployment"
msgstr "デプロイ"
@@ -3534,7 +3703,7 @@ msgid "Environments|Job"
msgstr "ジョブ"
msgid "Environments|Learn more about stopping environments"
-msgstr ""
+msgstr "環境ã®åœæ­¢ã«ã¤ã„ã¦"
msgid "Environments|New environment"
msgstr "æ–°ã—ã„環境"
@@ -3552,18 +3721,36 @@ msgid "Environments|Note that this action will stop the environment, but it will
msgstr ""
msgid "Environments|Open live environment"
-msgstr ""
+msgstr "ライブ環境を開ã"
msgid "Environments|Pod logs from"
msgstr " 㮠pod ログ"
-msgid "Environments|Re-deploy to environment"
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
msgstr ""
msgid "Environments|Show all"
@@ -3576,6 +3763,18 @@ msgid "Environments|Stop environment"
msgstr "環境ã®åœæ­¢"
msgid "Environments|Stopping"
+msgstr "åœæ­¢ä¸­"
+
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
msgstr ""
msgid "Environments|Updated"
@@ -3600,10 +3799,10 @@ msgid "Epics let you manage your portfolio of projects more efficiently and with
msgstr "エピックを使用ã™ã‚‹ã¨ã€ãƒ—ロジェクトã®ãƒãƒ¼ãƒˆãƒ•ã‚©ãƒªã‚ªã‚’より効率的ã‹ã¤å°‘ãªã„労力ã§ç®¡ç†ã§ãã¾ã™"
msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
+msgstr "%{epicDateType} ã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Epics|How can I solve this?"
-msgstr ""
+msgstr "ã©ã†ã™ã‚Œã°è§£æ±ºã§ãã¾ã™ã‹ï¼Ÿ"
msgid "Epics|More information"
msgstr "詳ã—ã„情報"
@@ -3615,7 +3814,7 @@ msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, a
msgstr ""
msgid "Epics|due"
-msgstr ""
+msgstr "期日"
msgid "Epics|start"
msgstr "開始"
@@ -3627,6 +3826,9 @@ msgid "Error Reporting and Logging"
msgstr "エラー報告ã¨ãƒ­ã‚°"
msgid "Error Tracking"
+msgstr "エラートラッキング"
+
+msgid "Error creating a new path"
msgstr ""
msgid "Error creating epic"
@@ -3693,14 +3895,41 @@ msgid "Error updating todo status."
msgstr "TODO ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’更新中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Error while loading the merge request. Please try again."
-msgstr ""
+msgstr "マージリクエストã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "Error:"
+msgstr "エラー:"
+
+msgid "ErrorTracking|Active"
msgstr ""
-msgid "Errors"
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
msgstr ""
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
+msgid "Errors"
+msgstr "エラー"
+
msgid "Estimated"
msgstr "見ç©"
@@ -3723,7 +3952,7 @@ msgid "EventFilterBy|Filter by team"
msgstr "ãƒãƒ¼ãƒ ã§ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼"
msgid "Events"
-msgstr ""
+msgstr "イベント"
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
msgstr ""
@@ -3738,7 +3967,7 @@ msgid "Every week (Sundays at 4:00am)"
msgstr "毎週 (日曜日ã®åˆå‰4:00)"
msgid "Everyone"
-msgstr ""
+msgstr "全員"
msgid "Everyone can contribute"
msgstr ""
@@ -3759,16 +3988,16 @@ msgid "Everything you need to create a GitLab Pages site using plain HTML."
msgstr ""
msgid "Except policy:"
-msgstr ""
+msgstr "除外ãƒãƒªã‚·ãƒ¼:"
msgid "Existing Git repository"
-msgstr ""
+msgstr "既存㮠Git リãƒã‚¸ãƒˆãƒª"
msgid "Existing folder"
-msgstr ""
+msgstr "既存ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼"
msgid "Existing members and groups"
-msgstr ""
+msgstr "既存ã®ãƒ¡ãƒ³ãƒãƒ¼ã¨ã‚°ãƒ«ãƒ¼ãƒ—"
msgid "Expand"
msgstr "展開"
@@ -3776,6 +4005,9 @@ msgstr "展開"
msgid "Expand all"
msgstr "ã™ã¹ã¦å±•é–‹"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "サイドãƒãƒ¼ã‚’é–‹ã"
@@ -3783,10 +4015,10 @@ msgid "Expiration date"
msgstr "有効期é™"
msgid "Expired %{expiredOn}"
-msgstr ""
+msgstr "%{expiredOn} ã«æœŸé™åˆ‡ã‚Œ"
msgid "Expires in %{expires_at}"
-msgstr ""
+msgstr "%{expires_at} ã§æœŸé™åˆ‡ã‚Œ"
msgid "Explain the problem. If appropriate, provide a link to the relevant issue or comment."
msgstr ""
@@ -3810,19 +4042,19 @@ msgid "Explore public groups"
msgstr "公開グループを検索"
msgid "Export as CSV"
-msgstr ""
+msgstr "CSV å½¢å¼ã§ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ"
msgid "Export issues"
-msgstr ""
+msgstr "課題をエクスãƒãƒ¼ãƒˆ"
msgid "External Classification Policy Authorization"
msgstr "外部分類èªè¨¼ãƒãƒªã‚·ãƒ¼"
msgid "External URL"
-msgstr ""
+msgstr "外部 URL"
msgid "External Wiki"
-msgstr ""
+msgstr "外部 Wiki"
msgid "External authentication"
msgstr "外部èªè¨¼"
@@ -3863,14 +4095,14 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
msgstr "ボードã®èª²é¡Œã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
msgid "Failed to remove mirror."
-msgstr ""
+msgstr "ミラーã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸã€‚"
msgid "Failed to remove the pipeline schedule"
msgstr "パイプラインスケジュールを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ"
@@ -3897,7 +4129,7 @@ msgid "Faster as it re-uses the project workspace (falling back to clone if it d
msgstr ""
msgid "Feature Flags"
-msgstr ""
+msgstr "機能フラグ"
msgid "FeatureFlags|* (All Environments)"
msgstr ""
@@ -3909,7 +4141,7 @@ msgid "FeatureFlags|API URL"
msgstr "API URL"
msgid "FeatureFlags|Active"
-msgstr ""
+msgstr "アクティブ"
msgid "FeatureFlags|Configure"
msgstr ""
@@ -3929,9 +4161,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr "説明"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr "%{feature_flag_name} を編集"
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr "機能フラグを編集"
@@ -3989,9 +4218,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr "変更をä¿å­˜"
-
msgid "FeatureFlags|Status"
msgstr "ステータス"
@@ -4021,13 +4247,13 @@ msgstr "ã“ã®ãƒšãƒ¼ã‚¸ã®é …ç›®ã¯ç·¨é›†ã§ããªã„設定ã§ã™ã€‚次㮠Kuber
msgid "File"
msgid_plural "Files"
-msgstr[0] ""
+msgstr[0] "ファイル"
msgid "File added"
-msgstr ""
+msgstr "ファイルã®è¿½åŠ "
msgid "File browser"
-msgstr ""
+msgstr "ファイルブラウザー"
msgid "File deleted"
msgstr ""
@@ -4042,7 +4268,7 @@ msgid "File templates"
msgstr "ファイルテンプレート"
msgid "File upload error."
-msgstr ""
+msgstr "ファイルアップロードエラー"
msgid "Files"
msgstr "ファイル"
@@ -4077,9 +4303,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "パスã§æ¤œç´¢"
@@ -4090,19 +4313,19 @@ msgid "Find file"
msgstr "ファイルを検索"
msgid "Find the downloaded ZIP file and decompress it."
-msgstr ""
+msgstr "ダウンロードã—㟠ZIP ファイルを展開ã—ã¾ã™ã€‚"
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr ""
msgid "Fingerprints"
-msgstr ""
+msgstr "フィンガープリント"
msgid "Finish editing this message first!"
msgstr ""
msgid "Finish review"
-msgstr ""
+msgstr "レビューを完了ã™ã‚‹"
msgid "Finished"
msgstr "完了"
@@ -4126,7 +4349,7 @@ msgid "Fixed start date"
msgstr ""
msgid "Fixed:"
-msgstr ""
+msgstr "修正:"
msgid "FogBugz Email"
msgstr "FogBugz メール"
@@ -4183,7 +4406,7 @@ msgid "Forking in progress"
msgstr "フォーク中ã§ã™"
msgid "Forks"
-msgstr ""
+msgstr "フォーク"
msgid "Format"
msgstr "フォーマット"
@@ -4194,8 +4417,8 @@ msgstr ".gitlab-ci.yml ã«ã‚¨ãƒ©ãƒ¼ãŒè¦‹ã¤ã‹ã‚Šã¾ã—ãŸ:"
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
-msgstr "%{provider_title}ã‹ã‚‰"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "Bitbucket ã‹ã‚‰"
@@ -4224,9 +4447,15 @@ msgstr "マイルストーンã‹ã‚‰:"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Kubernetes クラスターã®è©³ç´°ç”»é¢ã‚’介ã—ã¦ã€ã‚¢ãƒ—リケーションリストã‹ã‚‰ Runner をインストールã—ã¾ã™ã€‚"
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "GPG キー"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "一般"
@@ -4237,7 +4466,7 @@ msgid "Generate a default set of labels"
msgstr "åˆæœŸè¨­å®šãƒ©ãƒ™ãƒ«ã‚»ãƒƒãƒˆã‚’生æˆã™ã‚‹"
msgid "Generate key"
-msgstr ""
+msgstr "éµã‚’生æˆ"
msgid "Geo"
msgstr "Geo"
@@ -4414,13 +4643,13 @@ msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection.
msgstr "安全ã§ãªã„ HTTP 接続を使用ã—㦠Geo ノードを設定ã—ã¾ã—ãŸã€‚HTTPS 通信ã®ä½¿ç”¨ã‚’推奨ã—ã¾ã™ã€‚"
msgid "Geo|%{name} is scheduled for forced re-download"
-msgstr ""
+msgstr "%{name} ã¯å¼·åˆ¶çš„ã«å†ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã•ã‚Œã‚‹äºˆå®šã§ã™"
msgid "Geo|%{name} is scheduled for re-check"
-msgstr ""
+msgstr "%{name} ã¯å†ãƒã‚§ãƒƒã‚¯ãŒäºˆå®šã•ã‚Œã¦ã„ã¾ã™"
msgid "Geo|%{name} is scheduled for re-sync"
-msgstr ""
+msgstr "%{name} ã¯å†åŒæœŸãŒäºˆå®šã•ã‚Œã¦ã„ã¾ã™"
msgid "Geo|All"
msgstr "ã™ã¹ã¦"
@@ -4429,16 +4658,16 @@ msgid "Geo|All projects"
msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクト"
msgid "Geo|All projects are being scheduled for re-check"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクトã®å†ãƒã‚§ãƒƒã‚¯ãŒäºˆå®šã•ã‚Œã¦ã„ã¾ã™"
msgid "Geo|All projects are being scheduled for re-sync"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクトã®å†åŒæœŸãŒäºˆå®šã•ã‚Œã¦ã„ã¾ã™"
msgid "Geo|Batch operations"
msgstr "ãƒãƒƒãƒå‡¦ç†"
msgid "Geo|Could not remove tracking entry for an existing project."
-msgstr ""
+msgstr "既存ã®ãƒ—ロジェクトã®ãƒˆãƒ©ãƒƒã‚­ãƒ³ã‚°ã‚¨ãƒ³ãƒˆãƒªã‚’削除ã§ãã¾ã›ã‚“ã§ã—ãŸã€‚"
msgid "Geo|Failed"
msgstr "失敗"
@@ -4447,7 +4676,7 @@ msgid "Geo|File sync capacity"
msgstr "ファイルåŒæœŸå®¹é‡"
msgid "Geo|Geo Status"
-msgstr ""
+msgstr "ジオステータス"
msgid "Geo|Groups to synchronize"
msgstr "åŒæœŸã‚°ãƒ«ãƒ¼ãƒ—"
@@ -4459,13 +4688,13 @@ msgid "Geo|Last repository check run"
msgstr ""
msgid "Geo|Last successful sync"
-msgstr ""
+msgstr "最後ã«æˆåŠŸã—ãŸåŒæœŸ"
msgid "Geo|Last sync attempt"
-msgstr ""
+msgstr "最後ã®åŒæœŸè©¦è¡Œ"
msgid "Geo|Last time verified"
-msgstr ""
+msgstr "å‰å›žã®ç¢ºèªæ—¥æ™‚"
msgid "Geo|Never"
msgstr ""
@@ -4474,7 +4703,7 @@ msgid "Geo|Next sync scheduled at"
msgstr ""
msgid "Geo|Not synced yet"
-msgstr ""
+msgstr "ã¾ã åŒæœŸã—ã¦ã„ã¾ã›ã‚“。"
msgid "Geo|Pending"
msgstr ""
@@ -4498,25 +4727,25 @@ msgid "Geo|Re-verification interval"
msgstr ""
msgid "Geo|Recheck"
-msgstr ""
+msgstr "å†ãƒã‚§ãƒƒã‚¯"
msgid "Geo|Recheck all projects"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクトをå†ãƒã‚§ãƒƒã‚¯"
msgid "Geo|Redownload"
-msgstr ""
+msgstr "å†ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰"
msgid "Geo|Remove"
-msgstr ""
+msgstr "削除"
msgid "Geo|Repository sync capacity"
msgstr "リãƒã‚¸ãƒˆãƒªåŒæœŸå®¹é‡"
msgid "Geo|Resync"
-msgstr ""
+msgstr "å†åŒæœŸ"
msgid "Geo|Resync all projects"
-msgstr ""
+msgstr "ã™ã¹ã¦ã®ãƒ—ロジェクトをå†åŒæœŸ"
msgid "Geo|Retry count"
msgstr "リトライ回数"
@@ -4528,19 +4757,19 @@ msgid "Geo|Shards to synchronize"
msgstr "シャードã®åŒæœŸ"
msgid "Geo|Status"
-msgstr ""
+msgstr "状態"
msgid "Geo|Synced"
-msgstr ""
+msgstr "åŒæœŸæ¸ˆã¿"
msgid "Geo|Synchronization failed - %{error}"
-msgstr ""
+msgstr "åŒæœŸã«å¤±æ•—ã—ã¾ã—㟠- %{error}"
msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
-msgstr ""
+msgstr "プロジェクト(%{project_id})ã®ãƒˆãƒ©ãƒƒã‚­ãƒ³ã‚°ã‚¨ãƒ³ãƒˆãƒªãŒæ­£å¸¸ã«å‰Šé™¤ã•ã‚Œã¾ã—ãŸã€‚"
msgid "Geo|Tracking entry will be removed. Are you sure?"
-msgstr ""
+msgstr "トラッキングエントリã¯å‰Šé™¤ã•ã‚Œã¾ã™ã€‚本当ã«å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ"
msgid "Geo|Unknown state"
msgstr ""
@@ -4561,19 +4790,19 @@ msgid "Geo|You are on a secondary, <b>read-only</b> Geo node. You may be able to
msgstr ""
msgid "Geo|You need a different license to use Geo replication"
-msgstr ""
+msgstr "ジオレプリケーションを使用ã™ã‚‹ã«ã¯åˆ¥ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ãŒå¿…è¦ã§ã™"
msgid "Geo|misconfigured"
msgstr ""
msgid "Geo|primary"
-msgstr ""
+msgstr "プライマリー"
msgid "Geo|secondary"
msgstr ""
msgid "Get a free instance review"
-msgstr ""
+msgstr "ç„¡æ–™ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ãƒ¬ãƒ“ューã®å–å¾—"
msgid "Get started with error tracking"
msgstr ""
@@ -4612,7 +4841,7 @@ msgid "GitLab Group Runners can execute code for all the projects in this group.
msgstr "GitLab グループ Runner ã¯ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—内ã®å…¨ã¦ã®ãƒ—ロジェクトã®ã‚³ãƒ¼ãƒ‰ã‚’実行ã§ãã¾ã™ã€‚"
msgid "GitLab Import"
-msgstr ""
+msgstr "GitLab インãƒãƒ¼ãƒˆ"
msgid "GitLab Shared Runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com)."
msgstr ""
@@ -4621,7 +4850,7 @@ msgid "GitLab User"
msgstr "GitLab ユーザー"
msgid "GitLab metadata URL"
-msgstr ""
+msgstr "GitLab メタデータ URL"
msgid "GitLab project export"
msgstr "GitLab プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆ"
@@ -4659,20 +4888,29 @@ msgstr ""
msgid "Go Back"
msgstr "戻る"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "å‰ã«æˆ»ã‚‹"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr "Google コードã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
msgid "Google Takeout"
-msgstr ""
+msgstr "Google テイクアウト"
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 管ç†è€…ã«å•ã„åˆã‚ã›ã¦ãã ã•ã„。"
@@ -4702,7 +4940,7 @@ msgid "Group Runners"
msgstr "グループ Runner"
msgid "Group SAML must be enabled to test"
-msgstr ""
+msgstr "グループ SAML を有効ã«ã—ã¦ãƒ†ã‚¹ãƒˆã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™"
msgid "Group URL"
msgstr "グループ URL"
@@ -4714,7 +4952,7 @@ msgid "Group description"
msgstr "グループã®èª¬æ˜Ž"
msgid "Group description (optional)"
-msgstr ""
+msgstr "グループã®èª¬æ˜Ž(ä»»æ„)"
msgid "Group details"
msgstr "グループã®è©³ç´°"
@@ -4725,6 +4963,9 @@ msgstr "グループ情報:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "グループ Maintainer 㯠%{link} ã§ã‚°ãƒ«ãƒ¼ãƒ— Runner を登録ã§ãã¾ã™ã€‚"
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "グループå"
@@ -4732,7 +4973,7 @@ msgid "Group overview content"
msgstr ""
msgid "Group:"
-msgstr ""
+msgstr "グループ:"
msgid "Group: %{group_name}"
msgstr "グループ:%{group_name}"
@@ -4816,7 +5057,7 @@ msgid "GroupsDropdown|Loading groups"
msgstr "グループã®èª­ã¿è¾¼ã¿ä¸­"
msgid "GroupsDropdown|Search your groups"
-msgstr ""
+msgstr "ã‚ãªãŸã®ã‚°ãƒ«ãƒ¼ãƒ—を検索"
msgid "GroupsDropdown|Something went wrong on our end."
msgstr ""
@@ -4840,7 +5081,7 @@ msgid "GroupsEmptyState|You can manage your group member’s permissions and acc
msgstr "グループメンãƒãƒ¼ã®æ¨©é™ç®¡ç†ã€ãŠã‚ˆã³ã‚°ãƒ«ãƒ¼ãƒ—内ã®å„プロジェクトã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©é™ã‚’管ç†ã§ãã¾ã™ã€‚"
msgid "GroupsTree|Are you sure you want to leave the \"%{fullName}\" group?"
-msgstr ""
+msgstr "\"%{fullName}\" グループã‹ã‚‰é›¢è„±ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "GroupsTree|Create a project in this group."
msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ãƒ—ロジェクトを作æˆã™ã‚‹ã€‚"
@@ -4867,7 +5108,7 @@ msgid "GroupsTree|No groups or projects matched your search"
msgstr ""
msgid "GroupsTree|Search by name"
-msgstr ""
+msgstr "åå‰ã§æ¤œç´¢"
msgid "Have your users email"
msgstr "ユーザーã«ãƒ¡ãƒ¼ãƒ«ã‚’é€ä¿¡"
@@ -4906,20 +5147,20 @@ msgid "Here is the public SSH key that needs to be added to the remote server. F
msgstr ""
msgid "Hide file browser"
-msgstr ""
+msgstr "ファイルブラウザをéžè¡¨ç¤º"
msgid "Hide host keys manual input"
-msgstr ""
+msgstr "ホストキーã®ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«å…¥åŠ›ã‚’éš ã™"
msgid "Hide payload"
-msgstr ""
+msgstr "ペイロードを隠ã™"
msgid "Hide value"
msgid_plural "Hide values"
msgstr[0] "éžè¡¨ç¤º"
msgid "Hide values"
-msgstr ""
+msgstr "éžè¡¨ç¤º"
msgid "History"
msgstr "履歴"
@@ -4943,10 +5184,10 @@ msgid "IDE|Allow live previews of JavaScript projects in the Web IDE using CodeS
msgstr ""
msgid "IDE|Back"
-msgstr ""
+msgstr "戻る"
msgid "IDE|Client side evaluation"
-msgstr ""
+msgstr "クライアントサイド評価"
msgid "IDE|Commit"
msgstr "コミット"
@@ -4955,28 +5196,28 @@ msgid "IDE|Edit"
msgstr "編集"
msgid "IDE|Get started with Live Preview"
-msgstr ""
+msgstr "ライブプレビューã®é–‹å§‹"
msgid "IDE|Go to project"
-msgstr ""
+msgstr "プロジェクトã«ç§»å‹•"
msgid "IDE|Live Preview"
-msgstr ""
+msgstr "ライブプレビュー"
msgid "IDE|Open in file view"
msgstr "ファイルビューã§é–‹ã"
msgid "IDE|Preview your web application using Web IDE client-side evaluation."
-msgstr ""
+msgstr "Web IDE クライアントサイド評価を使用ã—ã¦ã€ã‚ãªãŸã® Web アプリケーションをプレビューã—ã¾ã™ã€‚"
msgid "IDE|Refresh preview"
-msgstr ""
+msgstr "プレビューã®æ›´æ–°"
msgid "IDE|Review"
msgstr "レビュー"
msgid "IP Address"
-msgstr ""
+msgstr "IP アドレス"
msgid "Identifier"
msgstr "識別å­"
@@ -5000,7 +5241,7 @@ 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 "GitHub を使用ã—ã¦ã„ã‚‹å ´åˆã€GitHub 上ã®ã‚³ãƒŸãƒƒãƒˆã‚„プルリクエスã‹ã‚‰ãƒ‘イプラインã®çŠ¶æ…‹ã‚’確èªã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚%{more_info_link}"
@@ -5027,7 +5268,7 @@ msgid "Import"
msgstr "インãƒãƒ¼ãƒˆ"
msgid "Import CSV"
-msgstr ""
+msgstr "CSV ã®å–ã‚Šè¾¼ã¿"
msgid "Import Projects from Gitea"
msgstr "Gitea ã‹ã‚‰ãƒ—ロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
@@ -5048,13 +5289,13 @@ msgid "Import in progress"
msgstr "インãƒãƒ¼ãƒˆä¸­ã§ã™"
msgid "Import issues"
-msgstr ""
+msgstr "課題ã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
msgid "Import members"
-msgstr ""
+msgstr "メンãƒãƒ¼ã‚’インãƒãƒ¼ãƒˆ"
msgid "Import members from another project"
-msgstr ""
+msgstr "ä»–ã®ãƒ—ロジェクトã‹ã‚‰ãƒ¡ãƒ³ãƒãƒ¼ã‚’インãƒãƒ¼ãƒˆ"
msgid "Import multiple repositories by uploading a manifest file."
msgstr "manifest ファイルã®ã‚¢ãƒƒãƒ—ロードã«ã‚ˆã‚‹è¤‡æ•°ãƒªãƒã‚¸ãƒˆãƒªã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
@@ -5063,10 +5304,10 @@ msgid "Import project"
msgstr "プロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
msgid "Import project members"
-msgstr ""
+msgstr "プロジェクトメンãƒãƒ¼ã‚’インãƒãƒ¼ãƒˆ"
msgid "Import projects from Bitbucket"
-msgstr ""
+msgstr "Bitbucket ã‹ã‚‰ãƒ—ロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
msgid "Import projects from Bitbucket Server"
msgstr "Bitbucket Server ã‹ã‚‰ãƒ—ロジェクトã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
@@ -5092,9 +5333,24 @@ msgstr "リãƒã‚¸ãƒˆãƒªã‚’インãƒãƒ¼ãƒˆ"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr "リãƒã‚¸ãƒˆãƒªã¸æŽ¥ç¶š"
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr "GitLab エンタープライズエディションを使用ã™ã‚‹ã¨ã€èª²é¡Œãƒœãƒ¼ãƒ‰ã®æ©Ÿèƒ½ãŒå¼·åŒ–ã•ã‚Œã¾ã™ã€‚"
@@ -5141,16 +5397,22 @@ msgid "Inline"
msgstr "インライン"
msgid "Input host keys manually"
-msgstr ""
+msgstr "ホストキーã®ãƒžãƒ‹ãƒ¥ã‚¢ãƒ«å…¥åŠ›"
msgid "Input your repository URL"
+msgstr "リãƒã‚¸ãƒˆãƒªã® URL を入力ã—ã¦ãã ã•ã„"
+
+msgid "Insert a quote"
msgstr ""
-msgid "Insert suggestion"
+msgid "Insert code"
msgstr ""
+msgid "Insert suggestion"
+msgstr "候補を挿入ã™ã‚‹"
+
msgid "Install GitLab Runner"
-msgstr ""
+msgstr "GitLab Runner ã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«"
msgid "Install Runner on Kubernetes"
msgstr "Kubernetes 㫠Runner をインストール"
@@ -5163,7 +5425,7 @@ msgid "Instance Statistics"
msgstr "インスタンス統計"
msgid "Instance Statistics visibility"
-msgstr ""
+msgstr "インスタンス統計ã®å¯è¦–性"
msgid "Instance does not support multiple Kubernetes clusters"
msgstr "インスタンスã¯ãƒžãƒ«ãƒ Kubernetes クラスターをサãƒãƒ¼ãƒˆã—ã¦ã„ã¾ã›ã‚“"
@@ -5178,16 +5440,16 @@ msgid "Interested parties can even contribute by pushing commits if they want to
msgstr "貢献をã—ãŸã„関係者ã¯ã€ã‚³ãƒŸãƒƒãƒˆã‚’プッシュã™ã‚‹ã“ã¨ã§è²¢çŒ®ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ã€‚"
msgid "Internal"
-msgstr ""
+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 ""
+msgstr "内部 - プロジェクトã¯ã€ãƒ­ã‚°ã‚¤ãƒ³ã—ã¦ã„ã‚‹ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™ã€‚"
msgid "Internal users"
-msgstr ""
+msgstr "内部ユーザー"
msgid "Interval Pattern"
msgstr "é–“éš”ã®ãƒ‘ターン"
@@ -5198,22 +5460,28 @@ msgstr "サイクル分æžã®ã”紹介"
msgid "Introducing Your Conversational Development Index"
msgstr ""
-msgid "Invitation"
+msgid "Invalid input, please avoid emojis"
msgstr ""
+msgid "Invitation"
+msgstr "招待状"
+
msgid "Invite"
-msgstr ""
+msgstr "招待"
msgid "Invite group"
-msgstr ""
+msgstr "グループã«æ‹›å¾…ã™ã‚‹"
msgid "Invite member"
-msgstr ""
+msgstr "メンãƒãƒ¼ã‚’招待ã™ã‚‹"
msgid "Invoke Count"
-msgstr ""
+msgstr "呼ã³å‡ºã—回数"
msgid "Invoke Time"
+msgstr "呼ã³å‡ºã—時間"
+
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
msgstr ""
msgid "Issue"
@@ -5258,17 +5526,17 @@ msgstr "課題ã¨ã¯ãƒã‚°ã€ã‚¿ã‚¹ã‚¯ã€ã¾ãŸã¯è­°è«–ã®å¿…è¦ãªã‚¢ã‚¤ãƒ‡ã‚¢
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
msgstr ""
msgid "IssuesAnalytics|Issues Created"
-msgstr ""
+msgstr "課題ãŒä½œæˆã•ã‚Œã¾ã—ãŸ"
msgid "IssuesAnalytics|Issues created per month"
-msgstr ""
+msgstr "課題ã®ä½œæˆï¼æœˆ"
msgid "IssuesAnalytics|Last 12 months"
msgstr ""
@@ -5310,43 +5578,43 @@ msgid "Job is stuck. Check runners."
msgstr ""
msgid "Job was retried"
-msgstr ""
+msgstr "ジョブãŒå†è©¦è¡Œã•ã‚Œã¾ã—ãŸ"
msgid "Jobs"
msgstr "ジョブ"
msgid "Job|Browse"
-msgstr ""
+msgstr "ブラウズ"
msgid "Job|Complete Raw"
-msgstr ""
+msgstr "完全㪠Raw"
msgid "Job|Download"
-msgstr ""
+msgstr "ダウンロード"
msgid "Job|Erase job log"
-msgstr ""
+msgstr "ジョブログã®æ¶ˆåŽ»"
msgid "Job|Job artifacts"
msgstr ""
msgid "Job|Job has been erased"
-msgstr ""
+msgstr "ジョブãŒæ¶ˆåŽ»ã•ã‚Œã¾ã—ãŸ"
msgid "Job|Job has been erased by"
-msgstr ""
+msgstr "ジョブãŒæ¶ˆåŽ»ã•ã‚Œã¾ã—ãŸ:"
msgid "Job|Keep"
-msgstr ""
+msgstr "維æŒ"
msgid "Job|Scroll to bottom"
-msgstr ""
+msgstr "最下部ã«ã‚¹ã‚¯ãƒ­ãƒ¼ãƒ«"
msgid "Job|Scroll to top"
-msgstr ""
+msgstr "最上部ã«ã‚¹ã‚¯ãƒ­ãƒ¼ãƒ«"
msgid "Job|Show complete raw"
-msgstr ""
+msgstr "完全㪠Raw を表示ã™ã‚‹"
msgid "Job|The artifacts were removed"
msgstr ""
@@ -5370,7 +5638,7 @@ msgid "June"
msgstr "6月"
msgid "Key (PEM)"
-msgstr ""
+msgstr "キー (PEM)"
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -5379,7 +5647,7 @@ msgid "Kubernetes Cluster"
msgstr "Kubernetes クラスター"
msgid "Kubernetes Clusters"
-msgstr ""
+msgstr "Kubernetes クラスター"
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
msgstr "Kubernetes クラスターã®ä½œæˆä¸­ã«ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆã—ã¾ã—ãŸ:%{timeout}"
@@ -5412,7 +5680,7 @@ msgid "Label"
msgstr "ラベル"
msgid "Label actions dropdown"
-msgstr ""
+msgstr "ラベルアクションドロップダウン"
msgid "Label lists show all issues with the selected label."
msgstr "ラベル一覧ã«ã¯ã€é¸æŠžã—ãŸãƒ©ãƒ™ãƒ«ãŒä»˜ã„ãŸã™ã¹ã¦ã®èª²é¡ŒãŒè¡¨ç¤ºã•ã‚Œã¾ã™ã€‚"
@@ -5447,6 +5715,9 @@ msgstr "ラベルã®æ˜‡æ ¼"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5509,26 +5780,29 @@ msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple
msgstr ""
msgid "Learn more about Auto DevOps"
-msgstr ""
+msgstr "Auto DevOps ã®è©³ç´°"
msgid "Learn more about Kubernetes"
msgstr "Kubernetes ã®è©³ç´°"
msgid "Learn more about Web Terminal"
-msgstr ""
+msgstr "Web Terminalã®è©³ç´°"
msgid "Learn more about custom project templates"
-msgstr ""
+msgstr "カスタムプロジェクトテンプレートã®è©³ç´°"
msgid "Learn more about group-level project templates"
-msgstr ""
+msgstr "グループレベルプロジェクトテンプレートã®è©³ç´°"
msgid "Learn more about incoming email addresses"
-msgstr ""
+msgstr "å—信メールアドレスã®è©³ç´°"
msgid "Learn more about protected branches"
msgstr "ä¿è­·ã•ã‚ŒãŸãƒ–ランãƒã«ã¤ã„ã¦ã®è©³ç´°"
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "詳ã—ã見る:"
@@ -5551,28 +5825,28 @@ msgid "License"
msgstr "ライセンス"
msgid "LicenseManagement|Add a license"
-msgstr ""
+msgstr "ライセンスを追加"
msgid "LicenseManagement|Add licenses manually to approve or blacklist"
msgstr ""
msgid "LicenseManagement|Approve"
-msgstr ""
+msgstr "承èª"
msgid "LicenseManagement|Approve license"
-msgstr ""
+msgstr "ライセンスã®æ‰¿èª"
msgid "LicenseManagement|Approve license?"
-msgstr ""
+msgstr "ライセンスを承èªã—ã¾ã™ã‹ï¼Ÿ"
msgid "LicenseManagement|Approved"
-msgstr ""
+msgstr "承èªæ¸ˆã¿"
msgid "LicenseManagement|Blacklist"
-msgstr ""
+msgstr "ブラックリスト"
msgid "LicenseManagement|Blacklist license"
-msgstr ""
+msgstr "ブラックリストライセンス"
msgid "LicenseManagement|Blacklist license?"
msgstr ""
@@ -5581,19 +5855,19 @@ msgid "LicenseManagement|Blacklisted"
msgstr ""
msgid "LicenseManagement|Cancel"
-msgstr ""
+msgstr "キャンセル"
msgid "LicenseManagement|License"
msgstr "ライセンス"
msgid "LicenseManagement|License Management"
-msgstr ""
+msgstr "ライセンス管ç†"
msgid "LicenseManagement|License details"
-msgstr ""
+msgstr "ライセンスã®è©³ç´°"
msgid "LicenseManagement|License name"
-msgstr ""
+msgstr "ライセンスå"
msgid "LicenseManagement|Manage approved and blacklisted licenses for this project."
msgstr ""
@@ -5602,25 +5876,25 @@ msgid "LicenseManagement|Packages"
msgstr "パッケージ"
msgid "LicenseManagement|Remove license"
-msgstr ""
+msgstr "ライセンスを削除"
msgid "LicenseManagement|Remove license?"
-msgstr ""
+msgstr "ライセンスを削除ã—ã¾ã™ã‹ï¼Ÿ"
msgid "LicenseManagement|Submit"
-msgstr ""
+msgstr "é€ä¿¡"
msgid "LicenseManagement|There are currently no approved or blacklisted licenses in this project."
msgstr ""
msgid "LicenseManagement|This license already exists in this project."
-msgstr ""
+msgstr "ã“ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã¯ã™ã§ã«ã“ã®ãƒ—ロジェクトã«å­˜åœ¨ã—ã¾ã™ã€‚"
msgid "LicenseManagement|URL"
msgstr "URL"
msgid "LicenseManagement|You are about to remove the license, %{name}, from this project."
-msgstr ""
+msgstr "ã“ã®ãƒ—ロジェクトã‹ã‚‰ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ %{name} を削除ã—よã†ã¨ã—ã¦ã„ã¾ã™ã€‚"
msgid "Licenses"
msgstr "ライセンス"
@@ -5639,10 +5913,10 @@ msgid "List Your Gitea Repositories"
msgstr ""
msgid "List available repositories"
-msgstr ""
+msgstr "利用å¯èƒ½ãªãƒªãƒã‚¸ãƒˆãƒªã®ä¸€è¦§"
msgid "List view"
-msgstr ""
+msgstr "一覧表示"
msgid "List your Bitbucket Server repositories"
msgstr ""
@@ -5651,10 +5925,10 @@ msgid "List your GitHub repositories"
msgstr "GitHub リãƒã‚¸ãƒˆãƒªã‚’一覧表示"
msgid "Live preview"
-msgstr ""
+msgstr "ライブプレビュー"
msgid "Loading contribution stats for group members"
-msgstr ""
+msgstr "グループメンãƒãƒ¼ã®è²¢çŒ®åº¦æƒ…報を読ã¿è¾¼ã¿ä¸­"
msgid "Loading the GitLab IDE..."
msgstr "GitLab IDE ã®èª­ã¿è¾¼ã¿ä¸­..."
@@ -5663,7 +5937,7 @@ msgid "Loading..."
msgstr "読ã¿è¾¼ã¿ä¸­..."
msgid "Loading…"
-msgstr ""
+msgstr "読ã¿è¾¼ã¿ä¸­â€¦"
msgid "Localization"
msgstr ""
@@ -5678,7 +5952,7 @@ msgid "Lock not found"
msgstr "ロックãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
msgid "Lock this %{issuableDisplayName}? Only <strong>project members</strong> will be able to comment."
-msgstr ""
+msgstr "%{issuableDisplayName} をロックã—ã¾ã™ã‹ï¼Ÿ<strong>プロジェクトメンãƒãƒ¼</strong> ã®ã¿ã‚³ãƒ¡ãƒ³ãƒˆã§ãã¾ã™ã€‚"
msgid "Lock to current projects"
msgstr "ç¾åœ¨ã®ãƒ—ロジェクトをロックã™ã‚‹"
@@ -5693,14 +5967,23 @@ msgid "Locked to current projects"
msgstr "ç¾åœ¨ã®ãƒ—ロジェクトã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™"
msgid "Locks give the ability to lock specific file or folder."
-msgstr ""
+msgstr "ロックã¯ã€ç‰¹å®šã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚„フォルダをロックã™ã‚‹æ©Ÿèƒ½ã‚’æä¾›ã—ã¾ã™ã€‚"
msgid "Login with smartcard"
-msgstr ""
+msgstr "スマートカードã§ãƒ­ã‚°ã‚¤ãƒ³ã™ã‚‹"
msgid "Logs"
msgstr "ログ"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5735,22 +6018,25 @@ msgid "Manage project labels"
msgstr "プロジェクトラベルã®ç®¡ç†"
msgid "Manage two-factor authentication"
-msgstr ""
+msgstr "2è¦ç´ èªè¨¼ã®ç®¡ç†"
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr "グループã®ãƒ¡ãƒ³ãƒãƒ¼ã‚·ãƒƒãƒ—を管ç†ã—ãªãŒã‚‰ã€SAML ã§åˆ¥ã®ã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ãƒ¬ãƒ™ãƒ«ã‚’追加ã—ã¾ã™ã€‚"
msgid "Manifest"
-msgstr ""
+msgstr "マニフェスト"
msgid "Manifest file import"
+msgstr "マニフェストファイルã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ"
+
+msgid "Manual job"
msgstr ""
msgid "Map a FogBugz account ID to a GitLab user"
-msgstr ""
+msgstr "FogBugz アカウントIDã‚’ GitLab ユーザーã«ãƒžãƒƒãƒ—ã™ã‚‹"
msgid "Map a Google Code user to a GitLab user"
-msgstr ""
+msgstr "Google コードユーザーを GitLab ユーザーã«ãƒžãƒƒãƒ—ã™ã‚‹"
msgid "Map a Google Code user to a full email address"
msgstr ""
@@ -5773,41 +6059,11 @@ msgstr ""
msgid "Markdown enabled"
msgstr "マークダウンを使用ã§ãã¾ã™"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
msgid "Max access level"
-msgstr ""
+msgstr "最大アクセスレベル"
msgid "Maximum job timeout"
msgstr ""
@@ -5855,6 +6111,9 @@ msgid "Merge immediately"
msgstr ""
msgid "Merge in progress"
+msgstr "進行中ã®ãƒžãƒ¼ã‚¸"
+
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
msgstr ""
msgid "Merge request"
@@ -5870,13 +6129,13 @@ msgid "Merge requests are a place to propose changes you've made to a project an
msgstr "マージリクエストã¨ã¯ã€ãƒ—ロジェクトã«åŠ ãˆãŸå¤‰æ›´ã‚’æ示ã—ã€ãã®å¤‰æ›´ã«ã¤ã„ã¦ä»–ã®ãƒ¡ãƒ³ãƒãƒ¼ã¨è©±ã—åˆã†ãŸã‚ã®å ´æ‰€ã§ã™"
msgid "Merge when pipeline succeeds"
-msgstr ""
+msgstr "パイプラインãŒæˆåŠŸã—ãŸã¨ãã«ãƒžãƒ¼ã‚¸"
msgid "MergeRequests|Add a reply"
-msgstr ""
+msgstr "返信を追加"
msgid "MergeRequests|An error occurred while saving the draft comment."
-msgstr ""
+msgstr "下書ãコメントã®ä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "MergeRequests|Discussion stays resolved"
msgstr ""
@@ -5900,7 +6159,7 @@ msgid "MergeRequests|Resolve this discussion in a new issue"
msgstr "æ–°ã—ã„課題ã§ã“ã®æ¤œè¨Žã‚’解決ã™ã‚‹"
msgid "MergeRequests|Saving the comment failed"
-msgstr ""
+msgstr "コメントã®ä¿å­˜ã«å¤±æ•—ã—ã¾ã—ãŸ"
msgid "MergeRequests|Toggle comments for this file"
msgstr ""
@@ -5933,13 +6192,13 @@ msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChang
msgstr ""
msgid "MergeRequest|Filter files"
-msgstr ""
+msgstr "ファイルã®ãƒ•ã‚£ãƒ«ã‚¿ãƒªãƒ³ã‚°"
msgid "MergeRequest|No files found"
-msgstr ""
+msgstr "ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“"
msgid "MergeRequest|Search files"
-msgstr ""
+msgstr "ファイル検索"
msgid "Merged"
msgstr "マージ済ã¿"
@@ -5957,7 +6216,7 @@ msgid "Metrics - Prometheus"
msgstr "メトリクス - Prometheus"
msgid "Metrics and profiling"
-msgstr ""
+msgstr "メトリクスã¨ãƒ—ロファイリング"
msgid "Metrics for environment"
msgstr ""
@@ -6007,14 +6266,11 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr "Prometheus クエリã®ãƒ‰ã‚­ãƒ¥ãƒ¡ãƒ³ãƒˆ"
-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 ""
+msgstr "デプロイ情報ã®å–得中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "Metrics|There was an error getting environments information."
msgstr ""
@@ -6053,7 +6309,7 @@ msgid "Milestone"
msgstr "マイルストーン"
msgid "Milestone lists not available with your current license"
-msgstr ""
+msgstr "ç¾åœ¨ã®ãƒ©ã‚¤ã‚»ãƒ³ã‚¹ã§ã¯ マイルストーンリストを利用ã§ãã¾ã›ã‚“"
msgid "Milestone lists show all issues from the selected milestone."
msgstr ""
@@ -6143,7 +6399,7 @@ msgid "More"
msgstr ""
msgid "More actions"
-msgstr ""
+msgstr "ãã®ä»–ã®æ“作"
msgid "More info"
msgstr ""
@@ -6154,6 +6410,9 @@ msgstr "詳ã—ã„情報"
msgid "More information is available|here"
msgstr "ã“ã“ã‚’å‚ç…§"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6194,7 +6453,7 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã§ã‚µã‚¤ãƒ³ã‚¤ãƒ³ã™ã‚‹"
msgid "Need help?"
-msgstr ""
+msgstr "ãŠå›°ã‚Šã§ã™ã‹?"
msgid "Network"
msgstr "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯"
@@ -6209,7 +6468,7 @@ msgid "New Application"
msgstr "æ–°ã—ã„アプリケーション"
msgid "New Environment"
-msgstr ""
+msgstr "æ–°ã—ã„環境"
msgid "New Group"
msgstr "æ–°ã—ã„グループ"
@@ -6230,15 +6489,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "æ–°è¦ãƒ‘イプラインスケジュール"
msgid "New Snippet"
msgstr "æ–°è¦ã‚¹ãƒ‹ãƒšãƒƒãƒˆ"
-msgid "New Snippets"
-msgstr "æ–°è¦ã‚¹ãƒ‹ãƒšãƒƒãƒˆ"
-
msgid "New branch"
msgstr "æ–°è¦ãƒ–ランãƒ"
@@ -6299,9 +6558,15 @@ msgstr "æ–°è¦...\t"
msgid "No"
msgstr "ã„ã„ãˆ"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr "ラベルãŒã‚ã‚Šã¾ã›ã‚“"
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6327,15 +6592,18 @@ msgid "No contributions were found"
msgstr ""
msgid "No credit card required."
+msgstr "クレジット カードã¯å¿…è¦ã‚ã‚Šã¾ã›ã‚“"
+
+msgid "No designs found."
msgstr ""
msgid "No details available"
-msgstr ""
+msgstr "詳細ãŒã‚ã‚Šã¾ã›ã‚“"
msgid "No due date"
msgstr "期é™ãªã—"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6345,7 +6613,7 @@ msgid "No file chosen"
msgstr "ファイルãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "No file selected"
-msgstr ""
+msgstr "ファイルãŒé¸æŠžã•ã‚Œã¦ã„ã¾ã›ã‚“"
msgid "No files found."
msgstr "ファイルãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。"
@@ -6405,7 +6673,7 @@ msgid "No, directly import the existing email addresses and usernames."
msgstr ""
msgid "Nodes"
-msgstr ""
+msgstr "ノード"
msgid "None"
msgstr "ãªã—"
@@ -6429,6 +6697,9 @@ msgid "Not enough data"
msgstr "データä¸è¶³"
msgid "Not now"
+msgstr "後ã§"
+
+msgid "Not started"
msgstr ""
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
@@ -6450,7 +6721,7 @@ msgid "Note: Consider asking your GitLab administrator to configure %{github_int
msgstr "注: GitLab ã®ç®¡ç†è€…ã«%{github_integration_link} を設定ã—ã¦ã€GitHub 経由ã®ãƒ­ã‚°ã‚¤ãƒ³ãŒè¨±å¯ã—ã€å€‹äººç”¨ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³ã‚’生æˆã›ãšã«ãƒªãƒã‚¸ãƒˆãƒªã‚’インãƒãƒ¼ãƒˆã§ããªã„ã‹å•ã„åˆã‚ã›ãã ã•ã„。"
msgid "Notes|Are you sure you want to cancel creating this comment?"
-msgstr ""
+msgstr "本当ã«ã“ã®ã‚³ãƒ¡ãƒ³ãƒˆã®ä½œæˆã‚’キャンセルã—ã¾ã™ã‹ï¼Ÿ"
msgid "Notes|Collapse replies"
msgstr ""
@@ -6459,19 +6730,19 @@ msgid "Notes|Show all activity"
msgstr ""
msgid "Notes|Show comments only"
-msgstr ""
+msgstr "コメントã®ã¿è¡¨ç¤º"
msgid "Notes|Show history only"
-msgstr ""
+msgstr "履歴ã®ã¿è¡¨ç¤º"
msgid "Notification events"
msgstr "イベント通知"
msgid "Notification setting"
-msgstr ""
+msgstr "通知設定"
msgid "Notification setting - %{notification_title}"
-msgstr ""
+msgstr "通知設定 - %{notification_title}"
msgid "NotificationEvent|Close issue"
msgstr "課題をクローズ"
@@ -6568,7 +6839,7 @@ msgid "One or more of your Google Code projects cannot be imported into GitLab d
msgstr ""
msgid "Only admins"
-msgstr ""
+msgstr "管ç†è€…ã®ã¿"
msgid "Only mirror protected branches"
msgstr ""
@@ -6589,10 +6860,10 @@ msgid "Oops, are you sure?"
msgstr ""
msgid "Open"
-msgstr ""
+msgstr "é–‹ã"
msgid "Open Documentation"
-msgstr ""
+msgstr "ドキュメントを開ã"
msgid "Open comment type dropdown"
msgstr ""
@@ -6604,7 +6875,7 @@ msgid "Open in Xcode"
msgstr "Xcode ã§é–‹ã"
msgid "Open projects"
-msgstr ""
+msgstr "プロジェクトを開ã"
msgid "Open sidebar"
msgstr "サイドãƒãƒ¼ã‚’é–‹ã"
@@ -6633,6 +6904,9 @@ msgstr "é‹ç”¨"
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6642,6 +6916,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6676,10 +6953,10 @@ msgid "Owner"
msgstr "オーナー"
msgid "Package information"
-msgstr ""
+msgstr "パッケージ情報"
msgid "Package was removed"
-msgstr ""
+msgstr "パッケージãŒå‰Šé™¤ã•ã‚Œã¾ã—ãŸ"
msgid "Packages"
msgstr "パッケージ"
@@ -6706,7 +6983,7 @@ msgid "Pagination|« First"
msgstr "« 最åˆ"
msgid "Parameter"
-msgstr ""
+msgstr "パラメーター"
msgid "Parent epic"
msgstr ""
@@ -6757,7 +7034,7 @@ msgid "Permissions"
msgstr "権é™"
msgid "Permissions, LFS, 2FA"
-msgstr ""
+msgstr "パーミッションã€LFSã€2FA"
msgid "Personal Access Token"
msgstr "個人ã®ã‚¢ã‚¯ã‚»ã‚¹ãƒˆãƒ¼ã‚¯ãƒ³"
@@ -6784,7 +7061,7 @@ msgid "Pipeline quota"
msgstr "パイプラインã®ã‚¯ã‚©ãƒ¼ã‚¿"
msgid "Pipeline triggers"
-msgstr ""
+msgstr "パイプラインã®ãƒˆãƒªã‚¬ãƒ¼"
msgid "PipelineCharts|Failed:"
msgstr "失敗:"
@@ -6834,6 +7111,12 @@ msgstr "変数"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "カスタム"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "パイプライン"
@@ -6849,6 +7132,9 @@ msgstr "先週ã®ãƒ‘イプライン"
msgid "Pipelines for last year"
msgstr "昨年ã®ãƒ‘イプライン"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -6883,13 +7169,13 @@ msgid "Pipelines|There are currently no pipelines."
msgstr "パイプラインã¯ç¾åœ¨ã‚ã‚Šã¾ã›ã‚“。"
msgid "Pipelines|There was an error fetching the pipelines. Try again in a few moments or contact your support team."
-msgstr ""
+msgstr "パイプラインをフェッãƒã™ã‚‹éš›ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã—ã°ã‚‰ãã—ã¦ã‹ã‚‰ã‚‚ã†ä¸€åº¦ãŠè©¦ã—ã„ãŸã ãã‹ã€ã‚µãƒãƒ¼ãƒˆãƒãƒ¼ãƒ ã«ãŠå•ã„åˆã‚ã›ãã ã•ã„。"
msgid "Pipelines|This project is not currently set up to run pipelines."
msgstr "ã“ã®ãƒ—ロジェクトã¯ç¾åœ¨ãƒ‘イプラインを実行ã™ã‚‹ã‚ˆã†ã«è¨­å®šã•ã‚Œã¦ã„ã¾ã›ã‚“。"
msgid "Pipeline|Commit"
-msgstr ""
+msgstr "コミット"
msgid "Pipeline|Create for"
msgstr "実行対象"
@@ -6898,13 +7184,13 @@ msgid "Pipeline|Create pipeline"
msgstr "パイプラインを作æˆ"
msgid "Pipeline|Duration"
-msgstr ""
+msgstr "期間"
msgid "Pipeline|Existing branch name or tag"
msgstr ""
msgid "Pipeline|Pipeline"
-msgstr ""
+msgstr "パイプライン"
msgid "Pipeline|Run Pipeline"
msgstr "パイプラインを実行"
@@ -6916,10 +7202,10 @@ msgid "Pipeline|Specify variable values to be used in this run. The values speci
msgstr "ã“ã®å®Ÿè¡Œã§ä½¿ç”¨ã•ã‚Œã‚‹å¤‰æ•°ã®å€¤ã‚’指定ã—ã¾ã™ã€‚ %{settings_link} ã§æŒ‡å®šã•ã‚ŒãŸå€¤ãŒãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã§ä½¿ç”¨ã•ã‚Œã¾ã™ã€‚"
msgid "Pipeline|Stages"
-msgstr ""
+msgstr "ステージ"
msgid "Pipeline|Status"
-msgstr ""
+msgstr "ステータス"
msgid "Pipeline|Stop pipeline"
msgstr "パイプラインã®åœæ­¢"
@@ -6969,9 +7255,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -6981,9 +7279,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr "çµæžœã‚’表示ã™ã‚‹ã«ã¯ã€å°‘ãªãã¨ã‚‚1ã¤ã®ãƒ•ã‚£ãƒ«ã‚¿ãƒ¼ã‚’é¸æŠžã—ã¦ãã ã•ã„"
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "reCAPTCHA を解決ã—ã¦ãã ã•ã„"
@@ -7008,6 +7315,9 @@ msgstr "基本設定"
msgid "Preferences|Navigation theme"
msgstr "ナビゲーションテーマ"
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7015,13 +7325,13 @@ msgid "Prevent adding new members to project membership within this group"
msgstr ""
msgid "Preview"
-msgstr ""
+msgstr "プレビュー"
msgid "Preview payload"
msgstr ""
msgid "Primary"
-msgstr ""
+msgstr "プライマリ"
msgid "Prioritize"
msgstr "優先順ä½ã‚’付ã‘ã‚‹"
@@ -7036,7 +7346,7 @@ msgid "Prioritized label"
msgstr "優先ラベル"
msgid "Private"
-msgstr ""
+msgstr "プライベート"
msgid "Private - Project access must be granted explicitly to each user."
msgstr "プライベート - å„ユーザーã«ãƒ—ロジェクトã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©é™ã‚’明示的ã«è¨±å¯ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
@@ -7051,7 +7361,7 @@ msgid "Profile"
msgstr "プロフィール"
msgid "Profile Settings"
-msgstr ""
+msgstr "プロファイルã®è¨­å®š"
msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
msgstr ""
@@ -7075,13 +7385,13 @@ msgid "Profiles|Add key"
msgstr "キーを追加"
msgid "Profiles|Add status emoji"
-msgstr ""
+msgstr "ステータス絵文字を追加"
msgid "Profiles|Avatar cropper"
msgstr ""
msgid "Profiles|Avatar will be removed. Are you sure?"
-msgstr ""
+msgstr "ã‚¢ãƒã‚¿ãƒ¼ã‚’削除ã—ã¾ã™ã€‚よã‚ã—ã„ã§ã™ã‹ï¼Ÿ"
msgid "Profiles|Change username"
msgstr "ユーザーåã®å¤‰æ›´"
@@ -7090,7 +7400,7 @@ msgid "Profiles|Changing your username can have unintended side effects."
msgstr ""
msgid "Profiles|Choose file..."
-msgstr ""
+msgstr "ファイルをé¸æŠž..."
msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information"
msgstr ""
@@ -7099,7 +7409,7 @@ msgid "Profiles|City, country"
msgstr ""
msgid "Profiles|Clear status"
-msgstr ""
+msgstr "ステータスをクリア"
msgid "Profiles|Click on icon to activate signin with one of the following services"
msgstr ""
@@ -7114,7 +7424,7 @@ msgid "Profiles|Current path: %{path}"
msgstr "ç¾åœ¨ã®ãƒ‘ス: %{path}"
msgid "Profiles|Current status"
-msgstr ""
+msgstr "ç¾åœ¨ã®çŠ¶æ…‹"
msgid "Profiles|Delete Account"
msgstr "アカウント削除"
@@ -7132,13 +7442,13 @@ msgid "Profiles|Disconnect"
msgstr ""
msgid "Profiles|Do not show on profile"
-msgstr ""
+msgstr "プロフィールã«è¡¨ç¤ºã—ãªã„"
msgid "Profiles|Don't display activity-related personal information on your profiles"
msgstr ""
msgid "Profiles|Edit Profile"
-msgstr ""
+msgstr "プロフィールを編集"
msgid "Profiles|Enter your name, so people you know can recognize you"
msgstr ""
@@ -7180,7 +7490,7 @@ msgid "Profiles|Remove avatar"
msgstr ""
msgid "Profiles|Set new profile picture"
-msgstr ""
+msgstr "æ–°ã—ã„プロフィール画åƒã‚’設定ã™ã‚‹"
msgid "Profiles|Social sign-in"
msgstr ""
@@ -7206,9 +7516,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7285,10 +7592,10 @@ msgid "Profiles|Your status"
msgstr ""
msgid "Profiles|e.g. My MacBook key"
-msgstr ""
+msgstr "例:MacBook ã®ã‚­ãƒ¼"
msgid "Profiles|username"
-msgstr ""
+msgstr "ユーザーå"
msgid "Profiles|website.com"
msgstr ""
@@ -7308,6 +7615,9 @@ msgstr "進行状æ³"
msgid "Project"
msgstr "プロジェクト"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "プロジェクト '%{project_name}' ã¯å‰Šé™¤ä¸­ã§ã™ã€‚"
@@ -7350,9 +7660,12 @@ msgstr "プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆãƒªãƒ³ã‚¯ã¯æœŸé™åˆ‡ã‚Œã«ãªã‚Š
msgid "Project export started. A download link will be sent by email."
msgstr "プロジェクトã®ã‚¨ã‚¯ã‚¹ãƒãƒ¼ãƒˆã‚’開始ã—ã¾ã—ãŸã€‚ダウンロードã®ãƒªãƒ³ã‚¯ã¯ãƒ¡ãƒ¼ãƒ«ã§é€ä¿¡ã—ã¾ã™"
-msgid "Project members"
+msgid "Project has too many %{label_for_message} to search"
msgstr ""
+msgid "Project members"
+msgstr "プロジェクトメンãƒãƒ¼"
+
msgid "Project name"
msgstr "プロジェクトå"
@@ -7360,7 +7673,7 @@ msgid "Project slug"
msgstr ""
msgid "Project:"
-msgstr ""
+msgstr "プロジェクト:"
msgid "ProjectActivityRSS|Subscribe"
msgstr "講読"
@@ -7449,6 +7762,9 @@ msgstr ""
msgid "Projects"
msgstr "プロジェクト"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7624,31 +7940,31 @@ msgid "ProtectedEnvironment|Environment"
msgstr ""
msgid "ProtectedEnvironment|Protect"
-msgstr ""
+msgstr "ä¿è­·"
msgid "ProtectedEnvironment|Protect Environments in order to restrict who can execute deployments."
msgstr ""
msgid "ProtectedEnvironment|Protect an environment"
-msgstr ""
+msgstr "環境をä¿è­·"
msgid "ProtectedEnvironment|Protected Environment (%{protected_environments_count})"
-msgstr ""
+msgstr "ä¿è­·ã•ã‚ŒãŸç’°å¢ƒ (%{protected_environments_count})"
msgid "ProtectedEnvironment|Select an environment"
-msgstr ""
+msgstr "環境をé¸æŠž"
msgid "ProtectedEnvironment|There are currently no protected environments, protect an environment with the form above."
-msgstr ""
+msgstr "ç¾åœ¨ä¿è­·ã•ã‚ŒãŸç’°å¢ƒã¯ã‚ã‚Šã¾ã›ã‚“。上ã®ãƒ•ã‚©ãƒ¼ãƒ ã§ç’°å¢ƒã‚’ä¿è­·ã—ã¦ãã ã•ã„。"
msgid "ProtectedEnvironment|Unprotect"
-msgstr ""
+msgstr "ä¿è­·ã®è§£é™¤"
msgid "ProtectedEnvironment|Your environment can't be unprotected"
-msgstr ""
+msgstr "ã‚ãªãŸã®ç’°å¢ƒã®ä¿è­·ã‚’解除ã§ãã¾ã›ã‚“。"
msgid "ProtectedEnvironment|Your environment has been protected."
-msgstr ""
+msgstr "ã‚ãªãŸã®ç’°å¢ƒã¯ä¿è­·ã•ã‚Œã¦ã„ã¾ã™ã€‚"
msgid "ProtectedEnvironment|Your environment has been unprotected"
msgstr ""
@@ -7657,13 +7973,13 @@ msgid "Protip:"
msgstr ""
msgid "Provider"
-msgstr ""
+msgstr "プロãƒã‚¤ãƒ€ãƒ¼"
msgid "Pseudonymizer data collection"
msgstr ""
msgid "Public"
-msgstr ""
+msgstr "公開"
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "公開- グループãŠã‚ˆã³å…¬é–‹ãƒ—ロジェクトã¯èªè¨¼ç„¡ã—ã§é–²è¦§ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™"
@@ -7672,10 +7988,10 @@ msgid "Public - The project can be accessed without any authentication."
msgstr "公開 - プロジェクトã¯èªè¨¼ç„¡ã—ã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã¾ã™"
msgid "Public pipelines"
-msgstr ""
+msgstr "公開パイプライン"
msgid "Pull"
-msgstr ""
+msgstr "プル"
msgid "Push"
msgstr ""
@@ -7807,6 +8123,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr "ã‚¢ãƒã‚¿ãƒ¼ã‚’削除"
@@ -7942,6 +8264,17 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "GitLab ã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹éš›ã«ã€ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒåˆ©ç”¨è¦ç´„ã«åŒæ„ã™ã‚‹ã“ã¨ã‚’è¦æ±‚ã—ã¾ã™ã€‚"
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+
msgid "Resend invite"
msgstr ""
@@ -8085,7 +8418,7 @@ msgid "Runners can be placed on separate users, servers, even on your local mach
msgstr ""
msgid "Runners currently online: %{active_runners_count}"
-msgstr ""
+msgstr "ç¾åœ¨ã‚ªãƒ³ãƒ©ã‚¤ãƒ³ã®Runner: %{active_runners_count}"
msgid "Runners page"
msgstr ""
@@ -8099,6 +8432,9 @@ msgstr ""
msgid "Running"
msgstr "稼åƒä¸­"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "SAML SSO"
@@ -8129,6 +8465,9 @@ msgstr "SSH 公開éµ"
msgid "SSL Verification"
msgstr "SSL ã®æ¤œè¨¼"
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "ä¿å­˜"
@@ -8162,6 +8501,9 @@ msgstr "スケジュール済"
msgid "Schedules"
msgstr "スケジュール"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "パイプラインスケジューリング"
@@ -8217,11 +8559,14 @@ msgid "Search project"
msgstr "プロジェクトを検索"
msgid "Search projects"
-msgstr ""
+msgstr "プロジェクトを検索"
msgid "Search users"
msgstr "ユーザーを検索"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8480,6 +8825,9 @@ msgstr "インスタンス全体レベルã®ãƒ†ãƒ³ãƒ—レートリãƒã‚¸ãƒˆãƒªã‚’
msgid "Set max session time for web terminal."
msgstr "ウェブターミナルã®æœ€å¤§ã‚»ãƒƒã‚·ãƒ§ãƒ³ã‚¿ã‚¤ãƒ ã‚’設定ã™ã‚‹ã€‚"
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "迷惑行為レãƒãƒ¼ãƒˆã®é€šçŸ¥ãƒ¡ãƒ¼ãƒ«ã‚’設定ã™ã‚‹ã€‚"
@@ -8504,6 +8852,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8561,9 +8912,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr "シャーロックトランザクション"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "コマンドを表示"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "完全ãªç”Ÿãƒ­ã‚°ã‚’表示ã™ã‚‹"
@@ -8661,6 +9018,24 @@ msgstr ""
msgid "Snippets"
msgstr "スニペット"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8931,6 +9306,12 @@ msgstr "スター付ãプロジェクトã®æ´»å‹•"
msgid "Starred projects"
msgstr "スター付ãプロジェクト"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9130,7 +9511,7 @@ msgid "Suggested change"
msgstr ""
msgid "Sunday"
-msgstr ""
+msgstr "日曜日"
msgid "Support for custom certificates is disabled. Ask your system's administrator to enable it."
msgstr ""
@@ -9141,6 +9522,9 @@ msgstr "ブランãƒãƒ»ã‚¿ã‚°åˆ‡ã‚Šæ›¿ãˆ"
msgid "Sync information"
msgstr "åŒæœŸæƒ…å ±"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "システムフック"
@@ -9160,10 +9544,10 @@ msgid "System metrics (Kubernetes)"
msgstr ""
msgid "Tag"
-msgstr ""
+msgstr "ã‚¿ã‚°"
msgid "Tag list:"
-msgstr ""
+msgstr "タグ一覧:"
msgid "Tags"
msgstr "ã‚¿ã‚°"
@@ -9330,6 +9714,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr "許å¯ã•ã‚Œã‚‹æœ€å¤§ãƒ•ã‚¡ã‚¤ãƒ«ã‚µã‚¤ã‚ºã¯ 200KB ã§ã™ã€‚"
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "秘密éµã®å¾©å·ã«å¿…è¦ã¨ãªã‚‹ãƒ‘スフレーズ。ã“ã‚Œã¯ã‚ªãƒ—ションã§ã€å€¤ã¯æš—å·åŒ–ã—ã¦ä¿å­˜ã•ã‚Œã¾ã™ã€‚"
@@ -9453,6 +9840,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr "ユーザーã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティカレンダーã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr "通知設定をä¿å­˜ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
@@ -9498,6 +9888,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr "編集を開始ã—ã¦ã‹ã‚‰ãƒ–ランãƒãŒæ›´æ–°ã•ã‚Œã¦ã„ã¾ã™ã€‚æ–°ã—ã„ブランãƒã‚’作æˆã—ã¾ã™ã‹ï¼Ÿ"
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9508,7 +9913,7 @@ msgid "This date is before the start date, so this epic won't appear in the road
msgstr ""
msgid "This diff is collapsed."
-msgstr "ã“ã®å·®åˆ†ã¯å´©å£Šã—ã¦ã„ã¾ã™ã€‚"
+msgstr "ã“ã®å·®åˆ†ã¯æŠ˜ã‚ŠãŸãŸã¾ã‚Œã¦ã„ã¾ã™ã€‚"
msgid "This diff was suppressed by a .gitattributes entry."
msgstr ""
@@ -9519,6 +9924,9 @@ msgstr "ディレクトリ"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr "ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—"
@@ -9603,6 +10011,12 @@ msgstr "空リãƒã‚¸ãƒˆãƒªã‚’作æˆã¾ãŸã¯æ—¢å­˜ãƒªãƒã‚¸ãƒˆãƒªã‚’インãƒãƒ¼
msgid "This merge request is locked."
msgstr "ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã¯ãƒ­ãƒƒã‚¯ã•ã‚Œã¦ã„ã¾ã™ã€‚"
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr "ã‚ãªãŸã¯ç¾åœ¨ã®ãƒ–ランãƒã«å¯¾ã—ã¦æ›¸ãè¾¼ã¿æ¨©é™ãŒãªã„ã®ã§ã€ã“ã®ã‚ªãƒ—ションã¯ç„¡åŠ¹ã§ã™"
@@ -10077,7 +10491,7 @@ msgid "Type"
msgstr "タイプ"
msgid "URL"
-msgstr ""
+msgstr "URL"
msgid "Unable to load the diff. %{button_try_again}"
msgstr "差分を読ã¿è¾¼ã‚€ã“ã¨ãŒã§ãã¾ã›ã‚“。%{button_try_again}"
@@ -10160,6 +10574,9 @@ msgstr ""
msgid "Update"
msgstr "アップデート"
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10169,6 +10586,9 @@ msgstr "今ã™ãæ›´æ–°"
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "更新中"
@@ -10397,6 +10817,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10406,6 +10829,9 @@ msgstr "ファイルを表示 @ "
msgid "View group labels"
msgstr "グループラベルを表示"
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "課題を表示"
@@ -10709,6 +11135,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "アクセスリクエストをå–り消ã™"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10814,6 +11243,9 @@ msgstr ""
msgid "You have no permissions"
msgstr "権é™ãŒã‚ã‚Šã¾ã›ã‚“"
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "プロジェクト数ã®ä¸Šé™ã«é”ã—ã¦ã„ã¾ã™"
@@ -10874,6 +11306,9 @@ msgstr "SSH éµã‚’プロフィールã«è¿½åŠ ã—ãªã„é™ã‚Šã€SSH 経由ã§ãƒ—ã
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "有効ãªæ¯”較を行ã†ãŸã‚ã«ã¯ã€ç•°ãªã‚‹ãƒ–ランãƒåを使用ã™ã‚‹å¿…è¦ãŒã‚ã‚Šã¾ã™ã€‚"
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -10967,6 +11402,9 @@ msgstr "自分ã«å‰²ã‚Šå½“ã¦"
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr "ブランãƒå"
@@ -11046,6 +11484,9 @@ msgstr "コードã®å“質"
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11138,9 +11579,6 @@ msgstr "パーフォーマンスメトリクスã«å¤‰æ›´ã¯ã‚ã‚Šã¾ã›ã‚“"
msgid "ciReport|Performance metrics"
msgstr "パフォーマンスメトリクス"
-msgid "ciReport|Revert dismissal"
-msgstr "無視ã®å–り消ã—"
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11180,6 +11618,9 @@ msgstr "ä¾å­˜é–¢ä¿‚スキャンレãƒãƒ¼ãƒˆã®èª­ã¿è¾¼ã¿ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™º
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "無視ã®å–り消ã—中ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚ã‚‚ã†ä¸€åº¦ã‚„ã‚Šç›´ã—ã¦ãã ã•ã„。"
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "%{name} ã‚’ %{version} ã‹ã‚‰ %{fixed} ã¸ã‚¢ãƒƒãƒ—グレードã—ã¦ãã ã•ã„。"
@@ -11245,10 +11686,10 @@ msgid "epic"
msgstr ""
msgid "error"
-msgstr ""
+msgstr "エラー"
msgid "error code:"
-msgstr ""
+msgstr "エラー コード:"
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr "%{slash_command} コマンドã§è¦‹ç©æ™‚é–“ã‚’æ›´æ–°ã§ãã¾ã™ã€‚"
@@ -11268,9 +11709,6 @@ msgstr "ヘルプ"
msgid "here"
msgstr "ã“ã“"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://your-bitbucket-server"
@@ -11387,6 +11825,9 @@ msgstr "承èªã‚’é€ä¿¡ä¸­ã«ã‚¨ãƒ©ãƒ¼ãŒç™ºç”Ÿã—ã¾ã—ãŸã€‚"
msgid "mrWidget|Approve"
msgstr "承èª"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "承èªè€…"
@@ -11459,6 +11900,9 @@ msgstr "ローカルã§ãƒžãƒ¼ã‚¸"
msgid "mrWidget|Merge request approved"
msgstr "マージリクエストãŒæ‰¿èªã•ã‚Œã¾ã—ãŸ"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11518,6 +11962,9 @@ msgstr "リãƒãƒ¼ãƒˆ"
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr "æ–°ã—ã„マージリクエストã§ã“ã®ãƒžãƒ¼ã‚¸ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’リãƒãƒ¼ãƒˆã™ã‚‹"
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "設定者"
diff --git a/locale/ko/gitlab.po b/locale/ko/gitlab.po
index 369ca42cb2f..36474fb85d1 100644
--- a/locale/ko/gitlab.po
+++ b/locale/ko/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ko\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 10:21\n"
+"PO-Revision-Date: 2019-03-06 15:16\n"
msgid " Status"
msgstr " ìƒíƒœ"
@@ -30,17 +30,21 @@ msgid_plural " improved on %d points"
msgstr[0] " %d í¬ì¸íŠ¸ í–¥ìƒ"
msgid " or "
-msgstr ""
+msgstr " ë˜ëŠ” "
msgid " or <#epic id>"
-msgstr ""
+msgstr " ë˜ëŠ” <#epic id>"
msgid " or <#issue id>"
-msgstr ""
+msgstr " ë˜ëŠ” <#issue id>"
msgid "\"%{query}\" in projects"
msgstr "프로ì íŠ¸ì—ì„œ \"%{query}\""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%dê°œì˜ ì»¤ë°‹"
@@ -50,7 +54,7 @@ msgid_plural "%d commits behind"
msgstr[0] "%d 커밋 behind"
msgid "%d commits"
-msgstr ""
+msgstr "%dê°œì˜ ì»¤ë°‹"
msgid "%d exporter"
msgid_plural "%d exporters"
@@ -70,7 +74,7 @@ msgstr[0] "%d ì´ìŠˆ"
msgid "%d issue selected"
msgid_plural "%d issues selected"
-msgstr[0] ""
+msgstr[0] "%d ì´ìŠˆê°€ ì„ íƒë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "%d layer"
msgid_plural "%d layers"
@@ -111,12 +115,25 @@ msgstr "%{counter_storage} (%{counter_repositories} 저장소, %{counter_build_a
msgid "%{count} %{alerts}"
msgstr "%{count} %{alerts}"
-msgid "%{count} more"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+
+msgid "%{count} approvals from %{name}"
msgstr ""
+msgid "%{count} more"
+msgstr "%{count} ê°œ ë”보기"
+
msgid "%{count} more assignees"
msgstr "%{count}ëª…ì˜ ë‹´ë‹¹ìž"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} ëª…ì˜ ì°¸ì—¬ìž"
@@ -131,18 +148,18 @@ msgstr "%{filePath} ì‚­ì œë¨"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} +%{labelCount} ë”보기"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{group_docs_link_start}그룹%{group_docs_link_end}ì„ ì‚¬ìš©í•˜ë©´ 여러 프로ì íŠ¸ë¥¼ 관리하고 ê³µë™ ìž‘ì—…ì„ ìˆ˜í–‰ í•  수 있습니다. 그룹 회ì›ì€ 모든 프로ì íŠ¸ì— 액세스 í•  수 있습니다."
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType}ì´ ì‚­ì œë©ë‹ˆë‹¤! 확실합니까?"
-msgid "%{link_start}Read more%{link_end} about role permissions"
+msgid "%{label_for_message} unavailable"
msgstr ""
+msgid "%{link_start}Read more%{link_end} about role permissions"
+msgstr "%{link_start}ì—­í•  ê¶Œí•œì— ëŒ€í•´ ìžì„¸ížˆ 알아보기%{link_end}"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 시작ë¨"
@@ -162,22 +179,22 @@ msgid "%{percent}%% complete"
msgstr "%{percent}%% 완료"
msgid "%{state} epics"
-msgstr ""
+msgstr "%{state} ì—픽"
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{branch_count}%{strong_end} 브랜치"
msgid "%{strong_start}%{commit_count}%{strong_end} Commit"
msgid_plural "%{strong_start}%{commit_count}%{strong_end} Commits"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{commit_count}%{strong_end} 커밋"
msgid "%{strong_start}%{human_size}%{strong_end} Files"
-msgstr ""
+msgstr "%{strong_start}%{human_size}%{strong_end} 파ì¼"
msgid "%{strong_start}%{tag_count}%{strong_end} Tag"
msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{tag_count}%{strong_end} 태그"
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
@@ -196,10 +213,10 @@ msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what infor
msgstr "GitLab Inc와 공유ë˜ëŠ” ì •ë³´ì— ëŒ€í•´ %{usage_ping_link_start}ë” ì•Œì•„ë³´ê¸°%{usage_ping_link_end}"
msgid "%{user_name} profile page"
-msgstr ""
+msgstr "%{user_name} 프로필 페ì´ì§€"
msgid "(external source)"
-msgstr ""
+msgstr "(외부 소스)"
msgid "+ %{count} more"
msgstr "+ %{count} ë”보기"
@@ -207,9 +224,12 @@ msgstr "+ %{count} ë”보기"
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} ë”"
-msgid ", or "
+msgid "+%{extraOptionCount} more"
msgstr ""
+msgid ", or "
+msgstr ", ë˜ëŠ” "
+
msgid "- Runner is active and can process any new jobs"
msgstr "- Runner ê°€ 활성화ë˜ì—ˆê³ , 새로운 ìž‘ì—…ì„ ì²˜ë¦¬í•  수 있습니다."
@@ -227,6 +247,10 @@ msgid "1 %{type} modification"
msgid_plural "%{count} %{type} modifications"
msgstr[0] "%{count} ê°œ %{type} ì˜ ìˆ˜ì •ì‚¬í•­"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "%d ê°œì˜ ì´ìŠˆ closed"
@@ -267,13 +291,13 @@ msgid "1st contribution!"
msgstr "첫번째 기여!"
msgid "2FA"
-msgstr ""
+msgstr "2단계 ì¸ì¦(2FA)"
msgid "2FA enabled"
msgstr "2FA 사용"
msgid "403|Please contact your GitLab administrator to get permission."
-msgstr ""
+msgstr "403|ê¶Œí•œì„ ì–»ìœ¼ë ¤ë©´ GitLab 관리ìžì—게 문ì˜í•˜ì‹­ì‹œì˜¤."
msgid "403|You don't have the permission to access this page."
msgstr "ì´ íŽ˜ì´ì§€ì— 대한 액세스 ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
@@ -315,11 +339,26 @@ msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong>
msgstr "<strong>%{pushes}</strong> íšŒì˜ í‘¸ì‹œ, <strong>%{commits}</strong> 회 ì´ìƒì˜ ì»¤ë°‹ì´ <strong>%{people}</strong> 기여ìžì— ì˜í•´ ì¼ì–´ 났습니다. "
msgid "<strong>Deletes</strong> source branch"
-msgstr ""
+msgstr "소스 브랜치 <strong>삭제</strong>"
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "'러너(Runner)'는 ìž‘ì—…ì„ ì‹¤í–‰í•˜ëŠ” 프로세스입니다. 필요한 ë§Œí¼ ëŸ¬ë„ˆë¥¼ ì…‹ì—…í•  수 있습니다."
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "지ì†ì ì¸ í†µí•©ì— ê´€í•œ 그래프 모ìŒ"
@@ -335,11 +374,14 @@ msgstr "GitLabì˜ ì‚¬ì´ë²„ í­ë ¥ ë°©ì§€íŒ€ì´ ìµœëŒ€í•œ 빨리 ê·€í•˜ì˜ ë¦¬í
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "í¬í¬ì™€ 새 머지 리퀘스트(MR)ê°€ 시작ë˜ë©´ 새로운 브랜치가 만들어질 것입니다."
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A 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 ""
+msgstr "ìž‘ì—… 트레ì´ìŠ¤ì˜ 테스트 커버리지 결과를 알아 보기 위해 사용ë˜ëŠ” 정규표현ì‹ìž…니다. ë¬´íš¨ì¼ ê²½ìš° 공백입니다."
msgid "A user with write access to the source branch selected this option"
msgstr "소스 ë¸Œëžœì¹˜ì— ëŒ€í•œ 쓰기 ê¶Œí•œì´ ìžˆëŠ” 사용ìžê°€ ì´ ì˜µì…˜ì„ ì„ íƒí•˜ì˜€ìŠµë‹ˆë‹¤."
@@ -363,7 +405,7 @@ msgid "Abuse reports"
msgstr "악용 사례 보고서"
msgid "Accept invitation"
-msgstr ""
+msgstr "초대 수ë½"
msgid "Accept terms"
msgstr "약관ë™ì˜"
@@ -402,7 +444,7 @@ msgid "Add"
msgstr "추가"
msgid "Add CHANGELOG"
-msgstr ""
+msgstr "ë³€ê²½ëœ ë¡œê·¸ 추가"
msgid "Add CONTRIBUTING"
msgstr ""
@@ -419,24 +461,51 @@ msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 추가"
msgid "Add README"
msgstr ""
-msgid "Add a general comment to this %{noteable_name}."
+msgid "Add a bullet list"
msgstr ""
+msgid "Add a general comment to this %{noteable_name}."
+msgstr "ì´ %{noteable_name} ì— ì¼ë°˜ì ì¸ 코멘트를 추가 합니다."
+
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "프로ì íŠ¸ì— 관한 ì •ë³´ê°€ 담긴 홈페ì´ì§€ë¥¼ wikiì— ì¶”ê°€í•˜ë©´ GitLabì´ ë©”ì‹œì§€ 대신 ì—¬ê¸°ì— í‘œì‹œ 합니다."
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "í…Œì´ë¸” 추가"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "모든 ì´ë©”ì¼ì— í‘œì‹œë  í…스트를 추가합니다. %{character_limit} ìž ì œí•œì´ ìžˆìŠµë‹ˆë‹¤."
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "댓글 추가"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "ì´ë¯¸ì§€ 댓글 추가"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "ë¼ì´ì„ ìŠ¤ 추가"
@@ -453,7 +522,7 @@ msgid "Add reaction"
msgstr "ë°˜ì‘ ì¶”ê°€"
msgid "Add to project"
-msgstr ""
+msgstr "프로ì íŠ¸ì— 추가"
msgid "Add to review"
msgstr "리뷰 추가"
@@ -465,7 +534,7 @@ msgid "Add user(s) to the group:"
msgstr "ê·¸ë£¹ì— ì‚¬ìš©ìž ì¶”ê°€:"
msgid "Add users or groups who are allowed to approve every merge request"
-msgstr ""
+msgstr "모든 머지요청(MR) ì„ ìŠ¹ì¸í•  수 있는 ì‚¬ìš©ìž ë˜ëŠ” 그룹 추가"
msgid "Add users to group"
msgstr "ê·¸ë£¹ì— ì‚¬ìš©ìž ì¶”ê°€"
@@ -516,40 +585,40 @@ msgid "AdminProjects|Delete project"
msgstr "프로ì íŠ¸ ì‚­ì œ"
msgid "AdminSettings|Auto DevOps domain"
-msgstr ""
+msgstr "Auto DevOps ë„ë©”ì¸"
msgid "AdminSettings|Environment variables are protected by default"
-msgstr ""
+msgstr "환경 변수는 기본ì ìœ¼ë¡œ 보호ë©ë‹ˆë‹¤."
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
msgstr "모든 프로ì íŠ¸ì˜ ìžë™ 앱 리뷰 ë° ìžë™ ë°°í¬ ë‹¨ê³„ì—ì„œ 기본ì ìœ¼ë¡œ 사용할 ë„ë©”ì¸ì„ 지정하십시오."
msgid "AdminSettings|When creating a new environment variable it will be protected by default."
-msgstr ""
+msgstr "새 환경 변수를 작성할 ë•Œ 기본ì ìœ¼ë¡œ 보호ë©ë‹ˆë‹¤."
msgid "AdminUsers|2FA Disabled"
-msgstr ""
+msgstr "2FA 사용 중지"
msgid "AdminUsers|2FA Enabled"
-msgstr ""
+msgstr "2FA 사용"
msgid "AdminUsers|Active"
-msgstr ""
+msgstr "활성"
msgid "AdminUsers|Admin"
-msgstr ""
+msgstr "관리ìž"
msgid "AdminUsers|Admins"
-msgstr ""
+msgstr "관리ìžë“¤"
msgid "AdminUsers|Block user"
msgstr "ì‚¬ìš©ìž ì°¨ë‹¨"
msgid "AdminUsers|Blocked"
-msgstr ""
+msgstr "차단ë¨"
msgid "AdminUsers|Cannot unblock LDAP blocked users"
-msgstr ""
+msgstr "LDAP ì°¨ë‹¨ëœ ì‚¬ìš©ìžì˜ ì°¨ë‹¨ì„ í•´ì œí•  수 없습니다."
msgid "AdminUsers|Delete User %{username} and contributions?"
msgstr " ì‚¬ìš©ìž %{username}와 기여를 삭제하시겠습니까?"
@@ -564,28 +633,28 @@ msgid "AdminUsers|Delete user and contributions"
msgstr "사용ìžì™€ 기여 ì‚­ì œ"
msgid "AdminUsers|External"
-msgstr ""
+msgstr "외부"
msgid "AdminUsers|It's you!"
-msgstr ""
+msgstr "ì´ê²ƒì€ 나!"
msgid "AdminUsers|New user"
-msgstr ""
+msgstr "새로운 유저"
msgid "AdminUsers|No users found"
-msgstr ""
+msgstr "유저를 ì°¾ì„ ìˆ˜ ì—†ìŒ"
msgid "AdminUsers|Search by name, email or username"
-msgstr ""
+msgstr "ì´ë¦„, ì´ë©”ì¼, ì‚¬ìš©ìž ì´ë¦„으로 검색"
msgid "AdminUsers|Search users"
-msgstr ""
+msgstr "유저 검색"
msgid "AdminUsers|Send email to users"
-msgstr ""
+msgstr "유저ì—게 ì´ë©”ì¼ ë³´ë‚´ê¸°"
msgid "AdminUsers|Sort by"
-msgstr ""
+msgstr "정렬 기준"
msgid "AdminUsers|To confirm, type %{projectName}"
msgstr "확ì¸ì„ 위해 %{projectName} 를 입력해주세요."
@@ -594,10 +663,10 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr "확ì¸ì„ 위해 %{username} ì„ ìž…ë ¥í•˜ì„¸ìš”"
msgid "AdminUsers|User will be blocked"
-msgstr ""
+msgstr "사용ìžê°€ 차단 ë©ë‹ˆë‹¤."
msgid "AdminUsers|Without projects"
-msgstr ""
+msgstr "프로ì íŠ¸ ì—†ì´"
msgid "Advanced permissions, Large File Storage and Two-Factor authentication settings."
msgstr "고급 사용 권한, 대용량 íŒŒì¼ ì €ìž¥ì†Œì™€ ì´ì¤‘-ì¸ì¦ 설정"
@@ -605,16 +674,22 @@ msgstr "고급 사용 권한, 대용량 íŒŒì¼ ì €ìž¥ì†Œì™€ ì´ì¤‘-ì¸ì¦ 설정
msgid "Advanced settings"
msgstr "고급 설정"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "알림"
msgid "Alerts"
-msgstr ""
+msgstr "알림"
msgid "All"
msgstr "ì „ì²´"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr "모든 ë³€ê²½ì‚¬í•­ì´ ì»¤ë°‹ë˜ì—ˆìŠµë‹ˆë‹¤."
@@ -622,13 +697,13 @@ msgid "All features are enabled for blank projects, from templates, or when impo
msgstr "모든 ê¸°ëŠ¥ì€ ë¹„ì–´ìžˆëŠ” 프로ì íŠ¸, 템플릿, 가져올 ë•Œ 사용할 수 있지만 ë‚˜ì¤‘ì— í”„ë¡œì íŠ¸ 설정ì—ì„œ 비활성화 í•  수 있습니다."
msgid "All issues for this milestone are closed. You may close this milestone now."
-msgstr ""
+msgstr "현재 마ì¼ìŠ¤í†¤ì— ê´€ë ¨ëœ ëª¨ë“  ì´ìŠˆë“¤ì´ 마ê°ë˜ì—ˆìŠµë‹ˆë‹¤. 현재 마ì¼ìŠ¤í†¤ì„ 마ê°í•  수 있습니다."
msgid "All users"
msgstr "모든 사용ìž"
msgid "Allow \"%{group_name}\" to sign you in"
-msgstr ""
+msgstr "\"%{group_name}\"ì„ ìž…ë ¥í•˜ë©´ ë¡œê·¸ì¸ í•  수 있습니다."
msgid "Allow commits from members who can merge to the target branch."
msgstr "ëŒ€ìƒ ë¸Œëžœì¹˜ì— ë¨¸ì§€í•  수 있는 ë©¤ë²„ì˜ ì»¤ë°‹ì„ í—ˆìš©í•©ë‹ˆë‹¤."
@@ -684,7 +759,7 @@ msgstr "비어 있는 GitLab User 필드는 FogBugzì˜ ì‚¬ìš©ìž ì „ì²´ ì´ë¦„ (
msgid "An error has occurred"
msgstr "ì—러가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -696,6 +771,15 @@ msgstr "새 드래프트를 추가하는 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr "BLOB 미리보기 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -708,6 +792,9 @@ msgstr "ì´ìŠˆ ì¤‘ìš”ë„ ì—…ë°ì´íŠ¸ 중 문제가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while adding approver"
msgstr "승ì¸ìžë¥¼ ì¶”ê°€í•˜ë˜ ì¤‘ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "ëŒ“ê¸€ì„ ì‚­ì œí•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -744,6 +831,9 @@ msgstr "ìž‘ì—…ì„ ê°€ì ¸ 오는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while fetching the pipeline."
msgstr "파ì´í”„ë¼ì¸ì„ ë°˜ì˜í•˜ë˜ 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "프로ì íŠ¸ë¥¼ 가져오는 ë™ì•ˆ 오류가 ë°œìƒ í–ˆìŠµë‹ˆë‹¤."
@@ -801,12 +891,18 @@ msgstr "LDAP 무시 ìƒíƒœë¥¼ 저장하는 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤. 다
msgid "An error occurred while saving assignees"
msgstr "담당ìžë¥¼ 저장하는 중 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr "ì•Œë¦¼ì„ êµ¬ë…하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "An error occurred while unsubscribing to notifications."
msgstr "알림 구ë…ì„ í•´ì œí•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "ëŒ“ê¸€ì„ ì—…ë°ì´íŠ¸í•˜ëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -897,6 +993,40 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -939,9 +1069,15 @@ msgstr "공개키를 재ìƒì„±í•˜ì‹œê² ìŠµë‹ˆê¹Œ? 미러ë§ì´ 다시 ë™ìž‘하ê
msgid "Are you sure you want to remove %{group_name}?"
msgstr "ì •ë§ë¡œ %{group_name}(ì„)를 ì‚­ì œ 하시겠습니까?"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1026,6 +1162,9 @@ msgstr "ë‹´ë‹¹ìž ëª©ë¡ì€ ì„ íƒëœ 사용ìžì—게 할당 ëœ ëª¨ë“  ì´ìŠˆê°
msgid "Assignee(s)"
msgstr "담당ìž"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1041,9 +1180,6 @@ msgstr "8ì›”"
msgid "August"
msgstr "8ì›”"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "ì¸ì¦ 로그"
@@ -1510,19 +1646,19 @@ msgid "ByAuthor|by"
msgstr "작성ìž"
msgid "CHANGELOG"
-msgstr ""
+msgstr "변경 로그"
msgid "CI / CD"
msgstr "CI / CD"
msgid "CI / CD Charts"
-msgstr ""
+msgstr "CI/CD 차트"
msgid "CI / CD Settings"
msgstr "CI/CD 설정"
msgid "CI Lint"
-msgstr ""
+msgstr "CI 린트"
msgid "CI will run using the credentials assigned above."
msgstr "ìœ„ì˜ ìžê²© ì¦ëª…ì„ ì‚¬ìš©í•˜ì—¬ CIê°€ 실행ë©ë‹ˆë‹¤."
@@ -1602,6 +1738,9 @@ msgstr "ìžë™ìœ¼ë¡œ 머지할 수 없습니다."
msgid "Cannot modify managed Kubernetes cluster"
msgstr "ì´ë¯¸ êµ¬ì„±ëœ Kubernetes í´ëŸ¬ìŠ¤í„°ëŠ” 수정할 수 없습니다"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1638,6 +1777,9 @@ msgstr "Revert"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "기존 변경 ì‚¬í•­ì„ ë˜ëŒë¦¬ê¸° 위해 새로운 ì»¤ë°‹ì„ ë§Œë“­ë‹ˆë‹¤."
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "변경 ì‚¬í•­ì€ <b>source</b> ë¦¬ë¹„ì „ì´ <b>target</b> ë¦¬ë¹„ì „ì— ë¨¸ì§€ëœ ê²ƒì²˜ëŸ¼ 표시ë©ë‹ˆë‹¤."
@@ -1650,6 +1792,9 @@ msgstr "차트"
msgid "Chat"
msgstr "채팅"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "%{docs_link_start} 여기부터 %{docs_link_end} 여기까지 확ì¸."
@@ -1716,9 +1861,6 @@ msgstr "ì´ ì„¸ì»¨ë”리 ë…¸ë“œì— ë™ê¸°í™” í•  ê·¸ë£¹ì„ ì„ íƒí•˜ì„¸ìš”."
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr "Ci/CD 파ì´í”„ë¼ì¸ì´ ì—°ê²°ë˜ê³  실행할 저장소를 ì„ íƒí•´ 주세요."
-msgid "Choose which repositories you want to import."
-msgstr "가져올 저장소를 ì„ íƒí•´ì£¼ì„¸ìš”."
-
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr "ì´ ì„¸ì»¨ë”리 ë…¸ë“œì— ì–´ë–¤ 샤드 (shards)를 ë™ê¸°í™” 할지 ì„ íƒí•˜ì„¸ìš”."
@@ -1878,6 +2020,9 @@ msgstr "저장소 í´ë¡ "
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1893,9 +2038,6 @@ msgstr ""
msgid "Closed"
msgstr "닫힘"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "닫힌 ì´ìŠˆ"
@@ -1941,12 +2083,12 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "프로ì íŠ¸ ì˜ì—­ì„ 가져 오는 ë™ì•ˆ 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤: %{error}"
-
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "Google Cloud APIì— ì—°ê²°ì„ ì‹œë„하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤. ë‚˜ì¤‘ì— ë‹¤ì‹œ ì‹œë„하십시오."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr ""
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
msgstr ""
@@ -1983,6 +2125,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2079,7 +2224,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2094,9 +2239,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr "설치"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr "설치ë¨"
@@ -2142,9 +2284,6 @@ msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° ìƒì„¸"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° ìƒíƒœ"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„°ê°€ Google Kubernetes Engineì—ì„œ ìƒì„± 중입니다..."
@@ -2382,15 +2521,27 @@ msgstr "ClusterIntegration|가입"
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr "코드 소유ìž"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr "Cohorts"
msgid "Collapse"
msgstr "ê°ì¶”기"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "사ì´ë“œ ë°” 축소"
@@ -2689,6 +2840,9 @@ msgstr "%{protocol} í´ë¡  URL 복사"
msgid "Copy ID to clipboard"
msgstr "í´ë¦½ ë³´ë“œì— ID 복사"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr "SSH í´ë¡  URL 복사"
@@ -3176,6 +3330,9 @@ msgstr ""
msgid "Description:"
msgstr "설명:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr "제거"
@@ -3254,6 +3411,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr "닫기"
@@ -3425,6 +3585,9 @@ msgstr "ì´ í”„ë¡œì íŠ¸ì— 대해 사용"
msgid "Enable group Runners"
msgstr "그룹 Runner 사용"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3461,6 +3624,9 @@ msgstr "(UTC)ì— ì¢…ë£Œë©ë‹ˆë‹¤"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3479,9 +3645,6 @@ msgstr "머지 리퀘스트(MR) ì„¤ëª…ì„ ìž…ë ¥ 해주세요"
msgid "Enter the merge request title"
msgstr "머지 리퀘스트(MR) ì œëª©ì„ ìž…ë ¥ 해주세요"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3506,6 +3669,12 @@ msgstr "í™˜ê²½ê°’ë“¤ì„ ë°˜ì˜í•˜ëŠ” ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "Environments|An error occurred while making the request."
msgstr "ìš”ì²­ì„ ë§Œë“œëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3557,15 +3726,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "ëª¨ë‘ ë³´ê¸°"
@@ -3578,6 +3765,18 @@ msgstr "중지 환경"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "갱신ë¨"
@@ -3629,6 +3828,9 @@ msgstr "오류 ë³´ê³  ë° ë¡œê¹…"
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr "ì—í”½ì„ ë§Œë“œëŠ” 중 오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤."
@@ -3698,6 +3900,33 @@ msgstr "머지 리퀘스트(MR) 요청 중 오류 ë°œìƒ. 다시 ì‹œë„하십시
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3776,6 +4005,9 @@ msgstr "펼치기"
msgid "Expand all"
msgstr "ëª¨ë‘ í™•ìž¥"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "사ì´ë“œë°” 확장"
@@ -3863,7 +4095,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr "ì´ëª¨ì§€ 목ë¡ì„ 불러올 수 없습니다"
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3929,9 +4161,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr "설명"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr "%{feature_flag_name} 수정"
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr "Feature 플래그 수정"
@@ -3989,9 +4218,6 @@ msgstr "새로운"
msgid "FeatureFlags|New Feature Flag"
msgstr "새로운 Feature 플래그"
-msgid "FeatureFlags|Save changes"
-msgstr "변경사항 저장"
-
msgid "FeatureFlags|Status"
msgstr "ìƒíƒœ"
@@ -4077,9 +4303,6 @@ msgstr ""
msgid "Filter..."
msgstr "í•„í„°..."
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "경로로 찾기"
@@ -4194,8 +4417,8 @@ msgstr ".gitlab-ci.ymlì—ì„œ 오류를 발견했습니다:"
msgid "Free Trial of GitLab.com Gold"
msgstr "GitLab.com Gold 무료 ì²´í—˜íŒ"
-msgid "From %{provider_title}"
-msgstr "%{provider_title}ì—ì„œ"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "Bitbucketì—ì„œ"
@@ -4224,9 +4447,15 @@ msgstr "마ì¼ìŠ¤í†¤ì—ì„œ:"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Kubernetes í´ëŸ¬ìŠ¤í„° 세부사항 ë³´ê¸°ì˜ ì• í”Œë¦¬ì¼€ì´ì…˜ 목ë¡ì—ì„œ Runner를 설치하십시오."
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "GPG 키"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "ì¼ë°˜"
@@ -4659,15 +4888,24 @@ msgstr ""
msgid "Go Back"
msgstr "ì´ì „으로"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "뒤로 가기"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr "ì´ë™"
msgid "Go to %{link_to_google_takeout}."
msgstr "%{link_to_google_takeout}ë¡œ ì´ë™í•˜ì‹­ì‹œì˜¤."
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr "Google Code 가져오기"
@@ -4725,6 +4963,9 @@ msgstr "그룹 정보:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "그룹 관리ìžëŠ” grouo runners를 여기서 ë“±ë¡ í•  수 있습니다: %{link}"
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5092,9 +5333,24 @@ msgstr "저장소 가져 오기"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr "GitLab Enterprise Editionì„ í†µí•´ ì´ìŠˆ 보드를 í–¥ìƒ ì‹œí‚µë‹ˆë‹¤."
@@ -5146,6 +5402,12 @@ msgstr "호스트 키를 수ë™ìœ¼ë¡œ ìž…ë ¥"
msgid "Input your repository URL"
msgstr "저장소 URLì„ ìž…ë ¥ 하세요"
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5198,6 +5460,9 @@ msgstr "Cycle Analytics 소개"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5216,6 +5481,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr "ì´ìŠˆ"
@@ -5258,7 +5526,7 @@ msgstr "ì´ìŠˆëŠ” 버그, ìž‘ì—… í˜¹ì€ ë…¼ì˜í•  ì•„ì´ë””ì–´ì¼ ìˆ˜ 있습니ë
msgid "Issues closed"
msgstr "ì´ìŠˆ 닫힘"
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5447,6 +5715,9 @@ msgstr "ë ˆì´ë¸” 승격"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5529,6 +5800,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr "ë³´í˜¸ëœ ë¸Œëžœì¹˜ë“¤ì— ëŒ€í•´ ë” ì•Œì•„ë³´ê¸°"
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "ë” ìžì„¸ížˆ 알아보기"
@@ -5701,6 +5975,15 @@ msgstr "스마트 카드로 로그ì¸"
msgid "Logs"
msgstr "로그"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5746,6 +6029,9 @@ msgstr "Manifest"
msgid "Manifest file import"
msgstr "Manifest íŒŒì¼ ê°€ì ¸ì˜¤ê¸°"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5773,36 +6059,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr "마í¬ë‹¤ìš´ 활성화ë¨"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr "Maven 메타 ë°ì´í„°"
@@ -5857,6 +6113,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "머지 리퀘스트(MR)"
@@ -6007,9 +6266,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr "시스템"
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6154,6 +6410,9 @@ msgstr "ë” ë§Žì€ ì •ë³´"
msgid "More information is available|here"
msgstr "여기"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr "ë§Žì€ ë³„"
@@ -6230,15 +6489,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "새로운 파ì´í”„ë¼ì¸ ì¼ì •"
msgid "New Snippet"
msgstr "새 스니펫"
-msgid "New Snippets"
-msgstr "새 스니펫"
-
msgid "New branch"
msgstr "새 브랜치"
@@ -6299,9 +6558,15 @@ msgstr "새로 만들기..."
msgid "No"
msgstr "아니오"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr "ë¼ë²¨ ì—†ìŒ"
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6329,13 +6594,16 @@ msgstr "기여를 ì°¾ì„ ìˆ˜ 없습니다."
msgid "No credit card required."
msgstr "신용 카드가 필요하지 않습니다."
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr "기한 ì—†ìŒ"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6431,6 +6699,9 @@ msgstr "ë°ì´í„°ê°€ 충분하지 않습니다."
msgid "Not now"
msgstr "나중ì—"
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "마스터 브랜치는 ìžë™ìœ¼ë¡œ 보호ë©ë‹ˆë‹¤. %{link_to_protected_branches}"
@@ -6633,6 +6904,9 @@ msgstr "ìš´ì˜ìž"
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6642,6 +6916,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6834,6 +7111,12 @@ msgstr "변수"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "ì‚¬ìš©ìž ì •ì˜"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "파ì´í”„ë¼ì¸"
@@ -6849,6 +7132,9 @@ msgstr "ì§€ë‚œì£¼ì˜ íŒŒì´í”„ë¼ì¸"
msgid "Pipelines for last year"
msgstr "ì§€ë‚œí•´ì˜ íŒŒì´í”„ë¼ì¸"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr "ìžì‹ ìžˆê²Œ 빌드하세요"
@@ -6969,9 +7255,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -6981,9 +7279,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr "결과를 보려면 최소 í•˜ë‚˜ì˜ í•„í„°ë¥¼ ì„ íƒí•˜ì‹­ì‹œì˜¤"
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "ReCAPTCHA를 풀어 주십시오."
@@ -7008,6 +7315,9 @@ msgstr "환경 설정"
msgid "Preferences|Navigation theme"
msgstr "테마 íƒìƒ‰"
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7066,7 +7376,7 @@ msgid "Profiles|Account scheduled for removal."
msgstr "ê³„ì •ì´ ì‚­ì œë  ì˜ˆì •ìž…ë‹ˆë‹¤."
msgid "Profiles|Activate signin with one of the following services"
-msgstr ""
+msgstr "ë‹¤ìŒ ì„œë¹„ìŠ¤ 중 하나로 ë¡œê·¸ì¸ í™œì„±í™”"
msgid "Profiles|Active"
msgstr ""
@@ -7087,7 +7397,7 @@ msgid "Profiles|Change username"
msgstr "사용ìžëª… 변경"
msgid "Profiles|Changing your username can have unintended side effects."
-msgstr ""
+msgstr "ì‚¬ìš©ìž ì´ë¦„ì„ ë³€ê²½í•˜ë©´ ì˜ë„하지 ì•Šì€ ë¬¸ì œê°€ ìƒê¸¸ 수 있습니다."
msgid "Profiles|Choose file..."
msgstr "íŒŒì¼ ì„ íƒ.."
@@ -7206,9 +7516,6 @@ msgstr "ì´ ì´ë©”ì¼ì€ 웹 기반 ìž‘ì—…, 수정ì´ë‚˜ mergeì— ì‚¬ìš©ë©ë‹ˆë‹
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr "ì´ ê·¸ë¦¼ê³¼ 메시지는 프로필과 ì¸í„°íŽ˜ì´ìŠ¤ ì „ì²´ì— ë‚˜íƒ€ë‚©ë‹ˆë‹¤."
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7308,6 +7615,9 @@ msgstr "진행률"
msgid "Project"
msgstr "프로ì íŠ¸"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "프로ì íŠ¸ '%{project_name}' ì‚­ì œ 중입니다."
@@ -7350,6 +7660,9 @@ msgstr "프로ì íŠ¸ 내보내기 ë§í¬ê°€ 만료ë˜ì—ˆìŠµë‹ˆë‹¤. 프로ì íŠ¸
msgid "Project export started. A download link will be sent by email."
msgstr "프로ì íŠ¸ 내보내기가 시작ë˜ì—ˆìŠµë‹ˆë‹¤. 다운로드 ë§í¬ëŠ” ì´ë©”ì¼ë¡œ 전송ë©ë‹ˆë‹¤."
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7357,7 +7670,7 @@ msgid "Project name"
msgstr "프로ì íŠ¸ ì´ë¦„"
msgid "Project slug"
-msgstr ""
+msgstr "프로ì íŠ¸ 슬러그"
msgid "Project:"
msgstr ""
@@ -7449,6 +7762,9 @@ msgstr "사용ìžëŠ” ì´ ì €ìž¥ì†Œì— ì¸ì¦ëœ ì´ë©”ì¼ë¡œ ì»¤ë°‹ëœ ì»¤ë°‹ë§Œ
msgid "Projects"
msgstr "프로ì íŠ¸"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr "%{group_name}ê³¼ 공유ë˜ëŠ” 프로ì íŠ¸"
@@ -7807,6 +8123,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr "아바타 제거"
@@ -7942,6 +8264,17 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "GitLabì— ì•¡ì„¸ìŠ¤ í•  ë•Œ 모든 사용ìžê°€ 서비스 약관 ë° ê°œì¸ ì •ë³´ 취급 ë°©ì¹¨ì— ë™ì˜í•˜ë„ë¡í•˜ì‹­ì‹œì˜¤."
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+
msgid "Resend invite"
msgstr ""
@@ -8099,6 +8432,9 @@ msgstr "모든 공유 Runners 파ì´í”„ë¼ì¸ ì‹œê°„ì„ ì‚¬ìš©í–ˆìŠµë‹ˆë‹¤."
msgid "Running"
msgstr "실행중"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "SAML SSO"
@@ -8129,6 +8465,9 @@ msgstr "SSH 공개키"
msgid "SSL Verification"
msgstr "SSL ê²€ì¦"
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "저장"
@@ -8162,11 +8501,14 @@ msgstr "예정ë¨"
msgid "Schedules"
msgstr "ì¼ì •"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "파ì´í”„ë¼ì¸ 스케줄ë§"
msgid "Scope"
-msgstr ""
+msgstr "스코프"
msgid "Scoped issue boards"
msgstr ""
@@ -8222,6 +8564,9 @@ msgstr "프로ì íŠ¸ 검색"
msgid "Search users"
msgstr "ì‚¬ìš©ìž ê²€ìƒ‰"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr "프로ì íŠ¸ 검색"
@@ -8480,6 +8825,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr "웹 터미ë„ì˜ ìµœëŒ€ 세션 ì‹œê°„ì„ ì„¤ì •í•©ë‹ˆë‹¤."
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "남용 ë³´ê³ ì„œì— ëŒ€í•œ 알림 ì´ë©”ì¼ì„ 설정합니다."
@@ -8504,6 +8852,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8561,9 +8912,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr "ì…œë¡ íŠ¸ëžœì ì…˜"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "명령 보기"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "완료 로그 표시"
@@ -8653,7 +9010,7 @@ msgid "Smartcard"
msgstr "스마트카드"
msgid "Smartcard authentication failed: client certificate header is missing."
-msgstr ""
+msgstr "스마트카드 ì¸ì¦ 실패: í´ë¼ì´ì–¸íŠ¸ ì¸ì¦ì„œ í—¤ë”ê°€ 잘못ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "Snippet Contents"
msgstr ""
@@ -8661,6 +9018,24 @@ msgstr ""
msgid "Snippets"
msgstr "스니펫"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8931,6 +9306,12 @@ msgstr "별표 í‘œì‹œëœ í”„ë¡œì íŠ¸ 활ë™"
msgid "Starred projects"
msgstr "ë³„í‘œëœ í”„ë¡œì íŠ¸"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -8944,7 +9325,7 @@ msgid "Start a review"
msgstr ""
msgid "Start and due date"
-msgstr ""
+msgstr "시작ì¼ê³¼ 마ê°ì¼"
msgid "Start cleanup"
msgstr ""
@@ -9141,6 +9522,9 @@ msgstr "스위치 브랜치/태그"
msgid "Sync information"
msgstr "ì •ë³´ ë™ê¸°í™”"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "시스템 훅"
@@ -9330,6 +9714,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr "허용ë˜ëŠ” 최대 íŒŒì¼ í¬ê¸°ëŠ” 200KB입니다."
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9453,6 +9840,9 @@ msgstr "í•  ì¼ì„ 삭제하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí–ˆìŠµë‹ˆë‹¤."
msgid "There was an error loading users activity calendar."
msgstr "ì‚¬ìš©ìž í™œë™ ìº˜ë¦°ë”를 로딩하는 ì¤‘ì— ì˜¤ë¥˜ê°€ ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr "알림 ì„¤ì •ì„ ì €ìž¥í•˜ë˜ ì¤‘ 오류가 ë°œìƒí•˜ì˜€ìŠµë‹ˆë‹¤."
@@ -9498,11 +9888,26 @@ msgstr "ì´ ë³´ë“œì˜ ë²”ìœ„ê°€ 축소ë˜ì—ˆìŠµë‹ˆë‹¤."
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
msgid "This date is after the due date, so this epic won't appear in the roadmap."
-msgstr ""
+msgstr "ì´ ë‚ ì§œëŠ” 마ê°ì¼ ì´í›„ì´ë¯€ë¡œ ì´ ì—í”½ì€ ë¡œë“œë§µì— ë‚˜íƒ€ë‚˜ì§€ 않습니다."
msgid "This date is before the start date, so this epic won't appear in the roadmap."
msgstr ""
@@ -9519,6 +9924,9 @@ msgstr "ì´ ë””ë ‰í† ë¦¬"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr "ì´ ê·¸ë£¹"
@@ -9603,6 +10011,12 @@ msgstr "즉, 빈 저장소를 만들거나 기존 저장소를 가져올 때까ì
msgid "This merge request is locked."
msgstr "ì´ ë¨¸ì§€ 리퀘스트(MR)는 잠겨있습니다."
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr "현재 ë¸Œëžœì¹˜ì— ëŒ€í•œ 쓰기 ê¶Œí•œì´ ì—†ìœ¼ë¯€ë¡œ ì´ ì˜µì…˜ì´ ë¹„í™œì„±í™”ë©ë‹ˆë‹¤."
@@ -10160,6 +10574,9 @@ msgstr ""
msgid "Update"
msgstr "ì—…ë°ì´íŠ¸"
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10169,6 +10586,9 @@ msgstr "지금 ì—…ë°ì´íŠ¸"
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "ì—…ë°ì´íŠ¸ì¤‘..."
@@ -10397,6 +10817,9 @@ msgstr ""
msgid "View documentation"
msgstr "문서 보기"
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr "ì—픽 ëª©ë¡ ë³´ê¸°"
@@ -10406,6 +10829,9 @@ msgstr "파ì¼ë³´ê¸° @ "
msgid "View group labels"
msgstr "그룹 ë¼ë²¨ 보기"
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "ì´ìŠˆ 보기"
@@ -10709,6 +11135,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "액세스 요청 철회"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10814,6 +11243,9 @@ msgstr ""
msgid "You have no permissions"
msgstr "ê¶Œí•œì´ ì—†ìŠµë‹ˆë‹¤."
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "프로ì íŠ¸ ìˆ«ìž í•œë„ì— ë„달했습니다."
@@ -10874,6 +11306,9 @@ msgstr "ë‹¹ì‹ ì˜ í”„ë¡œí•„ì— SSH 키를 등ë¡í•˜ê¸° 전까지 SSH를 통해 í
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "올바른 ë³€ê²½ì‚¬í•­ì„ ê°€ì ¸ì˜¤ë ¤ë©´ 다른 ì´ë¦„ì˜ ë¸Œëžœì¹˜ë¥¼ 사용해야합니다."
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -10967,6 +11402,9 @@ msgstr "ìžì‹ ì„ 담당ìžë¡œ 지정"
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr "브랜치 ì´ë¦„"
@@ -11046,6 +11484,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11138,9 +11579,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11180,6 +11618,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11268,9 +11709,6 @@ msgstr ""
msgid "here"
msgstr "여기"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://나ì˜-bitbucket-server"
@@ -11387,6 +11825,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "ë‹¤ìŒ ì‚¬ìš©ìžì— ì˜í•´ 승ì¸ë¨: "
@@ -11459,6 +11900,9 @@ msgstr "로컬ì—ì„œ 머지"
msgid "mrWidget|Merge request approved"
msgstr "머지 리퀘스트(MR) 승ì¸ë¨"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr "머지 리퀘스트(MR)승ì¸ë¨; 추가ì ìœ¼ë¡œ 승ì¸í•  수 있습니다."
@@ -11518,6 +11962,9 @@ msgstr "ë˜ëŒë¦¬ê¸°"
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr "새로운 머지 리퀘스트(MR)ì—ì„œ ì´ ë¨¸ì§€ 리퀘스트(MR)ë¡œ ë˜ëŒë¦¬ê¸°"
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "설정:"
diff --git a/locale/mn_MN/gitlab.po b/locale/mn_MN/gitlab.po
index 01bbcaaa9c6..3942a250799 100644
--- a/locale/mn_MN/gitlab.po
+++ b/locale/mn_MN/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: mn\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:07\n"
+"PO-Revision-Date: 2019-03-06 15:16\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/nb_NO/gitlab.po b/locale/nb_NO/gitlab.po
index ef5040725b3..fca801a32a5 100644
--- a/locale/nb_NO/gitlab.po
+++ b/locale/nb_NO/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: nb\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:07\n"
+"PO-Revision-Date: 2019-03-06 15:17\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/nl_NL/gitlab.po b/locale/nl_NL/gitlab.po
index 17bc141ae0c..44399927a09 100644
--- a/locale/nl_NL/gitlab.po
+++ b/locale/nl_NL/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: nl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:11\n"
+"PO-Revision-Date: 2019-03-06 15:51\n"
msgid " Status"
msgstr " Status"
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr "\"%{query}\" in projecten"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -126,12 +131,26 @@ msgstr "%{counter_storage} (%{counter_repositories} repositories, %{counter_buil
msgid "%{count} %{alerts}"
msgstr "%{count} %{alerts}"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr "%{count} andere toebedeelden"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} deelnemer"
@@ -148,15 +167,15 @@ msgstr "%{filePath} verwijderd"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} +%{labelCount} meer"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{group_docs_link_start}Groepen%{group_docs_link_end} stellen u in staat over meerdere projecten samen te werken en te beheren. Leden van een groep hebben toegang tot alle projecten."
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} wordt verwijderd! Weet je het zeker?"
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr "+ %{count} meer"
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} meer"
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] "1 %{type} aanpassing"
msgstr[1] "%{count} %{type} aanpassingen"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "1 gesloten issue"
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr "Alles"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr "Grafieken"
msgid "Chat"
msgstr "Chat"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/pa_IN/gitlab.po b/locale/pa_IN/gitlab.po
index 1f1f71a35e9..2291dc0e557 100644
--- a/locale/pa_IN/gitlab.po
+++ b/locale/pa_IN/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: pa-IN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:08\n"
+"PO-Revision-Date: 2019-03-06 15:18\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/pl_PL/gitlab.po b/locale/pl_PL/gitlab.po
index 39d0494790f..f3266c9c227 100644
--- a/locale/pl_PL/gitlab.po
+++ b/locale/pl_PL/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: pl\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:08\n"
+"PO-Revision-Date: 2019-03-06 15:17\n"
msgid " Status"
msgstr ""
@@ -47,6 +47,13 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -156,12 +163,28 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr "%{count} więcej beneficjentów"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -182,15 +205,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -270,6 +293,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -296,6 +322,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -416,6 +449,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -431,6 +479,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -515,24 +566,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -701,6 +779,9 @@ msgstr "Zaawansowane pozwolenia, Magazyn Dużych Plików i Dwuczynnikowe ustawie
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -714,6 +795,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -783,7 +867,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -795,6 +879,15 @@ msgstr "Wystąpił błąd podczas dodawania nowej wersji roboczej."
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -807,6 +900,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "Wystąpił błąd podczas usuwania komentarza"
@@ -843,6 +939,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -900,12 +999,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "Wystąpił błąd podczas aktualizacji komentarza"
@@ -996,6 +1101,52 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1038,9 +1189,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1125,6 +1282,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1140,9 +1300,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1701,6 +1858,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1737,6 +1897,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1749,6 +1912,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1815,9 +1981,6 @@ 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 ""
@@ -1977,6 +2140,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1992,9 +2158,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2040,12 +2203,12 @@ msgstr "Po zainstalowaniu Ingress będziesz musiał wskazać swoje symbole wielo
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "Wystąpił błąd podczas próby pobrania stref projektu: %{error}"
-
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "Wystąpił błąd podczas próby skontaktowania się z Google Cloud API. Spróbuj ponownie później."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr ""
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
msgstr ""
@@ -2082,6 +2245,9 @@ msgstr "Wybierz aplikacje do zainstalowania w Twoim klastrze Kubernetes. Do zain
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "Wybierz, które z Twoich środowisk będą używać tego klastra."
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2178,8 +2344,8 @@ 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 "Jeśli konfigurujesz wiele klastrów i używasz Auto DevOps, %{help_link_start}zapoznaj się najpierw z tym%{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 "Aby pokazać stan zdrowia klastra, musimy zaopatrzyć Twój klaster w Prometheus, aby zebrać wymagane dane."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
+msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr ""
@@ -2193,9 +2359,6 @@ msgstr "Wprowadzanie umożliwia kierowanie żądań do usług na podstawie hosta
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr "Zainstaluj Prometheus"
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2241,9 +2404,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Kondycja klastra Kubernetes"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2481,15 +2641,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2791,6 +2963,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3281,6 +3456,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3359,6 +3537,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3530,6 +3711,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3566,6 +3750,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3584,9 +3771,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3611,6 +3795,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3662,15 +3852,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3683,6 +3891,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3734,6 +3954,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3803,6 +4026,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3881,6 +4131,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3968,7 +4221,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -4034,9 +4287,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4094,9 +4344,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4185,9 +4432,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4302,7 +4546,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4332,9 +4576,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4767,15 +5017,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4833,6 +5092,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5203,9 +5465,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5257,6 +5534,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5312,6 +5595,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5330,6 +5616,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5372,7 +5661,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5561,6 +5850,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5646,6 +5938,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5821,6 +6116,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5866,6 +6170,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5893,36 +6200,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5977,6 +6254,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6127,9 +6407,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6274,6 +6551,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6353,13 +6633,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6422,9 +6702,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6452,13 +6738,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6554,6 +6843,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6759,6 +7051,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6768,6 +7063,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6960,6 +7258,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6975,6 +7279,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7095,9 +7402,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7107,9 +7426,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7134,6 +7462,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7332,9 +7663,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7434,6 +7762,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7476,6 +7807,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7575,6 +7909,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7936,6 +8273,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8071,6 +8414,23 @@ msgstr "Wymagaj od wszystkich użytkowników w tej grupie do ustawienia uwierzyt
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "Wymagaj od wszystkich użytkowników akceptacji Warunków Usługi i Polityki Prywatności, gdy będą chcieli korzystać z GitLab."
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Resend invite"
msgstr ""
@@ -8231,6 +8591,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8261,6 +8624,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8294,6 +8660,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8354,6 +8723,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8612,6 +8984,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8636,6 +9011,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8693,9 +9071,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8796,6 +9180,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9066,6 +9468,12 @@ msgstr "Aktywność Projektów oznaczonych gwiazdką"
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9276,6 +9684,9 @@ msgstr ""
msgid "Sync information"
msgstr "Synchronizuj informacje"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9465,6 +9876,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9588,6 +10002,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9633,6 +10050,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9654,6 +10086,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9738,6 +10173,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10301,6 +10742,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10310,6 +10754,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10538,6 +10985,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10547,6 +10997,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10850,6 +11303,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10955,6 +11411,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -11015,6 +11474,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11108,6 +11570,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11199,6 +11664,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11297,9 +11765,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11339,6 +11804,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11439,9 +11907,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11564,6 +12029,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11636,6 +12104,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11701,6 +12172,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/pt_BR/gitlab.po b/locale/pt_BR/gitlab.po
index c331c2e5cc6..f5b206ba288 100644
--- a/locale/pt_BR/gitlab.po
+++ b/locale/pt_BR/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: pt-BR\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:08\n"
+"PO-Revision-Date: 2019-03-06 15:17\n"
msgid " Status"
msgstr " Status"
@@ -32,10 +32,10 @@ msgstr[0] " melhorado em %d ponto"
msgstr[1] " melhorado em %d pontos"
msgid " or "
-msgstr ""
+msgstr " ou "
msgid " or <#epic id>"
-msgstr ""
+msgstr " ou <#epic id>"
msgid " or <#issue id>"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr "\"%{query}\" em projetos"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d commit"
@@ -54,7 +59,7 @@ msgstr[0] "%d commit atrás"
msgstr[1] "%d commits atrás"
msgid "%d commits"
-msgstr ""
+msgstr "%d commits"
msgid "%d exporter"
msgid_plural "%d exporters"
@@ -78,8 +83,8 @@ msgstr[1] "%d issues"
msgid "%d issue selected"
msgid_plural "%d issues selected"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d issue selecionada"
+msgstr[1] "%d issues selecionadas"
msgid "%d layer"
msgid_plural "%d layers"
@@ -126,12 +131,26 @@ msgstr "%{counter_storage} (%{counter_repositories} repositórios, %{counter_bui
msgid "%{count} %{alerts}"
msgstr "%{count} %{alerts}"
-msgid "%{count} more"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
msgstr ""
+msgid "%{count} more"
+msgstr "mais %{count}"
+
msgid "%{count} more assignees"
msgstr "mais %{count} responsáveis"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} participante"
@@ -148,15 +167,15 @@ msgstr "%{filePath} excluído"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} +%{labelCount} mais"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{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 "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} será removido! Você tem certeza?"
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -183,16 +202,16 @@ msgstr ""
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Branch"
+msgstr[1] "%{strong_start}%{branch_count}%{strong_end} Branches"
msgid "%{strong_start}%{commit_count}%{strong_end} Commit"
msgid_plural "%{strong_start}%{commit_count}%{strong_end} Commits"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{strong_start}%{commit_count}%{strong_end} Commit"
+msgstr[1] "%{strong_start}%{commit_count}%{strong_end} Commits"
msgid "%{strong_start}%{human_size}%{strong_end} Files"
-msgstr ""
+msgstr "%{strong_start}%{human_size}%{strong_end} Arquivos"
msgid "%{strong_start}%{tag_count}%{strong_end} Tag"
msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags"
@@ -217,10 +236,10 @@ msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what infor
msgstr "%{usage_ping_link_start}Saiba mais%{usage_ping_link_end} sobre quais informações são compartilhadas com o GitLab Inc."
msgid "%{user_name} profile page"
-msgstr ""
+msgstr "Página de perfil de %{user_name}"
msgid "(external source)"
-msgstr ""
+msgstr "(fonte externa)"
msgid "+ %{count} more"
msgstr "+ %{count} mais"
@@ -228,9 +247,12 @@ msgstr "+ %{count} mais"
msgid "+ %{moreCount} more"
msgstr "%{moreCount} mais"
-msgid ", or "
+msgid "+%{extraOptionCount} more"
msgstr ""
+msgid ", or "
+msgstr ", ou "
+
msgid "- Runner is active and can process any new jobs"
msgstr "- O runner está ativo e pode processar novas tarefas"
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] "1 mudança de %{type}"
msgstr[1] "%{count} mudanças de %{type}"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "1 issue fechada"
@@ -299,13 +326,13 @@ msgid "1st contribution!"
msgstr "1ª contribuição!"
msgid "2FA"
-msgstr ""
+msgstr "A2F"
msgid "2FA enabled"
msgstr "Autenticação de 2 passos ativada"
msgid "403|Please contact your GitLab administrator to get permission."
-msgstr ""
+msgstr "Por favor entre em contato com o seu administrador do GitLab para obter 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."
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "'Runner' é um processo que executa uma tarefa. Você pode configurar quantos Runners você precisar."
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Uma coleção de gráficos sobre Integração Contínua"
@@ -367,6 +409,9 @@ msgstr "Um membro da equipe de abusos GitLab irá rever a sua avaliação assim
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "Um novo \"branch\" será criado no seu \"fork\" e um novo merge request será iniciado."
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A 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}."
@@ -395,7 +440,7 @@ msgid "Abuse reports"
msgstr "Relatórios de abuso"
msgid "Accept invitation"
-msgstr ""
+msgstr "Aceitar convite"
msgid "Accept terms"
msgstr "Aceitar os temos"
@@ -434,10 +479,10 @@ msgid "Add"
msgstr "Adicionar"
msgid "Add CHANGELOG"
-msgstr ""
+msgstr "Adicionar CHANGELOG"
msgid "Add CONTRIBUTING"
-msgstr ""
+msgstr "Adicionar CONTRIBUTING"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
msgstr "Adicione Webhooks de grupo e GitLab Enterprise Edition."
@@ -449,26 +494,53 @@ msgid "Add Kubernetes cluster"
msgstr "Adicionar cluster Kubernetes"
msgid "Add README"
+msgstr "Adicionar README"
+
+msgid "Add a bullet list"
msgstr ""
msgid "Add a general comment to this %{noteable_name}."
-msgstr ""
+msgstr "Adicionar um comentário geral para este %{noteable_name}."
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "Adicione uma homepage ao seu wiki que contenha informações sobre o seu projeto e o GitLab irá exibi-lo aqui ao invés desta mensagem."
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "Adicionar uma tabela"
+msgid "Add a task list"
+msgstr ""
+
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 approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "Adicionar comentário"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "Adicionar comentário de imagem"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "Adicionar licença"
@@ -485,7 +557,7 @@ msgid "Add reaction"
msgstr "Adicionar reação"
msgid "Add to project"
-msgstr ""
+msgstr "Adicionar ao projeto"
msgid "Add to review"
msgstr "Adicionar à revisão"
@@ -497,7 +569,7 @@ msgid "Add user(s) to the group:"
msgstr "Adicionar usuário(s) ao grupo:"
msgid "Add users or groups who are allowed to approve every merge request"
-msgstr ""
+msgstr "Adicionar usuários ou grupos com permissão para aprovar todos os merge request"
msgid "Add users to group"
msgstr "Adicionar usuários ao grupo"
@@ -560,13 +632,13 @@ msgid "AdminSettings|When creating a new environment variable it will be protect
msgstr ""
msgid "AdminUsers|2FA Disabled"
-msgstr ""
+msgstr "A2F desativada"
msgid "AdminUsers|2FA Enabled"
-msgstr ""
+msgstr "A2F ativada"
msgid "AdminUsers|Active"
-msgstr ""
+msgstr "Ativo"
msgid "AdminUsers|Admin"
msgstr ""
@@ -578,10 +650,10 @@ msgid "AdminUsers|Block user"
msgstr "Bloquear usuário"
msgid "AdminUsers|Blocked"
-msgstr ""
+msgstr "Bloqueado"
msgid "AdminUsers|Cannot unblock LDAP blocked users"
-msgstr ""
+msgstr "Não é possível desbloquear usuários bloqueados pelo LDAP"
msgid "AdminUsers|Delete User %{username} and contributions?"
msgstr "Excluir o usuário %{username} e suas contribuições?"
@@ -596,28 +668,28 @@ msgid "AdminUsers|Delete user and contributions"
msgstr "Excluir o usuário e suas contribuições"
msgid "AdminUsers|External"
-msgstr ""
+msgstr "Externo"
msgid "AdminUsers|It's you!"
-msgstr ""
+msgstr "É você!"
msgid "AdminUsers|New user"
-msgstr ""
+msgstr "Novo usuário"
msgid "AdminUsers|No users found"
-msgstr ""
+msgstr "Nenhum usuário encontrado"
msgid "AdminUsers|Search by name, email or username"
-msgstr ""
+msgstr "Procure por nome, e-mail ou nome de usuário"
msgid "AdminUsers|Search users"
-msgstr ""
+msgstr "Procurar usuários"
msgid "AdminUsers|Send email to users"
-msgstr ""
+msgstr "Enviar e-mail para usuários"
msgid "AdminUsers|Sort by"
-msgstr ""
+msgstr "Ordenar por"
msgid "AdminUsers|To confirm, type %{projectName}"
msgstr "Para confirmar, digite %{projectName}"
@@ -626,10 +698,10 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr "Para confirmar, digite %{username}"
msgid "AdminUsers|User will be blocked"
-msgstr ""
+msgstr "Usuário será bloqueado"
msgid "AdminUsers|Without projects"
-msgstr ""
+msgstr "Sem projetos"
msgid "Advanced permissions, Large File Storage and Two-Factor authentication settings."
msgstr "Permissões avançadas, armazenamento de arquivos grandes e configurações de autenticação de dois fatores."
@@ -637,17 +709,23 @@ msgstr "Permissões avançadas, armazenamento de arquivos grandes e configuraçÃ
msgid "Advanced settings"
msgstr "Configurações avançadas"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "Alerta"
msgstr[1] "Alertas"
msgid "Alerts"
-msgstr ""
+msgstr "Alertas"
msgid "All"
msgstr "Todos"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr "Houve commit com todas as mudanças"
@@ -655,13 +733,13 @@ msgid "All features are enabled for blank projects, from templates, or when impo
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 "All issues for this milestone are closed. You may close this milestone now."
-msgstr ""
+msgstr "Todas as issues para este marco estão fechadas. Você pode fechar este marco agora."
msgid "All users"
msgstr "Todos os usuários"
msgid "Allow \"%{group_name}\" to sign you in"
-msgstr ""
+msgstr "Permitir que \"%{group_name}\" adicione você"
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."
@@ -685,7 +763,7 @@ msgid "Allow users to request access if visibility is public or internal."
msgstr "Permitir que os usuários solicitem acesso se a visibilidade for pública ou interna."
msgid "Allowed to fail"
-msgstr ""
+msgstr "Permitido falhar"
msgid "Allows you to add and manage Kubernetes clusters."
msgstr "Permite adicionar e gerenciar clusters do Kubernetes."
@@ -717,7 +795,7 @@ msgstr "Um campo vazio do usuário do GitLab adicionará o nome completo do usuÃ
msgid "An error has occurred"
msgstr "Ocorreu um erro"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -727,6 +805,15 @@ msgid "An error occurred adding a new draft."
msgstr "Ocorreu um erro ao adicionar um novo rascunho."
msgid "An error occurred creating the new branch."
+msgstr "Um erro ocorreu ao criar o novo branch."
+
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
msgstr ""
msgid "An error occurred previewing the blob"
@@ -741,6 +828,9 @@ msgstr "Ocorreu um erro ao atualizar o peso do issue"
msgid "An error occurred while adding approver"
msgstr "Ocorreu um erro ao adicionar o aprovador"
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "Ocorreu um erro ao remover o comentário"
@@ -777,6 +867,9 @@ msgstr "Ocorreu um erro ao recuperar as tarefas."
msgid "An error occurred while fetching the pipeline."
msgstr "Erro ao recuperar informações da pipeline."
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "Erro ao recuperar projetos"
@@ -802,7 +895,7 @@ msgid "An error occurred while loading the file"
msgstr "Erro ao carregar o arquivo"
msgid "An error occurred while loading the subscription details."
-msgstr ""
+msgstr "Um erro ocorreu ao carregar os detalhes da inscrição."
msgid "An error occurred while making the request."
msgstr "Erro ao fazer a requisição."
@@ -811,10 +904,10 @@ msgid "An error occurred while removing approver"
msgstr "Ocorreu um erro ao remover o aprovador"
msgid "An error occurred while removing epics."
-msgstr ""
+msgstr "Ocorreu um erro ao remover épicos."
msgid "An error occurred while removing issues."
-msgstr ""
+msgstr "Um erro ocorreu ao remover issues."
msgid "An error occurred while rendering KaTeX"
msgstr "Erro ao renderizar o KaTeX"
@@ -834,12 +927,18 @@ msgstr "Ocorreu um erro ao salvar o status de substituição do LDAP. Por favor,
msgid "An error occurred while saving assignees"
msgstr "Erro ao salvar assignees"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
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 updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "Ocorreu um erro durante a atualização do comentário"
@@ -850,10 +949,10 @@ msgid "An error occurred whilst committing your changes."
msgstr ""
msgid "An error occurred whilst fetching the job trace."
-msgstr ""
+msgstr "Ocorreu um erro ao buscar o rastreamento do trabalho."
msgid "An error occurred whilst fetching the latest pipeline."
-msgstr ""
+msgstr "Ocorreu um erro ao recuperar o último pipeline."
msgid "An error occurred whilst loading all the files."
msgstr ""
@@ -871,16 +970,16 @@ msgid "An error occurred whilst loading the merge request version data."
msgstr ""
msgid "An error occurred whilst loading the merge request."
-msgstr ""
+msgstr "Ocorreu um erro ao carregar o merge request."
msgid "An error occurred whilst loading the pipelines jobs."
-msgstr ""
+msgstr "Ocorreu um erro ao carregar os trabalhos de pipelines."
msgid "An error occurred. Please try again."
msgstr "Ocorreu um erro. Tente novamente."
msgid "An unexpected error occurred while checking the project environment."
-msgstr ""
+msgstr "Um erro inesperado ocorreu enquanto verificava o ambiente de projeto."
msgid "An unexpected error occurred while checking the project runners."
msgstr ""
@@ -925,20 +1024,58 @@ msgid "Applications"
msgstr "Aplicações"
msgid "Applied"
-msgstr ""
+msgstr "Aplicado"
msgid "Apply suggestion"
+msgstr "Aplicar sugestão"
+
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
msgstr ""
-msgid "Approvals"
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
msgstr ""
-msgid "Approvals required"
+msgid "ApprovalRule|Members"
msgstr ""
-msgid "Approvers"
+msgid "ApprovalRule|Name"
msgstr ""
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
+msgid "Approvals"
+msgstr "Aprovações"
+
+msgid "Approvals required"
+msgstr "Aprovações necessárias"
+
+msgid "Approvers"
+msgstr "Aprovadores"
+
msgid "Apr"
msgstr "Abr"
@@ -952,7 +1089,7 @@ msgid "Archived projects"
msgstr "Projetos arquivados"
msgid "Are you sure"
-msgstr ""
+msgstr "Você tem certeza"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Tem certeza que deseja excluir este agendamento de pipeline?"
@@ -972,9 +1109,15 @@ msgstr "Tem certeza de que deseja regenerar a chave pública? Você precisará a
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 approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1030,7 +1173,7 @@ msgid "Assign milestone"
msgstr "Atribuir marco"
msgid "Assign some issues to this milestone."
-msgstr ""
+msgstr "Atribua alguns issues a este marco."
msgid "Assign to"
msgstr "Atribuir à"
@@ -1059,9 +1202,12 @@ msgstr "Listas de responsáveis mostram todas as issues atribuídas ao usuário
msgid "Assignee(s)"
msgstr "Responsável(is)"
-msgid "Attach a file"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
msgstr ""
+msgid "Attach a file"
+msgstr "Anexar um arquivo"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Para anexar arquivo, arraste e solte ou %{upload_link}"
@@ -1074,9 +1220,6 @@ msgstr "Ago"
msgid "August"
msgstr "Agosto"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "Log de autenticação"
@@ -1093,7 +1236,7 @@ msgid "Authorization code:"
msgstr "Código de autorização:"
msgid "Authorization key"
-msgstr ""
+msgstr "Chave 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."
@@ -1156,25 +1299,25 @@ msgid "Automatically marked as default internal user"
msgstr "Marcado automaticamente como interno de usuário padrão"
msgid "Automatically resolved"
-msgstr ""
+msgstr "Resolvido automaticamente"
msgid "Available"
msgstr "Disponível"
msgid "Available group Runners: %{runners}"
-msgstr ""
+msgstr "Runners de grupo disponíveis: %{runners}"
msgid "Available shared Runners:"
-msgstr ""
+msgstr "Runners compartilhados disponíveis:"
msgid "Available specific runners"
-msgstr ""
+msgstr "Runners específicos disponíveis"
msgid "Avatar for %{assigneeName}"
-msgstr ""
+msgstr "Imagem de perfil para %{assigneeName}"
msgid "Avatar for %{name}"
-msgstr ""
+msgstr "Imagem de perfil para %{name}"
msgid "Avatar will be removed. Are you sure?"
msgstr "Foto de perfil será removida. Tem certeza?"
@@ -1360,10 +1503,10 @@ msgid "Bitbucket import"
msgstr "Importar do Bitbucket"
msgid "Block"
-msgstr ""
+msgstr "Bloquear"
msgid "Blocked"
-msgstr ""
+msgstr "Bloqueado"
msgid "Blog"
msgstr "Blog"
@@ -1528,34 +1671,34 @@ msgid "Browse files"
msgstr "Navegar pelos arquivos"
msgid "Built-in"
-msgstr ""
+msgstr "Embutido"
msgid "Business"
-msgstr ""
+msgstr "Negócios"
msgid "Business metrics (Custom)"
msgstr "Métricas de negócios (personalizadas)"
msgid "By %{user_name}"
-msgstr ""
+msgstr "Por %{user_name}"
msgid "ByAuthor|by"
msgstr "por"
msgid "CHANGELOG"
-msgstr ""
+msgstr "CHANGELOG"
msgid "CI / CD"
msgstr "CI / CD"
msgid "CI / CD Charts"
-msgstr ""
+msgstr "Gráficos CI / CD"
msgid "CI / CD Settings"
msgstr "Configurações de CI / CD"
msgid "CI Lint"
-msgstr ""
+msgstr "Checar syntaxe de CI"
msgid "CI will run using the credentials assigned above."
msgstr "O CI será executado usando as credenciais atribuídas acima."
@@ -1609,13 +1752,13 @@ msgid "CICD|instance enabled"
msgstr "Instância habilitada"
msgid "CONTRIBUTING"
-msgstr ""
+msgstr "CONTRIBUINDO"
msgid "Callback URL"
msgstr "URL de Retorno"
msgid "Can override approvers and approvals required per merge request"
-msgstr ""
+msgstr "Pode substituir aprovadores e aprovações necessárias por merge request"
msgid "Can't find HEAD commit for this branch"
msgstr "Não é possível encontrar o commit HEAD para este branch"
@@ -1635,11 +1778,14 @@ 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"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
msgstr ""
+msgid "Certificate"
+msgstr "Certificado"
+
msgid "Certificate (PEM)"
-msgstr ""
+msgstr "Certificado (PEM)"
msgid "Certificate fingerprint"
msgstr "Impressão digital do certificado"
@@ -1648,7 +1794,7 @@ msgid "Change Weight"
msgstr "Alterar Peso"
msgid "Change permissions"
-msgstr ""
+msgstr "Alterar permissões"
msgid "Change template"
msgstr "Mudar modelo"
@@ -1671,11 +1817,14 @@ msgstr "Reverter"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "Isso criará um novo commit para reverter as mudanças existentes."
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "Mudanças serão mostradas se revisão de <b>origem</b> tiver sofrido merge na revisão <b>alvo</b>."
msgid "Changes suppressed. Click to show."
-msgstr ""
+msgstr "Alterações suprimidas. Clique para mostrar."
msgid "Charts"
msgstr "Gráficos"
@@ -1683,17 +1832,20 @@ msgstr "Gráficos"
msgid "Chat"
msgstr "Bate-papo"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "Verifique a %{docs_link_start}documentação%{docs_link_end}."
msgid "Check your .gitlab-ci.yml"
-msgstr ""
+msgstr "Verifique o seu .gitlab-ci.yml"
msgid "Checking %{text} availability…"
msgstr "Verificando disponibilidade de %{text}…"
msgid "Checking approval status"
-msgstr ""
+msgstr "Verificando status de aprovação"
msgid "Checking branch availability..."
msgstr "Verificando disponibilidade de branch..."
@@ -1717,10 +1869,10 @@ msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to s
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 a file"
-msgstr ""
+msgstr "Escolha um arquivo"
msgid "Choose a role permission"
-msgstr ""
+msgstr "Escolha uma permissão de cargo"
msgid "Choose a template..."
msgstr "Escolha um modelo..."
@@ -1741,7 +1893,7 @@ msgid "Choose the top-level group for your repository imports."
msgstr "Escolha o grupo principal para importar seus repositórios."
msgid "Choose what content you want to see on a group’s overview page"
-msgstr ""
+msgstr "Escolha o conteúdo que você deseja ver na página de visão geral de um grupo"
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr "Escolha quais grupos você deseja sincronizar nesse nó secundário."
@@ -1749,9 +1901,6 @@ 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 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 sincronizar nesse nó secundário."
@@ -1858,10 +2007,10 @@ msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "está indisponível: %{reason}"
msgid "Clear"
-msgstr ""
+msgstr "Limpar"
msgid "Clear input"
-msgstr ""
+msgstr "Limpar entrada"
msgid "Clear search"
msgstr "Limpar Pesquisa"
@@ -1903,16 +2052,19 @@ msgid "Clients"
msgstr "Clientes"
msgid "Clone"
-msgstr ""
+msgstr "Clonar"
msgid "Clone repository"
msgstr "Clonar repositório"
msgid "Clone with %{http_label}"
+msgstr "Clonar com %{http_label}"
+
+msgid "Clone with KRB5"
msgstr ""
msgid "Clone with SSH"
-msgstr ""
+msgstr "Clonar com SSH"
msgid "Close"
msgstr "Fechar"
@@ -1921,22 +2073,19 @@ msgid "Close epic"
msgstr "Fechar épico"
msgid "Close milestone"
-msgstr ""
+msgstr "Fechar marco"
msgid "Closed"
msgstr "Fechado"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "Issues Fechadas"
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
-msgstr ""
+msgstr " %{custom_domain_start}Mais informações%{custom_domain_end}."
msgid "ClusterIntegration| can be used instead of a custom domain."
-msgstr ""
+msgstr " pode ser usado em vez de um domínio personalizado."
msgid "ClusterIntegration| is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster. %{environment_scope_start}More information%{environment_scope_end}"
msgstr ""
@@ -1974,12 +2123,12 @@ msgstr "Depois de instalar o Ingress, você precisará apontar seu DNS curinga p
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "Erro ao recuperar zonas de projeto: %{error}"
-
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "Ocorreu um erro ao entrar em contato com a Google Cloud API. Por favor, tente novamente mais tarde."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr ""
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
msgstr ""
@@ -1990,13 +2139,13 @@ msgid "ClusterIntegration|Applications"
msgstr "Aplicações"
msgid "ClusterIntegration|Apply for credit"
-msgstr ""
+msgstr "Inscreva-se para receber créditos"
msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluster's integration? This will not delete your actual Kubernetes cluster."
msgstr "Tem certeza de que deseja remover a integração deste cluster do Kubernetes? Isso não excluirá o seu cluster atual do Kubernetes."
msgid "ClusterIntegration|Base domain"
-msgstr ""
+msgstr "Domínio base"
msgid "ClusterIntegration|CA Certificate"
msgstr "Certificado CA"
@@ -2016,6 +2165,9 @@ msgstr "Escolha quais aplicativos instalar em seu cluster do Kubernetes. O Helm
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "Escolha quais dos seus ambientes usarão esse cluster."
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,8 +2264,8 @@ 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 "Para mostrar a saúde do cluster, nós precisamos provisionar seu cluster com Prometheus para coletar os dados necessários."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
+msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr "Ingressar"
@@ -2127,9 +2279,6 @@ msgstr "Ingress oferece uma maneira de rotear solicitações para serviços com
msgid "ClusterIntegration|Install"
msgstr "Instalar"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr "Instalar Prometheus"
-
msgid "ClusterIntegration|Installed"
msgstr "Instalado"
@@ -2175,9 +2324,6 @@ msgstr "Cluter Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Detalhes do cluster Kubernetes"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Saúde do cluster Kubernetes"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "O cluster Kubernetes está sendo criado no Google Kubernetes Engine..."
@@ -2413,31 +2559,43 @@ msgid "ClusterIntegration|sign up"
msgstr "cadastrar"
msgid "Code"
+msgstr "Código"
+
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
msgstr ""
msgid "Code owners"
msgstr "Proprietários de código"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr "Cohorts"
msgid "Collapse"
msgstr "Recolher"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "Minimizar barra lateral"
msgid "Command line instructions"
-msgstr ""
+msgstr "Instruções de linha de comando"
msgid "Comment"
msgstr "Comentário"
msgid "Comment & close %{noteable_name}"
-msgstr ""
+msgstr "Comentar e fechar %{noteable_name}"
msgid "Comment & reopen %{noteable_name}"
-msgstr ""
+msgstr "Comentar e reabrir %{noteable_name}"
msgid "Comment & resolve discussion"
msgstr "Comentar e marcar a discussão como resolvida"
@@ -2457,13 +2615,13 @@ msgstr[0] "Commit"
msgstr[1] "Commits"
msgid "Commit %{commit_id}"
-msgstr ""
+msgstr "Commit %{commit_id}"
msgid "Commit Message"
msgstr "Mensagem de Commit"
msgid "Commit deleted"
-msgstr ""
+msgstr "Commit excluído"
msgid "Commit duration in minutes for last 30 commits"
msgstr "Duração do commit em minutos para os últimos 30 commits"
@@ -2529,7 +2687,7 @@ msgid "Compare Revisions"
msgstr "Comparar revisões"
msgid "Compare changes"
-msgstr ""
+msgstr "Comparar alterações"
msgid "Compare changes with the last commit"
msgstr "Compare as mudanças do último commit"
@@ -2673,7 +2831,7 @@ msgid "Contribution"
msgstr "Contribuições"
msgid "Contribution Charts"
-msgstr ""
+msgstr "Gráficos de contribuição"
msgid "Contributions for <strong>%{calendar_date}</strong>"
msgstr "Contribuições para <strong>%{calendar_date}</strong>"
@@ -2715,7 +2873,7 @@ msgid "ConvDev Index"
msgstr "Ãndice ConvDev"
msgid "Copy %{http_label} clone URL"
-msgstr ""
+msgstr "Copiar URL de clone do %{http_label}"
msgid "Copy %{protocol} clone URL"
msgstr "Copiar URL de Clone do %{protocol}"
@@ -2723,11 +2881,14 @@ msgstr "Copiar URL de Clone do %{protocol}"
msgid "Copy ID to clipboard"
msgstr "Copiar ID para área de transferência"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr "Copiar URL de Clone do SSH"
msgid "Copy SSH public key"
-msgstr ""
+msgstr "Copiar chave pública SSH"
msgid "Copy SSH public key to clipboard"
msgstr "Copiar chave pública SSH para área de transferência"
@@ -2775,7 +2936,7 @@ msgid "Create New Directory"
msgstr "Criar Novo Diretório"
msgid "Create New Domain"
-msgstr ""
+msgstr "Criar novo domínio"
msgid "Create a new branch"
msgstr "Criar uma nova branch"
@@ -2787,7 +2948,7 @@ msgid "Create a new issue"
msgstr "Criar uma nova issue"
msgid "Create a new repository"
-msgstr ""
+msgstr "Criar um novo repositório"
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}."
@@ -2829,7 +2990,7 @@ msgid "Create merge request and branch"
msgstr "Abrir merge request e criar branch"
msgid "Create milestone"
-msgstr ""
+msgstr "Criar marco"
msgid "Create new branch"
msgstr "Criar novo branch"
@@ -2925,7 +3086,7 @@ msgid "Customize how Google Code email addresses and usernames are imported into
msgstr "Personalize como os endereços de e-mail e nomes de usuário do Google Code são importados para o GitLab. Na próxima etapa, você poderá selecionar os projetos que deseja importar."
msgid "Customize language and region related settings."
-msgstr ""
+msgstr "Personalize as configurações relacionadas a idioma e região."
msgid "Customize your merge request approval settings."
msgstr ""
@@ -2961,7 +3122,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "Teste"
msgid "DNS"
-msgstr ""
+msgstr "DNS"
msgid "Dashboard"
msgstr "Dashboard"
@@ -2973,7 +3134,7 @@ msgid "DashboardProjects|Personal"
msgstr "Pessoal"
msgid "Data is still calculating..."
-msgstr ""
+msgstr "Os dados ainda estão a ser calculados..."
msgid "Date picker"
msgstr "Seletor de data"
@@ -2988,7 +3149,7 @@ msgid "December"
msgstr "Dezembro"
msgid "Decline"
-msgstr ""
+msgstr "Recusar"
msgid "Decline and sign out"
msgstr "Recusar e sair"
@@ -3000,10 +3161,10 @@ msgid "Default classification label"
msgstr "Etiqueta de classificação padrão"
msgid "Default first day of the week"
-msgstr ""
+msgstr "Primeiro dia da semana padrão"
msgid "Default first day of the week in calendars and date pickers."
-msgstr ""
+msgstr "Primeiro dia da semana padrão em calendários e selecionadores de data."
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"
@@ -3048,10 +3209,10 @@ msgid "Delete list"
msgstr "Excluir lista"
msgid "Delete source branch"
-msgstr ""
+msgstr "Excluir branch de origem"
msgid "Delete this attachment"
-msgstr ""
+msgstr "Excluir este anexo"
msgid "Deleted"
msgstr "Excluído"
@@ -3211,6 +3372,9 @@ msgstr "Os modelos de descrição permitem que você defina modelos específicos
msgid "Description:"
msgstr "Descrição:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr "Destruir"
@@ -3218,7 +3382,7 @@ msgid "Details"
msgstr "Detalhes"
msgid "Details (default)"
-msgstr ""
+msgstr "Detalhes (padrão)"
msgid "Detect host keys"
msgstr "Detectar chaves de host"
@@ -3254,7 +3418,7 @@ msgid "Disable shared Runners"
msgstr ""
msgid "Disabled"
-msgstr ""
+msgstr "Desativado"
msgid "Discard"
msgstr "Descartar"
@@ -3284,9 +3448,12 @@ msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr "Descubra projetos, grupos e snippets. Compartilhe seus projetos com outras pessoas"
msgid "Discuss a specific suggestion or question"
-msgstr ""
+msgstr "Discuta uma sugestão ou pergunta específica"
msgid "Discuss a specific suggestion or question that needs to be resolved"
+msgstr "Discuta uma sugestão ou pergunta específica que precisa ser resolvida"
+
+msgid "Discussion"
msgstr ""
msgid "Dismiss"
@@ -3371,13 +3538,13 @@ msgid "Edit"
msgstr "Alterar"
msgid "Edit %{name}"
-msgstr ""
+msgstr "Editar %{name}"
msgid "Edit Label"
msgstr "Editar etiqueta"
msgid "Edit Milestone"
-msgstr ""
+msgstr "Editar marco"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Alterar Agendamento do Pipeline %{id}"
@@ -3389,10 +3556,10 @@ msgid "Edit application"
msgstr "Editar aplicativo"
msgid "Edit comment"
-msgstr ""
+msgstr "Editar comentário"
msgid "Edit environment"
-msgstr ""
+msgstr "Editar ambiente"
msgid "Edit files in the editor and commit changes here"
msgstr "Alterar arquivos no editor e fazer commit das alterações aqui"
@@ -3404,7 +3571,7 @@ msgid "Edit identity for %{user_name}"
msgstr "Editar identidade para %{user_name}"
msgid "Edit issues"
-msgstr ""
+msgstr "Editar issues"
msgid "Elasticsearch"
msgstr "Elasticsearch"
@@ -3425,7 +3592,7 @@ msgid "Embed"
msgstr "Embutido"
msgid "Empty file"
-msgstr ""
+msgstr "Arquivo vazio"
msgid "Enable"
msgstr "Ativar"
@@ -3452,7 +3619,7 @@ msgid "Enable classification control using an external service"
msgstr "Ativar controle de classificação usando um serviço externo"
msgid "Enable error tracking"
-msgstr ""
+msgstr "Ativar rastreamento de erros"
msgid "Enable for this project"
msgstr "Ativar para este projeto"
@@ -3460,6 +3627,9 @@ msgstr "Ativar para este projeto"
msgid "Enable group Runners"
msgstr "Ativar grupo de runners"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr "Ative ou desative a coleção de dados Pseudonymizer"
@@ -3470,7 +3640,7 @@ msgid "Enable reCAPTCHA or Akismet and set IP limits."
msgstr "Ativar reCAPTCHA ou Akismet e definir seus limites de IP."
msgid "Enable self approval of merge requests"
-msgstr ""
+msgstr "Ativar auto-aprovação de merge request"
msgid "Enable shared Runners"
msgstr ""
@@ -3479,7 +3649,7 @@ msgid "Enable the Performance Bar for a given group."
msgstr "Ative a barra de desempenho para um determinado grupo."
msgid "Enable two-factor authentication"
-msgstr ""
+msgstr "Ativar autenticação de dois fatores"
msgid "Enable usage ping"
msgstr "Ativar dados de uso"
@@ -3496,6 +3666,9 @@ msgstr "Termina em (UTC)"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr "Digite a descrição do merge request"
msgid "Enter the merge request title"
msgstr "Digite o título do merge request"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ 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 re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr "Ocorreu um erro ao parar o ambiente, por favor, tente novamente"
@@ -3592,15 +3768,33 @@ msgstr "Abrir ambiente ao vivo"
msgid "Environments|Pod logs from"
msgstr "Logs de pod de"
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+msgstr ""
+
msgid "Environments|Re-deploy to environment"
msgstr "Reimplantar no ambiente"
msgid "Environments|Read more about environments"
msgstr "Ler mais sobre ambiente"
+msgid "Environments|Rollback"
+msgstr ""
+
msgid "Environments|Rollback environment"
msgstr "Reverter ambiente"
+msgid "Environments|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "Mostrar tudo"
@@ -3613,6 +3807,18 @@ msgstr "Parar ambiente"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Atualizado"
@@ -3662,6 +3868,9 @@ msgid "Error Reporting and Logging"
msgstr "Relatório e registro de erros"
msgid "Error Tracking"
+msgstr "Acompanhamento de erros"
+
+msgid "Error creating a new path"
msgstr ""
msgid "Error creating epic"
@@ -3731,11 +3940,38 @@ msgid "Error while loading the merge request. Please try again."
msgstr "Erro ao carregar o merge request. Por favor, tente novamente."
msgid "Error:"
+msgstr "Erro:"
+
+msgid "ErrorTracking|Active"
msgstr ""
-msgid "Errors"
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
msgstr ""
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
+msgid "Errors"
+msgstr "Erros"
+
msgid "Estimated"
msgstr "Estimativa"
@@ -3758,10 +3994,10 @@ msgid "EventFilterBy|Filter by team"
msgstr "EventFilterBy|Filtrar por equipe"
msgid "Events"
-msgstr ""
+msgstr "Eventos"
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
-msgstr ""
+msgstr "Cada tentativa de %{action} falhou: %{job_error_message}. Por favor, tente novamente."
msgid "Every day (at 4:00am)"
msgstr "Todos os dias (às 4:00)"
@@ -3773,25 +4009,25 @@ msgid "Every week (Sundays at 4:00am)"
msgstr "Toda semana (domingos às 4:00)"
msgid "Everyone"
-msgstr ""
+msgstr "Todos"
msgid "Everyone can contribute"
msgstr "Todos podem contribuir"
msgid "Everything you need to create a GitLab Pages site using GitBook."
-msgstr ""
+msgstr "Tudo o que você precisa para criar um site do GitLab Pages usando o GitBook."
msgid "Everything you need to create a GitLab Pages site using Hexo."
-msgstr ""
+msgstr "Tudo o que você precisa para criar um site do GitLab Pages usando o Hexo."
msgid "Everything you need to create a GitLab Pages site using Hugo."
-msgstr ""
+msgstr "Tudo o que você precisa para criar um site do GitLab Pages usando o Hugo."
msgid "Everything you need to create a GitLab Pages site using Jekyll."
-msgstr ""
+msgstr "Tudo o que você precisa para criar um site do GitLab Pages usando o Jekyll."
msgid "Everything you need to create a GitLab Pages site using plain HTML."
-msgstr ""
+msgstr "Tudo o que você precisa para criar um site do GitLab Pages usando HTML simples."
msgid "Except policy:"
msgstr ""
@@ -3800,10 +4036,10 @@ msgid "Existing Git repository"
msgstr ""
msgid "Existing folder"
-msgstr ""
+msgstr "Pasta existente"
msgid "Existing members and groups"
-msgstr ""
+msgstr "Membros e grupos existentes"
msgid "Expand"
msgstr "Expandir"
@@ -3811,6 +4047,9 @@ msgstr "Expandir"
msgid "Expand all"
msgstr "Expandir tudo"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "Expandir barra lateral"
@@ -3818,10 +4057,10 @@ msgid "Expiration date"
msgstr "Data de validade"
msgid "Expired %{expiredOn}"
-msgstr ""
+msgstr "Expirou %{expiredOn}"
msgid "Expires in %{expires_at}"
-msgstr ""
+msgstr "Expira em %{expires_at}"
msgid "Explain the problem. If appropriate, provide a link to the relevant issue or comment."
msgstr "Explique o problema. Se necessário, forneça um link para a issue ou comentário relevante."
@@ -3845,19 +4084,19 @@ msgid "Explore public groups"
msgstr "Explorar grupos públicos"
msgid "Export as CSV"
-msgstr ""
+msgstr "Exportar como CSV"
msgid "Export issues"
-msgstr ""
+msgstr "Exportar issues"
msgid "External Classification Policy Authorization"
msgstr "Autorização de Política de Classificação Externa"
msgid "External URL"
-msgstr ""
+msgstr "URL externo"
msgid "External Wiki"
-msgstr ""
+msgstr "Wiki externo"
msgid "External authentication"
msgstr "Autenticação externa"
@@ -3898,7 +4137,7 @@ msgstr "Falha ao fazer deploy para"
msgid "Failed to load emoji list."
msgstr "Falha ao carregar a lista de emojis."
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3935,10 +4174,10 @@ msgid "Feature Flags"
msgstr "Sinalizadores de recurso"
msgid "FeatureFlags|* (All Environments)"
-msgstr ""
+msgstr "* (Todos os Ambientes)"
msgid "FeatureFlags|* (All environments)"
-msgstr ""
+msgstr "* (Todos os ambientes)"
msgid "FeatureFlags|API URL"
msgstr "URL da API"
@@ -3956,17 +4195,14 @@ msgid "FeatureFlags|Create feature flag"
msgstr "Criar sinalizador de recurso"
msgid "FeatureFlags|Delete %{name}?"
-msgstr ""
+msgstr "Excluir %{name}?"
msgid "FeatureFlags|Delete feature flag"
-msgstr ""
+msgstr "Excluir sinalizador de recurso"
msgid "FeatureFlags|Description"
msgstr "Descrição"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr "Editar %{feature_flag_name}"
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr "Editar sinalizador de recurso"
@@ -3983,22 +4219,22 @@ msgid "FeatureFlags|Feature Flag behavior is built up by creating a set of rules
msgstr ""
msgid "FeatureFlags|Feature Flags"
-msgstr ""
+msgstr "Sinalizadores de recursos"
msgid "FeatureFlags|Feature Flags allow you to configure your code into different flavors by dynamically toggling certain functionality."
-msgstr ""
+msgstr "Os sinalizadores de recursos permitem que você configure o seu código em diferentes versões alterando dinamicamente determinadas funcionalidades."
msgid "FeatureFlags|Feature flag %{name} will be removed. Are you sure?"
-msgstr ""
+msgstr "O sinalizador de recurso %{name} será removido. Você tem certeza?"
msgid "FeatureFlags|Get started with Feature Flags"
-msgstr ""
+msgstr "Comece a usar os sinalizadores de recursos"
msgid "FeatureFlags|Inactive"
msgstr "Inativo"
msgid "FeatureFlags|Inactive flag for %{scope}"
-msgstr ""
+msgstr "Sinalizador inativo para %{scope}"
msgid "FeatureFlags|Install a %{docs_link_start}compatible client library%{docs_link_end} and specify the API URL, application name, and instance ID during the configuration setup."
msgstr "Instale uma %{docs_link_start}biblioteca de cliente compatível%{docs_link_end} e especifique o URL da API, o nome do aplicativo e o ID da instância durante a configuração."
@@ -4007,10 +4243,10 @@ msgid "FeatureFlags|Instance ID"
msgstr "ID da instância"
msgid "FeatureFlags|Loading Feature Flags"
-msgstr ""
+msgstr "Carregando sinalizadores de recursos"
msgid "FeatureFlags|More Information"
-msgstr ""
+msgstr "Mais informações"
msgid "FeatureFlags|More information"
msgstr "Mais informações"
@@ -4024,26 +4260,23 @@ msgstr "Novo"
msgid "FeatureFlags|New Feature Flag"
msgstr "Novo sinalizador de recurso"
-msgid "FeatureFlags|Save changes"
-msgstr "Salvar alterações"
-
msgid "FeatureFlags|Status"
msgstr "Status"
msgid "FeatureFlags|Target environments"
-msgstr ""
+msgstr "Ambientes alvo"
msgid "FeatureFlags|There are no active Feature Flags"
-msgstr ""
+msgstr "Não há sinalizadores de recursos ativos"
msgid "FeatureFlags|There are no inactive Feature Flags"
-msgstr ""
+msgstr "Não há sinalizadores de recursos inativos"
msgid "FeatureFlags|There was an error fetching the feature flags."
-msgstr ""
+msgstr "Ocorreu um erro ao buscar os sinalizadores de recursos."
msgid "FeatureFlags|Try again in a few moments or contact your support team."
-msgstr ""
+msgstr "Tente novamente daqui a pouco ou entre em contato com a sua equipe de suporte."
msgid "Feb"
msgstr "Fev"
@@ -4056,29 +4289,29 @@ msgstr "Campos nessa página não são mais editáveis, você pode configurar"
msgid "File"
msgid_plural "Files"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Arquivo"
+msgstr[1] "Arquivos"
msgid "File added"
-msgstr ""
+msgstr "Arquivo adicionado"
msgid "File browser"
-msgstr ""
+msgstr "Navegador de arquivos"
msgid "File deleted"
-msgstr ""
+msgstr "Arquivo excluído"
msgid "File mode changed from %{a_mode} to %{b_mode}"
-msgstr ""
+msgstr "Modo de arquivo alterado de %{a_mode} para %{b_mode}"
msgid "File moved"
-msgstr ""
+msgstr "Arquivo movido"
msgid "File templates"
msgstr "Modelos de arquivos"
msgid "File upload error."
-msgstr ""
+msgstr "Erro ao enviar arquivo."
msgid "Files"
msgstr "Arquivos"
@@ -4099,28 +4332,25 @@ msgid "Filter by commit message"
msgstr "Filtrar por mensagem de commit"
msgid "Filter by milestone name"
-msgstr ""
+msgstr "Filtrar por nome de marco"
msgid "Filter by two-factor authentication"
-msgstr ""
+msgstr "Filtrar por autenticação de dois fatores"
msgid "Filter results by group"
-msgstr ""
+msgstr "Filtrar resultados por grupo"
msgid "Filter results by project"
-msgstr ""
+msgstr "Filtrar resultados por projeto"
msgid "Filter..."
msgstr "Filtro..."
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "Localizar por caminho"
msgid "Find existing members by name"
-msgstr ""
+msgstr "Encontre membros existentes por nome"
msgid "Find file"
msgstr "Localizar arquivo"
@@ -4135,7 +4365,7 @@ msgid "Fingerprints"
msgstr "Impressões digitais"
msgid "Finish editing this message first!"
-msgstr ""
+msgstr "Conclua a edição desta mensagem primeiro!"
msgid "Finish review"
msgstr "Concluir a revisão"
@@ -4144,7 +4374,7 @@ msgid "Finished"
msgstr "Finalizado"
msgid "First day of the week"
-msgstr ""
+msgstr "Primeiro dia da semana"
msgid "FirstPushedBy|First"
msgstr "Primeiro"
@@ -4192,7 +4422,7 @@ msgid "For internal projects, any logged in user can view pipelines and access j
msgstr "Para projetos internos, qualquer usuário conectado pode visualizar pipelines e acessar detalhes da tarefa (logs de saída e artefatos)"
msgid "For more info, read the documentation."
-msgstr ""
+msgstr "Para mais informações, leia a documentação."
msgid "For more information, go to the "
msgstr "Para mais informações, vá para o "
@@ -4219,7 +4449,7 @@ msgid "Forking in progress"
msgstr "Fork em andamento"
msgid "Forks"
-msgstr ""
+msgstr "Forks"
msgid "Format"
msgstr "Formato"
@@ -4230,8 +4460,8 @@ msgstr "Erros encontrados em seu .gitlab-ci.yml:"
msgid "Free Trial of GitLab.com Gold"
msgstr "Avaliação gratuita do GitLab.com Gold"
-msgid "From %{provider_title}"
-msgstr "De %{provider_title}"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "Do Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr "A partir de marcos:"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Na visualização de detalhes do cluster do Kubernetes, instale o Runner pela lista de aplicativos"
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "Chaves GPG"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "Geral"
@@ -4273,7 +4509,7 @@ msgid "Generate a default set of labels"
msgstr "Gerar etiquetas padrão"
msgid "Generate key"
-msgstr ""
+msgstr "Gerar chave"
msgid "Geo"
msgstr "Geo"
@@ -4615,7 +4851,7 @@ msgid "Get started with error tracking"
msgstr ""
msgid "Getting started with releases"
-msgstr ""
+msgstr "Introdução às versões"
msgid "Git"
msgstr "Git"
@@ -4657,7 +4893,7 @@ msgid "GitLab User"
msgstr "Usuário GitLab"
msgid "GitLab metadata URL"
-msgstr ""
+msgstr "URL de metadados do GitLab"
msgid "GitLab project export"
msgstr "Exportação do projeto GitLab"
@@ -4690,20 +4926,29 @@ msgid "Gitea Import"
msgstr "Importação do Gitea"
msgid "Given access %{time_ago}"
-msgstr ""
+msgstr "Acesso concedido %{time_ago}"
msgid "Go Back"
msgstr "Voltar"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "Voltar"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr "Ir para"
msgid "Go to %{link_to_google_takeout}."
msgstr "Ir para %{link_to_google_takeout}."
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr "Importação do Google Code"
@@ -4717,7 +4962,7 @@ msgid "Got it!"
msgstr "Entendi!"
msgid "Grant access"
-msgstr ""
+msgstr "Conceder acesso"
msgid "Graph"
msgstr "Gráfico"
@@ -4761,14 +5006,17 @@ 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 managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "Nome do grupo"
msgid "Group overview content"
-msgstr ""
+msgstr "Conteúdo da visão geral do grupo"
msgid "Group:"
-msgstr ""
+msgstr "Grupo:"
msgid "Group: %{group_name}"
msgstr "Grupo: %{group_name}"
@@ -4840,7 +5088,7 @@ msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroup
msgstr "Grupos também podem ser aninhados criando %{subgroup_docs_link_start}subgrupos%{subgroup_docs_link_end}."
msgid "Groups with access to <strong>%{project_name}</strong>"
-msgstr ""
+msgstr "Grupos com acesso a <strong>%{project_name}</strong>"
msgid "GroupsDropdown|Frequently visited"
msgstr "Visitados frequentemente"
@@ -4956,7 +5204,7 @@ msgstr[0] "Ocultar valor"
msgstr[1] "Ocultar valores"
msgid "Hide values"
-msgstr ""
+msgstr "Ocultar valores"
msgid "History"
msgstr "Histórico"
@@ -5091,7 +5339,7 @@ msgid "Import members"
msgstr ""
msgid "Import members from another project"
-msgstr ""
+msgstr "Importar membros de outro projeto"
msgid "Import multiple repositories by uploading a manifest file."
msgstr "Importar vários repositórios fazendo o upload de um arquivo manifest."
@@ -5100,7 +5348,7 @@ msgid "Import project"
msgstr "Importar projeto"
msgid "Import project members"
-msgstr ""
+msgstr "Importar membros do projeto"
msgid "Import projects from Bitbucket"
msgstr "Importar projetos do Bitbucket"
@@ -5127,11 +5375,26 @@ msgid "Import repository"
msgstr "Importar repositório"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
+msgstr "Importação expirou. A importação demorou mais de %{import_jobs_expiration} segundos"
+
+msgid "Import/Export illustration"
msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr "Conectar repositórios de"
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr "Melhore os painéis com GitLab Enterprise Edition."
@@ -5183,9 +5446,15 @@ msgstr "Insira as chaves do host manualmente"
msgid "Input your repository URL"
msgstr "Insira seu URL do repositório"
-msgid "Insert suggestion"
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
msgstr ""
+msgid "Insert suggestion"
+msgstr "Inserir sugestão"
+
msgid "Install GitLab Runner"
msgstr "Instalar o GitLab Runner"
@@ -5216,7 +5485,7 @@ msgid "Interested parties can even contribute by pushing commits if they want to
msgstr "As partes interessadas podem até contribuir enviando commits, caso queiram."
msgid "Internal"
-msgstr ""
+msgstr "Interno"
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."
@@ -5236,17 +5505,20 @@ msgstr "Apresentando a Análise de Ciclo"
msgid "Introducing Your Conversational Development Index"
msgstr ""
-msgid "Invitation"
+msgid "Invalid input, please avoid emojis"
msgstr ""
+msgid "Invitation"
+msgstr "Convite"
+
msgid "Invite"
msgstr "Convidar"
msgid "Invite group"
-msgstr ""
+msgstr "Convidar grupo"
msgid "Invite member"
-msgstr ""
+msgstr "Convidar membro"
msgid "Invoke Count"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr "Issue"
@@ -5273,10 +5548,10 @@ msgid "IssueBoards|Boards"
msgstr "Painéis"
msgid "IssueBoards|Create new board"
-msgstr ""
+msgstr "Criar novo painel"
msgid "IssueBoards|Delete board"
-msgstr ""
+msgstr "Excluir painel"
msgid "IssueBoards|No matching boards found"
msgstr ""
@@ -5285,7 +5560,7 @@ msgid "IssueBoards|Some of your boards are hidden, activate a license to see the
msgstr ""
msgid "IssueBoards|Switch board"
-msgstr ""
+msgstr "Alternar painel"
msgid "Issues"
msgstr "Issues"
@@ -5296,8 +5571,8 @@ msgstr "Issues podem ser bugs, tarefas ou ideias a serem discutidas. Além disso
msgid "Issues closed"
msgstr "Issues fechadas"
-msgid "Issues, merge requests, pushes and comments."
-msgstr "Issues, merge requests, pushes e comentários."
+msgid "Issues, merge requests, pushes, and comments."
+msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
msgstr "Depois que você começa a criar issues para seus projetos, podemos começar a acompanhar e exibir métricas para elas"
@@ -5324,7 +5599,7 @@ msgid "It must have a header row and at least two columns: the first column is t
msgstr ""
msgid "It's you"
-msgstr ""
+msgstr "É você"
msgid "Jaeger URL"
msgstr "URL Jaeger"
@@ -5408,7 +5683,7 @@ msgid "June"
msgstr "Junho"
msgid "Key (PEM)"
-msgstr ""
+msgstr "Chave (PEM)"
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -5485,6 +5760,9 @@ msgstr "Promover etiqueta"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr "Promover %{labelTitle} irá disponibilizá-la para todos os projetos dentro de %{groupName}. Etiquetas de projetos existentes com o mesmo título serão mescladas. Esta ação não pode ser revertida."
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr "Armazenamento de arquivos grandes"
@@ -5497,7 +5775,7 @@ msgid "Last Pipeline"
msgstr "Último Pipeline"
msgid "Last activity"
-msgstr ""
+msgstr "Última atividade"
msgid "Last commit"
msgstr "Último commit"
@@ -5515,7 +5793,7 @@ msgid "Last reply by"
msgstr "Última resposta de"
msgid "Last seen"
-msgstr ""
+msgstr "Visto pela última vez"
msgid "Last update"
msgstr "Última atualização"
@@ -5539,7 +5817,7 @@ msgid "Lead"
msgstr ""
msgid "Learn how to %{no_packages_link_start}publish and share your packages%{no_packages_link_end} with GitLab."
-msgstr ""
+msgstr "Saiba como %{no_packages_link_start}publicar e compartilhar seus pacotes%{no_packages_link_end} com o GitLab."
msgid "Learn more"
msgstr "Saiba mais"
@@ -5548,26 +5826,29 @@ msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple
msgstr "Saiba mais sobre %{issue_boards_url}, para acompanhar issues em diversas listas, usando etiquetas, atribuições e marcos. Se você notar algo faltando nos painéis, por favor, crie uma issue em %{gitlab_issues_url}."
msgid "Learn more about Auto DevOps"
-msgstr ""
+msgstr "Saiba mais sobre o Auto DevOps"
msgid "Learn more about Kubernetes"
msgstr "Saiba mais sobre o Kubernetes"
msgid "Learn more about Web Terminal"
-msgstr ""
+msgstr "Saiba mais sobre o Terminal Web"
msgid "Learn more about custom project templates"
-msgstr ""
+msgstr "Saiba mais sobre os modelos de projetos personalizados"
msgid "Learn more about group-level project templates"
-msgstr ""
+msgstr "Saiba mais sobre os modelos de projeto de nível de grupo"
msgid "Learn more about incoming email addresses"
-msgstr ""
+msgstr "Saiba mais sobre endereços de e-mail recebidos"
msgid "Learn more about protected branches"
msgstr "Saiba mais sobre branches protegidos"
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Saiba mais em"
@@ -5703,10 +5984,10 @@ msgid "Loading..."
msgstr "Carregando..."
msgid "Loading…"
-msgstr ""
+msgstr "Carregando…"
msgid "Localization"
-msgstr ""
+msgstr "Localização"
msgid "Lock"
msgstr "Bloquear"
@@ -5741,6 +6022,15 @@ msgstr "Entrar com cartão inteligente"
msgid "Logs"
msgstr "Logs"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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."
@@ -5775,7 +6065,7 @@ msgid "Manage project labels"
msgstr "Gerenciar etiquetas de projetos"
msgid "Manage two-factor authentication"
-msgstr ""
+msgstr "Gerenciar a autenticação de dois fatores"
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."
@@ -5786,6 +6076,9 @@ msgstr "Manifesto"
msgid "Manifest file import"
msgstr "Importação de arquivo de manifesto"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Associar um ID de conta do FogBugz para um usuário do GitLab"
@@ -5808,41 +6101,11 @@ msgid "Mark todo as done"
msgstr "Marcar como concluído"
msgid "Markdown"
-msgstr ""
+msgstr "Markdown"
msgid "Markdown enabled"
msgstr "Markdown habilitado"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr "Adicionar uma lista de marcadores"
-
-msgid "MarkdownToolbar|Add a link"
-msgstr "Adicionar um link"
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr "Adicionar uma lista numerada"
-
-msgid "MarkdownToolbar|Add a table"
-msgstr "Adicionar uma tabela"
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr "Adicionar uma lista de tarefas"
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr "Adicionar texto em negrito"
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr "Adicionar texto em itálico"
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr "Ir para tela cheia"
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr "Inserir uma citação"
-
-msgid "MarkdownToolbar|Insert code"
-msgstr "Inserir código"
-
msgid "Maven Metadata"
msgstr "Metadado do Maven"
@@ -5868,10 +6131,10 @@ msgid "Members"
msgstr "Membros"
msgid "Members can be added by project <i>Maintainers</i> or <i>Owners</i>"
-msgstr ""
+msgstr "Os membros podem ser adicionados pelos <i>mantenedores</i> ou <i>proprietários</i> do projeto"
msgid "Members of <strong>%{project_name}</strong>"
-msgstr ""
+msgstr "Membros de <strong>%{project_name}</strong>"
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 "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\"."
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "Merge requests"
@@ -5913,7 +6179,7 @@ msgid "Merge when pipeline succeeds"
msgstr ""
msgid "MergeRequests|Add a reply"
-msgstr ""
+msgstr "Adicionar uma resposta"
msgid "MergeRequests|An error occurred while saving the draft comment."
msgstr "Ocorreu um erro ao salvar o rascunho do comentário."
@@ -5934,7 +6200,7 @@ msgid "MergeRequests|Jump to next unresolved discussion"
msgstr ""
msgid "MergeRequests|Reply..."
-msgstr ""
+msgstr "Responder..."
msgid "MergeRequests|Resolve this discussion in a new issue"
msgstr "Resolver essa discussão em um novo issue"
@@ -5955,7 +6221,7 @@ msgid "MergeRequests|commented on commit %{commitLink}"
msgstr ""
msgid "MergeRequests|started a discussion"
-msgstr ""
+msgstr "iniciou uma discussão"
msgid "MergeRequests|started a discussion on %{linkStart}an old version of the diff%{linkEnd}"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr "Documentação de consulta do Prometheus"
-msgid "Metrics|System"
-msgstr "Sistema"
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr "Houve um erro ao obter os dados do ambiente; por favor, tente novamente"
@@ -6168,7 +6431,7 @@ msgid "Modify merge commit"
msgstr ""
msgid "Monday"
-msgstr ""
+msgstr "Segunda-feira"
msgid "Monitor your errors by integrating with Sentry"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr "Mais informações"
msgid "More information is available|here"
msgstr "Mais informações estão disponíveis|aqui"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr "Mais estrelas"
@@ -6234,7 +6500,7 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "Saia e faça login com uma conta diferente"
msgid "Need help?"
-msgstr ""
+msgstr "Precisa de ajuda?"
msgid "Network"
msgstr "Rede"
@@ -6266,20 +6532,20 @@ msgid "New Label"
msgstr "Nova etiqueta"
msgid "New Milestone"
-msgstr ""
+msgstr "Novo Marco"
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Novo Agendamento de Pipeline"
msgid "New Snippet"
msgstr "Novo Snippet"
-msgid "New Snippets"
-msgstr "Novos Snippets"
-
msgid "New branch"
msgstr "Novo branch"
@@ -6314,7 +6580,7 @@ msgid "New merge request"
msgstr "Novo merge request"
msgid "New milestone"
-msgstr ""
+msgstr "Novo marco"
msgid "New pipelines will cancel older, pending pipelines on the same branch"
msgstr "Novos pipelines cancelarão pipelines pendentes mais antigos no mesmo branch"
@@ -6340,12 +6606,18 @@ msgstr "Novo..."
msgid "No"
msgstr "Não"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr "Sem etiqueta"
-msgid "No activities found"
+msgid "No Tag"
msgstr ""
+msgid "No activities found"
+msgstr "Nenhuma atividade encontrada"
+
msgid "No assignee"
msgstr "Sem responsável"
@@ -6370,13 +6642,16 @@ msgstr "Nenhuma contribuição foi encontrada"
msgid "No credit card required."
msgstr "Não é necessário cartão de crédito."
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr "Sem validade"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6413,7 +6688,7 @@ msgid "No messages were logged"
msgstr "Nenhuma mensagem foi registrada"
msgid "No milestones to show"
-msgstr ""
+msgstr "Sem marcos para mostrar"
msgid "No other labels with such name or description"
msgstr "Sem outras etiquetas com esse nome ou descrição"
@@ -6472,6 +6747,9 @@ msgstr "Dados insuficientes"
msgid "Not now"
msgstr "Agora não"
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Observe que o branch master é automaticamente protegido. %{link_to_protected_branches}"
@@ -6509,10 +6787,10 @@ msgid "Notification events"
msgstr "Eventos de notificação"
msgid "Notification setting"
-msgstr ""
+msgstr "Configuração de notificação"
msgid "Notification setting - %{notification_title}"
-msgstr ""
+msgstr "Configuração de notificação - %{notification_title}"
msgid "NotificationEvent|Close issue"
msgstr "Fechar issue"
@@ -6634,13 +6912,13 @@ msgid "Open"
msgstr "Abrir"
msgid "Open Documentation"
-msgstr ""
+msgstr "Abrir documentação"
msgid "Open comment type dropdown"
msgstr ""
msgid "Open errors"
-msgstr ""
+msgstr "Abrir erros"
msgid "Open in Xcode"
msgstr "Abrir no Xcode"
@@ -6675,6 +6953,9 @@ msgstr "Operações"
msgid "Operations Dashboard"
msgstr "Painel de Operações"
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr "Adicione um projeto ao painel"
@@ -6684,6 +6965,9 @@ msgstr "O painel de operações fornece um resumo da integridade operacional de
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "Opcionalmente, você pode %{link_to_customize} como os endereços de e-mail e nomes de usuários do FogBugz são importados para o GitLab."
@@ -6730,10 +7014,10 @@ msgid "Pages"
msgstr "Páginas"
msgid "Pages Domain"
-msgstr ""
+msgstr "Domínio de páginas"
msgid "Pages Domains"
-msgstr ""
+msgstr "Domínios de páginas"
msgid "Pagination|Last »"
msgstr "Último >>"
@@ -6748,7 +7032,7 @@ msgid "Pagination|« First"
msgstr "<< Primeiro"
msgid "Parameter"
-msgstr ""
+msgstr "Parâmetro"
msgid "Parent epic"
msgstr ""
@@ -6808,7 +7092,7 @@ msgid "Personal project creation is not allowed. Please contact your administrat
msgstr ""
msgid "Pick a name"
-msgstr ""
+msgstr "Escolha um nome"
msgid "Pipeline"
msgstr "Pipeline"
@@ -6876,6 +7160,12 @@ msgstr "Variáveis"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Personalizado"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "Pipelines"
@@ -6891,6 +7181,9 @@ msgstr "Pipelines para a última semana"
msgid "Pipelines for last year"
msgstr "Pipelines para o último ano"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr "Construa com confiança"
@@ -6997,7 +7290,7 @@ msgid "Play"
msgstr "Iniciar"
msgid "Please %{link_to_register} or %{link_to_sign_in} to comment"
-msgstr ""
+msgstr "Por favor, %{link_to_register} ou %{link_to_sign_in} para comentar"
msgid "Please accept the Terms of Service before continuing."
msgstr "Por favor, aceite os Termos de Serviço antes de continuar."
@@ -7011,9 +7304,21 @@ msgstr "Por favor, converta-os para %{link_to_git} e passe pelo %{link_to_import
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr "Por favor, converta-os em Git no Google Code e passe pelo %{link_to_import_flow} novamente."
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr "Por favor, preencha um nome descritivo para o seu grupo."
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr "Por favor, note que esse aplicativo não é fornecido pelo GitLab e você deve verificar a sua autenticidade antes de permitir o acesso."
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+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 set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "Por favor, resolva o reCAPTCHA"
@@ -7033,7 +7347,7 @@ msgid "Please try again"
msgstr "Por favor, tente novamente"
msgid "Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version."
-msgstr ""
+msgstr "Por favor, atualize o PostgreSQL para a versão 9.6 ou superior. O status da replicação não pode ser determinado de maneira confiável com a versão atual."
msgid "Please use this form to report users to GitLab who create spam issues, comments or behave inappropriately."
msgstr "Use este formulário para denunciar usuários ao GitLab que criam spam de issues, comentários ou se comportam de maneira inapropriada."
@@ -7050,6 +7364,9 @@ msgstr "Preferências"
msgid "Preferences|Navigation theme"
msgstr "Tema de navegação"
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr "Pressione Enter ou clique para pesquisar"
@@ -7078,7 +7395,7 @@ msgid "Prioritized label"
msgstr "Etiqueta priorizada"
msgid "Private"
-msgstr ""
+msgstr "Privado"
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."
@@ -7102,16 +7419,16 @@ msgid "Profiles| You are going to change the username %{currentUsernameBold} to
msgstr "Você vai alterar o nome de usuário %{currentUsernameBold} para %{newUsernameBold}. O perfil e os projetos serão redirecionados para %{newUsername} mas esse redirecionamento expirará quando %{currentUsername} for registrado por outro usuário ou grupo. Por favor, atualize seus repositórios remotos Git o mais rápido possível."
msgid "Profiles|@username"
-msgstr ""
+msgstr "@nomedeusuário"
msgid "Profiles|Account scheduled for removal."
msgstr "Conta agendada para remoção."
msgid "Profiles|Activate signin with one of the following services"
-msgstr ""
+msgstr "Ative o login com um dos seguintes serviços"
msgid "Profiles|Active"
-msgstr ""
+msgstr "Ativo"
msgid "Profiles|Add key"
msgstr "Adicionar chave"
@@ -7129,28 +7446,28 @@ msgid "Profiles|Change username"
msgstr "Alterar nome de usuário"
msgid "Profiles|Changing your username can have unintended side effects."
-msgstr ""
+msgstr "Alterar o seu nome de usuário pode ter efeitos colaterais indesejados."
msgid "Profiles|Choose file..."
msgstr "Escolher arquivo..."
msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information"
-msgstr ""
+msgstr "Opte por mostrar contribuições de projetos privados em seu perfil público sem quaisquer informações do projeto, repositório ou da organização"
msgid "Profiles|City, country"
-msgstr ""
+msgstr "Cidade, país"
msgid "Profiles|Clear status"
msgstr "Limpar status"
msgid "Profiles|Click on icon to activate signin with one of the following services"
-msgstr ""
+msgstr "Clique no ícone para ativar o login com um dos seguintes serviços"
msgid "Profiles|Connect"
-msgstr ""
+msgstr "Conectar"
msgid "Profiles|Connected Accounts"
-msgstr ""
+msgstr "Contas conectadas"
msgid "Profiles|Current path: %{path}"
msgstr "Caminho atual: %{path}"
@@ -7171,7 +7488,7 @@ msgid "Profiles|Deleting an account has the following effects:"
msgstr "A exclusão de uma conta tem os seguintes efeitos:"
msgid "Profiles|Disconnect"
-msgstr ""
+msgstr "Desconectar"
msgid "Profiles|Do not show on profile"
msgstr "Não mostrar no perfil"
@@ -7183,10 +7500,10 @@ msgid "Profiles|Edit Profile"
msgstr "Editar perfil"
msgid "Profiles|Enter your name, so people you know can recognize you"
-msgstr ""
+msgstr "Digite seu nome, então as pessoas que você conhece podem reconhecê-lo"
msgid "Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)"
-msgstr ""
+msgstr "Aumente a segurança da sua conta ativando a autenticação de dois fatores (A2F)"
msgid "Profiles|Invalid password"
msgstr "Senha inválida"
@@ -7225,13 +7542,13 @@ msgid "Profiles|Set new profile picture"
msgstr "Definir nova foto de perfil"
msgid "Profiles|Social sign-in"
-msgstr ""
+msgstr "Login social"
msgid "Profiles|Some options are unavailable for LDAP accounts"
msgstr "Algumas opções estão indisponíveis para contas LDAP"
msgid "Profiles|Tell us about yourself in fewer than 250 characters"
-msgstr ""
+msgstr "Conte-nos sobre você em menos de 250 caracteres"
msgid "Profiles|The maximum file size allowed is 200KB."
msgstr "O tamanho máximo de arquivo permitido é de 200KB."
@@ -7240,7 +7557,7 @@ msgid "Profiles|This doesn't look like a public SSH key, are you sure you want t
msgstr "Isso não se parece com uma chave pública SSH, você tem certeza que gostaria de adicioná-la?"
msgid "Profiles|This email will be displayed on your public profile"
-msgstr ""
+msgstr "Este e-mail será exibido no seu perfil público"
msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}"
msgstr "Este e-mail será usado para operações baseadas na web, como edições e merges. %{learn_more}"
@@ -7248,14 +7565,11 @@ msgstr "Este e-mail será usado para operações baseadas na web, como edições
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr "Este emoji e mensagem aparecerão no seu perfil e em toda a interface."
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
-msgstr ""
+msgstr "Esta informação aparecerá no seu perfil"
msgid "Profiles|Two-Factor Authentication"
-msgstr ""
+msgstr "Autenticação de dois fatores"
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Escreva sua %{confirmationValue} para confirmar:"
@@ -7282,13 +7596,13 @@ msgid "Profiles|Username successfully changed"
msgstr "Alteração de nome de usuário realizada com sucesso"
msgid "Profiles|Using emojis in names seems fun, but please try to set a status message instead"
-msgstr ""
+msgstr "Usar emojis em nomes parece divertido, mas tente definir uma mensagem de status"
msgid "Profiles|What's your status?"
msgstr "Qual é o seu status?"
msgid "Profiles|Who you represent or work for"
-msgstr ""
+msgstr "Por quem você representa ou trabalha"
msgid "Profiles|You can change your avatar here"
msgstr "Você pode alterar o seu avatar aqui"
@@ -7309,7 +7623,7 @@ msgid "Profiles|You must transfer ownership or delete these groups before you ca
msgstr "Você precisa delegar outro usuário para ser dono ou apagar esses grupos antes de excluir sua conta."
msgid "Profiles|Your LinkedIn profile name from linkedin.com/in/profilename"
-msgstr ""
+msgstr "Seu nome de perfil no LinkedIn em linkedin.com/in/nomedeperfil"
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Sua conta é atualmente proprietária dos seguintes grupos:"
@@ -7330,10 +7644,10 @@ msgid "Profiles|e.g. My MacBook key"
msgstr "por exemplo, Chave do meu MacBook"
msgid "Profiles|username"
-msgstr ""
+msgstr "nome de usuário"
msgid "Profiles|website.com"
-msgstr ""
+msgstr "sitedaweb.com"
msgid "Profiles|your account"
msgstr "sua conta"
@@ -7350,6 +7664,9 @@ msgstr "Progresso"
msgid "Project"
msgstr "Projeto"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "O projeto '%{project_name}' está sendo excluído."
@@ -7392,9 +7709,12 @@ 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 members"
+msgid "Project has too many %{label_for_message} to search"
msgstr ""
+msgid "Project members"
+msgstr "Membros do projeto"
+
msgid "Project name"
msgstr "Nome do projeto"
@@ -7402,7 +7722,7 @@ msgid "Project slug"
msgstr "Slug do projeto"
msgid "Project:"
-msgstr ""
+msgstr "Projeto:"
msgid "ProjectActivityRSS|Subscribe"
msgstr "Inscreva-se"
@@ -7491,6 +7811,9 @@ msgstr "Usuários só podem fazer push para este repositório com commits que co
msgid "Projects"
msgstr "Projetos"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr "Projetos compartilhados com %{group_name}"
@@ -7705,7 +8028,7 @@ msgid "Pseudonymizer data collection"
msgstr "Coleção de dados Pseudonymizer"
msgid "Public"
-msgstr ""
+msgstr "Público"
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."
@@ -7747,13 +8070,13 @@ msgid "Quarters"
msgstr "Trimestres"
msgid "Query"
-msgstr ""
+msgstr "Consulta"
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."
msgid "README"
-msgstr ""
+msgstr "README"
msgid "Read more"
msgstr "Leia mais"
@@ -7797,7 +8120,7 @@ msgid "Register / Sign In"
msgstr "Registrar/Login"
msgid "Register U2F device"
-msgstr ""
+msgstr "Registrar dispositivo U2F"
msgid "Register and see your runners for this group."
msgstr "Registre-se e veja seus runners para este grupo."
@@ -7830,10 +8153,10 @@ msgid "Related merge requests"
msgstr "Merge requests relacionados"
msgid "Releases"
-msgstr ""
+msgstr "Versões"
msgid "Releases mark specific points in a project's development history, communicate information about the type of change, and deliver on prepared, often compiled, versions of the software to be reused elsewhere. Currently, releases can only be created through the API."
-msgstr ""
+msgstr "As versões marcam pontos específicos no histórico de desenvolvimento de um projeto, comunicam informações sobre o tipo de mudança e fornecem versões preparadas, muitas vezes compiladas do software para serem reutilizadas em outro lugar. Atualmente, as versões só podem ser criadas por meio da API."
msgid "Remind later"
msgstr "Lembrar mais tarde"
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr "Remover imagem"
@@ -7881,13 +8210,13 @@ msgid "Reopen epic"
msgstr "Reabrir epic"
msgid "Reopen milestone"
-msgstr ""
+msgstr "Reabrir marco"
msgid "Repair authentication"
msgstr "Corrigir autenticação"
msgid "Reply to comment"
-msgstr ""
+msgstr "Responder ao comentário"
msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr "Responda a este e-mail diretamente ou %{view_it_on_gitlab}."
@@ -7974,7 +8303,7 @@ msgid "Request Access"
msgstr "Solicitar acesso"
msgid "Requested %{time_ago}"
-msgstr ""
+msgstr "Solicitado %{time_ago}"
msgid "Requests Profiles"
msgstr "Solicita Perfis"
@@ -7985,26 +8314,39 @@ msgstr "Exigir que todos os usuários deste grupo configurem a autenticação de
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 "Resend invite"
+msgid "Require approval from code owners"
msgstr ""
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Resend invite"
+msgstr "Reenviar convite"
+
msgid "Reset authorization key"
-msgstr ""
+msgstr "Redefinir chave de autorização"
msgid "Reset authorization key?"
-msgstr ""
+msgstr "Redefinir chave de autorização?"
msgid "Reset health check access token"
msgstr "Recriar o token de status de saúde"
msgid "Reset key"
-msgstr ""
+msgstr "Redefinir chave"
msgid "Reset runners registration token"
msgstr "Recriar o token de registro de runners"
msgid "Resetting the authorization key will invalidate the previous key. Existing alert configurations will need to be updated with the new key."
-msgstr ""
+msgstr "Redefinir a chave de autorização invalidará a chave anterior. As configurações de alerta existentes terão de ser atualizadas com a nova chave."
msgid "Resolve all discussions in new issue"
msgstr "Resolver todas discussões em novo issue"
@@ -8016,10 +8358,10 @@ msgid "Resolve discussion"
msgstr "Resolver discussão"
msgid "Resolved"
-msgstr ""
+msgstr "Resolvido"
msgid "Response"
-msgstr ""
+msgstr "Resposta"
msgid "Response metrics (AWS ELB)"
msgstr "Métricas de resposta (AWS ELB)"
@@ -8031,7 +8373,7 @@ msgid "Response metrics (HA Proxy)"
msgstr "Métricas de resposta (HA Proxy)"
msgid "Response metrics (NGINX Ingress VTS)"
-msgstr ""
+msgstr "Métricas de resposta (NGINX Ingress VTS)"
msgid "Response metrics (NGINX Ingress)"
msgstr "Métricas de resposta (NGINX Ingress)"
@@ -8143,6 +8485,9 @@ msgstr "Você usou todos os seus minutos compartilhados para executores."
msgid "Running"
msgstr "Executando"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "SAML SSO"
@@ -8173,11 +8518,14 @@ msgstr "Chave SSH pública"
msgid "SSL Verification"
msgstr "Verificação SSL"
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "Salvar"
msgid "Save Changes"
-msgstr ""
+msgstr "Salvar alterações"
msgid "Save application"
msgstr "Salvar aplicativo"
@@ -8189,7 +8537,7 @@ msgid "Save changes before testing"
msgstr "Salvar alterações antes de testar"
msgid "Save comment"
-msgstr ""
+msgstr "Salvar comentário"
msgid "Save pipeline schedule"
msgstr "Salvar agendamento da pipeline"
@@ -8206,6 +8554,9 @@ msgstr "Agendado"
msgid "Schedules"
msgstr "Agendamentos"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "Agendando pipelines"
@@ -8266,6 +8617,9 @@ msgstr "Pesquisar projetos"
msgid "Search users"
msgstr "Procurar usuários"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr "Pesquisar nos seus projetos"
@@ -8372,7 +8726,7 @@ msgid "See metrics"
msgstr ""
msgid "See the affected projects in the GitLab admin panel"
-msgstr ""
+msgstr "Veja os projetos afetados no painel de administração do GitLab"
msgid "Select"
msgstr "Selecionar"
@@ -8402,7 +8756,7 @@ msgid "Select branch/tag"
msgstr "Selecionar o branch/tag"
msgid "Select members to invite"
-msgstr ""
+msgstr "Selecione membros para convidar"
msgid "Select project"
msgstr "Selecionar projeto"
@@ -8438,7 +8792,7 @@ msgid "Send email"
msgstr "Enviar e-mail"
msgid "Send report"
-msgstr ""
+msgstr "Enviar relatório"
msgid "Send usage data"
msgstr "Enviar dados de uso"
@@ -8524,6 +8878,9 @@ msgstr "Definir repositório de modelos para toda a instância"
msgid "Set max session time for web terminal."
msgstr "Defina o tempo máximo da sessão para o terminal da web."
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "Definir notificação por e-mail para relatórios de abuso."
@@ -8548,6 +8905,9 @@ msgstr "Configurar asserções/atributos/alegações (email, first_name, last_na
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "Configure seu projeto para fazer push e/ou pull de um repositório para outro automaticamente. Branches, tags e commits serão sincronizados automaticamente."
@@ -8605,9 +8965,15 @@ msgstr "Redefinir minutos usados de pipeline"
msgid "Sherlock Transactions"
msgstr "Transações de Sherlock"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "Exibir comando"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "Visualizar raw log completo"
@@ -8653,19 +9019,19 @@ msgid "Sign in / Register"
msgstr "Entrar / Criar conta"
msgid "Sign in to \"%{group_name}\""
-msgstr ""
+msgstr "Entrar em \"%{group_name}\""
msgid "Sign in using smart card"
-msgstr ""
+msgstr "Entrar usando o cartão inteligente"
msgid "Sign in via 2FA code"
-msgstr ""
+msgstr "Entrar via código da A2F"
msgid "Sign in with Single Sign-On"
msgstr "Entre com logon único"
msgid "Sign in with smart card"
-msgstr ""
+msgstr "Entrar com cartão inteligente"
msgid "Sign out"
msgstr "Sair"
@@ -8677,7 +9043,7 @@ msgid "Sign-up restrictions"
msgstr "Restrições de cadastro"
msgid "Similar issues"
-msgstr ""
+msgstr "Issues semelhantes"
msgid "Size"
msgstr "Tamanho"
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr "Snippets"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr "Atividade dos projetos favoritos"
msgid "Starred projects"
msgstr "Projetos favoritos"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9016,13 +9406,13 @@ msgid "Started"
msgstr "Iniciado"
msgid "Started %{startsIn}"
-msgstr ""
+msgstr "Iniciado %{startsIn}"
msgid "Starting..."
-msgstr ""
+msgstr "Iniciando..."
msgid "Starts %{startsIn}"
-msgstr ""
+msgstr "Inicia %{startsIn}"
msgid "Starts at (UTC)"
msgstr "Começa em (UTC)"
@@ -9034,10 +9424,10 @@ msgid "Status"
msgstr "Status"
msgid "Status:"
-msgstr ""
+msgstr "Status:"
msgid "Stop Terminal"
-msgstr ""
+msgstr "Parar terminal"
msgid "Stop environment"
msgstr "Parar o ambiente"
@@ -9055,7 +9445,7 @@ msgid "Stopping this environment is currently not possible as a deployment is in
msgstr "Não é possível parar esse ambiente no momento pois um deploy está em andamento"
msgid "Stopping..."
-msgstr ""
+msgstr "Parando..."
msgid "Storage"
msgstr "Armazenamento"
@@ -9073,7 +9463,7 @@ msgid "Submit as spam"
msgstr "Enviar como spam"
msgid "Submit feedback"
-msgstr ""
+msgstr "Enviar feedback"
msgid "Submit review"
msgstr "Enviar análise"
@@ -9091,7 +9481,7 @@ msgid "Subscribe at project level"
msgstr "Inscrever-se no nível do projeto"
msgid "Subscribe to RSS feed"
-msgstr ""
+msgstr "Inscrever-se no feed RSS"
msgid "Subscribe to calendar"
msgstr ""
@@ -9100,16 +9490,16 @@ msgid "Subscribed"
msgstr "Inscrito"
msgid "SubscriptionTable|Billing"
-msgstr ""
+msgstr "Cobrança"
msgid "SubscriptionTable|Free"
-msgstr ""
+msgstr "Grátis"
msgid "SubscriptionTable|GitLab allows you to continue using your subscription even if you exceed the number of seats you purchased. You will be required to pay for these seats upon renewal."
msgstr ""
msgid "SubscriptionTable|GitLab.com %{planName} %{suffix}"
-msgstr ""
+msgstr "GitLab.com %{planName} %{suffix}"
msgid "SubscriptionTable|Last invoice"
msgstr ""
@@ -9154,7 +9544,7 @@ msgid "SubscriptionTable|This is the number of seats you will be required to pur
msgstr ""
msgid "SubscriptionTable|Trial"
-msgstr ""
+msgstr "Avaliação"
msgid "SubscriptionTable|Trial end date"
msgstr ""
@@ -9175,7 +9565,7 @@ msgid "Suggested change"
msgstr ""
msgid "Sunday"
-msgstr ""
+msgstr "Domingo"
msgid "Support for custom certificates is disabled. Ask your system's administrator to enable it."
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr "Trocar branch/tag"
msgid "Sync information"
msgstr "Informação de sincronização"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "Hooks do sistema"
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr "O tamanho máximo do arquivo é de 200KB."
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "A senha necessária para descriptografar a chave privada. Isso é opcional e o seu valor é criptografado."
@@ -9498,6 +9894,9 @@ msgstr "Houve um erro ao excluir a tarefa."
msgid "There was an error loading users activity calendar."
msgstr "Erro ao carregar calendário de atividades."
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr "Erro ao salvar suas configurações de notificação."
@@ -9543,6 +9942,21 @@ msgstr "O escopo deste painel está reduzido"
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr "Esse branch mudou desde quando você começou sua edição. Você quer criar um novo branch?"
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr "Esse container registry foi agendado para exclusão."
@@ -9564,6 +9978,9 @@ msgstr "Esse diretório"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr "Esse grupo"
@@ -9648,6 +10065,12 @@ 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 merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr "Esta opção está desativada porque você não tem permissões de escrita para o branch atual"
@@ -10103,7 +10526,7 @@ msgid "Try again"
msgstr "Tente novamente"
msgid "Try again?"
-msgstr ""
+msgstr "Tentar novamente?"
msgid "Try all GitLab has to offer for 30 days."
msgstr "Use tudo que o GitLab tem para oferecer por 30 dias."
@@ -10207,6 +10630,9 @@ msgstr "Em breve"
msgid "Update"
msgstr "Atualizar"
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr "Atualizar agora"
msgid "Update your group name, description, avatar, and visibility."
msgstr "Atualize o nome do seu grupo, descrição, avatar e visibilidade."
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "Atualizando"
@@ -10343,7 +10772,7 @@ msgid "UserProfile|Most Recent Activity"
msgstr "Atividade mais recente"
msgid "UserProfile|No snippets found."
-msgstr ""
+msgstr "Nenhum snippet encontrado."
msgid "UserProfile|Overview"
msgstr "Visão geral"
@@ -10358,19 +10787,19 @@ msgid "UserProfile|Snippets"
msgstr "Snippets"
msgid "UserProfile|Snippets in GitLab can either be private, internal, or public."
-msgstr ""
+msgstr "Snippets no GitLab podem ser privados, internos ou públicos."
msgid "UserProfile|Subscribe"
msgstr "Inscrever-se"
msgid "UserProfile|This user doesn't have any personal projects"
-msgstr ""
+msgstr "Este usuário não tem nenhum projeto pessoal"
msgid "UserProfile|This user has a private profile"
msgstr "Este usuário tem um perfil privado"
msgid "UserProfile|This user hasn't contributed to any projects"
-msgstr ""
+msgstr "Este usuário não contribuiu para nenhum projeto"
msgid "UserProfile|View all"
msgstr "Ver tudo"
@@ -10385,10 +10814,10 @@ msgid "UserProfile|You haven't created any personal projects."
msgstr ""
msgid "UserProfile|You haven't created any snippets."
-msgstr ""
+msgstr "Você não criou nenhum snippet."
msgid "UserProfile|Your projects can be available publicly, internally, or privately, at your choice."
-msgstr ""
+msgstr "Seus projetos podem estar disponíveis publicamente, internamente ou privadamente, à sua escolha."
msgid "Users"
msgstr "Usuários"
@@ -10412,7 +10841,7 @@ msgid "Various email settings."
msgstr "Várias configurações de email."
msgid "Various localization settings."
-msgstr ""
+msgstr "Várias configurações de localização."
msgid "Various settings that affect GitLab performance."
msgstr "Várias configurações que afetam o desempenho do GitLab."
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr "Ver documentação"
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr "Ve lista de épicos"
@@ -10453,6 +10885,9 @@ msgstr "Ver arquivo @ "
msgid "View group labels"
msgstr "Visualizar etiquetas de grupo"
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "Ver issue"
@@ -10756,6 +11191,9 @@ msgstr "Com a análise de contribuições, você pode ter uma visão geral da at
msgid "Withdraw Access Request"
msgstr "Remover Requisição de Acesso"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10775,7 +11213,7 @@ msgid "Yesterday"
msgstr "Ontem"
msgid "You"
-msgstr ""
+msgstr "Você"
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 "Você é um administrador, o que significa que conceder acesso a <strong>%{client_name}</strong> permitirá que eles também interajam com o GitLab como administrador. Prossiga com cuidado."
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr "Você não tem permissão"
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Você atingiu o limite de seu projeto"
@@ -10921,6 +11362,9 @@ 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 only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr "Você está recebendo este e-mail porque %{reason}."
@@ -11014,6 +11458,9 @@ msgstr "atribuir a si mesmo"
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr "nome da branch"
@@ -11097,6 +11544,9 @@ msgstr "Qualidade do código"
msgid "ciReport|Confidence"
msgstr "Confiança"
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr "Fazer scan no container"
@@ -11191,9 +11641,6 @@ msgstr "Sem mudanças nas métricas de desempenho"
msgid "ciReport|Performance metrics"
msgstr "Métricas de desempenho"
-msgid "ciReport|Revert dismissal"
-msgstr "Reverter ignorar"
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11233,6 +11680,9 @@ msgstr "Ocorreu um erro ao carregar o relatório de verificação de dependênci
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "Houve um erro ao reverter o descarte. Por favor, tente novamente."
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "Atualizar %{name} de %{version} para %{fixed}."
@@ -11325,9 +11775,6 @@ msgstr "ajuda"
msgid "here"
msgstr "aqui"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://seu-servidor-do-bitbucket"
@@ -11446,6 +11893,9 @@ msgstr "Ocorreu um erro ao enviar sua aprovação."
msgid "mrWidget|Approve"
msgstr "Aprovar"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "Aprovado por"
@@ -11518,6 +11968,9 @@ msgstr "Fazer merge localmente"
msgid "mrWidget|Merge request approved"
msgstr "Merge request aprovado"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr "Merge request aprovado; você pode adicionalmente"
@@ -11579,6 +12032,9 @@ msgstr "Reverter"
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr "Reverter esse merge request com um novo merge request"
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "Definir por"
@@ -11658,7 +12114,7 @@ msgid "new merge request"
msgstr "novo merge request"
msgid "none"
-msgstr ""
+msgstr "nenhum"
msgid "notification emails"
msgstr "e-mails de notificação"
@@ -11692,7 +12148,7 @@ msgid "personal access token"
msgstr "token de acesso pessoal"
msgid "private"
-msgstr ""
+msgstr "privado"
msgid "private key does not match certificate."
msgstr "chave privada não corresponde ao certificado."
@@ -11703,10 +12159,10 @@ msgstr[0] "projeto"
msgstr[1] "projetos"
msgid "quick actions"
-msgstr ""
+msgstr "ações rápidas"
msgid "register"
-msgstr ""
+msgstr "registrar"
msgid "remaining"
msgstr "restante"
@@ -11738,7 +12194,7 @@ msgid "show less"
msgstr ""
msgid "sign in"
-msgstr ""
+msgstr "entrar"
msgid "source"
msgstr "origem"
@@ -11771,7 +12227,7 @@ msgid "triggered"
msgstr ""
msgid "updated"
-msgstr ""
+msgstr "atualizado"
msgid "username"
msgstr "nome do usuário"
@@ -11780,7 +12236,7 @@ msgid "uses Kubernetes clusters to deploy your code!"
msgstr "use clusters Kubernetes para deploy do seu código!"
msgid "verify ownership"
-msgstr ""
+msgstr "verificar propriedade"
msgid "view it on GitLab"
msgstr "ver no GitLab"
diff --git a/locale/pt_PT/gitlab.po b/locale/pt_PT/gitlab.po
index 1584a4159cc..2d43d0ebe4d 100644
--- a/locale/pt_PT/gitlab.po
+++ b/locale/pt_PT/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: pt-PT\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:08\n"
+"PO-Revision-Date: 2019-03-06 15:17\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/ro_RO/gitlab.po b/locale/ro_RO/gitlab.po
index 17805691aff..e4b0be5e2a8 100644
--- a/locale/ro_RO/gitlab.po
+++ b/locale/ro_RO/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ro\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:07\n"
+"PO-Revision-Date: 2019-03-06 15:16\n"
msgid " Status"
msgstr ""
@@ -45,6 +45,12 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -141,12 +147,27 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -165,15 +186,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -249,6 +270,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -273,6 +297,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -384,6 +414,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -399,6 +444,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -483,24 +531,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -669,6 +744,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -681,6 +759,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -750,7 +831,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -762,6 +843,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -774,6 +864,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -810,6 +903,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -867,12 +963,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -963,6 +1065,48 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1005,9 +1149,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1092,6 +1242,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1107,9 +1260,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1668,6 +1818,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1704,6 +1857,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1716,6 +1872,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1782,9 +1941,6 @@ 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 ""
@@ -1944,6 +2100,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1959,9 +2118,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2007,10 +2163,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2049,6 +2205,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2145,7 +2304,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2160,9 +2319,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2208,9 +2364,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2448,15 +2601,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2757,6 +2922,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3246,6 +3414,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3324,6 +3495,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3495,6 +3669,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3531,6 +3708,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3549,9 +3729,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3576,6 +3753,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3627,15 +3810,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3648,6 +3849,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3699,6 +3912,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3768,6 +3984,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3846,6 +4089,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3933,7 +4179,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3999,9 +4245,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4059,9 +4302,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4149,9 +4389,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4266,7 +4503,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4296,9 +4533,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4731,15 +4974,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4797,6 +5049,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5166,9 +5421,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5220,6 +5490,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5274,6 +5550,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5292,6 +5571,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5334,7 +5616,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5523,6 +5805,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5607,6 +5892,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5781,6 +6069,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5826,6 +6123,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5853,36 +6153,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5937,6 +6207,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6087,9 +6360,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6234,6 +6504,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6312,13 +6585,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6381,9 +6654,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6411,13 +6690,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6513,6 +6795,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6717,6 +7002,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6726,6 +7014,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6918,6 +7209,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6933,6 +7230,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7053,9 +7353,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7065,9 +7377,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7092,6 +7413,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7290,9 +7614,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7392,6 +7713,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7434,6 +7758,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7533,6 +7860,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7893,6 +8223,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8028,6 +8364,21 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Resend invite"
msgstr ""
@@ -8187,6 +8538,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8217,6 +8571,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8250,6 +8607,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8310,6 +8670,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8568,6 +8931,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8592,6 +8958,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8649,9 +9018,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8751,6 +9126,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9021,6 +9414,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9231,6 +9630,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9420,6 +9822,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9543,6 +9948,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9588,6 +9996,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9609,6 +10032,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9693,6 +10119,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10254,6 +10686,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10263,6 +10698,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10491,6 +10929,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10500,6 +10941,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10803,6 +11247,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10908,6 +11355,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10968,6 +11418,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11061,6 +11514,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11148,6 +11604,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11244,9 +11703,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11286,6 +11742,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11382,9 +11841,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11505,6 +11961,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11577,6 +12036,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11640,6 +12102,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/ru/gitlab.po b/locale/ru/gitlab.po
index b5db3291862..ee94200a9f7 100644
--- a/locale/ru/gitlab.po
+++ b/locale/ru/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: ru\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:09\n"
+"PO-Revision-Date: 2019-03-06 15:18\n"
msgid " Status"
msgstr " СтатуÑ"
@@ -47,6 +47,13 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr "\"%{query}\" в проектах"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d коммит"
@@ -156,12 +163,28 @@ msgstr "%{counter_storage} (%{counter_repositories} репозиториев, %{
msgid "%{count} %{alerts}"
msgstr "%{count} %{alerts}"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr "Ещё %{count} иÑполнителей"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} учаÑтник"
@@ -182,15 +205,15 @@ msgstr "%{filePath} удален"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} + ещё %{labelCount}"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} будет удален! Вы уверены?"
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -270,6 +293,9 @@ msgstr "+ ещё %{count}"
msgid "+ %{moreCount} more"
msgstr "+ ещё %{moreCount}"
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -296,6 +322,13 @@ msgstr[1] "%{count} модификаций типа %{type}"
msgstr[2] "%{count} модификаций типа %{type}"
msgstr[3] "%{count} модификаций типа %{type}"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "1 закрытое обÑуждение"
@@ -416,6 +449,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Графики непрерывной интеграции (CI)"
@@ -431,6 +479,9 @@ msgstr "Сотрудник группы по борьбе Ñ Ð½Ð°Ñ€ÑƒÑˆÐµÐ½Ð¸ÑÐ
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A 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}."
@@ -515,24 +566,51 @@ msgstr "Добавить Kubernetes клаÑтер"
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "Добавить таблицу"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "Добавить комментарий"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "Добавить комментарий к изображению"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "Добавить лицензию"
@@ -701,6 +779,9 @@ msgstr ""
msgid "Advanced settings"
msgstr "РаÑширенные наÑтройки"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "Предупреждение"
@@ -714,6 +795,9 @@ msgstr ""
msgid "All"
msgstr "Ð’Ñе"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr "Ð’Ñе Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð¸Ñ Ð·Ð°Ñ„Ð¸ÐºÑированы"
@@ -783,7 +867,7 @@ msgstr ""
msgid "An error has occurred"
msgstr "Произошла ошибка"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -795,6 +879,15 @@ msgstr "Произошла ошибка Ð´Ð¾Ð±Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ чер
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr "Произошла ошибка при предварительном проÑмотре объекта"
@@ -807,6 +900,9 @@ msgstr "Произошла ошибка при обновлении веÑа оÐ
msgid "An error occurred while adding approver"
msgstr "Произошла ошибка при добавлении одобрÑющего"
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "Во Ð²Ñ€ÐµÐ¼Ñ ÑƒÐ´Ð°Ð»ÐµÐ½Ð¸Ñ ÐºÐ¾Ð¼Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ð¸Ñ Ð¿Ñ€Ð¾Ð¸Ð·Ð¾ÑˆÐ»Ð° ошибка"
@@ -843,6 +939,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr "Произошла ошибка при получении Ñборочной линии."
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "Произошла ошибка при получении ÑпиÑка проектов"
@@ -900,12 +999,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr "Произошла ошибка при Ñохранении ответÑтвенных"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -996,6 +1101,52 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1038,9 +1189,15 @@ msgstr "Ð’Ñ‹ уверены, что вы хотите заново ÑгенерÐ
msgid "Are you sure you want to remove %{group_name}?"
msgstr "Вы уверены, что вы хотите удалить %{group_name}?"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1125,6 +1282,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr "ОтветÑтвенный(ные)"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1140,9 +1300,6 @@ msgstr "Ðвг."
msgid "August"
msgstr "ÐвгуÑÑ‚"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "Журнал аутентификации"
@@ -1701,6 +1858,9 @@ msgstr "Ðет возможноÑти объединить автоматичеÑ
msgid "Cannot modify managed Kubernetes cluster"
msgstr "Ðевозможно изменить управлÑемый клаÑтер Kubernetes"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1737,6 +1897,9 @@ msgstr "Отменить"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "Это ÑоздаÑÑ‚ новый коммит Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, чтобы откатить ÑущеÑтвующие изменениÑ."
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1749,6 +1912,9 @@ msgstr "Диаграммы"
msgid "Chat"
msgstr "Чат"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1815,9 +1981,6 @@ 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 ""
@@ -1977,6 +2140,9 @@ msgstr "Клонировать репозиторий"
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1992,9 +2158,6 @@ msgstr ""
msgid "Closed"
msgstr "Закрыто"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "Закрытые обÑуждениÑ"
@@ -2040,12 +2203,12 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "Произошла ошибка при попытке Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ Ð·Ð¾Ð½ проекта: %{error}"
-
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr ""
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
msgstr ""
@@ -2082,6 +2245,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2178,7 +2344,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2193,9 +2359,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr "УÑтановить"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr "УÑтановлен"
@@ -2241,9 +2404,6 @@ msgstr "КлаÑтер Kubernetes"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Ð¡Ð²ÐµÐ´ÐµÐ½Ð¸Ñ Ð¾ клаÑтере Kubernetes"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "КлаÑтер Kubernetes ÑоздаётÑÑ Ð² Google Kubernetes Engine..."
@@ -2481,15 +2641,27 @@ msgstr "зарегиÑтрироватьÑÑ"
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr "Владельцы кода"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr "Когорты"
msgid "Collapse"
msgstr "Свернуть"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "Свернуть боковую панель"
@@ -2791,6 +2963,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3281,6 +3456,9 @@ msgstr ""
msgid "Description:"
msgstr "ОпиÑание:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3359,6 +3537,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3530,6 +3711,9 @@ msgstr "Включить Ð´Ð»Ñ Ñтого проекта"
msgid "Enable group Runners"
msgstr "Включить групповые обработчики заданий"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3566,6 +3750,9 @@ msgstr "ЗаканчиваетÑÑ Ð² (UTC)"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3584,9 +3771,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr "Введите заголовок запроÑа на ÑлиÑние"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3611,6 +3795,12 @@ msgstr "Произошла ошибка при получении окружен
msgid "Environments|An error occurred while making the request."
msgstr "Произошла ошибка во Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñа."
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3662,15 +3852,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "Показать вÑе"
@@ -3683,6 +3891,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "Обновлено"
@@ -3734,6 +3954,9 @@ msgstr "Отчеты об ошибках и журналирование"
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3803,6 +4026,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3881,6 +4131,9 @@ msgstr "Развернуть"
msgid "Expand all"
msgstr "Развернуть вÑе"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "Развернуть боковую панель"
@@ -3968,7 +4221,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -4034,9 +4287,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr "ОпиÑание"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4094,9 +4344,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr "СтатуÑ"
@@ -4185,9 +4432,6 @@ msgstr ""
msgid "Filter..."
msgstr "Фильтр..."
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "ПоиÑк по пути"
@@ -4302,8 +4546,8 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
-msgstr "Из %{provider_title}"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr ""
@@ -4332,9 +4576,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "GPG Ключи"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "ОÑновныe"
@@ -4767,15 +5017,24 @@ msgstr ""
msgid "Go Back"
msgstr "ВернутьÑÑ Ð½Ð°Ð·Ð°Ð´"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "ВернутьÑÑ"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4833,6 +5092,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "Ðазвание группы"
@@ -5203,9 +5465,24 @@ msgstr "Импорт репозиториÑ"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5257,6 +5534,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5312,6 +5595,9 @@ msgstr "Внедрение Цикла Ðналитик"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5330,6 +5616,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr "ОбÑуждение"
@@ -5372,8 +5661,8 @@ msgstr "ОбÑуждениÑми могут быть ошибки, задачи
msgid "Issues closed"
msgstr "ОбÑÑƒÐ¶Ð´ÐµÐ½Ð¸Ñ Ð·Ð°ÐºÑ€Ñ‹Ñ‚Ñ‹"
-msgid "Issues, merge requests, pushes and comments."
-msgstr "ОбÑуждениÑ, запроÑÑ‹ на ÑлиÑниÑ, отправки изменений и комментарии."
+msgid "Issues, merge requests, pushes, and comments."
+msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
msgstr ""
@@ -5561,6 +5850,9 @@ msgstr "ПеренеÑти Метку"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5646,6 +5938,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "Узнайте больше в"
@@ -5821,6 +6116,15 @@ msgstr ""
msgid "Logs"
msgstr "Журналы"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5866,6 +6170,9 @@ msgstr "МанифеÑÑ‚"
msgid "Manifest file import"
msgstr "Импорт файла манифеÑта"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5893,36 +6200,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr "Включен режим Markdown"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5977,6 +6254,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние"
@@ -6127,9 +6407,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6274,6 +6551,9 @@ msgstr "Ð”Ð¾Ð¿Ð¾Ð»Ð½Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ"
msgid "More information is available|here"
msgstr "Больше информации доÑтупно|тут"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6353,15 +6633,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "Ðовое РаÑпиÑание Сборочной Линии"
msgid "New Snippet"
msgstr "Ðовый пример кода"
-msgid "New Snippets"
-msgstr "Ðовые примеры кода"
-
msgid "New branch"
msgstr "ÐÐ¾Ð²Ð°Ñ Ð²ÐµÑ‚ÐºÐ°"
@@ -6422,9 +6702,15 @@ msgstr ""
msgid "No"
msgstr "Ðет"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6452,13 +6738,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr "Плановый Ñрок не указан"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6554,6 +6843,9 @@ msgstr "ÐедоÑтаточно данных"
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Обратите внимание, что маÑтер ветка автоматичеÑки защищена. %{link_to_protected_branches}"
@@ -6759,6 +7051,9 @@ msgstr "Операции"
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6768,6 +7063,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6960,6 +7258,12 @@ msgstr "Переменные"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "ÐаÑтраиваемый"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "Сборочные линии"
@@ -6975,6 +7279,9 @@ msgstr "Сборочные линии за поÑледнюю неделю"
msgid "Pipelines for last year"
msgstr "Сборочные линии за поÑледний год"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7095,9 +7402,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7107,9 +7426,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr "ПожалуйÑта, выберите по крайней мере один фильтр, чтобы увидеть результаты"
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "ПожалуйÑта, решите reCAPTCHA"
@@ -7134,6 +7462,9 @@ msgstr "ПредпочтениÑ"
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7332,9 +7663,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7434,6 +7762,9 @@ msgstr ""
msgid "Project"
msgstr "Проект"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Проект '%{project_name}' находитÑÑ Ð² процеÑÑе удалениÑ."
@@ -7476,6 +7807,9 @@ msgstr "ИÑтек Ñрок дейÑÑ‚Ð²Ð¸Ñ ÑÑылки на проект. СÐ
msgid "Project export started. A download link will be sent by email."
msgstr "Ðачат ÑкÑпорт проекта. СÑылка Ð´Ð»Ñ ÑÐºÐ°Ñ‡Ð¸Ð²Ð°Ð½Ð¸Ñ Ð±ÑƒÐ´ÐµÑ‚ отправлена по Ñлектронной почте."
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7575,6 +7909,9 @@ msgstr ""
msgid "Projects"
msgstr "Проекты"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7936,6 +8273,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr "Удалить аватар"
@@ -8071,6 +8414,23 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Resend invite"
msgstr ""
@@ -8231,6 +8591,9 @@ msgstr ""
msgid "Running"
msgstr "ВыполнÑетÑÑ"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8261,6 +8624,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "Сохранить"
@@ -8294,6 +8660,9 @@ msgstr "Запланировано"
msgid "Schedules"
msgstr "РаÑпиÑаниÑ"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "Планирование Сборочных Линий"
@@ -8354,6 +8723,9 @@ msgstr ""
msgid "Search users"
msgstr "ПоиÑк пользователей"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8612,6 +8984,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr "УÑтановить макÑимальное Ð²Ñ€ÐµÐ¼Ñ ÑеанÑа Ð´Ð»Ñ Ð²ÐµÐ±-терминала."
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "ÐаÑтроить ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾ Ñлектронной почте Ð´Ð»Ñ ÑÐ¾Ð¾Ð±Ñ‰ÐµÐ½Ð¸Ñ Ð¾ злоупотреблениÑÑ…."
@@ -8636,6 +9011,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8693,9 +9071,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "Показать команду"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8796,6 +9180,24 @@ msgstr ""
msgid "Snippets"
msgstr "Примеры кода"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9066,6 +9468,12 @@ msgstr "ÐктивноÑÑ‚ÑŒ в избранных проектах"
msgid "Starred projects"
msgstr "Избранные проекты"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9276,6 +9684,9 @@ msgstr "Переключить ветка/тег"
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "СиÑтемные Обработчики"
@@ -9465,6 +9876,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr "МакÑимально допуÑтимый размер файла ÑоÑтавлÑет 200 Кб."
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9588,6 +10002,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr "Произошла ошибка при Ñохранении наÑтроек уведомлений."
@@ -9633,6 +10050,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9654,6 +10086,9 @@ msgstr "Этот каталог"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9738,6 +10173,12 @@ msgstr "Это означает, что вы не можете отправитÑ
msgid "This merge request is locked."
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние заблокирован."
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10301,6 +10742,9 @@ msgstr ""
msgid "Update"
msgstr "Обновить"
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10310,6 +10754,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr "Обновить наименование вашей группы, её опиÑание, аватар и видимоÑÑ‚ÑŒ."
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10538,6 +10985,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr "Показать ÑпиÑок целей"
@@ -10547,6 +10997,9 @@ msgstr "ПроÑмотр файла @ "
msgid "View group labels"
msgstr "ПроÑмотр меток группы"
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "ПроÑмотр обÑуждениÑ"
@@ -10850,6 +11303,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "Отменить Ð·Ð°Ð¿Ñ€Ð¾Ñ Ð´Ð¾Ñтупа"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10955,6 +11411,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "Ð’Ñ‹ доÑтигли Ð¾Ð³Ñ€Ð°Ð½Ð¸Ñ‡ÐµÐ½Ð¸Ñ Ð² вашем проекте"
@@ -11015,6 +11474,9 @@ msgstr "Ð’Ñ‹ не Ñможете работать Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¾Ð¼ через
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Ð”Ð»Ñ Ð¿Ð¾Ð»ÑƒÑ‡ÐµÐ½Ð¸Ñ ÐºÐ¾Ñ€Ñ€ÐµÐºÑ‚Ð½Ð¾Ð³Ð¾ ÑÑ€Ð°Ð²Ð½ÐµÐ½Ð¸Ñ Ð²Ð°Ð¼ нужно иÑпользовать разные имена веток."
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11108,6 +11570,9 @@ msgstr "назначить ÑебÑ"
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr "Ð¸Ð¼Ñ Ð²ÐµÑ‚Ð²Ð¸"
@@ -11199,6 +11664,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11297,9 +11765,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11339,6 +11804,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11439,9 +11907,6 @@ msgstr "помощь"
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11564,6 +12029,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr "Одобрить"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "Одобрено"
@@ -11636,6 +12104,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние одобрен"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr "Ð—Ð°Ð¿Ñ€Ð¾Ñ Ð½Ð° ÑлиÑние одобрен; вы можете дополнительно одобрить"
@@ -11701,6 +12172,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/sk_SK/gitlab.po b/locale/sk_SK/gitlab.po
index a76359352a9..e1d860895f6 100644
--- a/locale/sk_SK/gitlab.po
+++ b/locale/sk_SK/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: sk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:09\n"
+"PO-Revision-Date: 2019-03-06 15:19\n"
msgid " Status"
msgstr ""
@@ -47,6 +47,13 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -156,12 +163,28 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -182,15 +205,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -270,6 +293,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -296,6 +322,13 @@ msgstr[1] ""
msgstr[2] ""
msgstr[3] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -416,6 +449,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -431,6 +479,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -515,24 +566,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -701,6 +779,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -714,6 +795,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -783,7 +867,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -795,6 +879,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -807,6 +900,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -843,6 +939,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -900,12 +999,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -996,6 +1101,52 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1038,9 +1189,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1125,6 +1282,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1140,9 +1300,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1701,6 +1858,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1737,6 +1897,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1749,6 +1912,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1815,9 +1981,6 @@ 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 ""
@@ -1977,6 +2140,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1992,9 +2158,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2040,10 +2203,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2082,6 +2245,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2178,7 +2344,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2193,9 +2359,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2241,9 +2404,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2481,15 +2641,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2791,6 +2963,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3281,6 +3456,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3359,6 +3537,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3530,6 +3711,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3566,6 +3750,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3584,9 +3771,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3611,6 +3795,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3662,15 +3852,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3683,6 +3891,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3734,6 +3954,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3803,6 +4026,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3881,6 +4131,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3968,7 +4221,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -4034,9 +4287,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4094,9 +4344,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4185,9 +4432,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4302,7 +4546,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4332,9 +4576,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4767,15 +5017,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4833,6 +5092,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5203,9 +5465,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5257,6 +5534,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5312,6 +5595,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5330,6 +5616,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5372,7 +5661,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5561,6 +5850,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5646,6 +5938,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5821,6 +6116,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5866,6 +6170,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5893,36 +6200,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5977,6 +6254,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6127,9 +6407,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6274,6 +6551,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6353,13 +6633,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6422,9 +6702,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6452,13 +6738,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6554,6 +6843,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6759,6 +7051,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6768,6 +7063,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6960,6 +7258,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6975,6 +7279,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7095,9 +7402,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7107,9 +7426,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7134,6 +7462,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7332,9 +7663,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7434,6 +7762,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7476,6 +7807,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7575,6 +7909,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7936,6 +8273,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8071,6 +8414,23 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "Resend invite"
msgstr ""
@@ -8231,6 +8591,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8261,6 +8624,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8294,6 +8660,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8354,6 +8723,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8612,6 +8984,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8636,6 +9011,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8693,9 +9071,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8796,6 +9180,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9066,6 +9468,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9276,6 +9684,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9465,6 +9876,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9588,6 +10002,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9633,6 +10050,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9654,6 +10086,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9738,6 +10173,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10301,6 +10742,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10310,6 +10754,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10538,6 +10985,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10547,6 +10997,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10850,6 +11303,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10955,6 +11411,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -11015,6 +11474,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11108,6 +11570,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11199,6 +11664,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11297,9 +11765,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11339,6 +11804,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11439,9 +11907,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11564,6 +12029,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11636,6 +12104,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11701,6 +12172,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/sq_AL/gitlab.po b/locale/sq_AL/gitlab.po
index 3774bd3ad14..cb3bb5e42a1 100644
--- a/locale/sq_AL/gitlab.po
+++ b/locale/sq_AL/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: sq\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:07\n"
+"PO-Revision-Date: 2019-03-06 15:16\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/sr_CS/gitlab.po b/locale/sr_CS/gitlab.po
index 85059aa0f90..25f70503b18 100644
--- a/locale/sr_CS/gitlab.po
+++ b/locale/sr_CS/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: sr-CS\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:09\n"
+"PO-Revision-Date: 2019-03-06 15:19\n"
msgid " Status"
msgstr ""
@@ -45,6 +45,12 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -141,12 +147,27 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -165,15 +186,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -249,6 +270,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -273,6 +297,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -384,6 +414,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -399,6 +444,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -483,24 +531,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -669,6 +744,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -681,6 +759,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -750,7 +831,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -762,6 +843,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -774,6 +864,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -810,6 +903,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -867,12 +963,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -963,6 +1065,48 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1005,9 +1149,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1092,6 +1242,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1107,9 +1260,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1668,6 +1818,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1704,6 +1857,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1716,6 +1872,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1782,9 +1941,6 @@ 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 ""
@@ -1944,6 +2100,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1959,9 +2118,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2007,10 +2163,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2049,6 +2205,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2145,7 +2304,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2160,9 +2319,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2208,9 +2364,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2448,15 +2601,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2757,6 +2922,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3246,6 +3414,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3324,6 +3495,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3495,6 +3669,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3531,6 +3708,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3549,9 +3729,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3576,6 +3753,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3627,15 +3810,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3648,6 +3849,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3699,6 +3912,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3768,6 +3984,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3846,6 +4089,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3933,7 +4179,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3999,9 +4245,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4059,9 +4302,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4149,9 +4389,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4266,7 +4503,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4296,9 +4533,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4731,15 +4974,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4797,6 +5049,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5166,9 +5421,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5220,6 +5490,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5274,6 +5550,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5292,6 +5571,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5334,7 +5616,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5523,6 +5805,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5607,6 +5892,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5781,6 +6069,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5826,6 +6123,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5853,36 +6153,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5937,6 +6207,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6087,9 +6360,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6234,6 +6504,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6312,13 +6585,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6381,9 +6654,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6411,13 +6690,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6513,6 +6795,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6717,6 +7002,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6726,6 +7014,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6918,6 +7209,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6933,6 +7230,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7053,9 +7353,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7065,9 +7377,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7092,6 +7413,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7290,9 +7614,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7392,6 +7713,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7434,6 +7758,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7533,6 +7860,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7893,6 +8223,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8028,6 +8364,21 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Resend invite"
msgstr ""
@@ -8187,6 +8538,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8217,6 +8571,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8250,6 +8607,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8310,6 +8670,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8568,6 +8931,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8592,6 +8958,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8649,9 +9018,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8751,6 +9126,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9021,6 +9414,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9231,6 +9630,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9420,6 +9822,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9543,6 +9948,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9588,6 +9996,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9609,6 +10032,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9693,6 +10119,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10254,6 +10686,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10263,6 +10698,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10491,6 +10929,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10500,6 +10941,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10803,6 +11247,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10908,6 +11355,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10968,6 +11418,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11061,6 +11514,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11148,6 +11604,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11244,9 +11703,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11286,6 +11742,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11382,9 +11841,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11505,6 +11961,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11577,6 +12036,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11640,6 +12102,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/sr_SP/gitlab.po b/locale/sr_SP/gitlab.po
index 1a877ebde6e..f60a4196aec 100644
--- a/locale/sr_SP/gitlab.po
+++ b/locale/sr_SP/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: sr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:09\n"
+"PO-Revision-Date: 2019-03-06 15:18\n"
msgid " Status"
msgstr ""
@@ -45,6 +45,12 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -141,12 +147,27 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -165,15 +186,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -249,6 +270,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -273,6 +297,12 @@ msgstr[0] ""
msgstr[1] ""
msgstr[2] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -384,6 +414,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -399,6 +444,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -483,24 +531,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -669,6 +744,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -681,6 +759,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -750,7 +831,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -762,6 +843,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -774,6 +864,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -810,6 +903,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -867,12 +963,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -963,6 +1065,48 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -1005,9 +1149,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1092,6 +1242,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1107,9 +1260,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1668,6 +1818,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1704,6 +1857,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1716,6 +1872,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1782,9 +1941,6 @@ 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 ""
@@ -1944,6 +2100,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1959,9 +2118,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -2007,10 +2163,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2049,6 +2205,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2145,7 +2304,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2160,9 +2319,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2208,9 +2364,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2448,15 +2601,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2757,6 +2922,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3246,6 +3414,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3324,6 +3495,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3495,6 +3669,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3531,6 +3708,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3549,9 +3729,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3576,6 +3753,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3627,15 +3810,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3648,6 +3849,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3699,6 +3912,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3768,6 +3984,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3846,6 +4089,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3933,7 +4179,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3999,9 +4245,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4059,9 +4302,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4149,9 +4389,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4266,7 +4503,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4296,9 +4533,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4731,15 +4974,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4797,6 +5049,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5166,9 +5421,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5220,6 +5490,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5274,6 +5550,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5292,6 +5571,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5334,7 +5616,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5523,6 +5805,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5607,6 +5892,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5781,6 +6069,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5826,6 +6123,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5853,36 +6153,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5937,6 +6207,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6087,9 +6360,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6234,6 +6504,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6312,13 +6585,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6381,9 +6654,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6411,13 +6690,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6513,6 +6795,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6717,6 +7002,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6726,6 +7014,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6918,6 +7209,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6933,6 +7230,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7053,9 +7353,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7065,9 +7377,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7092,6 +7413,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7290,9 +7614,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7392,6 +7713,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7434,6 +7758,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7533,6 +7860,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7893,6 +8223,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -8028,6 +8364,21 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+
msgid "Resend invite"
msgstr ""
@@ -8187,6 +8538,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8217,6 +8571,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8250,6 +8607,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8310,6 +8670,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8568,6 +8931,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8592,6 +8958,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8649,9 +9018,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8751,6 +9126,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -9021,6 +9414,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9231,6 +9630,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9420,6 +9822,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9543,6 +9948,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9588,6 +9996,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9609,6 +10032,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9693,6 +10119,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10254,6 +10686,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10263,6 +10698,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10491,6 +10929,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10500,6 +10941,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10803,6 +11247,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10908,6 +11355,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10968,6 +11418,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11061,6 +11514,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11148,6 +11604,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11244,9 +11703,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11286,6 +11742,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11382,9 +11841,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11505,6 +11961,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11577,6 +12036,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11640,6 +12102,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/sv_SE/gitlab.po b/locale/sv_SE/gitlab.po
index 1d66cc3af44..ca342915099 100644
--- a/locale/sv_SE/gitlab.po
+++ b/locale/sv_SE/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: sv-SE\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:10\n"
+"PO-Revision-Date: 2019-03-06 15:20\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/sw_KE/gitlab.po b/locale/sw_KE/gitlab.po
index d6f57772b1b..e4c780f147f 100644
--- a/locale/sw_KE/gitlab.po
+++ b/locale/sw_KE/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: sw\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:10\n"
+"PO-Revision-Date: 2019-03-06 15:19\n"
msgid " Status"
msgstr ""
@@ -43,6 +43,11 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] ""
@@ -126,12 +131,26 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -148,15 +167,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -228,6 +247,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -250,6 +272,11 @@ msgid_plural "%{count} %{type} modifications"
msgstr[0] ""
msgstr[1] ""
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+msgstr[1] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -352,6 +379,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr ""
@@ -367,6 +409,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -451,24 +496,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -637,6 +709,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -648,6 +723,9 @@ msgstr ""
msgid "All"
msgstr ""
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -717,7 +795,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -729,6 +807,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -777,6 +867,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -834,12 +927,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -930,6 +1029,44 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1059,6 +1202,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1074,9 +1220,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1635,6 +1778,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1683,6 +1832,9 @@ msgstr ""
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,9 +2078,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3211,6 +3372,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3514,9 +3687,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,6 +3807,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3733,6 +3942,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4230,7 +4460,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,9 +4490,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5296,7 +5571,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5897,6 +6160,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6340,9 +6606,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6675,6 +6953,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7050,6 +7364,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7248,9 +7565,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7392,6 +7709,9 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7491,6 +7811,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8266,6 +8617,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8706,6 +9072,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9498,6 +9894,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10921,6 +11362,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11325,9 +11775,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11446,6 +11893,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/tr_TR/gitlab.po b/locale/tr_TR/gitlab.po
index dcdc049d9a6..dd5e8f6db76 100644
--- a/locale/tr_TR/gitlab.po
+++ b/locale/tr_TR/gitlab.po
@@ -13,123 +13,142 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: tr\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:10\n"
+"PO-Revision-Date: 2019-03-06 18:45\n"
msgid " Status"
-msgstr ""
+msgstr " Durum"
msgid " and"
-msgstr ""
+msgstr " ve"
msgid " degraded on %d point"
msgid_plural " degraded on %d points"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] " %d noktasında bozuldu"
+msgstr[1] " %d noktasında bozuldu"
msgid " improved on %d point"
msgid_plural " improved on %d points"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] " %d noktasında geliştirildi"
+msgstr[1] " %d noktalasında geliştirildi"
msgid " or "
-msgstr ""
+msgstr " veya "
msgid " or <#epic id>"
-msgstr ""
+msgstr " veya <#epic id>"
msgid " or <#issue id>"
-msgstr ""
+msgstr " veya <#issue id>"
msgid "\"%{query}\" in projects"
-msgstr ""
+msgstr "\"%{query}\" projelerde"
+
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d yorum"
+msgstr[1] "%d yorum"
msgid "%d commit"
msgid_plural "%d commits"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d iÅŸlem"
+msgstr[1] "%d iÅŸlem"
msgid "%d commit behind"
msgid_plural "%d commits behind"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d 'e bağlı işlem"
+msgstr[1] "%d 'e bağlı işlem"
msgid "%d commits"
-msgstr ""
+msgstr "%d iÅŸlem"
msgid "%d exporter"
msgid_plural "%d exporters"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d dışa aktaran"
+msgstr[1] "%d dışa aktaran"
msgid "%d failed test result"
msgid_plural "%d failed test results"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d başarısız test sonucu"
+msgstr[1] "%d başarısız test sonucu"
msgid "%d fixed test result"
msgid_plural "%d fixed test results"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d sabit test sonucu"
+msgstr[1] "%d sabit test sonucu"
msgid "%d issue"
msgid_plural "%d issues"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d sorun"
+msgstr[1] "%d soru"
msgid "%d issue selected"
msgid_plural "%d issues selected"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d sorun seçili"
+msgstr[1] "%d sorun seçili"
msgid "%d layer"
msgid_plural "%d layers"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d katman"
+msgstr[1] "%d katmanlar"
msgid "%d merge request"
msgid_plural "%d merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d birleÅŸtirme isteÄŸi"
+msgstr[1] "%d birleÅŸtirme isteÄŸi"
msgid "%d metric"
msgid_plural "%d metrics"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d metrik"
+msgstr[1] "%d metrik"
msgid "%d staged change"
msgid_plural "%d staged changes"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d aşamalı değişiklik"
+msgstr[1] "%d aşamalı değişiklik"
msgid "%d unstaged change"
msgid_plural "%d unstaged changes"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d grupsuz deÄŸiÅŸiklik"
+msgstr[1] "%d grupsuz deÄŸiÅŸiklik"
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[0] "%s performans sorunlarını önlemek için ek işlem konulmuştur."
+msgstr[1] "%s performans sorunlarını önlemek için ek işlem konulmuştur."
msgid "%{actionText} & %{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{actionText} ve %{openOrClose} %{noteable}"
msgid "%{authorsName}'s discussion"
-msgstr ""
+msgstr "%{authorsName} kişisinin tartışması"
msgid "%{commit_author_link} authored %{commit_timeago}"
-msgstr ""
+msgstr "%{commit_author_link} %{commit_timeago} yazdı"
msgid "%{counter_storage} (%{counter_repositories} repositories, %{counter_build_artifacts} build artifacts, %{counter_lfs_objects} LFS)"
-msgstr ""
+msgstr "%{counter_storage} (%{counter_repositories} depo, %{counter_build_artifacts} yapı eseri, %{counter_lfs_objects} LFS)"
msgid "%{count} %{alerts}"
+msgstr "%{count} %{alerts}"
+
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "%{count} approvals from %{name}"
msgstr ""
msgid "%{count} more"
-msgstr ""
+msgstr "%{count} daha"
msgid "%{count} more assignees"
+msgstr "%{count} daha fazla atanan"
+
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
msgstr ""
msgid "%{count} participant"
@@ -143,181 +162,189 @@ msgstr[0] ""
msgstr[1] ""
msgid "%{filePath} deleted"
-msgstr ""
+msgstr "%{filePath} silindi"
msgid "%{firstLabel} +%{labelCount} more"
-msgstr ""
-
-msgid "%{firstOption} +%{extraOptionCount} more"
-msgstr ""
+msgstr "%{firstLabel} +%{labelCount} daha"
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 "%{issuableType} will be removed! Are you sure?"
+msgstr "%{issuableType} kaldırılacak! Emin misiniz?"
+
+msgid "%{label_for_message} unavailable"
msgstr ""
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
msgid "%{loadingIcon} Started"
-msgstr ""
+msgstr "%{loadingIcon} Başladı"
msgid "%{lock_path} is locked by GitLab User %{lock_user_id}"
-msgstr ""
+msgstr "%{lock_path} dizini %{lock_user_id} GitLab kullanıcısı tarafından kilitlendi"
msgid "%{name}'s avatar"
-msgstr ""
+msgstr "%{name} kullanıcısının avatarı"
msgid "%{number_commits_behind} commits behind %{default_branch}, %{number_commits_ahead} commits ahead"
msgstr ""
msgid "%{openOrClose} %{noteable}"
-msgstr ""
+msgstr "%{openOrClose} %{noteable}"
msgid "%{percent}%% complete"
-msgstr ""
+msgstr "%{percent}%% tamamlandı"
msgid "%{state} epics"
-msgstr ""
+msgstr "%{state} epik"
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Dal"
+msgstr[1] "%{strong_start}%{branch_count}%{strong_end} Dal"
msgid "%{strong_start}%{commit_count}%{strong_end} Commit"
msgid_plural "%{strong_start}%{commit_count}%{strong_end} Commits"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{strong_start}%{commit_count}%{strong_end} Ä°ÅŸlem"
+msgstr[1] "%{strong_start}%{commit_count}%{strong_end} Ä°ÅŸlem"
msgid "%{strong_start}%{human_size}%{strong_end} Files"
-msgstr ""
+msgstr "%{strong_start}%{human_size}%{strong_end} Dosya"
msgid "%{strong_start}%{tag_count}%{strong_end} Tag"
msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{strong_start}%{tag_count}%{strong_end} Etiket"
+msgstr[1] "%{strong_start}%{tag_count}%{strong_end} Etiket"
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%{text} %{files}"
+msgstr[1] "%{text} %{files} dosyaları"
msgid "%{text} is available"
-msgstr ""
+msgstr "%{text} kullanılabilir"
msgid "%{title} changes"
-msgstr ""
+msgstr "%{title} deÄŸiÅŸiklik"
msgid "%{unstaged} unstaged and %{staged} staged changes"
-msgstr ""
+msgstr "%{unstaged} aşamasız ve %{staged} aşamalı değişiklik"
msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what information is shared with GitLab Inc."
msgstr ""
msgid "%{user_name} profile page"
-msgstr ""
+msgstr "%{user_name} profil sayfası"
msgid "(external source)"
-msgstr ""
+msgstr "(dış kaynak)"
msgid "+ %{count} more"
-msgstr ""
+msgstr "+ %{count} daha"
msgid "+ %{moreCount} more"
msgstr "+ %{moreCount} daha fazla"
-msgid ", or "
+msgid "+%{extraOptionCount} more"
msgstr ""
+msgid ", or "
+msgstr ", veya "
+
msgid "- Runner is active and can process any new jobs"
-msgstr ""
+msgstr "- Çalıştırıcı aktif ve yeni işler işleyebilir"
msgid "- Runner is paused and will not receive any new jobs"
-msgstr ""
+msgstr "- Çalıştırıcı duraklatıldı ve yeni işler almayacak"
msgid "- show less"
msgstr "- daha az göster"
msgid "1 %{type} addition"
msgid_plural "%{count} %{type} additions"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 %{type} ek"
+msgstr[1] "%{count} %{type} ek"
msgid "1 %{type} modification"
msgid_plural "%{count} %{type} modifications"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 %{type} deÄŸiÅŸiklik"
+msgstr[1] "%{count} %{type} deÄŸiÅŸiklik"
+
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] "1 gün"
+msgstr[1] "%d gün"
msgid "1 closed issue"
msgid_plural "%d closed issues"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 kapatılmış sorun"
+msgstr[1] "%d kapatılmış sorun"
msgid "1 closed merge request"
msgid_plural "%d closed merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 kapatılmış birleştirme talebi"
+msgstr[1] "%d kapatılmış birleştirme talebi"
msgid "1 group"
msgid_plural "%d groups"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 grup"
+msgstr[1] "%d grup"
msgid "1 merged merge request"
msgid_plural "%d merged merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 birleÅŸtirilmiÅŸ birleÅŸtirme talebi"
+msgstr[1] "%d birleÅŸtirilmiÅŸ birleÅŸtirme talebi"
msgid "1 open issue"
msgid_plural "%d open issues"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 açık sorun"
+msgstr[1] "%d açık sorun"
msgid "1 open merge request"
msgid_plural "%d open merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 açık birleştirme talebi"
+msgstr[1] "%d açık birleştirme talebi"
msgid "1 pipeline"
msgid_plural "%d pipelines"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 iş hattı"
+msgstr[1] "%d iş hattı"
msgid "1 role"
msgid_plural "%d roles"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 rol"
+msgstr[1] "%d rol"
msgid "1 user"
msgid_plural "%d users"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 kullanıcı"
+msgstr[1] "%d kullanıcı"
msgid "1st contribution!"
msgstr "İlk katkı!"
msgid "2FA"
-msgstr ""
+msgstr "2FA"
msgid "2FA enabled"
-msgstr ""
+msgstr "2FA etkin"
msgid "403|Please contact your GitLab administrator to get permission."
-msgstr ""
+msgstr "403|İzin almak için lütfen GitLab yöneticinize başvurun."
msgid "403|You don't have the permission to access this page."
-msgstr ""
+msgstr "403 | Bu sayfaya eriÅŸim izniniz yok."
msgid "404|Make sure the address is correct and the page hasn't moved."
-msgstr ""
+msgstr "404|Adresin doğru olduğundan ve sayfanın taşınmadığından emin olun."
msgid "404|Page Not Found"
-msgstr ""
+msgstr "404|Sayfa Bulunamadı"
msgid "404|Please contact your GitLab administrator if you think this is a mistake."
-msgstr ""
+msgstr "404|Lütfen bunun bir hata olduğunu düşünüyorsanız, GitLab yöneticinizle görüşün."
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 ""
@@ -335,13 +362,13 @@ msgid "<strong>%{changedFilesLength} unstaged</strong> and <strong>%{stagedFiles
msgstr ""
msgid "<strong>%{created_count}</strong> created, <strong>%{accepted_count}</strong> accepted."
-msgstr ""
+msgstr "<strong>%{created_count}</strong> oluÅŸturuldu, <strong>%{accepted_count}</strong> kabul edildi."
msgid "<strong>%{created_count}</strong> created, <strong>%{closed_count}</strong> closed."
-msgstr ""
+msgstr "<strong>%{created_count}</strong> oluşturuldu, <strong>%{closed_count}</strong> kapatıldı."
msgid "<strong>%{group_name}</strong> group members"
-msgstr ""
+msgstr "<strong>%{group_name}</strong> grup üyeleri"
msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong> commits by <strong>%{people}</strong> contributors."
msgstr ""
@@ -352,167 +379,212 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
-msgid "A collection of graphs regarding Continuous Integration"
+msgid "A .NET Core console application template, customizable for any .NET Core project"
msgstr ""
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A collection of graphs regarding Continuous Integration"
+msgstr "Sürekli Entegrasyon için bir grafik derlemesi"
+
msgid "A default branch cannot be chosen for an empty project."
msgstr ""
msgid "A deleted user"
-msgstr ""
+msgstr "Silinmiş kullanıcı"
msgid "A member of GitLab's abuse team will review your report as soon as possible."
msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
+msgstr "Çatalınızdan yeni bir dal oluşturulacak ve yeni bir birleştirme talebi başlatılacak."
+
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
msgstr ""
msgid "A 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 ""
+msgstr "İş izlemede test kapsamı çıktısını bulmak için kullanılacak düzenli bir ifade. Devre dışı bırakmak için boş bırakın"
msgid "A user with write access to the source branch selected this option"
-msgstr ""
+msgstr "Bu dala yazma yetkisi olan kullanıcı bu seçeneği seçti"
msgid "About GitLab"
-msgstr ""
+msgstr "GitLab Hakkında"
msgid "About GitLab CE"
-msgstr ""
+msgstr "GitLab CE hakkında"
msgid "About auto deploy"
-msgstr ""
+msgstr "Otomatik dağıtım hakkında"
msgid "About this feature"
-msgstr ""
+msgstr "Bu özellik hakkında"
msgid "Abuse Reports"
msgstr "Kötüye Kullanım Raporları"
msgid "Abuse reports"
-msgstr ""
+msgstr "Kötüye kullanım raporları"
msgid "Accept invitation"
-msgstr ""
+msgstr "Daveti kabul et"
msgid "Accept terms"
-msgstr ""
+msgstr "Şartları kabul et"
msgid "Accepted MR"
-msgstr ""
+msgstr "Kabul edilen MR"
msgid "Access Tokens"
-msgstr "Erişim anahtarları"
+msgstr "Erişim Anahtarları"
msgid "Access denied! Please verify you can add deploy keys to this repository."
-msgstr ""
+msgstr "Erişim reddedildi! Lütfen bu depoya dağıtım anahtarlarını ekleyebileceğinizi doğrulayın."
msgid "Access expiration date"
-msgstr ""
+msgstr "EriÅŸim bitiÅŸ tarihi"
msgid "Access to '%{classification_label}' not allowed"
-msgstr ""
+msgstr "'%{classification_label}' eriÅŸimine izin verilmedi"
msgid "Account"
msgstr "Hesap"
msgid "Account and limit"
-msgstr ""
+msgstr "Hesap ve sınırı"
msgid "Active"
msgstr "Etkin"
msgid "Active Sessions"
-msgstr ""
+msgstr "Etkin Oturumlar"
msgid "Activity"
msgstr "Etkinlik"
msgid "Add"
-msgstr ""
+msgstr "Ekle"
msgid "Add CHANGELOG"
-msgstr ""
+msgstr "CHANGELOG ekle"
msgid "Add CONTRIBUTING"
-msgstr ""
+msgstr "CONTRIBUTING ekle"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
msgstr ""
msgid "Add Jaeger URL"
-msgstr ""
+msgstr "Jaeger URL ekle"
msgid "Add Kubernetes cluster"
-msgstr ""
+msgstr "Kubernetes kümesi ekle"
msgid "Add README"
-msgstr ""
+msgstr "BENÄ°OKU ekle"
+
+msgid "Add a bullet list"
+msgstr "Madde iÅŸareti listesi ekle"
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
-msgstr ""
+msgstr "Wiki'nize projeniz hakkında bilgi içeren bir ana sayfa ekleyin. GitLab bu mesaj yerine onu burada görüntüleyecektir."
+
+msgid "Add a link"
+msgstr "Bağlantı ekle"
+
+msgid "Add a numbered list"
+msgstr "Numaralı liste ekle"
msgid "Add a table"
-msgstr ""
+msgstr "Tablo ekle"
+
+msgid "Add a task list"
+msgstr "Görev listesi ekle"
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
+msgstr "Tüm e-posta iletişiminde görünecek ek metin ekleyin. %{character_limit} karakter sınırı"
+
+msgid "Add approver(s)"
msgstr ""
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr "Kalın metin ekle"
+
msgid "Add comment now"
+msgstr "Åžimdi yorum ekle"
+
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
msgstr ""
msgid "Add image comment"
-msgstr ""
+msgstr "Resim yorumu ekle"
+
+msgid "Add italic text"
+msgstr "EÄŸik metin ekle"
msgid "Add license"
-msgstr ""
+msgstr "Lisans ekle"
msgid "Add new application"
-msgstr ""
+msgstr "Yeni uygulama ekle"
msgid "Add new directory"
msgstr "Yeni dizin ekle"
msgid "Add projects"
-msgstr ""
+msgstr "Proje ekle"
msgid "Add reaction"
-msgstr ""
+msgstr "Tepki ekle"
msgid "Add to project"
-msgstr ""
+msgstr "Projeye ekle"
msgid "Add to review"
-msgstr ""
+msgstr "Ä°ncelemeye ekle"
msgid "Add todo"
msgstr "Yapılacaklara Ekle"
msgid "Add user(s) to the group:"
-msgstr ""
+msgstr "Gruba kullanıcı(lar) ekleyin:"
msgid "Add users or groups who are allowed to approve every merge request"
msgstr ""
msgid "Add users to group"
-msgstr ""
+msgstr "Kullanıcıları gruba ekle"
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
-msgstr ""
+msgstr "GitLab örneğinizde yeni uygulamalar eklemek devre dışı bırakıldı. İzin almak için lütfen GitLab yöneticinize başvurun"
msgid "Additional text"
-msgstr ""
+msgstr "Ek metin"
msgid "Admin Area"
-msgstr ""
+msgstr "Yönetici alanı"
msgid "Admin Overview"
-msgstr ""
+msgstr "Yönetim Genel Bakış"
msgid "AdminArea| You are about to permanently delete the user %{username}. Issues, merge requests, and groups linked to them will be transferred to a system-wide \"Ghost-user\". To avoid data loss, consider using the %{strong_start}block user%{strong_end} feature instead. Once you %{strong_start}Delete user%{strong_end}, it cannot be undone or recovered."
msgstr ""
@@ -530,22 +602,22 @@ msgid "AdminArea|Stop jobs"
msgstr "Ä°ÅŸleri durdur"
msgid "AdminArea|Stopping jobs failed"
-msgstr ""
+msgstr "AdminArea|Durdurma işleri başarısız oldu"
msgid "AdminArea|You’re about to stop all jobs.This will halt all current jobs that are running."
-msgstr ""
+msgstr "AdminArea|Bütün işleri durdurmak üzeresiniz. Bu işlem çalışan tüm mevcut işleri durduracaktır."
msgid "AdminProjects| You’re about to permanently delete the project %{projectName}, its repository, and all related resources including issues, merge requests, etc.. Once you confirm and press %{strong_start}Delete project%{strong_end}, it cannot be undone or recovered."
msgstr ""
msgid "AdminProjects|Delete"
-msgstr ""
+msgstr "AdminProjects|Sil"
msgid "AdminProjects|Delete Project %{projectName}?"
-msgstr ""
+msgstr "AdminProjects|%{projectName} projesi silinsin mi?"
msgid "AdminProjects|Delete project"
-msgstr ""
+msgstr "AdminProjects|Projeyi sil"
msgid "AdminSettings|Auto DevOps domain"
msgstr ""
@@ -560,52 +632,52 @@ msgid "AdminSettings|When creating a new environment variable it will be protect
msgstr ""
msgid "AdminUsers|2FA Disabled"
-msgstr ""
+msgstr "AdminUsers|2FA Devre dışı"
msgid "AdminUsers|2FA Enabled"
-msgstr ""
+msgstr "AdminUsers|2FA Etkin"
msgid "AdminUsers|Active"
-msgstr ""
+msgstr "AdminUsers|Etkin"
msgid "AdminUsers|Admin"
-msgstr ""
+msgstr "AdminUsers|Yönetici"
msgid "AdminUsers|Admins"
-msgstr ""
+msgstr "AdminUsers|Yöneticiler"
msgid "AdminUsers|Block user"
-msgstr ""
+msgstr "AdminUsers | Kullanıcıyı engelle"
msgid "AdminUsers|Blocked"
-msgstr ""
+msgstr "AdminUsers|Engellendi"
msgid "AdminUsers|Cannot unblock LDAP blocked users"
msgstr ""
msgid "AdminUsers|Delete User %{username} and contributions?"
-msgstr ""
+msgstr "AdminUsers |%{username} kullanıcısını ve katkılarını sil?"
msgid "AdminUsers|Delete User %{username}?"
-msgstr ""
+msgstr "AdminUsers|%{username} kullanısını sil?"
msgid "AdminUsers|Delete user"
-msgstr ""
+msgstr "AdminUsers|Kullanıcıyı sil"
msgid "AdminUsers|Delete user and contributions"
-msgstr ""
+msgstr "AdminUsers|Kullanıcıyı ve katkılarını sil"
msgid "AdminUsers|External"
-msgstr ""
+msgstr "AdminUsers|Harici"
msgid "AdminUsers|It's you!"
-msgstr ""
+msgstr "AdminUsers|Bu sensin!"
msgid "AdminUsers|New user"
-msgstr ""
+msgstr "AdminUsers|Yeni kullanıcı"
msgid "AdminUsers|No users found"
-msgstr ""
+msgstr "AdminUsers|Kullanıcı bulunamadı"
msgid "AdminUsers|Search by name, email or username"
msgstr ""
@@ -617,7 +689,7 @@ msgid "AdminUsers|Send email to users"
msgstr ""
msgid "AdminUsers|Sort by"
-msgstr ""
+msgstr "AdminUsers|Buna göre sırala"
msgid "AdminUsers|To confirm, type %{projectName}"
msgstr ""
@@ -637,20 +709,26 @@ msgstr ""
msgid "Advanced settings"
msgstr "GeliÅŸmiÅŸ ayarlar"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Uyarı"
+msgstr[1] "Uyarı"
msgid "Alerts"
-msgstr ""
+msgstr "Uyarılar"
msgid "All"
msgstr "Tümü"
-msgid "All changes are committed"
+msgid "All Members"
msgstr ""
+msgid "All changes are committed"
+msgstr "Tüm değişiklikler işlendi"
+
msgid "All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings."
msgstr ""
@@ -658,16 +736,16 @@ msgid "All issues for this milestone are closed. You may close this milestone no
msgstr ""
msgid "All users"
-msgstr ""
+msgstr "Tüm kullanıcılar"
msgid "Allow \"%{group_name}\" to sign you in"
msgstr ""
msgid "Allow commits from members who can merge to the target branch."
-msgstr ""
+msgstr "Hedef dala bağlanabilecek üyelerin işlemlerine izin verin."
msgid "Allow projects within this group to use Git LFS"
-msgstr ""
+msgstr "Bu gruptaki projelerin Git LFS'yi kullanmasına izin verin"
msgid "Allow public access to pipelines and job details, including output logs and artifacts"
msgstr ""
@@ -679,7 +757,7 @@ msgid "Allow requests to the local network from hooks and services."
msgstr ""
msgid "Allow users to request access"
-msgstr ""
+msgstr "Kullanıcıların erişim isteğinde bulunmasına izin ver"
msgid "Allow users to request access if visibility is public or internal."
msgstr ""
@@ -709,26 +787,35 @@ msgid "An SSH key will be automatically generated when the form is submitted. Fo
msgstr ""
msgid "An application called %{link_to_client} is requesting access to your GitLab account."
-msgstr ""
+msgstr "%{link_to_client} adlı uygulama, GitLab hesabınıza erişim talep ediyor."
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 has occurred"
-msgstr ""
+msgstr "Bir hata oluÅŸtu"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
msgstr ""
msgid "An error occurred adding a new draft."
-msgstr ""
+msgstr "Yeni taslak eklenirken bir hata oluÅŸtu."
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -741,6 +828,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -760,52 +850,55 @@ msgid "An error occurred while fetching pending comments"
msgstr ""
msgid "An error occurred while fetching sidebar data"
-msgstr ""
+msgstr "Kenar çubuğu verileri getirilirken bir hata oluştu"
msgid "An error occurred while fetching stages."
msgstr ""
msgid "An error occurred while fetching the job log."
-msgstr ""
+msgstr "İş kayıtları alınırken bir hata oluştu."
msgid "An error occurred while fetching the job."
-msgstr ""
+msgstr "İş alınırken bir hata oluştu."
msgid "An error occurred while fetching the jobs."
-msgstr ""
+msgstr "İşler alınırken bir hata oluştu."
msgid "An error occurred while fetching the pipeline."
+msgstr "İş hattı alınırken bir hata oluştu."
+
+msgid "An error occurred while fetching the releases. Please try again."
msgstr ""
msgid "An error occurred while getting projects"
msgstr "Projeler yüklenirken bir hata oluştu"
msgid "An error occurred while importing project: %{details}"
-msgstr ""
+msgstr "Projeyi içe aktarırken bir sorun oluştu: %{details}"
msgid "An error occurred while initializing path locks"
msgstr ""
msgid "An error occurred while loading chart data"
-msgstr ""
+msgstr "Tablo verileri alınırken bir hata oluştu"
msgid "An error occurred while loading commit signatures"
-msgstr ""
+msgstr "İşlem imzaları yüklenirken bir hata oluştu"
msgid "An error occurred while loading diff"
-msgstr ""
+msgstr "Değişiklikler uygulanırken bir hata oluştu"
msgid "An error occurred while loading filenames"
msgstr "Dosya isimleri yüklenirken bir hata oluştu"
msgid "An error occurred while loading the file"
-msgstr ""
+msgstr "Dosya yüklenirken bir hata oluştu"
msgid "An error occurred while loading the subscription details."
msgstr ""
msgid "An error occurred while making the request."
-msgstr ""
+msgstr "Talep edilirken bir hata oluÅŸtu."
msgid "An error occurred while removing approver"
msgstr ""
@@ -823,7 +916,7 @@ msgid "An error occurred while rendering preview broadcast message"
msgstr "Önizleme yayını iletisi oluşturulurken bir hata oluştu"
msgid "An error occurred while retrieving calendar activity"
-msgstr ""
+msgstr "Takvim etkinlikleri getirilirken bir hata meydana geldi"
msgid "An error occurred while retrieving diff"
msgstr "Değişiklikler yüklenirken bir hata oluştu"
@@ -832,16 +925,22 @@ msgid "An error occurred while saving LDAP override status. Please try again."
msgstr ""
msgid "An error occurred while saving assignees"
+msgstr "Atanmış kişiler kaydedilirken bir hata oluştu"
+
+msgid "An error occurred while saving the approval settings"
msgstr ""
msgid "An error occurred while subscribing to notifications."
-msgstr ""
+msgstr "Bildirimlere abone olunurken bir hata oluÅŸtu."
msgid "An error occurred while unsubscribing to notifications."
+msgstr "Bildirim aboneliÄŸi iptal edilirken bir hata oluÅŸtu."
+
+msgid "An error occurred while updating approvers"
msgstr ""
msgid "An error occurred while updating the comment"
-msgstr ""
+msgstr "Yorum güncellenirken bir hata oluştu"
msgid "An error occurred while validating username"
msgstr "Kullanıcı adı doğrulanırken bir hata oluştu"
@@ -895,49 +994,87 @@ msgid "An unexpected error occurred while stopping the Web Terminal."
msgstr ""
msgid "Analytics"
-msgstr ""
+msgstr "Analizler"
msgid "Anonymous"
-msgstr ""
+msgstr "Anonim"
msgid "Anti-spam verification"
-msgstr ""
+msgstr "Anti-spam doÄŸrulama"
msgid "Any"
-msgstr ""
+msgstr "Herhangi"
msgid "Any Label"
-msgstr ""
+msgstr "Herhangi bir etiket"
msgid "Appearance"
msgstr "Görünüm"
msgid "Application"
-msgstr ""
+msgstr "Uygulama"
msgid "Application ID"
-msgstr ""
+msgstr "Uygulama KimliÄŸi"
msgid "Application: %{name}"
-msgstr ""
+msgstr "Uygulama: %{name}"
msgid "Applications"
msgstr "Uygulamalar"
msgid "Applied"
-msgstr ""
+msgstr "Uygulandı"
msgid "Apply suggestion"
+msgstr "Öneriyi uygula"
+
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
msgstr ""
-msgid "Approvals"
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
msgstr ""
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
+msgid "Approvals"
+msgstr "Onaylar"
+
msgid "Approvals required"
msgstr ""
msgid "Approvers"
-msgstr ""
+msgstr "Onaylayanlar"
msgid "Apr"
msgstr "Nis"
@@ -949,13 +1086,13 @@ msgid "Archived project! Repository and other project resources are read-only"
msgstr ""
msgid "Archived projects"
-msgstr ""
+msgstr "ArÅŸivlenmiÅŸ projeler"
msgid "Are you sure"
-msgstr ""
+msgstr "Emin misiniz"
msgid "Are you sure you want to delete this pipeline schedule?"
-msgstr ""
+msgstr "Bu iş hattı planını silmek istediğinizden emin misiniz?"
msgid "Are you sure you want to erase this build?"
msgstr ""
@@ -972,9 +1109,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -982,7 +1125,7 @@ msgid "Are you sure you want to remove the attachment?"
msgstr ""
msgid "Are you sure you want to remove this identity?"
-msgstr ""
+msgstr "Bu kimliği kaldırmak istediğinizden emin misiniz?"
msgid "Are you sure you want to reset registration token?"
msgstr ""
@@ -994,7 +1137,7 @@ msgid "Are you sure you want to stop this environment?"
msgstr ""
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr ""
+msgstr "%{path_lock_path} silmek istediÄŸinizden emin misiniz?"
msgid "Are you sure you want to unsubscribe from the %{type}: %{link_to_noteable_text}?"
msgstr ""
@@ -1003,10 +1146,10 @@ msgid "Are you sure?"
msgstr "Emin misiniz?"
msgid "Artifact ID"
-msgstr ""
+msgstr "Yapı Kimliği"
msgid "Artifacts"
-msgstr ""
+msgstr "Yapılar"
msgid "Ascending"
msgstr ""
@@ -1027,7 +1170,7 @@ msgid "Assign labels"
msgstr "Etiket tanımla"
msgid "Assign milestone"
-msgstr ""
+msgstr "Kilometre taşı ata"
msgid "Assign some issues to this milestone."
msgstr ""
@@ -1036,7 +1179,7 @@ msgid "Assign to"
msgstr "Ata"
msgid "Assigned Issues"
-msgstr ""
+msgstr "Atanan sorunlar"
msgid "Assigned Merge Requests"
msgstr ""
@@ -1045,10 +1188,10 @@ msgid "Assigned to :name"
msgstr ""
msgid "Assigned to me"
-msgstr ""
+msgstr "Bana atanan"
msgid "Assignee"
-msgstr ""
+msgstr "Atanan"
msgid "Assignee lists not available with your current license"
msgstr ""
@@ -1057,6 +1200,9 @@ msgid "Assignee lists show all issues assigned to the selected user."
msgstr ""
msgid "Assignee(s)"
+msgstr "Atanan(lar)"
+
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
msgstr ""
msgid "Attach a file"
@@ -1066,7 +1212,7 @@ 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 ""
+msgstr "Denetim Etkinlikleri"
msgid "Aug"
msgstr "AÄŸustos"
@@ -1074,50 +1220,47 @@ msgstr "AÄŸustos"
msgid "August"
msgstr "AÄŸustos"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "Kimlik Doğrulama Günlüğü"
msgid "Authentication log"
-msgstr ""
+msgstr "Kimlik Doğrulama Günlüğü"
msgid "Authentication method"
-msgstr ""
+msgstr "Kimlik doğrulama yöntemi"
msgid "Author"
msgstr "Yazar"
msgid "Authorization code:"
-msgstr ""
+msgstr "Yetkilendirme kodu:"
msgid "Authorization key"
msgstr ""
msgid "Authorization was granted by entering your username and password in the application."
-msgstr ""
+msgstr "Uygulamaya kullanıcı adınız ve şifreniz girilererek yetki verildi."
msgid "Authorize"
-msgstr ""
+msgstr "Yetki Ver"
msgid "Authorize %{link_to_client} to use your account?"
msgstr ""
msgid "Authorized At"
-msgstr ""
+msgstr "Yetkili"
msgid "Authorized applications (%{size})"
-msgstr ""
+msgstr "Yetkili uygulamalar (%{size})"
msgid "Authors: %{authors}"
msgstr "Yazarlar: %{authors}"
msgid "Auto DevOps"
-msgstr ""
+msgstr "Otomatik DevOps"
msgid "Auto DevOps enabled"
-msgstr ""
+msgstr "Auto DevOps etkinleÅŸtirildi"
msgid "Auto DevOps, runners and job artifacts"
msgstr ""
@@ -1132,7 +1275,7 @@ msgid "AutoDevOps|Auto DevOps documentation"
msgstr ""
msgid "AutoDevOps|Enable in settings"
-msgstr ""
+msgstr "AutoDevOps|Ayarlardan etkinleÅŸtirin"
msgid "AutoDevOps|It will automatically build, test, and deploy your application based on a predefined CI/CD configuration."
msgstr ""
@@ -1147,13 +1290,13 @@ msgid "AutoDevOps|You can automatically build and test your application if you %
msgstr ""
msgid "AutoDevOps|add a Kubernetes cluster"
-msgstr ""
+msgstr "AutoDevOps|Kubernetes Cluster Ekle"
msgid "AutoDevOps|enable Auto DevOps"
-msgstr ""
+msgstr "AutoDevOps | Auto DevOps'u etkinleÅŸtir"
msgid "Automatically marked as default internal user"
-msgstr ""
+msgstr "Otomatik olarak varsayılan kullanıcı olarak işaretlendi"
msgid "Automatically resolved"
msgstr ""
@@ -1180,31 +1323,31 @@ msgid "Avatar will be removed. Are you sure?"
msgstr "Avatar kaldırılacak. Emin misiniz?"
msgid "Average per day: %{average}"
-msgstr ""
+msgstr "Günlük ortalama: %{average}"
msgid "Background Color"
-msgstr ""
+msgstr "Arkaplan Rengi"
msgid "Background Jobs"
-msgstr ""
+msgstr "Arka plan iÅŸleri"
msgid "Background color"
-msgstr ""
+msgstr "Arkaplan rengi"
msgid "Badges"
-msgstr ""
+msgstr "Rozetler"
msgid "Badges|A new badge was added."
-msgstr ""
+msgstr "Badges|Yeni bir rozet eklendi."
msgid "Badges|Add badge"
-msgstr ""
+msgstr "Badges|Rozet ekle"
msgid "Badges|Adding the badge failed, please check the entered URLs and try again."
-msgstr ""
+msgstr "Badges|Rozetin eklenmesi başarısız oldu, lütfen girilen bağlantıları kontrol edin ve tekrar deneyin."
msgid "Badges|Badge image URL"
-msgstr ""
+msgstr "Badges|Rozet resim bağlantısı"
msgid "Badges|Badge image preview"
msgstr ""
@@ -1330,7 +1473,7 @@ msgid "BillingPlans|Upgrade"
msgstr ""
msgid "BillingPlans|You are currently on the %{plan_link} plan."
-msgstr ""
+msgstr "BillingPlans|Şu an %{plan_link} plan kullanıyorsunuz."
msgid "BillingPlans|Your GitLab.com trial expired on %{expiration_date}. %{learn_more_text}"
msgstr ""
@@ -1345,7 +1488,7 @@ msgid "BillingPlans|frequently asked questions"
msgstr ""
msgid "BillingPlans|monthly"
-msgstr ""
+msgstr "BillingPlans|aylık"
msgid "BillingPlans|paid annually at %{price_per_year}"
msgstr ""
@@ -1366,10 +1509,10 @@ msgid "Blocked"
msgstr ""
msgid "Blog"
-msgstr ""
+msgstr "Blog"
msgid "Boards"
-msgstr ""
+msgstr "Panolar"
msgid "Branch %{branchName} was not found in this project's repository."
msgstr ""
@@ -1378,13 +1521,13 @@ msgid "Branch <strong>%{branch_name}</strong> was created. To set up auto deploy
msgstr ""
msgid "Branch has changed"
-msgstr ""
+msgstr "Dal deÄŸiÅŸti"
msgid "Branch is already taken"
msgstr ""
msgid "Branch name"
-msgstr ""
+msgstr "Dal adı"
msgid "BranchSwitcherPlaceholder|Search branches"
msgstr ""
@@ -1393,22 +1536,22 @@ msgid "BranchSwitcherTitle|Switch branch"
msgstr ""
msgid "Branches"
-msgstr ""
+msgstr "Dallar"
msgid "Branches|Active"
-msgstr ""
+msgstr "Branches|Etkin"
msgid "Branches|Active branches"
msgstr ""
msgid "Branches|All"
-msgstr ""
+msgstr "Branches|Tümü"
msgid "Branches|Cant find HEAD commit for this branch"
msgstr ""
msgid "Branches|Compare"
-msgstr ""
+msgstr "Branches|Karşılaştırma"
msgid "Branches|Delete all branches that are merged into '%{default_branch}'"
msgstr ""
@@ -1417,7 +1560,7 @@ msgid "Branches|Delete branch"
msgstr ""
msgid "Branches|Delete merged branches"
-msgstr ""
+msgstr "Branches|Birleştirilmiş dalları sil"
msgid "Branches|Delete protected branch"
msgstr ""
@@ -1450,7 +1593,7 @@ msgid "Branches|Only a project maintainer or owner can delete a protected branch
msgstr ""
msgid "Branches|Overview"
-msgstr ""
+msgstr "Branches|Genel Bakış"
msgid "Branches|Protected branches can be managed in %{project_settings_link}."
msgstr ""
@@ -1624,17 +1767,20 @@ msgid "Canary Deployments is a popular CI strategy, where a small portion of the
msgstr ""
msgid "Cancel"
-msgstr ""
+msgstr "Ä°ptal"
msgid "Cancel this job"
-msgstr ""
+msgstr "Bu iÅŸi iptal et"
msgid "Cannot be merged automatically"
-msgstr ""
+msgstr "Otomatik olarak birleÅŸtirilemez"
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1671,6 +1817,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1678,9 +1827,12 @@ msgid "Changes suppressed. Click to show."
msgstr ""
msgid "Charts"
-msgstr ""
+msgstr "Grafikler"
msgid "Chat"
+msgstr "Sohbet"
+
+msgid "Check again"
msgstr ""
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
@@ -1749,9 +1901,6 @@ 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 ""
@@ -1911,6 +2060,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1926,11 +2078,8 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
-msgstr ""
+msgstr "Kapalı sorunlar"
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
msgstr ""
@@ -1974,10 +2123,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -2016,6 +2165,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2038,7 +2190,7 @@ msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr ""
msgid "ClusterIntegration|Copy Token"
-msgstr ""
+msgstr "ClusterIntegration|Erişim Anahtarını Kopyala"
msgid "ClusterIntegration|Create Kubernetes cluster"
msgstr ""
@@ -2112,7 +2264,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2127,9 +2279,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2175,9 +2324,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2415,15 +2561,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2520,7 +2678,7 @@ msgid "Commit…"
msgstr ""
msgid "Compare"
-msgstr ""
+msgstr "Karşılaştır"
msgid "Compare Git revisions"
msgstr ""
@@ -2682,10 +2840,10 @@ msgid "Contributions per group member"
msgstr ""
msgid "Contributors"
-msgstr ""
+msgstr "Katkıda Bulunanlar"
msgid "ContributorsPage|%{startDate} – %{endDate}"
-msgstr ""
+msgstr "ContributorsPage|%{startDate} – %{endDate}"
msgid "ContributorsPage|Building repository graph."
msgstr ""
@@ -2694,7 +2852,7 @@ msgid "ContributorsPage|Commits to %{branch_name}, excluding merge commits. Limi
msgstr ""
msgid "ContributorsPage|Please wait a moment, this page will automatically refresh when ready."
-msgstr ""
+msgstr "ContributorsPage|Lütfen bekleyin, hazır olduğunda bu sayfa otomatik olarak yenilenir."
msgid "Control the display of third party offers."
msgstr ""
@@ -2723,6 +2881,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -2763,7 +2924,7 @@ msgid "Copy to clipboard"
msgstr ""
msgid "Copy token to clipboard"
-msgstr ""
+msgstr "Erişim anahtarını panoya kopyala"
msgid "Could not retrieve the pipeline status. For troubleshooting steps, read the %{linkStart}documentation.%{linkEnd}"
msgstr ""
@@ -2778,13 +2939,13 @@ msgid "Create New Domain"
msgstr ""
msgid "Create a new branch"
-msgstr ""
+msgstr "Yeni bir dal oluÅŸtur"
msgid "Create a new branch and merge request"
-msgstr ""
+msgstr "Yeni bir dal ve birleÅŸtirme isteÄŸi oluÅŸtur"
msgid "Create a new issue"
-msgstr ""
+msgstr "Yeni bir sorun oluÅŸtur"
msgid "Create a new repository"
msgstr ""
@@ -2823,31 +2984,31 @@ msgid "Create lists from labels. Issues with that label appear in that list."
msgstr ""
msgid "Create merge request"
-msgstr ""
+msgstr "BirleÅŸtirme isteÄŸi oluÅŸtur"
msgid "Create merge request and branch"
-msgstr ""
+msgstr "BirleÅŸtirme isteÄŸi ve dal oluÅŸtur"
msgid "Create milestone"
msgstr ""
msgid "Create new branch"
-msgstr ""
+msgstr "Yeni dal oluÅŸtur"
msgid "Create new directory"
-msgstr ""
+msgstr "Yeni dizin oluÅŸtur"
msgid "Create new file"
-msgstr ""
+msgstr "Yeni dosya oluÅŸtur"
msgid "Create new file or directory"
-msgstr ""
+msgstr "Yeni dosya veya dizin oluÅŸtur"
msgid "Create new label"
-msgstr ""
+msgstr "Yeni etiket oluÅŸtur"
msgid "Create new..."
-msgstr ""
+msgstr "Yeni oluÅŸtur..."
msgid "Create project label"
msgstr ""
@@ -2895,7 +3056,7 @@ msgid "CurrentUser|Profile"
msgstr ""
msgid "CurrentUser|Settings"
-msgstr ""
+msgstr "CurrentUser|Ayarlar"
msgid "Custom CI config path"
msgstr ""
@@ -2904,7 +3065,7 @@ msgid "Custom hostname (for private commit emails)"
msgstr ""
msgid "Custom notification events"
-msgstr ""
+msgstr "Özel bildirim etkinlikleri"
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 ""
@@ -2925,7 +3086,7 @@ msgid "Customize how Google Code email addresses and usernames are imported into
msgstr ""
msgid "Customize language and region related settings."
-msgstr ""
+msgstr "Dil ve bölgeyle ilgili ayarları özelleştirin."
msgid "Customize your merge request approval settings."
msgstr ""
@@ -2934,7 +3095,7 @@ msgid "Customize your pipeline configuration, view your pipeline status and cove
msgstr ""
msgid "Cycle Analytics"
-msgstr ""
+msgstr "AÅŸama Analizi"
msgid "Cycle Analytics gives an overview of how much time it takes to go from idea to production in your project."
msgstr ""
@@ -2952,13 +3113,13 @@ msgid "CycleAnalyticsStage|Production"
msgstr ""
msgid "CycleAnalyticsStage|Review"
-msgstr ""
+msgstr "CycleAnalyticsStage|Ä°nceleme"
msgid "CycleAnalyticsStage|Staging"
msgstr ""
msgid "CycleAnalyticsStage|Test"
-msgstr ""
+msgstr "CycleAnalyticsStage|Test"
msgid "DNS"
msgstr ""
@@ -2970,7 +3131,7 @@ msgid "DashboardProjects|All"
msgstr ""
msgid "DashboardProjects|Personal"
-msgstr ""
+msgstr "DashboardProjects|KiÅŸisel"
msgid "Data is still calculating..."
msgstr ""
@@ -2982,10 +3143,10 @@ msgid "Debug"
msgstr ""
msgid "Dec"
-msgstr ""
+msgstr "Ara"
msgid "December"
-msgstr ""
+msgstr "Aralık"
msgid "Decline"
msgstr ""
@@ -3164,7 +3325,7 @@ msgid "DeployTokens|Scopes"
msgstr ""
msgid "DeployTokens|This action cannot be undone."
-msgstr ""
+msgstr "DeployTokens|Bu işlem geri alınamaz."
msgid "DeployTokens|This project has no active Deploy Tokens."
msgstr ""
@@ -3211,14 +3372,17 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
msgid "Details"
-msgstr ""
+msgstr "Ayrıntılar"
msgid "Details (default)"
-msgstr ""
+msgstr "Ayrıntılar (varsayılan)"
msgid "Detect host keys"
msgstr ""
@@ -3289,6 +3453,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3302,7 +3469,7 @@ msgid "Dismiss Merge Request promotion"
msgstr ""
msgid "Dismiss trial promotion"
-msgstr ""
+msgstr "Deneme promosyonunu reddet"
msgid "Do you want to customize how Google Code email addresses and usernames are imported into GitLab?"
msgstr ""
@@ -3314,10 +3481,10 @@ msgid "Domain"
msgstr ""
msgid "Don't show again"
-msgstr ""
+msgstr "Bir daha gösterme"
msgid "Done"
-msgstr ""
+msgstr "Tamamlandı"
msgid "Download"
msgstr ""
@@ -3404,7 +3571,7 @@ msgid "Edit identity for %{user_name}"
msgstr ""
msgid "Edit issues"
-msgstr ""
+msgstr "Sorunları düzenle"
msgid "Elasticsearch"
msgstr ""
@@ -3413,13 +3580,13 @@ msgid "Elasticsearch integration. Elasticsearch AWS IAM."
msgstr ""
msgid "Email"
-msgstr ""
+msgstr "E-posta"
msgid "Email patch"
msgstr ""
msgid "Emails"
-msgstr ""
+msgstr "E-postalar"
msgid "Embed"
msgstr ""
@@ -3460,6 +3627,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3479,7 +3649,7 @@ msgid "Enable the Performance Bar for a given group."
msgstr ""
msgid "Enable two-factor authentication"
-msgstr ""
+msgstr "İki aşamalı doğrulamayı etkinleştir"
msgid "Enable usage ping"
msgstr ""
@@ -3496,6 +3666,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3512,10 +3685,7 @@ msgid "Enter the merge request description"
msgstr ""
msgid "Enter the merge request title"
-msgstr ""
-
-msgid "Enter your Sentry API URL"
-msgstr ""
+msgstr "Birleştirme isteğinin başlığını girin"
msgid "Environment variables"
msgstr ""
@@ -3541,6 +3711,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3592,15 +3768,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3613,9 +3807,21 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
-msgid "Environments|Updated"
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
msgstr ""
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|Updated"
+msgstr "Environments|Güncellendi:"
+
msgid "Environments|You don't have any environments right now"
msgstr ""
@@ -3664,6 +3870,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3671,7 +3880,7 @@ msgid "Error deleting %{issuableType}"
msgstr ""
msgid "Error fetching contributors data."
-msgstr ""
+msgstr "Katkıda bulunanlar verileri alınırken hata oluştu."
msgid "Error fetching labels."
msgstr ""
@@ -3698,7 +3907,7 @@ msgid "Error loading markdown preview"
msgstr ""
msgid "Error loading merge requests."
-msgstr ""
+msgstr "Birleştirme isteği yüklenirken hata oluştu."
msgid "Error loading project data. Please try again."
msgstr ""
@@ -3722,17 +3931,44 @@ msgid "Error updating %{issuableType}"
msgstr ""
msgid "Error updating status for all todos."
-msgstr ""
+msgstr "Bütün yapılacaklar için durum güncellenirken hata oluştu."
msgid "Error updating todo status."
msgstr ""
msgid "Error while loading the merge request. Please try again."
-msgstr ""
+msgstr "Birleştirme isteğini yüklerken hata oluştu. Lütfen tekrar deneyin."
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3758,7 +3994,7 @@ msgid "EventFilterBy|Filter by team"
msgstr ""
msgid "Events"
-msgstr ""
+msgstr "Etkinlikler"
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
msgstr ""
@@ -3811,6 +4047,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3833,16 +4072,16 @@ msgid "Explore GitLab"
msgstr ""
msgid "Explore Groups"
-msgstr ""
+msgstr "Grupları Keşfet"
msgid "Explore groups"
-msgstr ""
+msgstr "Grupları keşfet"
msgid "Explore projects"
msgstr ""
msgid "Explore public groups"
-msgstr ""
+msgstr "Genel grupları keşfet"
msgid "Export as CSV"
msgstr ""
@@ -3898,7 +4137,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3964,9 +4203,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -4024,9 +4260,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4046,10 +4279,10 @@ msgid "FeatureFlags|Try again in a few moments or contact your support team."
msgstr ""
msgid "Feb"
-msgstr ""
+msgstr "Åžub"
msgid "February"
-msgstr ""
+msgstr "Åžubat"
msgid "Fields on this page are now uneditable, you can configure"
msgstr ""
@@ -4081,7 +4314,7 @@ msgid "File upload error."
msgstr ""
msgid "Files"
-msgstr ""
+msgstr "Dosyalar"
msgid "Fill in the fields below, turn on <strong>%{enable_label}</strong>, and press <strong>%{save_changes}</strong>"
msgstr ""
@@ -4113,9 +4346,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr ""
@@ -4228,9 +4458,9 @@ msgid "Found errors in your .gitlab-ci.yml:"
msgstr ""
msgid "Free Trial of GitLab.com Gold"
-msgstr ""
+msgstr "GitLab.com Gold Ãœcretsiz Deneme"
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4260,7 +4490,13 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
+msgstr "GPG Anahtarları"
+
+msgid "GPG signature (loading...)"
msgstr ""
msgid "General"
@@ -4462,7 +4698,7 @@ msgid "Geo|All"
msgstr ""
msgid "Geo|All projects"
-msgstr ""
+msgstr "Geo|Tüm projeler"
msgid "Geo|All projects are being scheduled for re-check"
msgstr ""
@@ -4525,7 +4761,7 @@ msgid "Geo|Project (ID: %{project_id}) no longer exists on the primary. It is sa
msgstr ""
msgid "Geo|Projects in certain groups"
-msgstr ""
+msgstr "Geo|Belli gruplardaki projeler"
msgid "Geo|Projects in certain storage shards"
msgstr ""
@@ -4564,7 +4800,7 @@ msgid "Geo|Shards to synchronize"
msgstr ""
msgid "Geo|Status"
-msgstr ""
+msgstr "Geo|Durum"
msgid "Geo|Synced"
msgstr ""
@@ -4695,15 +4931,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4720,7 +4965,7 @@ msgid "Grant access"
msgstr ""
msgid "Graph"
-msgstr ""
+msgstr "Çizelge"
msgid "Group"
msgstr ""
@@ -4753,7 +4998,7 @@ msgid "Group description (optional)"
msgstr ""
msgid "Group details"
-msgstr ""
+msgstr "Grup ayrıntıları"
msgid "Group info:"
msgstr ""
@@ -4761,6 +5006,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -4834,7 +5082,7 @@ msgid "GroupSettings|remove the share with group lock from %{ancestor_group_name
msgstr ""
msgid "Groups"
-msgstr ""
+msgstr "Gruplar"
msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroups%{subgroup_docs_link_end}."
msgstr ""
@@ -4870,7 +5118,7 @@ msgid "GroupsEmptyState|If you organize your projects under a group, it works li
msgstr ""
msgid "GroupsEmptyState|No groups found"
-msgstr ""
+msgstr "GroupsEmptyState|Grup bulunamadı"
msgid "GroupsEmptyState|You can manage your group member’s permissions and access to each project in the group."
msgstr ""
@@ -4930,13 +5178,13 @@ msgid "HealthCheck|Unhealthy"
msgstr ""
msgid "Help"
-msgstr ""
+msgstr "Yardım"
msgid "Help page"
-msgstr ""
+msgstr "Yardım sayfası"
msgid "Help page text and support page url."
-msgstr ""
+msgstr "Yardım sayfası metni ve destek sayfası bağlantısı."
msgid "Here is the public SSH key that needs to be added to the remote server. For more information, please refer to the documentation."
msgstr ""
@@ -5073,7 +5321,7 @@ msgid "Import all compatible projects"
msgstr ""
msgid "Import all projects"
-msgstr ""
+msgstr "Tüm projeleri içe aktar"
msgid "Import all repositories"
msgstr ""
@@ -5103,19 +5351,19 @@ msgid "Import project members"
msgstr ""
msgid "Import projects from Bitbucket"
-msgstr ""
+msgstr "Bitbucket'ten projeleri içe aktar"
msgid "Import projects from Bitbucket Server"
msgstr ""
msgid "Import projects from FogBugz"
-msgstr ""
+msgstr "FogBugz'dan projeleri içe aktar"
msgid "Import projects from GitLab.com"
-msgstr ""
+msgstr "GitLab.com'dan projeleri içe aktar"
msgid "Import projects from Google Code"
-msgstr ""
+msgstr "Google Code'dan projeler içe aktar"
msgid "Import repositories from Bitbucket Server"
msgstr ""
@@ -5129,9 +5377,24 @@ msgstr ""
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5183,6 +5446,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5236,6 +5505,9 @@ msgstr ""
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5254,6 +5526,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5264,7 +5539,7 @@ msgid "Issue board focus mode"
msgstr ""
msgid "Issue events"
-msgstr ""
+msgstr "Sorun etkinlikleri"
msgid "IssueBoards|Board"
msgstr ""
@@ -5288,15 +5563,15 @@ msgid "IssueBoards|Switch board"
msgstr ""
msgid "Issues"
-msgstr ""
+msgstr "Sorunlar"
msgid "Issues can be bugs, tasks or ideas to be discussed. Also, issues are searchable and filterable."
msgstr ""
msgid "Issues closed"
-msgstr ""
+msgstr "Sorunlar kapalı"
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5333,10 +5608,10 @@ msgid "Jaeger tracing"
msgstr ""
msgid "Jan"
-msgstr ""
+msgstr "Oca"
msgid "January"
-msgstr ""
+msgstr "Ocak"
msgid "Job"
msgstr ""
@@ -5396,16 +5671,16 @@ msgid "Job|This job is stuck because the project doesn't have any runners online
msgstr ""
msgid "Jul"
-msgstr ""
+msgstr "Tem"
msgid "July"
-msgstr ""
+msgstr "Temmuz"
msgid "Jun"
-msgstr ""
+msgstr "Haz"
msgid "June"
-msgstr ""
+msgstr "Haziran"
msgid "Key (PEM)"
msgstr ""
@@ -5441,10 +5716,10 @@ msgid "LFS"
msgstr ""
msgid "LFSStatus|Disabled"
-msgstr ""
+msgstr "LFSStatus|Kapalı"
msgid "LFSStatus|Enabled"
-msgstr ""
+msgstr "LFSStatus|Etkin"
msgid "Label"
msgstr ""
@@ -5465,7 +5740,7 @@ msgid "LabelSelect|Labels"
msgstr ""
msgid "Labels"
-msgstr ""
+msgstr "Etiketler"
msgid "Labels can be applied to %{features}. Group labels are available for any project within the group."
msgstr ""
@@ -5485,6 +5760,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5497,7 +5775,7 @@ msgid "Last Pipeline"
msgstr ""
msgid "Last activity"
-msgstr ""
+msgstr "Son etkinlik"
msgid "Last commit"
msgstr ""
@@ -5521,7 +5799,7 @@ msgid "Last update"
msgstr ""
msgid "Last updated"
-msgstr ""
+msgstr "Son güncelleme"
msgid "LastPushEvent|You pushed to"
msgstr ""
@@ -5568,6 +5846,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr ""
@@ -5673,7 +5954,7 @@ msgid "LinkedIn"
msgstr ""
msgid "List"
-msgstr ""
+msgstr "Liste"
msgid "List Your Gitea Repositories"
msgstr ""
@@ -5727,7 +6008,7 @@ msgid "Locked"
msgstr ""
msgid "Locked Files"
-msgstr ""
+msgstr "Kilitli Dosyalar"
msgid "Locked to current projects"
msgstr ""
@@ -5741,6 +6022,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5757,7 +6047,7 @@ msgid "Manage access"
msgstr ""
msgid "Manage all notifications"
-msgstr ""
+msgstr "Tüm bildirimleri yönet"
msgid "Manage applications that can use GitLab as an OAuth provider, and applications that you've authorized to use your account."
msgstr ""
@@ -5786,6 +6076,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5799,13 +6092,13 @@ msgid "Map a Google Code user to a full name"
msgstr ""
msgid "Mar"
-msgstr ""
+msgstr "Mar"
msgid "March"
-msgstr ""
+msgstr "Mart"
msgid "Mark todo as done"
-msgstr ""
+msgstr "Yapılacağı tamamlandı olarak işaretle"
msgid "Markdown"
msgstr ""
@@ -5813,36 +6106,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5853,7 +6116,7 @@ msgid "Maximum job timeout"
msgstr ""
msgid "May"
-msgstr ""
+msgstr "May"
msgid "Median"
msgstr ""
@@ -5862,10 +6125,10 @@ msgid "Member lock"
msgstr ""
msgid "Member since %{date}"
-msgstr ""
+msgstr "%{date} tarihinden beri üye"
msgid "Members"
-msgstr ""
+msgstr "Ãœyeler"
msgid "Members can be added by project <i>Maintainers</i> or <i>Owners</i>"
msgstr ""
@@ -5877,10 +6140,10 @@ msgid "Members will be forwarded here when signing in to your group. Get this fr
msgstr ""
msgid "Merge Request"
-msgstr ""
+msgstr "BirleÅŸtirme Ä°steÄŸi"
msgid "Merge Requests"
-msgstr ""
+msgstr "BirleÅŸtirme Ä°stekleri"
msgid "Merge Requests created"
msgstr ""
@@ -5897,14 +6160,17 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
-msgid "Merge request"
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
msgstr ""
+msgid "Merge request"
+msgstr "BirleÅŸtirme isteÄŸi"
+
msgid "Merge request approvals"
-msgstr ""
+msgstr "Birleştirme isteği onayları"
msgid "Merge requests"
-msgstr ""
+msgstr "BirleÅŸtirme istekleri"
msgid "Merge requests are a place to propose changes you've made to a project and discuss those changes with others"
msgstr ""
@@ -5982,7 +6248,7 @@ msgid "MergeRequest|Search files"
msgstr ""
msgid "Merged"
-msgstr ""
+msgstr "BirleÅŸtirildi"
msgid "Messages"
msgstr ""
@@ -6047,9 +6313,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6099,7 +6362,7 @@ msgid "Milestone lists show all issues from the selected milestone."
msgstr ""
msgid "Milestones"
-msgstr ""
+msgstr "Dönüm Noktaları"
msgid "Milestones| You’re about to permanently delete the milestone %{milestoneTitle} and remove it from %{issuesWithCount} and %{mergeRequestsWithCount}. Once deleted, it cannot be undone or recovered."
msgstr ""
@@ -6156,7 +6419,7 @@ msgid "MissingSSHKeyWarningLink|add an SSH key"
msgstr ""
msgid "Modal|Cancel"
-msgstr ""
+msgstr "Modal|Ä°ptal"
msgid "Modal|Close"
msgstr ""
@@ -6194,6 +6457,9 @@ msgstr ""
msgid "More information is available|here"
msgstr ""
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6234,7 +6500,7 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr ""
msgid "Need help?"
-msgstr ""
+msgstr "Yardım ister misiniz?"
msgid "Network"
msgstr ""
@@ -6243,27 +6509,27 @@ msgid "Never"
msgstr ""
msgid "New"
-msgstr ""
+msgstr "Yeni"
msgid "New Application"
-msgstr ""
+msgstr "Yeni Uygulama"
msgid "New Environment"
-msgstr ""
+msgstr "Yeni Ortam"
msgid "New Group"
-msgstr ""
+msgstr "Yeni Grup"
msgid "New Identity"
-msgstr ""
+msgstr "Yeni Kimlik"
msgid "New Issue"
msgid_plural "New Issues"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Yeni Sorun"
+msgstr[1] "Yeni Sorunlar"
msgid "New Label"
-msgstr ""
+msgstr "Yeni Etiket"
msgid "New Milestone"
msgstr ""
@@ -6271,13 +6537,13 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
-msgid "New Pipeline Schedule"
+msgid "New Password"
msgstr ""
-msgid "New Snippet"
+msgid "New Pipeline Schedule"
msgstr ""
-msgid "New Snippets"
+msgid "New Snippet"
msgstr ""
msgid "New branch"
@@ -6293,25 +6559,25 @@ msgid "New environment"
msgstr ""
msgid "New epic"
-msgstr ""
+msgstr "Yeni epik"
msgid "New file"
-msgstr ""
+msgstr "Yeni dosya"
msgid "New group"
-msgstr ""
+msgstr "Yeni grup"
msgid "New identity"
-msgstr ""
+msgstr "Yeni kimlik"
msgid "New issue"
-msgstr ""
+msgstr "Yeni sorun"
msgid "New label"
-msgstr ""
+msgstr "Yeni etiket"
msgid "New merge request"
-msgstr ""
+msgstr "Yeni birleÅŸtirme isteÄŸi"
msgid "New milestone"
msgstr ""
@@ -6320,32 +6586,38 @@ msgid "New pipelines will cancel older, pending pipelines on the same branch"
msgstr ""
msgid "New project"
-msgstr ""
+msgstr "Yeni proje"
msgid "New schedule"
msgstr ""
msgid "New snippet"
-msgstr ""
+msgstr "Yeni kod parçası"
msgid "New subgroup"
msgstr ""
msgid "New tag"
-msgstr ""
+msgstr "Yeni etiket"
msgid "New..."
-msgstr ""
+msgstr "Yeni..."
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
-msgid "No activities found"
+msgid "No Tag"
msgstr ""
+msgid "No activities found"
+msgstr "Etkinlik bulunamadı"
+
msgid "No assignee"
msgstr ""
@@ -6370,13 +6642,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
-msgid "No details available"
+msgid "No designs found."
msgstr ""
+msgid "No details available"
+msgstr "Ayrıntı yok"
+
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6389,7 +6664,7 @@ msgid "No file selected"
msgstr ""
msgid "No files found."
-msgstr ""
+msgstr "Dosya bulunamadı."
msgid "No issues for the selected time period."
msgstr ""
@@ -6407,7 +6682,7 @@ msgid "No merge requests for the selected time period."
msgstr ""
msgid "No merge requests found"
-msgstr ""
+msgstr "Birleştirme isteği bulunamadı"
msgid "No messages were logged"
msgstr ""
@@ -6425,7 +6700,7 @@ msgid "No prioritised labels with such name or description"
msgstr ""
msgid "No public groups"
-msgstr ""
+msgstr "Genel grup yok"
msgid "No pushes for the selected time period."
msgstr ""
@@ -6472,6 +6747,9 @@ msgstr ""
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6569,28 +6847,28 @@ msgid "NotificationLevel|Watch"
msgstr ""
msgid "Notifications"
-msgstr ""
+msgstr "Bildirimler"
msgid "Notifications off"
-msgstr ""
+msgstr "Bildirimler kapalı"
msgid "Notifications on"
-msgstr ""
+msgstr "Bildirimler açık"
msgid "Nov"
-msgstr ""
+msgstr "Kas"
msgid "November"
-msgstr ""
+msgstr "Kasım"
msgid "OK"
msgstr ""
msgid "Oct"
-msgstr ""
+msgstr "Eki"
msgid "October"
-msgstr ""
+msgstr "Ekim"
msgid "OfSearchInADropdown|Filter"
msgstr ""
@@ -6646,7 +6924,7 @@ msgid "Open in Xcode"
msgstr ""
msgid "Open projects"
-msgstr ""
+msgstr "Projeleri aç"
msgid "Open sidebar"
msgstr ""
@@ -6655,13 +6933,13 @@ msgid "Open source software to collaborate on code"
msgstr ""
msgid "Opened"
-msgstr ""
+msgstr "Açtı:"
msgid "Opened MR"
msgstr ""
msgid "Opened issues"
-msgstr ""
+msgstr "Açılan sorunlar"
msgid "OpenedNDaysAgo|Opened"
msgstr ""
@@ -6673,6 +6951,9 @@ msgid "Operations"
msgstr ""
msgid "Operations Dashboard"
+msgstr "Ä°ÅŸlemler Panosu"
+
+msgid "Operations Settings"
msgstr ""
msgid "OperationsDashboard|Add a project to the dashboard"
@@ -6684,6 +6965,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6757,7 +7041,7 @@ msgid "Part of merge request changes"
msgstr ""
msgid "Password"
-msgstr ""
+msgstr "Åžifre"
msgid "Past due"
msgstr ""
@@ -6802,7 +7086,7 @@ msgid "Permissions, LFS, 2FA"
msgstr ""
msgid "Personal Access Token"
-msgstr ""
+msgstr "Kişisel Erişim Anahtarı"
msgid "Personal project creation is not allowed. Please contact your administrator with questions"
msgstr ""
@@ -6823,7 +7107,7 @@ msgid "Pipeline Schedules"
msgstr ""
msgid "Pipeline quota"
-msgstr ""
+msgstr "İş Hattı Kotası"
msgid "Pipeline triggers"
msgstr ""
@@ -6876,6 +7160,12 @@ msgstr ""
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr ""
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr ""
@@ -6891,6 +7181,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -7011,9 +7304,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -7023,9 +7328,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7045,13 +7359,16 @@ msgid "Please wait while we import the repository for you. Refresh at will."
msgstr ""
msgid "Preferences"
-msgstr ""
+msgstr "Tercihler"
msgid "Preferences|Navigation theme"
+msgstr "Preferences|Gezinme teması"
+
+msgid "Preferences|This feature is experimental and translations are not complete yet"
msgstr ""
msgid "Press Enter or click to search"
-msgstr ""
+msgstr "Aramak için Enter tuşuna basın veya tıklayın"
msgid "Prevent adding new members to project membership within this group"
msgstr ""
@@ -7090,10 +7407,10 @@ msgid "Private projects can be created in your personal namespace with:"
msgstr ""
msgid "Profile"
-msgstr ""
+msgstr "Profil"
msgid "Profile Settings"
-msgstr ""
+msgstr "Profil Ayarları"
msgid "Profiles| You are about to permanently delete %{yourAccount}, and all of the issues, merge requests, and groups linked to your account. Once you confirm %{deleteAccount}, it cannot be undone or recovered."
msgstr ""
@@ -7117,7 +7434,7 @@ msgid "Profiles|Add key"
msgstr ""
msgid "Profiles|Add status emoji"
-msgstr ""
+msgstr "Profiles|Durum ifadesi ekle"
msgid "Profiles|Avatar cropper"
msgstr ""
@@ -7141,7 +7458,7 @@ msgid "Profiles|City, country"
msgstr ""
msgid "Profiles|Clear status"
-msgstr ""
+msgstr "Profiles|Durumu temizle"
msgid "Profiles|Click on icon to activate signin with one of the following services"
msgstr ""
@@ -7150,22 +7467,22 @@ msgid "Profiles|Connect"
msgstr ""
msgid "Profiles|Connected Accounts"
-msgstr ""
+msgstr "Profiles|Bağlı Hesaplar"
msgid "Profiles|Current path: %{path}"
msgstr ""
msgid "Profiles|Current status"
-msgstr ""
+msgstr "Profiles|Mevcut durum"
msgid "Profiles|Delete Account"
-msgstr ""
+msgstr "Profiles|Hesabı Sil"
msgid "Profiles|Delete account"
-msgstr ""
+msgstr "Profiles|Hesabı sil"
msgid "Profiles|Delete your account?"
-msgstr ""
+msgstr "Profiles|Hesabınız silinsin mi?"
msgid "Profiles|Deleting an account has the following effects:"
msgstr ""
@@ -7180,7 +7497,7 @@ msgid "Profiles|Don't display activity-related personal information on your prof
msgstr ""
msgid "Profiles|Edit Profile"
-msgstr ""
+msgstr "Profiles|Profili Düzenle"
msgid "Profiles|Enter your name, so people you know can recognize you"
msgstr ""
@@ -7189,7 +7506,7 @@ msgid "Profiles|Increase your account's security by enabling Two-Factor Authenti
msgstr ""
msgid "Profiles|Invalid password"
-msgstr ""
+msgstr "Profiles|Geçersiz şifre"
msgid "Profiles|Invalid username"
msgstr ""
@@ -7198,10 +7515,10 @@ msgid "Profiles|Learn more"
msgstr ""
msgid "Profiles|Made a private contribution"
-msgstr ""
+msgstr "Profiles|Özel bir katkı yaptı"
msgid "Profiles|Main settings"
-msgstr ""
+msgstr "Profiles|Ana ayarlar"
msgid "Profiles|No file chosen"
msgstr ""
@@ -7213,10 +7530,10 @@ msgid "Profiles|Position and size your new avatar"
msgstr ""
msgid "Profiles|Private contributions"
-msgstr ""
+msgstr "Profiles|Özel katkılar"
msgid "Profiles|Public Avatar"
-msgstr ""
+msgstr "Profiles|Genel Avatar"
msgid "Profiles|Remove avatar"
msgstr ""
@@ -7225,7 +7542,7 @@ msgid "Profiles|Set new profile picture"
msgstr ""
msgid "Profiles|Social sign-in"
-msgstr ""
+msgstr "Profiles|Sosyal oturum açma"
msgid "Profiles|Some options are unavailable for LDAP accounts"
msgstr ""
@@ -7246,13 +7563,10 @@ msgid "Profiles|This email will be used for web based operations, such as edits
msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
-msgstr ""
-
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
+msgstr "Profiles|Bu ifade ve mesaj profilinizde ve arayüz boyunca görünecektir."
msgid "Profiles|This information will appear on your profile"
-msgstr ""
+msgstr "Profiles|Bu bilgiler profilinizde görünecek"
msgid "Profiles|Two-Factor Authentication"
msgstr ""
@@ -7264,13 +7578,13 @@ msgid "Profiles|Typically starts with \"ssh-rsa …\""
msgstr ""
msgid "Profiles|Update profile settings"
-msgstr ""
+msgstr "Profiles|Profil ayarlarını güncelle"
msgid "Profiles|Update username"
msgstr ""
msgid "Profiles|Upload new avatar"
-msgstr ""
+msgstr "Profiles|Yeni avatar yükle"
msgid "Profiles|Use a private email - %{email}"
msgstr ""
@@ -7279,13 +7593,13 @@ msgid "Profiles|Username change failed - %{message}"
msgstr ""
msgid "Profiles|Username successfully changed"
-msgstr ""
+msgstr "Profiles|Kullanıcı adı başarıyla değiştirildi"
msgid "Profiles|Using emojis in names seems fun, but please try to set a status message instead"
msgstr ""
msgid "Profiles|What's your status?"
-msgstr ""
+msgstr "Profiles|Durumunuz nedir?"
msgid "Profiles|Who you represent or work for"
msgstr ""
@@ -7324,7 +7638,7 @@ msgid "Profiles|Your name was automatically set based on your %{provider_label}
msgstr ""
msgid "Profiles|Your status"
-msgstr ""
+msgstr "Profiles|Durumunuz"
msgid "Profiles|e.g. My MacBook key"
msgstr ""
@@ -7342,7 +7656,7 @@ msgid "Profiling - Performance bar"
msgstr ""
msgid "Programming languages used in this repository"
-msgstr ""
+msgstr "Bu depoda kullanılan programlama dilleri"
msgid "Progress"
msgstr ""
@@ -7350,6 +7664,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7357,7 +7674,7 @@ msgid "Project '%{project_name}' queued for deletion."
msgstr ""
msgid "Project '%{project_name}' was successfully created."
-msgstr ""
+msgstr "'%{project_name}' projesi başarıyla oluşturuldu."
msgid "Project '%{project_name}' was successfully updated."
msgstr ""
@@ -7378,7 +7695,7 @@ msgid "Project avatar in repository: %{link}"
msgstr ""
msgid "Project details"
-msgstr ""
+msgstr "Proje ayrıntıları"
msgid "Project export could not be deleted."
msgstr ""
@@ -7392,11 +7709,14 @@ msgstr ""
msgid "Project export started. A download link will be sent by email."
msgstr ""
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
msgid "Project name"
-msgstr ""
+msgstr "Proje adı"
msgid "Project slug"
msgstr ""
@@ -7414,10 +7734,10 @@ msgid "ProjectCreationLevel|Default project creation protection"
msgstr ""
msgid "ProjectCreationLevel|Developers + Maintainers"
-msgstr ""
+msgstr "ProjectCreationLevel|GeliÅŸtirici + Sorumlu"
msgid "ProjectCreationLevel|Maintainers"
-msgstr ""
+msgstr "ProjectCreationLevel|Sorumlu"
msgid "ProjectCreationLevel|No one"
msgstr ""
@@ -7489,6 +7809,9 @@ msgid "ProjectSettings|Users can only push commits to this repository that were
msgstr ""
msgid "Projects"
+msgstr "Projeler"
+
+msgid "Projects Successfully Retrieved"
msgstr ""
msgid "Projects shared with %{group_name}"
@@ -7806,7 +8129,7 @@ msgid "Register and see your runners for this project."
msgstr ""
msgid "Registry"
-msgstr ""
+msgstr "Kayıt Defteri"
msgid "Related Commits"
msgstr ""
@@ -7824,13 +8147,13 @@ msgid "Related Merge Requests"
msgstr ""
msgid "Related Merged Requests"
-msgstr ""
+msgstr "Ä°lgili BirleÅŸtirme Talepleri"
msgid "Related merge requests"
msgstr ""
msgid "Releases"
-msgstr ""
+msgstr "Sürümler"
msgid "Releases mark specific points in a project's development history, communicate information about the type of change, and deliver on prepared, often compiled, versions of the software to be reused elsewhere. Currently, releases can only be created through the API."
msgstr ""
@@ -7850,6 +8173,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7941,7 +8270,7 @@ msgid "Reports|no changed test results"
msgstr ""
msgid "Repository"
-msgstr ""
+msgstr "Depo"
msgid "Repository Settings"
msgstr ""
@@ -7985,6 +8314,19 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+
msgid "Resend invite"
msgstr ""
@@ -8143,6 +8485,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8162,10 +8507,10 @@ msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from you
msgstr ""
msgid "SSH Keys"
-msgstr ""
+msgstr "SSH Anahtarları"
msgid "SSH host keys"
-msgstr ""
+msgstr "SSH ana bilgisayar anahtarları"
msgid "SSH public key"
msgstr ""
@@ -8173,6 +8518,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8180,7 +8528,7 @@ msgid "Save Changes"
msgstr ""
msgid "Save application"
-msgstr ""
+msgstr "Uygulamayı kaydet"
msgid "Save changes"
msgstr ""
@@ -8206,6 +8554,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr ""
@@ -8252,23 +8603,26 @@ msgid "Search milestones"
msgstr ""
msgid "Search or filter results..."
-msgstr ""
+msgstr "Ara veya sonuçları filtrele..."
msgid "Search or jump to…"
-msgstr ""
+msgstr "Ara veya atla…"
msgid "Search project"
msgstr ""
msgid "Search projects"
-msgstr ""
+msgstr "Projeleri ara"
msgid "Search users"
msgstr ""
-msgid "Search your projects"
+msgid "Search users or groups"
msgstr ""
+msgid "Search your projects"
+msgstr "Projelerinizi arayın"
+
msgid "SearchAutocomplete|All GitLab"
msgstr ""
@@ -8414,7 +8768,7 @@ msgid "Select project to choose zone"
msgstr ""
msgid "Select projects you want to import."
-msgstr ""
+msgstr "İçe aktarmak istediğiniz projeleri seçin."
msgid "Select source branch"
msgstr ""
@@ -8447,10 +8801,10 @@ msgid "Sentry API URL"
msgstr ""
msgid "Sep"
-msgstr ""
+msgstr "Eyl"
msgid "September"
-msgstr ""
+msgstr "Eylül"
msgid "Server version"
msgstr ""
@@ -8498,7 +8852,7 @@ msgid "Serverless|There is currently no function data available from Knative. Th
msgstr ""
msgid "Service Desk"
-msgstr ""
+msgstr "Servis Masası"
msgid "Service Templates"
msgstr ""
@@ -8507,13 +8861,13 @@ msgid "Service URL"
msgstr ""
msgid "Session expiration, projects limit and attachment size."
-msgstr ""
+msgstr "Oturum zaman aşımı, proje sınırı ve ek boyutu."
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr ""
msgid "Set a template repository for projects in this group"
-msgstr ""
+msgstr "Bu gruptaki projeler için bir şablon deposu ayarlayın"
msgid "Set default and restrict visibility levels. Configure import sources and git access protocol."
msgstr ""
@@ -8524,6 +8878,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8548,6 +8905,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8555,28 +8915,28 @@ msgid "SetPasswordToCloneLink|set a password"
msgstr ""
msgid "SetStatusModal|Add status emoji"
-msgstr ""
+msgstr "SetStatusModal|Durum ifadesi ekle"
msgid "SetStatusModal|Clear status"
-msgstr ""
+msgstr "SetStatusModal|Durumu temizle"
msgid "SetStatusModal|Edit status"
-msgstr ""
+msgstr "SetStatusModal|Durumu düzenle"
msgid "SetStatusModal|Remove status"
-msgstr ""
+msgstr "SetStatusModal|Durumu kaldır"
msgid "SetStatusModal|Set a status"
-msgstr ""
+msgstr "SetStatusModal|Bir durum ayarla"
msgid "SetStatusModal|Set status"
-msgstr ""
+msgstr "SetStatusModal|Durum ayarla"
msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try again later."
msgstr ""
msgid "SetStatusModal|What's your status?"
-msgstr ""
+msgstr "SetStatusModal|Durumunuz nedir?"
msgid "Settings"
msgstr ""
@@ -8605,9 +8965,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8628,8 +8994,8 @@ msgstr ""
msgid "Showing %d event"
msgid_plural "Showing %d events"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%d etkinliği gösteriliyor"
+msgstr[1] "%d etkinlikleri gösteriliyor"
msgid "Side-by-side"
msgstr ""
@@ -8668,7 +9034,7 @@ msgid "Sign in with smart card"
msgstr ""
msgid "Sign out"
-msgstr ""
+msgstr "Oturumu kapat"
msgid "Sign-in restrictions"
msgstr ""
@@ -8677,7 +9043,7 @@ msgid "Sign-up restrictions"
msgstr ""
msgid "Similar issues"
-msgstr ""
+msgstr "Benzer sorunlar"
msgid "Size"
msgstr ""
@@ -8704,6 +9070,24 @@ msgid "Snippet Contents"
msgstr ""
msgid "Snippets"
+msgstr "Parçacıklar"
+
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
msgstr ""
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
@@ -8833,7 +9217,7 @@ msgid "SortOptions|Last joined"
msgstr ""
msgid "SortOptions|Last updated"
-msgstr ""
+msgstr "SortOptions|Son güncelleme"
msgid "SortOptions|Least popular"
msgstr ""
@@ -8881,7 +9265,7 @@ msgid "SortOptions|Oldest sign in"
msgstr ""
msgid "SortOptions|Oldest updated"
-msgstr ""
+msgstr "SortOptions|En eski güncelleme"
msgid "SortOptions|Popularity"
msgstr ""
@@ -8914,7 +9298,7 @@ msgid "Source (branch or tag)"
msgstr ""
msgid "Source code"
-msgstr ""
+msgstr "Kaynak kodu"
msgid "Source is not available"
msgstr ""
@@ -8944,7 +9328,7 @@ msgid "Squash commits"
msgstr ""
msgid "Stage"
-msgstr ""
+msgstr "AÅŸama"
msgid "Stage & Commit"
msgstr ""
@@ -8976,6 +9360,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9010,7 +9400,7 @@ msgid "Start the Runner!"
msgstr ""
msgid "Start your trial"
-msgstr ""
+msgstr "Denemenizi başlatın"
msgid "Started"
msgstr ""
@@ -9064,10 +9454,10 @@ msgid "Storage:"
msgstr ""
msgid "Subgroups"
-msgstr ""
+msgstr "Alt gruplar"
msgid "Subgroups and projects"
-msgstr ""
+msgstr "Alt gruplar ve projeler"
msgid "Submit as spam"
msgstr ""
@@ -9082,7 +9472,7 @@ msgid "Submit search"
msgstr ""
msgid "Subscribe"
-msgstr ""
+msgstr "Abone ol"
msgid "Subscribe at group level"
msgstr ""
@@ -9091,13 +9481,13 @@ msgid "Subscribe at project level"
msgstr ""
msgid "Subscribe to RSS feed"
-msgstr ""
+msgstr "RSS beslemesine abone olun"
msgid "Subscribe to calendar"
-msgstr ""
+msgstr "Takvime abone ol"
msgid "Subscribed"
-msgstr ""
+msgstr "Abone"
msgid "SubscriptionTable|Billing"
msgstr ""
@@ -9186,6 +9576,9 @@ msgstr ""
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9211,13 +9604,13 @@ msgid "Tag list:"
msgstr ""
msgid "Tags"
-msgstr ""
+msgstr "Etiketler"
msgid "Tags feed"
msgstr ""
msgid "Tags:"
-msgstr ""
+msgstr "Etiketler:"
msgid "TagsPage|Browse commits"
msgstr ""
@@ -9229,7 +9622,7 @@ msgid "TagsPage|Can't find HEAD commit for this tag"
msgstr ""
msgid "TagsPage|Cancel"
-msgstr ""
+msgstr "TagsPage|Ä°ptal"
msgid "TagsPage|Create tag"
msgstr ""
@@ -9322,7 +9715,7 @@ msgid "Test coverage parsing"
msgstr ""
msgid "Thanks! Don't show me this again"
-msgstr ""
+msgstr "Teşekkürler! Bu mesajı tekrar gösterme"
msgid "The \"%{group_path}\" group allows you to sign in with your Single Sign-On Account"
msgstr ""
@@ -9375,6 +9768,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9496,6 +9892,9 @@ msgid "There was an error deleting the todo."
msgstr ""
msgid "There was an error loading users activity calendar."
+msgstr "Kullanıcı etkinlik takvimi yüklenirken bir hata oluştu."
+
+msgid "There was an error saving your changes."
msgstr ""
msgid "There was an error saving your notification settings."
@@ -9543,6 +9942,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9564,6 +9978,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9598,7 +10015,7 @@ msgid "This job does not have a trace."
msgstr ""
msgid "This job has been canceled"
-msgstr ""
+msgstr "Bu iÅŸ iptal edildi"
msgid "This job has been skipped"
msgstr ""
@@ -9648,6 +10065,12 @@ msgstr ""
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -9754,130 +10177,130 @@ msgid "TimeTracking|Spent"
msgstr ""
msgid "Timeago|%s days ago"
-msgstr ""
+msgstr "Timeago|%s gün önce"
msgid "Timeago|%s days remaining"
-msgstr ""
+msgstr "Timeago|%s gün kaldı"
msgid "Timeago|%s hours ago"
-msgstr ""
+msgstr "Timeago|%s saat önce"
msgid "Timeago|%s hours remaining"
-msgstr ""
+msgstr "Timeago|%s saat kaldı"
msgid "Timeago|%s minutes ago"
-msgstr ""
+msgstr "Timeago|%s dakika önce"
msgid "Timeago|%s minutes remaining"
-msgstr ""
+msgstr "Timeago|%s dakika kaldı"
msgid "Timeago|%s months ago"
-msgstr ""
+msgstr "Timeago|%s ay önce"
msgid "Timeago|%s months remaining"
-msgstr ""
+msgstr "Timeago|%s ay kaldı"
msgid "Timeago|%s seconds ago"
-msgstr ""
+msgstr "Timeago|%s saniye önce"
msgid "Timeago|%s seconds remaining"
-msgstr ""
+msgstr "Timeago|%s saniye kaldı"
msgid "Timeago|%s weeks ago"
-msgstr ""
+msgstr "Timeago|%s hafta önce"
msgid "Timeago|%s weeks remaining"
-msgstr ""
+msgstr "Timeago|%s hafta kaldı"
msgid "Timeago|%s years ago"
-msgstr ""
+msgstr "Timeago|%s yıl önce"
msgid "Timeago|%s years remaining"
-msgstr ""
+msgstr "Timeago|%s yıl kaldı"
msgid "Timeago|1 day ago"
msgstr ""
msgid "Timeago|1 day remaining"
-msgstr ""
+msgstr "Timeago|1 gün kaldı"
msgid "Timeago|1 hour ago"
-msgstr ""
+msgstr "Timeago|1 saat önce"
msgid "Timeago|1 hour remaining"
-msgstr ""
+msgstr "Timeago|1 saat kaldı"
msgid "Timeago|1 minute ago"
-msgstr ""
+msgstr "Timeago|1 dakika önce"
msgid "Timeago|1 minute remaining"
-msgstr ""
+msgstr "Timeago|1 dakika kaldı"
msgid "Timeago|1 month ago"
-msgstr ""
+msgstr "Timeago|1 ay önce"
msgid "Timeago|1 month remaining"
-msgstr ""
+msgstr "Timeago|1 ay kaldı"
msgid "Timeago|1 week ago"
-msgstr ""
+msgstr "Timeago|1 hafta önce"
msgid "Timeago|1 week remaining"
-msgstr ""
+msgstr "Timeago|1 hafta kaldı"
msgid "Timeago|1 year ago"
-msgstr ""
+msgstr "Timeago|1 yıl önce"
msgid "Timeago|1 year remaining"
-msgstr ""
+msgstr "Timeago|1 yıl kaldı"
msgid "Timeago|Past due"
-msgstr ""
+msgstr "Timeago|Vadesi geçmiş"
msgid "Timeago|in %s days"
-msgstr ""
+msgstr "Timeago|%s gün içinde"
msgid "Timeago|in %s hours"
-msgstr ""
+msgstr "Timeago|%s saat içinde"
msgid "Timeago|in %s minutes"
-msgstr ""
+msgstr "Timeago|%s dakika içinde"
msgid "Timeago|in %s months"
-msgstr ""
+msgstr "Timeago|%s ay içinde"
msgid "Timeago|in %s seconds"
-msgstr ""
+msgstr "Timeago|%s saniye içinde"
msgid "Timeago|in %s weeks"
-msgstr ""
+msgstr "Timeago|%s hafta içinde"
msgid "Timeago|in %s years"
-msgstr ""
+msgstr "Timeago|%s yıl içinde"
msgid "Timeago|in 1 day"
-msgstr ""
+msgstr "Timeago|1 gün içinde"
msgid "Timeago|in 1 hour"
-msgstr ""
+msgstr "Timeago|1 saat içinde"
msgid "Timeago|in 1 minute"
-msgstr ""
+msgstr "Timeago|1 dakika içinde"
msgid "Timeago|in 1 month"
-msgstr ""
+msgstr "Timeago|1 ay içinde"
msgid "Timeago|in 1 week"
-msgstr ""
+msgstr "Timeago|1 hafta içinde"
msgid "Timeago|in 1 year"
-msgstr ""
+msgstr "Timeago|1 yıl içinde"
msgid "Timeago|just now"
-msgstr ""
+msgstr "Timeago|ÅŸimdi"
msgid "Timeago|right now"
-msgstr ""
+msgstr "Timeago|ÅŸimdi"
msgid "Timeout"
msgstr ""
@@ -10010,7 +10433,7 @@ msgid "Todo"
msgstr ""
msgid "Todos"
-msgstr ""
+msgstr "Yapılacaklar"
msgid "Toggle Sidebar"
msgstr ""
@@ -10040,7 +10463,7 @@ msgid "ToggleButton|Toggle Status: ON"
msgstr ""
msgid "Token"
-msgstr ""
+msgstr "Erişim anahtarı"
msgid "Tomorrow"
msgstr ""
@@ -10184,7 +10607,7 @@ msgid "Unstar"
msgstr ""
msgid "Unsubscribe"
-msgstr ""
+msgstr "Abonelikten çık"
msgid "Unsubscribe at group level"
msgstr ""
@@ -10207,6 +10630,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10216,6 +10642,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10307,7 +10736,7 @@ msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{
msgstr ""
msgid "User Settings"
-msgstr ""
+msgstr "Kullanıcı Ayarları"
msgid "User and IP Rate Limits"
msgstr ""
@@ -10316,22 +10745,22 @@ msgid "User map"
msgstr ""
msgid "UserProfile|Activity"
-msgstr ""
+msgstr "UserProfile|Etkinlik"
msgid "UserProfile|Already reported for abuse"
msgstr ""
msgid "UserProfile|Contributed projects"
-msgstr ""
+msgstr "UserProfile|Katıldığı projeler"
msgid "UserProfile|Edit profile"
-msgstr ""
+msgstr "UserProfile|Profili düzenle"
msgid "UserProfile|Explore public groups to find projects to contribute to."
msgstr ""
msgid "UserProfile|Groups"
-msgstr ""
+msgstr "UserProfile|Gruplar"
msgid "UserProfile|Groups are the best way to manage projects and members."
msgstr ""
@@ -10340,43 +10769,43 @@ msgid "UserProfile|Join or create a group to start contributing by commenting on
msgstr ""
msgid "UserProfile|Most Recent Activity"
-msgstr ""
+msgstr "UserProfile|En Son Etkinlik"
msgid "UserProfile|No snippets found."
-msgstr ""
+msgstr "UserProfile|Parçacık bulunamadı."
msgid "UserProfile|Overview"
-msgstr ""
+msgstr "UserProfile|Genel bakış"
msgid "UserProfile|Personal projects"
-msgstr ""
+msgstr "UserProfile|KiÅŸisel projeler"
msgid "UserProfile|Report abuse"
-msgstr ""
+msgstr "UserProfile|Kötüye kullanımı bildir"
msgid "UserProfile|Snippets"
-msgstr ""
+msgstr "UserProfile|Parçacıklar"
msgid "UserProfile|Snippets in GitLab can either be private, internal, or public."
msgstr ""
msgid "UserProfile|Subscribe"
-msgstr ""
+msgstr "UserProfile|Abone"
msgid "UserProfile|This user doesn't have any personal projects"
-msgstr ""
+msgstr "UserProfile|Bu kullanıcının herhangi bir kişisel projesi yok"
msgid "UserProfile|This user has a private profile"
-msgstr ""
+msgstr "UserProfile|Bu kullanıcı özel bir profile sahip"
msgid "UserProfile|This user hasn't contributed to any projects"
-msgstr ""
+msgstr "UserProfile|Bu kullanıcı hiçbir projeye katkıda bulunmadı"
msgid "UserProfile|View all"
-msgstr ""
+msgstr "UserProfile|Tümünü görüntüle"
msgid "UserProfile|View user in admin area"
-msgstr ""
+msgstr "UserProfile|Kullanıcıyı yönetici alanında görüntüle"
msgid "UserProfile|You can create a group for several dependent projects."
msgstr ""
@@ -10444,6 +10873,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10453,6 +10885,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10756,6 +11191,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr ""
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10850,10 +11288,10 @@ msgid "You do not have the correct permissions to override the settings from the
msgstr ""
msgid "You don't have any applications"
-msgstr ""
+msgstr "Herhangi bir uygulamanız yok"
msgid "You don't have any authorized applications"
-msgstr ""
+msgstr "Herhangi bir yetkili uygulamanız yok"
msgid "You don't have any deployments right now."
msgstr ""
@@ -10861,6 +11299,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr ""
@@ -10895,16 +11336,16 @@ msgid "You will lose all the unstaged changes you've made in this project. This
msgstr ""
msgid "You will not get any notifications via email"
-msgstr ""
+msgstr "E-posta yoluyla herhangi bir bildirim almazsınız "
msgid "You will only receive notifications for the events you choose"
-msgstr ""
+msgstr "Yalnızca seçtiğiniz etkinlikler için bildirim alacaksınız"
msgid "You will only receive notifications for threads you have participated in"
msgstr ""
msgid "You will receive notifications for any activity"
-msgstr ""
+msgstr "Herhangi bir etkinlik için bildirim alacaksınız"
msgid "You will receive notifications only for comments in which you were @mentioned"
msgstr ""
@@ -10916,11 +11357,14 @@ msgid "You won't be able to pull or push project code via SSH until you %{add_ss
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 ""
+msgstr "Profilinize bir SSH anahtarı ekleyene kadar proje kodunu SSH üzerinden çekemez veya yollayamazsınız"
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -10937,7 +11381,7 @@ msgid "Your Conversational Development Index gives an overview of how you are us
msgstr ""
msgid "Your Groups"
-msgstr ""
+msgstr "Gruplarınız"
msgid "Your Kubernetes cluster information on this page is still editable, but you are advised to disable and reconfigure"
msgstr ""
@@ -10949,16 +11393,16 @@ msgid "Your Projects' Activity"
msgstr ""
msgid "Your Todos"
-msgstr ""
+msgstr "Yapacaklarınız"
msgid "Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left."
msgstr ""
msgid "Your applications (%{size})"
-msgstr ""
+msgstr "Uygulamalarınız (%{size})"
msgid "Your authorized applications"
-msgstr ""
+msgstr "Yetkili uygulamalarınız"
msgid "Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer)."
msgstr ""
@@ -10979,7 +11423,7 @@ msgid "Your device was successfully set up! Give it a name and register it with
msgstr ""
msgid "Your groups"
-msgstr ""
+msgstr "Gruplarınız"
msgid "Your issues are being imported. Once finished, you'll get a confirmation email."
msgstr ""
@@ -10994,13 +11438,13 @@ msgid "Your project limit is %{limit} projects! Please contact your administrato
msgstr ""
msgid "Your projects"
-msgstr ""
+msgstr "Projeleriniz"
msgid "a deleted user"
msgstr ""
msgid "ago"
-msgstr ""
+msgstr "önce"
msgid "allowed to fail"
msgstr ""
@@ -11014,6 +11458,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11097,6 +11544,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11191,9 +11641,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11233,6 +11680,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11251,7 +11701,7 @@ msgid "command line instructions"
msgstr ""
msgid "commented on %{link_to_project}"
-msgstr ""
+msgstr "Yorumladı: %{link_to_project}"
msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
msgstr ""
@@ -11288,7 +11738,7 @@ msgstr[0] ""
msgstr[1] ""
msgid "done"
-msgstr ""
+msgstr "tamamlandı"
msgid "draft"
msgid_plural "drafts"
@@ -11320,14 +11770,11 @@ msgid "group"
msgstr ""
msgid "help"
-msgstr ""
+msgstr "yardım"
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11401,8 +11848,8 @@ msgstr ""
msgid "merge request"
msgid_plural "merge requests"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "birleÅŸtirme isteÄŸi"
+msgstr[1] "birleÅŸtirme isteÄŸi"
msgid "missing"
msgstr ""
@@ -11446,11 +11893,14 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
msgid "mrWidget|Cancel automatic merge"
-msgstr ""
+msgstr "mrWidget|Otomatik birleÅŸtirmeyi iptal et"
msgid "mrWidget|Check out branch"
msgstr ""
@@ -11518,6 +11968,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11579,6 +12032,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
@@ -11655,13 +12111,13 @@ msgid "n/a"
msgstr ""
msgid "new merge request"
-msgstr ""
+msgstr "yeni birleÅŸtirme isteÄŸi"
msgid "none"
msgstr ""
msgid "notification emails"
-msgstr ""
+msgstr "bildirim e-postaları"
msgid "nounSeries|%{firstItem} and %{lastItem}"
msgstr ""
@@ -11686,10 +12142,10 @@ msgstr[0] ""
msgstr[1] ""
msgid "password"
-msgstr ""
+msgstr "ÅŸifre"
msgid "personal access token"
-msgstr ""
+msgstr "kişisel erişim anahtarı"
msgid "private"
msgstr ""
@@ -11765,13 +12221,13 @@ msgid "this document"
msgstr ""
msgid "to help your contributors communicate effectively!"
-msgstr ""
+msgstr "katkıda bulunanlara yardım etmek için etkili şekilde iletişim kurun!"
msgid "triggered"
msgstr ""
msgid "updated"
-msgstr ""
+msgstr "güncellendi"
msgid "username"
msgstr ""
diff --git a/locale/uk/gitlab.po b/locale/uk/gitlab.po
index 67d9a556621..86b7048ad3d 100644
--- a/locale/uk/gitlab.po
+++ b/locale/uk/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: uk\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:11\n"
+"PO-Revision-Date: 2019-03-14 07:35\n"
msgid " Status"
msgstr " СтатуÑ"
@@ -36,17 +36,24 @@ msgstr[2] " покращилоÑÑ Ð½Ð° %d одиниць"
msgstr[3] " покращилоÑÑ Ð½Ð° %d одиниць"
msgid " or "
-msgstr ""
+msgstr " або "
msgid " or <#epic id>"
-msgstr ""
+msgstr " або <#epic id>"
msgid " or <#issue id>"
-msgstr ""
+msgstr " або <#issue id>"
msgid "\"%{query}\" in projects"
msgstr "\"%{query}\" в проектах"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] "%d коментар"
+msgstr[1] "%d коментарі"
+msgstr[2] "%d коментарів"
+msgstr[3] "%d коментарів"
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d коміт"
@@ -62,7 +69,7 @@ msgstr[2] "%d комітів позаду"
msgstr[3] "%d комітів позаду"
msgid "%d commits"
-msgstr ""
+msgstr "%d комітів"
msgid "%d exporter"
msgid_plural "%d exporters"
@@ -94,10 +101,10 @@ msgstr[3] "%d задач"
msgid "%d issue selected"
msgid_plural "%d issues selected"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%d вибрана задача"
+msgstr[1] "%d вибрані задачі"
+msgstr[2] "%d вибраних задач"
+msgstr[3] "%d вибраних задач"
msgid "%d layer"
msgid_plural "%d layers"
@@ -156,12 +163,28 @@ msgstr "%{counter_storage} (%{counter_repositories} репозиторій, %{co
msgid "%{count} %{alerts}"
msgstr "%{count} %{alerts}"
-msgid "%{count} more"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "%{count} approvals from %{name}"
msgstr ""
+msgid "%{count} more"
+msgstr "%{count} більше"
+
msgid "%{count} more assignees"
msgstr "%{count} більше виконавців"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr "%{count} з %{total}"
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} учаÑтник"
@@ -182,18 +205,18 @@ msgstr "%{filePath} видалено"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} +%{labelCount} більше"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{group_docs_link_start}Групи%{group_docs_link_end} дозволÑÑŽÑ‚ÑŒ вам керувати Ñ– взаємодіÑти між кількома проектами. Члени групи мають доÑтуп до уÑÑ–Ñ… Ñ—Ñ— проектів."
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} буде видалено! Ви впевнені?"
-msgid "%{link_start}Read more%{link_end} about role permissions"
+msgid "%{label_for_message} unavailable"
msgstr ""
+msgid "%{link_start}Read more%{link_end} about role permissions"
+msgstr "%{link_start}Читати більше%{link_end} про дозволи ролей"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} Початок"
@@ -213,31 +236,31 @@ msgid "%{percent}%% complete"
msgstr "%{percent}%% завершено"
msgid "%{state} epics"
-msgstr ""
+msgstr "%{state} епіки"
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%{strong_start}%{branch_count}%{strong_end} Гілка"
+msgstr[1] "%{strong_start}%{branch_count}%{strong_end} Гілки"
+msgstr[2] "%{strong_start}%{branch_count}%{strong_end} Гілок"
+msgstr[3] "%{strong_start}%{branch_count}%{strong_end} Гілок"
msgid "%{strong_start}%{commit_count}%{strong_end} Commit"
msgid_plural "%{strong_start}%{commit_count}%{strong_end} Commits"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%{strong_start}%{commit_count}%{strong_end} Коміт"
+msgstr[1] "%{strong_start}%{commit_count}%{strong_end} Коміти"
+msgstr[2] "%{strong_start}%{commit_count}%{strong_end} Комітів"
+msgstr[3] "%{strong_start}%{commit_count}%{strong_end} Комітів"
msgid "%{strong_start}%{human_size}%{strong_end} Files"
-msgstr ""
+msgstr "%{strong_start}%{human_size}%{strong_end} Файлів"
msgid "%{strong_start}%{tag_count}%{strong_end} Tag"
msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "%{strong_start}%{tag_count}%{strong_end} Тег"
+msgstr[1] "%{strong_start}%{tag_count}%{strong_end} Теги"
+msgstr[2] "%{strong_start}%{tag_count}%{strong_end} Тегів"
+msgstr[3] "%{strong_start}%{tag_count}%{strong_end} Тегів"
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
@@ -259,10 +282,10 @@ msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what infor
msgstr "%{usage_ping_link_start}ДовідатиÑÑŒ більше%{usage_ping_link_end} про те, Ñкою інформацією Ви ділитеÑÑŒ із GitLab Inc."
msgid "%{user_name} profile page"
-msgstr ""
+msgstr "%{user_name} Ñторінка профілю"
msgid "(external source)"
-msgstr ""
+msgstr "(зовнішнє джерело)"
msgid "+ %{count} more"
msgstr "+ ще %{count}"
@@ -270,8 +293,11 @@ msgstr "+ ще %{count}"
msgid "+ %{moreCount} more"
msgstr "+ ще %{moreCount}"
+msgid "+%{extraOptionCount} more"
+msgstr "+%{extraOptionCount} більше"
+
msgid ", or "
-msgstr ""
+msgstr ", або "
msgid "- Runner is active and can process any new jobs"
msgstr "- Runner активний Ñ– може виконувати нові завданнÑ"
@@ -296,6 +322,13 @@ msgstr[1] "%{count} %{type} зміни"
msgstr[2] "%{count} %{type} змін"
msgstr[3] "%{count} %{type} змін"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] "1 день"
+msgstr[1] "%d дні"
+msgstr[2] "%d днів"
+msgstr[3] "%d днів"
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "1 закрита задача"
@@ -363,13 +396,13 @@ msgid "1st contribution!"
msgstr "Перший внеÑок!"
msgid "2FA"
-msgstr ""
+msgstr "двофакторна автентифікаціÑ"
msgid "2FA enabled"
-msgstr "Двоетапна Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð°"
+msgstr "Двофакторна Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð°"
msgid "403|Please contact your GitLab administrator to get permission."
-msgstr ""
+msgstr "Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора GitLab, щоб отримати дозвіл."
msgid "403|You don't have the permission to access this page."
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” доÑтупу до цієї Ñторінки."
@@ -411,11 +444,26 @@ msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong>
msgstr "<strong>%{pushes}</strong> відправок (push), більше ніж <strong>%{commits}</strong> зафікÑовано<strong>%{people}</strong> учаÑниками."
msgid "<strong>Deletes</strong> source branch"
-msgstr ""
+msgstr "<strong>ВидалÑÑ”</strong> гілку-джерело"
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "'Runner' — це процеÑ, Ñкий виконує завданнÑ. Ви можете Ñтворити потрібну кількіÑÑ‚ÑŒ Runner'ів."
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "Ðабір графіків відноÑно безперервної інтеграції"
@@ -431,9 +479,18 @@ msgstr "УчаÑник команди GitLab по боротьбі з поруш
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "У вашому форку буде Ñтворено нову гілку, а також буде ініційований новий запит на злиттÑ."
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A 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 ready-to-go template for use with Android apps."
+msgstr ""
+
+msgid "A ready-to-go template for use with iOS Swift apps."
+msgstr ""
+
msgid "A regular expression that will be used to find the test coverage output in the job trace. Leave blank to disable"
msgstr "РегулÑрний вираз, Ñкий буде викориÑтовуватиÑÑ Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ результатів Ð¿Ð¾ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ñ‚ÐµÑтами в завданні. Залиште пуÑтим Ð´Ð»Ñ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ"
@@ -459,7 +516,7 @@ msgid "Abuse reports"
msgstr "Звіти про зловживаннÑ"
msgid "Accept invitation"
-msgstr ""
+msgstr "ПрийнÑти запрошеннÑ"
msgid "Accept terms"
msgstr "ПрийнÑти умови"
@@ -498,41 +555,68 @@ msgid "Add"
msgstr "Додати"
msgid "Add CHANGELOG"
-msgstr ""
+msgstr "Додати ÑпиÑок змін (CHANGELOG)"
msgid "Add CONTRIBUTING"
-msgstr ""
+msgstr "Додати CONTRIBUTING"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
msgstr "Додайте групові веб-гуки та GitLab Enterprise Edition."
msgid "Add Jaeger URL"
-msgstr "Додати URL-Ð°Ð´Ñ€ÐµÑ Jaeger"
+msgstr "Додати URL-адреÑу Jaeger"
msgid "Add Kubernetes cluster"
msgstr "Додати Kubernetes-клаÑтер"
msgid "Add README"
-msgstr ""
+msgstr "Додати інÑтрукцію (README)"
+
+msgid "Add a bullet list"
+msgstr "Додати ненумерований ÑпиÑок"
msgid "Add a general comment to this %{noteable_name}."
-msgstr ""
+msgstr "Додайте загальний коментар до цього %{noteable_name}."
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "Додати домашню Ñторінку в вікі, Ñка міÑтить інформацію про ваш проект, Ñ– GitLab відображатиме його тут заміÑÑ‚ÑŒ цього повідомленнÑ."
+msgid "Add a link"
+msgstr "Додати поÑиланнÑ"
+
+msgid "Add a numbered list"
+msgstr "Додати нумерований ÑпиÑок"
+
msgid "Add a table"
msgstr "Додати таблицю"
+msgid "Add a task list"
+msgstr "Додати ÑпиÑок завдань"
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "Створіть додатковий текÑÑ‚, Ñкий буде приÑутній у вÑÑ–Ñ… повідомленнÑÑ… електронної пошти. МакÑимальна кількіÑÑ‚ÑŒ Ñимволів — %{character_limit}"
+msgid "Add approver(s)"
+msgstr "Додати затверджуючих оÑіб"
+
+msgid "Add approvers"
+msgstr "Додати затверджуючих оÑіб"
+
+msgid "Add bold text"
+msgstr "Додати жирний текÑÑ‚"
+
msgid "Add comment now"
msgstr "Додати коментар"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "Додати коментар до зображеннÑ"
+msgid "Add italic text"
+msgstr "Додати курÑивний текÑÑ‚"
+
msgid "Add license"
msgstr "Додати ліцензію"
@@ -549,23 +633,26 @@ msgid "Add reaction"
msgstr "Додати реакцію"
msgid "Add to project"
-msgstr ""
+msgstr "Додати до проекту"
msgid "Add to review"
msgstr "Додати до перевірки"
msgid "Add todo"
-msgstr "Додати задачу"
+msgstr "Додати нагадуваннÑ"
msgid "Add user(s) to the group:"
msgstr "Додати кориÑтувачів до групу:"
msgid "Add users or groups who are allowed to approve every merge request"
-msgstr ""
+msgstr "Додайте кориÑтувачів або групи, Ñким дозволено затверджувати будь-Ñкий запит на злиттÑ"
msgid "Add users to group"
msgstr "Додати кориÑтувача до групи"
+msgid "Added at"
+msgstr ""
+
msgid "Adding new applications is disabled in your GitLab instance. Please contact your GitLab administrator to get the permission"
msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð½Ð¾Ð²Ð¸Ñ… проектів Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ інÑтанÑу GitLab заборонено. ЗвернітьÑÑ Ð´Ð¾ Ñвого адмініÑтратора GitLab, щоб отримати дозвіл"
@@ -612,40 +699,40 @@ msgid "AdminProjects|Delete project"
msgstr "Видалити проект"
msgid "AdminSettings|Auto DevOps domain"
-msgstr ""
+msgstr "Домен Auto DevOps"
msgid "AdminSettings|Environment variables are protected by default"
-msgstr ""
+msgstr "Змінні Ñередовища Ñ” захищеними за замовчуваннÑм"
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
msgstr "Вкажіть домен, Ñкий буде викориÑтовуватиÑÑ Ð² проекті за замовчуваннÑм Ð´Ð»Ñ Ñтадій Auto Review Apps Ñ– Auto Deploy."
msgid "AdminSettings|When creating a new environment variable it will be protected by default."
-msgstr ""
+msgstr "При Ñтворенні нової змінної Ñередовища вона буде захищена за замовчуваннÑм."
msgid "AdminUsers|2FA Disabled"
-msgstr ""
+msgstr "2FA вимкнено"
msgid "AdminUsers|2FA Enabled"
-msgstr ""
+msgstr "2FA увімкнено"
msgid "AdminUsers|Active"
-msgstr ""
+msgstr "Ðктивні"
msgid "AdminUsers|Admin"
-msgstr ""
+msgstr "ÐдмініÑтратор"
msgid "AdminUsers|Admins"
-msgstr ""
+msgstr "ÐдмініÑтратори"
msgid "AdminUsers|Block user"
msgstr "Заблоквати кориÑтувача"
msgid "AdminUsers|Blocked"
-msgstr ""
+msgstr "Заблоковано"
msgid "AdminUsers|Cannot unblock LDAP blocked users"
-msgstr ""
+msgstr "Ðеможливо розблокувати кориÑтувачів заблокованих в LDAP"
msgid "AdminUsers|Delete User %{username} and contributions?"
msgstr "Видалити кориÑтувача %{username} та його внеÑки?"
@@ -660,28 +747,28 @@ msgid "AdminUsers|Delete user and contributions"
msgstr "Видалити кориÑтувача Ñ– його внеÑки"
msgid "AdminUsers|External"
-msgstr ""
+msgstr "Зовнішні"
msgid "AdminUsers|It's you!"
-msgstr ""
+msgstr "Це ви!"
msgid "AdminUsers|New user"
-msgstr ""
+msgstr "Ðовий кориÑтувач"
msgid "AdminUsers|No users found"
-msgstr ""
+msgstr "КориÑтувачів не знайдено"
msgid "AdminUsers|Search by name, email or username"
-msgstr ""
+msgstr "Шукати за іменем, електронною поштою або іменем кориÑтувача"
msgid "AdminUsers|Search users"
-msgstr ""
+msgstr "Пошук кориÑтувачів"
msgid "AdminUsers|Send email to users"
-msgstr ""
+msgstr "Відправити Ð¿Ð¾Ð²Ñ–Ð´Ð¼Ð¾Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти кориÑтувачам"
msgid "AdminUsers|Sort by"
-msgstr ""
+msgstr "Сортувати за"
msgid "AdminUsers|To confirm, type %{projectName}"
msgstr "Ð”Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ %{projectName}"
@@ -690,9 +777,12 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr "Ð”Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð²Ð²ÐµÐ´Ñ–Ñ‚ÑŒ %{username}"
msgid "AdminUsers|User will be blocked"
-msgstr ""
+msgstr "КориÑтувач буде заблокований"
msgid "AdminUsers|Without projects"
+msgstr "Без проектів"
+
+msgid "Advanced"
msgstr ""
msgid "Advanced permissions, Large File Storage and Two-Factor authentication settings."
@@ -701,6 +791,9 @@ msgstr "Додаткові дозволи, Ñховище великих файÐ
msgid "Advanced settings"
msgstr "Додаткові параметри"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "ПопередженнÑ"
@@ -709,11 +802,14 @@ msgstr[2] "Попереджень"
msgstr[3] "Попереджень"
msgid "Alerts"
-msgstr ""
+msgstr "ПопередженнÑ"
msgid "All"
msgstr "Ð’ÑÑ–"
+msgid "All Members"
+msgstr "Ð’ÑÑ– учаÑники"
+
msgid "All changes are committed"
msgstr "Ð’ÑÑ– зміни закомічені"
@@ -721,7 +817,7 @@ msgid "All features are enabled for blank projects, from templates, or when impo
msgstr "Ð’ÑÑ– функції Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… проектів берутьÑÑ Ñ–Ð· шаблонів або під Ñ‡Ð°Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ, але ви можете вимикати Ñ—Ñ… пізніше в налаштуваннÑÑ… проекту."
msgid "All issues for this milestone are closed. You may close this milestone now."
-msgstr ""
+msgstr "Ð’ÑÑ– задачі Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ етапу закриті. Ви можете закрити цей етап."
msgid "All users"
msgstr "Ð’ÑÑ– кориÑтувачі"
@@ -744,6 +840,9 @@ msgstr "Дозволити Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ñ–Ð°Ð³Ñ€Ð°Ð¼ PlantUML в Ð
msgid "Allow requests to the local network from hooks and services."
msgstr "Дозволити запити до локальної мережі із гуків та ÑервіÑів."
+msgid "Allow this key to push to repository as well? (Default only allows pull access.)"
+msgstr ""
+
msgid "Allow users to request access"
msgstr "Дозволити кориÑтувачам запитувати доÑтуп"
@@ -783,9 +882,6 @@ msgstr "Порожнє поле Gitlab-кориÑтувача буде запоÐ
msgid "An error has occurred"
msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
-msgid "An error occured while fetching the releases. Please try again."
-msgstr ""
-
msgid "An error occurred adding a draft to the discussion."
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñ‡ÐµÑ€Ð½ÐµÑ‚ÐºÐ¸ до обговореннÑ."
@@ -793,6 +889,15 @@ msgid "An error occurred adding a new draft."
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð½Ð¾Ð²Ð¾Ñ— чернетки."
msgid "An error occurred creating the new branch."
+msgstr "Помилка при Ñтворенні нової гілки."
+
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr "Помилка при отриманні затверджуючих оÑіб Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ правила."
+
+msgid "An error occurred fetching the dropdown data."
msgstr ""
msgid "An error occurred previewing the blob"
@@ -805,7 +910,10 @@ msgid "An error occurred when updating the issue weight"
msgstr "Збій під Ñ‡Ð°Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ð°Ð³Ð¸ задачі"
msgid "An error occurred while adding approver"
-msgstr "Помилка при додаванні учаÑника Ð´Ð»Ñ Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ"
+msgstr "Помилка при додаванні затверджуючої оÑоби"
+
+msgid "An error occurred while deleting the approvers group"
+msgstr "Помилка при видаленні групи затверджуючих оÑіб"
msgid "An error occurred while deleting the comment"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ"
@@ -843,6 +951,9 @@ msgstr "Помилка при отриманні завдань."
msgid "An error occurred while fetching the pipeline."
msgstr "Помилка при отриманні данних конвеєра."
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні проектів"
@@ -858,6 +969,9 @@ msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð
msgid "An error occurred while loading commit signatures"
msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при завантаженні Ñигнатур коміту"
+msgid "An error occurred while loading designs. Please try again."
+msgstr ""
+
msgid "An error occurred while loading diff"
msgstr "Помилка при завантаженні разниці (diff)"
@@ -868,19 +982,19 @@ msgid "An error occurred while loading the file"
msgstr "Помилка при завантаженні файлу"
msgid "An error occurred while loading the subscription details."
-msgstr ""
+msgstr "Помилка при завантаженні даних підпиÑки."
msgid "An error occurred while making the request."
msgstr "Помилка при Ñтворенні запиту."
msgid "An error occurred while removing approver"
-msgstr "Помилка при видаленні учаÑника Ð´Ð»Ñ Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ"
+msgstr "Помилка при видаленні затверджуючої оÑоби"
msgid "An error occurred while removing epics."
-msgstr ""
+msgstr "Помилка при видаленні епіків."
msgid "An error occurred while removing issues."
-msgstr ""
+msgstr "Помилка при видаленні задач."
msgid "An error occurred while rendering KaTeX"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при рендерингу KaTeX"
@@ -900,12 +1014,18 @@ msgstr "Помилка при збереженні ÑтатуÑу перевиз
msgid "An error occurred while saving assignees"
msgstr "Помилка при збереженні виконавців"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr "Помилка при підпиÑці на ÑповіщеннÑ."
msgid "An error occurred while unsubscribing to notifications."
msgstr "Помилка при відпиÑці від Ñповіщень."
+msgid "An error occurred while updating approvers"
+msgstr "Помилка при оновленні затверджуючих оÑіб"
+
msgid "An error occurred while updating the comment"
msgstr "Під Ñ‡Ð°Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
@@ -913,56 +1033,59 @@ msgid "An error occurred while validating username"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€ÐºÐ¸ імені кориÑтувача"
msgid "An error occurred whilst committing your changes."
-msgstr ""
+msgstr "Помилка при коміті ваших змін."
msgid "An error occurred whilst fetching the job trace."
-msgstr ""
+msgstr "Помилка при отриманні логу завданнÑ."
msgid "An error occurred whilst fetching the latest pipeline."
-msgstr ""
+msgstr "Помилка при отриманні оÑтаннього конвеєра."
msgid "An error occurred whilst loading all the files."
-msgstr ""
+msgstr "Помилка при завантаженні вÑÑ–Ñ… файлів."
msgid "An error occurred whilst loading the file content."
-msgstr ""
+msgstr "Помилка при завантаженні вміÑту файлу."
msgid "An error occurred whilst loading the file."
-msgstr ""
+msgstr "Помилка при завантаженні файлу."
msgid "An error occurred whilst loading the merge request changes."
-msgstr ""
+msgstr "Помилка при завантаженні змін запиту на злиттÑ."
msgid "An error occurred whilst loading the merge request version data."
-msgstr ""
+msgstr "Помилка при завантаженні даних верÑÑ–Ñ— запиту на злиттÑ."
msgid "An error occurred whilst loading the merge request."
-msgstr ""
+msgstr "Помилка при завантаженні запиту на злиттÑ."
msgid "An error occurred whilst loading the pipelines jobs."
-msgstr ""
+msgstr "Помилка при завантаженні завдань конвеєру."
msgid "An error occurred. Please try again."
msgstr "СталаÑÑŒ помилка. Спробуйте ще раз."
msgid "An unexpected error occurred while checking the project environment."
-msgstr ""
+msgstr "Ðеочікувана помилка при перевірці Ñередовища проекту."
msgid "An unexpected error occurred while checking the project runners."
-msgstr ""
+msgstr "Ðеочікувана помилка при перевірці runner'ів проекту."
msgid "An unexpected error occurred while communicating with the Web Terminal."
-msgstr ""
+msgstr "Ðеочікувана помилка з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ–Ð· Веб-терміналом."
msgid "An unexpected error occurred while starting the Web Terminal."
-msgstr ""
+msgstr "Ðеочікувана помилка при запуÑку Веб-терміналу."
msgid "An unexpected error occurred while stopping the Web Terminal."
-msgstr ""
+msgstr "Ðеочікувана помилка при зупинці Веб-терміналу."
msgid "Analytics"
msgstr "Ðналітика"
+msgid "Ancestors"
+msgstr ""
+
msgid "Anonymous"
msgstr "Ðнонімно"
@@ -991,19 +1114,65 @@ msgid "Applications"
msgstr "ЗаÑтоÑунки"
msgid "Applied"
-msgstr ""
+msgstr "ЗаÑтоÑовано"
msgid "Apply suggestion"
+msgstr "ЗаÑтоÑувати пропозицію"
+
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] "%d учаÑник"
+msgstr[1] "%d учаÑника"
+msgstr[2] "%d учаÑників"
+msgstr[3] "%d учаÑників"
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr "Ви збираєтеÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ групу затверджуючих оÑіб %{name} Ñка налічує %{nMembers} учаÑників."
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] "%d учаÑник"
+msgstr[1] "%d учаÑника"
+msgstr[2] "%d учаÑників"
+msgstr[3] "%d учаÑників"
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
msgstr ""
-msgid "Approvals"
+msgid "ApprovalRule|Members"
+msgstr "УчаÑники"
+
+msgid "ApprovalRule|Name"
+msgstr "Ім'Ñ"
+
+msgid "ApprovalRule|No. approvals required"
msgstr ""
-msgid "Approvals required"
+msgid "ApprovalRule|e.g. QA, Security, etc."
msgstr ""
+msgid "Approvals"
+msgstr "ЗатвердженнÑ"
+
+msgid "Approvals required"
+msgstr "Потрібні затвердженнÑ"
+
msgid "Approvers"
-msgstr ""
+msgstr "Затверджуючі оÑоби"
msgid "Apr"
msgstr "квіт."
@@ -1018,7 +1187,7 @@ msgid "Archived projects"
msgstr "Заархівовані проекти"
msgid "Are you sure"
-msgstr ""
+msgstr "Ви впевнені"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "Ви впевнені, що хочете видалити цей розклад Ð´Ð»Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ð°?"
@@ -1030,7 +1199,7 @@ msgid "Are you sure you want to lose unsaved changes?"
msgstr "Ви впевнені, що бажаєте втратити незбережені зміни?"
msgid "Are you sure you want to lose your issue information?"
-msgstr ""
+msgstr "Ви впевнені, що хочете втратити інформацію про задачу?"
msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
msgstr "Ви впевнені, що хочете повторно згенерувати відкритий ключ? Вам доведетьÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ відкритий ключ на віддаленому Ñервері, перш ніж Ð´Ð·ÐµÑ€ÐºÐ°Ð»ÑŽÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ñ€Ð°Ñ†ÑŽÑ” знову."
@@ -1038,14 +1207,20 @@ msgstr "Ви впевнені, що хочете повторно згенеру
msgid "Are you sure you want to remove %{group_name}?"
msgstr "Ви впевнені, що хочете видалити %{group_name}?"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr "Ви впевнені, що хочете видалити затверджуючу оÑобу %{name}"
+
msgid "Are you sure you want to remove approver %{name}?"
-msgstr ""
+msgstr "Ви впевнені, що хочете видалити затверджуючу оÑобу %{name}?"
+
+msgid "Are you sure you want to remove group %{name}"
+msgstr "Ви впевнені, що хочете видалити групу %{name}"
msgid "Are you sure you want to remove group %{name}?"
-msgstr ""
+msgstr "Ви впевнені, що хочете видалити групу %{name}?"
msgid "Are you sure you want to remove the attachment?"
-msgstr ""
+msgstr "Ви впевнені, що бажаєте видалити вкладеннÑ?"
msgid "Are you sure you want to remove this identity?"
msgstr "Ви впевнені, що хочете видалити цю ідентифікацію?"
@@ -1063,7 +1238,7 @@ msgid "Are you sure you want to unlock %{path_lock_path}?"
msgstr "Ви впевнені, що хочете розблокувати %{path_lock_path}?"
msgid "Are you sure you want to unsubscribe from the %{type}: %{link_to_noteable_text}?"
-msgstr ""
+msgstr "Ви дійÑно бажаєте ÑкаÑувати підпиÑку на %{type}: %{link_to_noteable_text}?"
msgid "Are you sure?"
msgstr "Ви впевнені?"
@@ -1096,7 +1271,7 @@ msgid "Assign milestone"
msgstr "Призначити етап"
msgid "Assign some issues to this milestone."
-msgstr ""
+msgstr "Призначити деÑкі задачі до цього етапу."
msgid "Assign to"
msgstr "Призначити"
@@ -1125,9 +1300,12 @@ msgstr "СпиÑки виконавців показують уÑÑ– задачі
msgid "Assignee(s)"
msgstr "Виконавець(ці)"
-msgid "Attach a file"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
msgstr ""
+msgid "Attach a file"
+msgstr "Прикріпити файл"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "Прикріпити файл за допомогою перетÑÐ³ÑƒÐ²Ð°Ð½Ð½Ñ Ð°Ð±Ð¾ %{upload_link}"
@@ -1140,9 +1318,6 @@ msgstr "Ñерп."
msgid "August"
msgstr "Ñерпень"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "Журнал автентифікації"
@@ -1159,7 +1334,7 @@ msgid "Authorization code:"
msgstr "Код авторизації:"
msgid "Authorization key"
-msgstr ""
+msgstr "Ключ авторизації"
msgid "Authorization was granted by entering your username and password in the application."
msgstr "ÐÐ²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ Ð²Ñ–Ð´Ð±ÑƒÐ»Ð°ÑÑ Ð¿Ñ–ÑÐ»Ñ Ð²Ð²Ð¾Ð´Ñƒ вашого імені та паролю у заÑтоÑунку."
@@ -1222,25 +1397,25 @@ msgid "Automatically marked as default internal user"
msgstr "Ðвтоматично позначено Ñк внутрішній кориÑтувач за замовчуваннÑм"
msgid "Automatically resolved"
-msgstr ""
+msgstr "Вирішено автоматично"
msgid "Available"
msgstr "ДоÑтупно"
msgid "Available group Runners: %{runners}"
-msgstr ""
+msgstr "ДоÑтупні групові Runner'и: %{runners}"
msgid "Available shared Runners:"
-msgstr ""
+msgstr "ДоÑтупні загальні Runner'и:"
msgid "Available specific runners"
-msgstr ""
+msgstr "ДоÑтупні Ñпеціальні Runner’и"
msgid "Avatar for %{assigneeName}"
-msgstr ""
+msgstr "Ðватар Ð´Ð»Ñ %{assigneeName}"
msgid "Avatar for %{name}"
-msgstr ""
+msgstr "Ðватар Ð´Ð»Ñ %{name}"
msgid "Avatar will be removed. Are you sure?"
msgstr "Ðватар буде видалено. Ви впевнені?"
@@ -1426,10 +1601,10 @@ msgid "Bitbucket import"
msgstr "Імпорт з Bitbucket"
msgid "Block"
-msgstr ""
+msgstr "Блок"
msgid "Blocked"
-msgstr ""
+msgstr "Заблокований"
msgid "Blog"
msgstr "Блог"
@@ -1594,34 +1769,34 @@ msgid "Browse files"
msgstr "ПереглÑд файлів"
msgid "Built-in"
-msgstr ""
+msgstr "Вбудований"
msgid "Business"
-msgstr ""
+msgstr "БізнеÑ"
msgid "Business metrics (Custom)"
msgstr "Ð‘Ñ–Ð·Ð½ÐµÑ Ð¼ÐµÑ‚Ñ€Ð¸ÐºÐ¸ (ВлаÑні)"
msgid "By %{user_name}"
-msgstr ""
+msgstr "Від %{user_name}"
msgid "ByAuthor|by"
msgstr "від"
msgid "CHANGELOG"
-msgstr ""
+msgstr "СпиÑок змін (CHANGELOG)"
msgid "CI / CD"
msgstr "CI / CD"
msgid "CI / CD Charts"
-msgstr ""
+msgstr "СтатиÑтика CI / CD"
msgid "CI / CD Settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ CI/CD"
msgid "CI Lint"
-msgstr ""
+msgstr "Перевірка CI конфігурації"
msgid "CI will run using the credentials assigned above."
msgstr "CI буде працювати з викориÑтаннÑм облікових даних, визначених вище."
@@ -1669,6 +1844,9 @@ msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration
msgstr "Конвеєр Auto DevOps буде запущено, Ñкщо не буде знайдено альтернативного файлу конфігуріції CI."
msgid "CICD|You must add a %{kubernetes_cluster_start}Kubernetes cluster integration%{kubernetes_cluster_end} to this project with a domain in order for your deployment strategy to work correctly."
+msgstr "Ви повинні додати %{kubernetes_cluster_start}клаÑтерну інтеграцію Kubernetes%{kubernetes_cluster_end} до цього проекту з доменом Ð´Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ñ— роботи Ñтратегії розгортаннÑ."
+
+msgid "CICD|group enabled"
msgstr ""
msgid "CICD|instance enabled"
@@ -1681,14 +1859,17 @@ msgid "Callback URL"
msgstr "URL зворотнього виклику"
msgid "Can override approvers and approvals required per merge request"
-msgstr ""
+msgstr "Можна змінювати необхідні Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ñ‚Ð° затверджуючих оÑіб Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на злиттÑ"
msgid "Can't find HEAD commit for this branch"
msgstr "Ðе можу знайти HEAD-коміт Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— гілки"
-msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
+msgid "Can't remove group members without group managed account"
msgstr ""
+msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
+msgstr "Canary Deployments — це популÑрна ÑÑ‚Ñ€Ð°Ñ‚ÐµÐ³Ñ–Ñ Ð±ÐµÐ·Ð¿ÐµÑ€ÐµÑ€Ð²Ð½Ð¾Ñ— інтеграції, коли нова верÑÑ–Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ заÑтоÑунку вÑтановлюєтьÑÑ Ð½Ð° невелику кількіÑÑ‚ÑŒ машин."
+
msgid "Cancel"
msgstr "СкаÑувати"
@@ -1701,11 +1882,14 @@ msgstr "Ðеможливо злити автоматично"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "Ðеможливо змінити керований клаÑтер Kubernetes"
-msgid "Certificate"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
msgstr ""
+msgid "Certificate"
+msgstr "Сертифікат"
+
msgid "Certificate (PEM)"
-msgstr ""
+msgstr "Сертифікат (PEM)"
msgid "Certificate fingerprint"
msgstr "Відбиток Ñертифіката"
@@ -1714,7 +1898,7 @@ msgid "Change Weight"
msgstr "Змінити вагу"
msgid "Change permissions"
-msgstr ""
+msgstr "Змінити права доÑтупу"
msgid "Change template"
msgstr "Змінити шаблон"
@@ -1737,6 +1921,9 @@ msgstr "Ðнулювати коміт"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "Це Ñтворить новий коміт, щоб анулювати Ñ–Ñнуючі зміни."
+msgid "Changes"
+msgstr "Зміни"
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "Зміни відображаютьÑÑ Ñ‚Ð°Ðº, ніби <b>редакціÑ-джерело</b> була злита в <b>цільову редакцію</b>."
@@ -1749,17 +1936,20 @@ msgstr "СтатиÑтика"
msgid "Chat"
msgstr "Чат"
+msgid "Check again"
+msgstr "Перевірити знову"
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "ПереглÑньте %{docs_link_start}документацію%{docs_link_end}."
msgid "Check your .gitlab-ci.yml"
-msgstr ""
+msgstr "Перевірте Ñвій .gitlab-ci.yml"
msgid "Checking %{text} availability…"
msgstr "Перевірка доÑтупноÑÑ‚Ñ– %{text}…"
msgid "Checking approval status"
-msgstr ""
+msgstr "Перевірка ÑтатуÑу затвердженнÑ"
msgid "Checking branch availability..."
msgstr "Перевірка доÑтупноÑÑ‚Ñ– гілки..."
@@ -1783,7 +1973,7 @@ msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to s
msgstr "Виберіть гілку чи тег (напр. %{master}) або введіть коміт (напр. %{sha}) Ð´Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду змін або Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ."
msgid "Choose a file"
-msgstr ""
+msgstr "Виберіть файл"
msgid "Choose a role permission"
msgstr ""
@@ -1806,21 +1996,21 @@ msgstr "Виберіть файл..."
msgid "Choose the top-level group for your repository imports."
msgstr "Оберіть групу найвищого Ñ€Ñ–Ð²Ð½Ñ Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ репозиторіїв."
-msgid "Choose what content you want to see on a group’s overview page"
+msgid "Choose visibility level, enable/disable project features (issues, repository, wiki, snippets) and set permissions."
msgstr ""
-msgid "Choose which groups you wish to synchronize to this secondary node."
-msgstr "Виберіть групи Ð´Ð»Ñ Ñинхронізації на цей вторинний вузол."
+msgid "Choose what content you want to see on a group’s overview page"
+msgstr "Вибрати вміÑÑ‚ оглÑдової Ñторінки групи"
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr "Виберіть, Ñкі репозиторії ви хочете підключити Ñ– запуÑтити конвеєри CI/CD."
-msgid "Choose which repositories you want to import."
-msgstr "Виберіть, Ñкі репозиторії ви хочете імпортувати."
-
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr "Виберіть Ñегменти Ð´Ð»Ñ Ñинхронізації на цей вторинний вузол."
+msgid "Choose your merge method, set up a default merge request description template."
+msgstr ""
+
msgid "CiStatusLabel|canceled"
msgstr "ÑкаÑовано"
@@ -1903,7 +2093,7 @@ msgid "CiVariable|Create wildcard"
msgstr "Створити шаблон"
msgid "CiVariable|Error occurred while saving variables"
-msgstr ""
+msgstr "Помилка при збереженні змінних"
msgid "CiVariable|New environment"
msgstr "Ðове Ñередовище"
@@ -1924,10 +2114,10 @@ msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "не доÑтупно: %{reason}"
msgid "Clear"
-msgstr ""
+msgstr "ОчиÑтити"
msgid "Clear input"
-msgstr ""
+msgstr "ОчиÑтити ввід"
msgid "Clear search"
msgstr "ОчиÑтити пошук"
@@ -1963,22 +2153,25 @@ msgid "Client authentication key"
msgstr "Ключ автентифікації клієнта"
msgid "Client authentication key password"
-msgstr "Пароль ключа аутентифікації клієнта"
+msgstr "Пароль ключа автентифікації клієнта"
msgid "Clients"
msgstr "Клієнти"
msgid "Clone"
-msgstr ""
+msgstr "Клонувати"
msgid "Clone repository"
msgstr "Клонувати репозиторій"
msgid "Clone with %{http_label}"
+msgstr "Клонувати з %{http_label}"
+
+msgid "Clone with KRB5"
msgstr ""
msgid "Clone with SSH"
-msgstr ""
+msgstr "Клонувати з SSH"
msgid "Close"
msgstr "Закрити"
@@ -1987,22 +2180,19 @@ msgid "Close epic"
msgstr "Закрити епік"
msgid "Close milestone"
-msgstr ""
+msgstr "Закрити етап"
msgid "Closed"
msgstr "Закрито"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "Закриті задачі"
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
-msgstr ""
+msgstr "%{custom_domain_start}Детальніше%{custom_domain_end}."
msgid "ClusterIntegration| can be used instead of a custom domain."
-msgstr ""
+msgstr "може викориÑтовуватиÑÑ Ð·Ð°Ð¼Ñ–ÑÑ‚ÑŒ влаÑного домену."
msgid "ClusterIntegration| is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster. %{environment_scope_start}More information%{environment_scope_end}"
msgstr ""
@@ -2010,9 +2200,6 @@ msgstr ""
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} були уÑпішно вÑтановлені на ваш Kubernetes-клаÑтер"
-msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like a load balancer, which may incur additional costs depending on the hosting provider your Kubernetes cluster is installed on. If you are using Google Kubernetes Engine, you can %{pricingLink}."
-msgstr "%{boldNotice} Це додаÑÑ‚ÑŒ додаткові реÑурÑи, такі Ñк баланÑувальник навантаженнÑ, Ñкий може збільшити витрати в залежноÑÑ‚Ñ– від провайдера хоÑтингу, на Ñкому вÑтановлено клаÑтер Kubernetes. Якщо ви викориÑтовуєте Google Kubernetes Engine, ви можете %{pricingLink}."
-
msgid "ClusterIntegration|%{title} upgraded successfully."
msgstr ""
@@ -2034,20 +2221,17 @@ msgstr "Ð”Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— до вашої групи наÐ
msgid "ClusterIntegration|Advanced options on this Kubernetes cluster's integration"
msgstr "Детальні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— із цим Kubernetes-клаÑтером"
-msgid "ClusterIntegration|After installing Ingress, you will need to point your wildcard DNS at the generated external IP address in order to view your app after it is deployed. %{ingressHelpLink}"
-msgstr "ПіÑÐ»Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ingress, вам необхідно направити Ñвій DNS на згенеровану зовнішню IP-адреÑу, щоб переглÑнути ваш заÑтоÑунок піÑÐ»Ñ Ð¹Ð¾Ð³Ð¾ розгортаннÑ. %{ingressHelpLink}"
-
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "Помилка при отриманні зон проекту: %{error}"
-
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "Помилка під Ñ‡Ð°Ñ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· Google Cloud API. Будь лаÑка, Ñпробуйте знову пізніше."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr "Помилка при отриманні зон проекту: %{error}"
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
-msgstr ""
+msgstr "Помилка при отриманні ваших проектів: %{error}"
msgid "ClusterIntegration|An error occurred while trying to fetch zone machine types: %{error}"
msgstr ""
@@ -2062,13 +2246,13 @@ msgid "ClusterIntegration|Are you sure you want to remove this Kubernetes cluste
msgstr "Ви впевнені, що хочете видалити інтеграцію із цим Kubernetes-клаÑтером? Це не призведе до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñамого клаÑтера."
msgid "ClusterIntegration|Base domain"
-msgstr ""
+msgstr "ОÑновний домен"
msgid "ClusterIntegration|CA Certificate"
msgstr "Сертифікат центру Ñертифікації"
msgid "ClusterIntegration|Cert-Manager"
-msgstr ""
+msgstr "Cert-Manager"
msgid "ClusterIntegration|Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert-Manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates are valid and up-to-date."
msgstr ""
@@ -2082,6 +2266,9 @@ msgstr "Виберіть, Ñкі заÑтоÑунки необхідно вÑÑ‚Ð
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "Виберіть, Ñке із ваших Ñередовищ буде викориÑтовувати цей клаÑтер."
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2091,13 +2278,13 @@ msgstr "Скопіювати API URL"
msgid "ClusterIntegration|Copy CA Certificate"
msgstr "Скопіювати Ñертифікат центру Ñертифікації"
-msgid "ClusterIntegration|Copy Ingress IP Address to clipboard"
-msgstr "Копіювати IP-адреÑу Ingress в буфер обміну"
+msgid "ClusterIntegration|Copy Ingress Endpoint to clipboard"
+msgstr ""
msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
msgstr "Скопіювати Ñ–Ð¼â€™Ñ Ñ…Ð¾Ñта Jupyter в буфер обміну"
-msgid "ClusterIntegration|Copy Knative IP Address to clipboard"
+msgid "ClusterIntegration|Copy Knative Endpoint to clipboard"
msgstr ""
msgid "ClusterIntegration|Copy Kubernetes cluster name"
@@ -2178,14 +2365,14 @@ 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|In order to view the health of your cluster, you must first install Prometheus below."
+msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
-msgid "ClusterIntegration|Ingress IP Address"
-msgstr "Ingress IP-адреÑа"
+msgid "ClusterIntegration|Ingress Endpoint"
+msgstr ""
msgid "ClusterIntegration|Ingress gives you a way to route requests to services based on the request host or path, centralizing a number of services into a single entrypoint."
msgstr "Ingress дозволÑÑ” вам маршрутизувати запити до Ñлужб на оÑнові запитаного хоÑта або шлÑху, об'єднуючи Ñ€Ñд ÑервіÑів в одну точку входу."
@@ -2193,15 +2380,18 @@ msgstr "Ingress дозволÑÑ” вам маршрутизувати запитÐ
msgid "ClusterIntegration|Install"
msgstr "Ð’Ñтановити"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr "Ð’Ñтановити Prometheus"
-
msgid "ClusterIntegration|Installed"
msgstr "Ð’Ñтановлений"
msgid "ClusterIntegration|Installing"
msgstr "Ð’ÑтановленнÑ"
+msgid "ClusterIntegration|Installing Ingress may incur additional costs. Learn more about %{pricingLink}."
+msgstr ""
+
+msgid "ClusterIntegration|Installing Knative may incur additional costs. Learn more about %{pricingLink}."
+msgstr ""
+
msgid "ClusterIntegration|Integrate Kubernetes cluster automation"
msgstr "Ð†Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ ÐºÐ»Ð°Ñтерної автоматизації Kubernetes"
@@ -2209,7 +2399,7 @@ msgid "ClusterIntegration|Integration status"
msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ—"
msgid "ClusterIntegration|Issuer Email"
-msgstr ""
+msgstr "Електронна пошта емітента"
msgid "ClusterIntegration|Issuers represent a certificate authority. You must provide an email address for your Issuer. "
msgstr ""
@@ -2229,7 +2419,7 @@ msgstr "Knative"
msgid "ClusterIntegration|Knative Domain Name:"
msgstr "Доменне ім'Ñ Knative:"
-msgid "ClusterIntegration|Knative IP Address:"
+msgid "ClusterIntegration|Knative Endpoint:"
msgstr ""
msgid "ClusterIntegration|Knative extends Kubernetes to provide a set of middleware components that are essential to build modern, source-centric, and container-based applications that can run anywhere: on premises, in the cloud, or even in a third-party data center."
@@ -2241,9 +2431,6 @@ msgstr "Kubernetes-клаÑтер"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Параметри Kubernetes-клаÑтера"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Стан Kubernetes-клаÑтера"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes-клаÑтер ÑтворюєтьÑÑ Ð½Ð° Google Kubernetes Engine..."
@@ -2272,7 +2459,7 @@ msgid "ClusterIntegration|Learn more about group Kubernetes clusters"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про групові клаÑтери Kubernetes"
msgid "ClusterIntegration|Let's Encrypt"
-msgstr ""
+msgstr "Let's Encrypt"
msgid "ClusterIntegration|Machine type"
msgstr "Тип машини"
@@ -2286,9 +2473,6 @@ msgstr "УправліннÑ"
msgid "ClusterIntegration|Manage your Kubernetes cluster by visiting %{link_gke}"
msgstr "Керуйте вашим Kubernetes-клаÑтером за допомогою %{link_gke}"
-msgid "ClusterIntegration|More information"
-msgstr "Додаткова інформаціÑ"
-
msgid "ClusterIntegration|No machine types matched your search"
msgstr "Жоден тип машин не відповідає вашому пошуку"
@@ -2301,9 +2485,6 @@ msgstr "Жоден проект не відповідає вашому пошуÐ
msgid "ClusterIntegration|No zones matched your search"
msgstr "Жодна зона не відповідає вашому пошуку"
-msgid "ClusterIntegration|Note:"
-msgstr "Примітка:"
-
msgid "ClusterIntegration|Number of nodes"
msgstr "КількіÑÑ‚ÑŒ вузлів"
@@ -2313,8 +2494,8 @@ msgstr "Введіть інформацію про доÑтуп до Ñвого
msgid "ClusterIntegration|Please make sure that your Google account meets the following requirements:"
msgstr "Будь-лаÑка впевнітьÑÑ, що ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Google задовольнÑÑ” наÑтупним вимогам:"
-msgid "ClusterIntegration|Point a wildcard DNS to this generated IP address in order to access your application after it has been deployed."
-msgstr "Ðаправте ваш DNS на цю згенеровану IP-адреÑу, щоб отримати доÑтуп до вашого додатку, піÑÐ»Ñ Ð¹Ð¾Ð³Ð¾ розгортаннÑ."
+msgid "ClusterIntegration|Point a wildcard DNS to this generated endpoint in order to access your application after it has been deployed."
+msgstr ""
msgid "ClusterIntegration|Project cluster"
msgstr "КлаÑтер проекту"
@@ -2352,7 +2533,7 @@ msgstr "При бажанні ви можете замінити це на ваÑ
msgid "ClusterIntegration|Request to begin installing failed"
msgstr "Запит про початок вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ виконано"
-msgid "ClusterIntegration|Retry upgrade"
+msgid "ClusterIntegration|Retry update"
msgstr ""
msgid "ClusterIntegration|Save changes"
@@ -2397,9 +2578,6 @@ msgstr "Показати"
msgid "ClusterIntegration|Something went wrong on our end."
msgstr "ЩоÑÑŒ пішло не так з нашого боку."
-msgid "ClusterIntegration|Something went wrong when upgrading %{title}. Please check the logs and try again."
-msgstr ""
-
msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr "Помилка при Ñтворенні вашого Kubernetes-клаÑтера на Google Kubernetes Engine"
@@ -2407,10 +2585,10 @@ msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "Під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ %{title} ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°"
msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain."
-msgstr ""
+msgstr "Ð—Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð´Ð¾Ð¼ÐµÐ½Ñƒ дозволить вам викориÑтовувати фази Auto Review Apps та Auto Deploy Ð´Ð»Ñ %{auto_devops_start}Auto DevOps%{auto_devops_end}. Домен повинен мати шаблон DNS, що задовільнÑÑ” цей домен."
-msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
-msgstr "ВідбуваєтьÑÑ Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ IP-адреÑи. Будь лаÑка, перевірте квоти вашого Kubernetes клаÑтера на Google Kubernetes Engine, Ñкщо це займає багато чаÑу."
+msgid "ClusterIntegration|The endpoint is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
+msgstr ""
msgid "ClusterIntegration|This account must have permissions to create a Kubernetes cluster in the %{link_to_container_project} specified below"
msgstr "Цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ мати наÑтупні права Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Kubernetes-клаÑтера в %{link_to_container_project}"
@@ -2418,12 +2596,21 @@ msgstr "Цей обліковий Ð·Ð°Ð¿Ð¸Ñ Ð¿Ð¾Ð²Ð¸Ð½ÐµÐ½ мати наÑтуÐ
msgid "ClusterIntegration|This option will allow you to install applications on RBAC clusters."
msgstr "Цей параметр дозволить вам вÑтановлювати заÑтоÑунки на клаÑтери RBAC."
+msgid "ClusterIntegration|To access your application after deployment, point a wildcard DNS to the Knative Endpoint."
+msgstr ""
+
msgid "ClusterIntegration|Toggle Kubernetes cluster"
msgstr "Увімкнути/вимкнути Kubernetes-клаÑтер"
msgid "ClusterIntegration|Token"
msgstr "Токен"
+msgid "ClusterIntegration|Update failed. Please check the logs and try again."
+msgstr ""
+
+msgid "ClusterIntegration|Updating"
+msgstr ""
+
msgid "ClusterIntegration|Upgrade"
msgstr ""
@@ -2460,9 +2647,6 @@ msgstr "Зона"
msgid "ClusterIntegration|access to Google Kubernetes Engine"
msgstr "доÑтуп до Google Kubernetes Engine"
-msgid "ClusterIntegration|check the pricing here"
-msgstr "переглÑнути ціни тут"
-
msgid "ClusterIntegration|documentation"
msgstr "документації"
@@ -2472,6 +2656,9 @@ msgstr "Ñторінка допомоги"
msgid "ClusterIntegration|meets the requirements"
msgstr "задовольнÑÑ” вимогам"
+msgid "ClusterIntegration|pricing"
+msgstr ""
+
msgid "ClusterIntegration|properly configured"
msgstr "правильно налаштований"
@@ -2479,31 +2666,43 @@ msgid "ClusterIntegration|sign up"
msgstr "реєÑтрації"
msgid "Code"
+msgstr "Код"
+
+msgid "Code Owners"
+msgstr "ВлаÑники коду"
+
+msgid "Code owner approval is required"
msgstr ""
msgid "Code owners"
msgstr "ВлаÑники коду"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr "Когорти"
msgid "Collapse"
msgstr "Згорнути"
+msgid "Collapse approvers"
+msgstr "Згорнути ÑпиÑок затверджуючих оÑіб"
+
msgid "Collapse sidebar"
msgstr "Згорнути панель"
msgid "Command line instructions"
-msgstr ""
+msgstr "ІнÑтрукції Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð³Ð¾ Ñ€Ñдка"
msgid "Comment"
msgstr "Коментар"
msgid "Comment & close %{noteable_name}"
-msgstr ""
+msgstr "Коментувати та закрити %{noteable_name}"
msgid "Comment & reopen %{noteable_name}"
-msgstr ""
+msgstr "Коментувати та повторно відкрити %{noteable_name}"
msgid "Comment & resolve discussion"
msgstr "Залишити коментар Ñ– завершити обговореннÑ"
@@ -2525,19 +2724,19 @@ msgstr[2] "Комітів"
msgstr[3] "Комітів"
msgid "Commit %{commit_id}"
-msgstr ""
+msgstr "Коміт %{commit_id}"
msgid "Commit Message"
-msgstr "Коміт-повідомелннÑ"
+msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ"
msgid "Commit deleted"
-msgstr ""
+msgstr "Коміт видалено"
msgid "Commit duration in minutes for last 30 commits"
msgstr "ТриваліÑÑ‚ÑŒ оÑтанніх 30 комітів у хвилинах"
msgid "Commit message"
-msgstr "Коміт-повідомленнÑ"
+msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ"
msgid "Commit statistics for %{ref} %{start_time} - %{end_time}"
msgstr "СтатиÑтика комітів Ð´Ð»Ñ %{ref} %{start_time} - %{end_time}"
@@ -2597,7 +2796,7 @@ msgid "Compare Revisions"
msgstr "ПорівнÑÐ½Ð½Ñ Ñ€ÐµÐ´Ð°ÐºÑ†Ñ–Ð¹"
msgid "Compare changes"
-msgstr ""
+msgstr "ПорівнÑти зміни"
msgid "Compare changes with the last commit"
msgstr "ПорівнÑти зміни з оÑтаннім комітом"
@@ -2627,7 +2826,7 @@ msgid "Confidentiality"
msgstr "КонфіденційніÑÑ‚ÑŒ"
msgid "Configure GitLab runners to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
-msgstr ""
+msgstr "Ðалаштувати runner'ів GitLab Ð´Ð»Ñ Ð¿Ð¾Ñ‡Ð°Ñ‚ÐºÑƒ викориÑÑ‚Ð°Ð½Ð½Ñ Ð’ÐµÐ±-терміналу. %{helpStart}Докладніше.%{helpEnd}"
msgid "Configure Gitaly timeouts."
msgstr "Ðалаштувати таймаути Gitaly."
@@ -2636,7 +2835,7 @@ msgid "Configure Tracing"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð’Ñ–Ð´ÑтеженнÑ"
msgid "Configure a <code>.gitlab-webide.yml</code> file in the <code>.gitlab</code> directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
-msgstr ""
+msgstr "Ðалаштуйте файл <code>.gitlab-webide.yml</code> у директорії <code>.gitlab</code>, щоб почати викориÑтовувати Веб-термінал. %{helpStart}Докладніше.%{helpEnd}"
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr "Ðалаштувати автоматичні перевірки git Ñ– Ð¾Ñ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð² репозиторіÑÑ…."
@@ -2672,7 +2871,7 @@ msgid "Connecting..."
msgstr "З'єднаннÑ..."
msgid "Contact sales to upgrade"
-msgstr ""
+msgstr "ЗвернітьÑÑ Ð´Ð¾ відділу продажів Ð´Ð»Ñ Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ"
msgid "Container Registry"
msgstr "РеєÑÑ‚Ñ€ Контейнерів"
@@ -2723,7 +2922,7 @@ msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access
msgstr "Ви також можете викориÑтовувати %{deploy_token} Ð´Ð»Ñ Ð´Ð¾Ñтупу тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ð´Ð¾ образів у реєÑтрі."
msgid "Contents of .gitlab-ci.yml"
-msgstr ""
+msgstr "ВміÑÑ‚ .gitlab-ci.yml"
msgid "Continue"
msgstr "Продовжити"
@@ -2741,7 +2940,7 @@ msgid "Contribution"
msgstr "ВнеÑок"
msgid "Contribution Charts"
-msgstr ""
+msgstr "СтатиÑтика внеÑків"
msgid "Contributions for <strong>%{calendar_date}</strong>"
msgstr "ВнеÑки за <strong>%{calendar_date}</strong>"
@@ -2767,23 +2966,14 @@ 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 "Control the minimum interval in days that a repository should be reverified for this primary node"
-msgstr ""
-
msgid "ConvDev Index"
msgstr "Ð†Ð½Ð´ÐµÐºÑ ConvDev"
msgid "Copy %{http_label} clone URL"
-msgstr ""
+msgstr "Скопіювати URL Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· %{http_label}"
msgid "Copy %{protocol} clone URL"
msgstr "Скопіювати URL Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· %{protocol}"
@@ -2791,11 +2981,14 @@ msgstr "Скопіювати URL Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· %{protoc
msgid "Copy ID to clipboard"
msgstr "Скопіювати ID в буфер обміну"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr "Скопіювати URL Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‡ÐµÑ€ÐµÐ· SSH"
msgid "Copy SSH public key"
-msgstr ""
+msgstr "Скопіювати публічний ключ SSH"
msgid "Copy SSH public key to clipboard"
msgstr "Скопіюйте відкритий SSH-ключ в буфер обміну"
@@ -2843,7 +3036,7 @@ msgid "Create New Directory"
msgstr "Створити новий каталог"
msgid "Create New Domain"
-msgstr ""
+msgstr "Створити новий домен"
msgid "Create a new branch"
msgstr "Створити нову гілку"
@@ -2855,7 +3048,7 @@ msgid "Create a new issue"
msgstr "Створити нову задачу"
msgid "Create a new repository"
-msgstr ""
+msgstr "Створити новий репозиторій"
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "Створіть токен доÑтупу Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ аккаунта, щоб відправлÑти та отримувати через %{protocol}."
@@ -2884,9 +3077,6 @@ msgstr "Створити групу"
msgid "Create group label"
msgstr "Створити мітку групи"
-msgid "Create issue"
-msgstr "Створити задачу"
-
msgid "Create lists from labels. Issues with that label appear in that list."
msgstr "Створити ÑпиÑок на оÑнові міток. Ð’ ньому будуть задачі з такими мітками."
@@ -2897,7 +3087,7 @@ msgid "Create merge request and branch"
msgstr "Створити запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ‚Ð° гілку"
msgid "Create milestone"
-msgstr ""
+msgstr "Створити етап"
msgid "Create new branch"
msgstr "Створити нову гілку"
@@ -2956,6 +3146,9 @@ msgstr "СинтакÑÐ¸Ñ Cron"
msgid "Current Branch"
msgstr "Поточна гілка"
+msgid "Current Project"
+msgstr "Поточний проект"
+
msgid "Current node"
msgstr "Поточний вузол"
@@ -2981,7 +3174,7 @@ msgid "Custom project templates"
msgstr "ВлаÑні шаблони проектів"
msgid "Custom project templates have not been set up for groups that you are a member of. They are enabled from a group’s settings page. Contact your group’s Owner or Maintainer to setup custom project templates."
-msgstr ""
+msgstr "ВлаÑні шаблони проектів не налаштовані Ð´Ð»Ñ Ð³Ñ€ÑƒÐ¿, до Ñких ви входите. Вони активуютьÑÑ Ð½Ð° Ñторінці налаштувань групи. ЗвернітьÑÑ Ð´Ð¾ влаÑника або керівника вашої групи Ð´Ð»Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð»Ð°Ñних шаблонів проектів."
msgid "Customize colors"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð»ÑŒÐ¾Ñ€Ñ–Ð²"
@@ -2993,9 +3186,9 @@ msgid "Customize how Google Code email addresses and usernames are imported into
msgstr "Ðалаштуйте, Ñк адреÑи електронної пошти та імена кориÑтувачів Google Code імпортуютьÑÑ Ð² GitLab. Ðа наÑтупному кроці ви зможете вибрати проекти, Ñкі потрібно імпортувати."
msgid "Customize language and region related settings."
-msgstr ""
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ð¾Ð²Ð¸ Ñ– параметрів, пов'Ñзаних із регіоном."
-msgid "Customize your merge request approval settings."
+msgid "Customize your issue restrictions."
msgstr ""
msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
@@ -3029,7 +3222,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "ТеÑтуваннÑ"
msgid "DNS"
-msgstr ""
+msgstr "DNS"
msgid "Dashboard"
msgstr "Панель керуваннÑ"
@@ -3041,7 +3234,7 @@ msgid "DashboardProjects|Personal"
msgstr "ОÑобиÑÑ‚Ñ–"
msgid "Data is still calculating..."
-msgstr ""
+msgstr "Дані вÑе ще обчиÑлюютьÑÑ..."
msgid "Date picker"
msgstr "Вибір дати"
@@ -3056,7 +3249,7 @@ msgid "December"
msgstr "грудень"
msgid "Decline"
-msgstr ""
+msgstr "Відхилити"
msgid "Decline and sign out"
msgstr "Відхити та вийти"
@@ -3068,10 +3261,10 @@ msgid "Default classification label"
msgstr "Мітка клаÑифікації за замовчуваннÑм"
msgid "Default first day of the week"
-msgstr ""
+msgstr "Перший день Ñ‚Ð¸Ð¶Ð½Ñ Ð·Ð° замовчуваннÑм"
msgid "Default first day of the week in calendars and date pickers."
-msgstr ""
+msgstr "Перший день Ñ‚Ð¸Ð¶Ð½Ñ Ð·Ð° замовчуваннÑм в календарÑÑ… та при виборі дати."
msgid "Default: Directly import the Google Code email address or username"
msgstr "За замовчуваннÑм: безпоÑередньо імпортувати адреÑу електронної пошти або ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача Google Code"
@@ -3083,7 +3276,7 @@ msgid "Define a custom pattern with cron syntax"
msgstr "Визначте влаÑний шаблон за допомогою ÑинтакÑиÑу cron"
msgid "Define environments in the deploy stage(s) in <code>.gitlab-ci.yml</code> to track deployments here."
-msgstr ""
+msgstr "Визначте Ñередовища на Ñтадії Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñƒ <code>.gitlab-ci.yml</code> щоб відÑтежувати Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ñ‚ÑƒÑ‚."
msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
msgstr "Ви впевнені, що ви хочете запуÑтити %{jobName} відразу? Ð’ іншому випадку це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ виконано автоматично по завершенню таймера."
@@ -3116,10 +3309,10 @@ msgid "Delete list"
msgstr "Видалити ÑпиÑок"
msgid "Delete source branch"
-msgstr ""
+msgstr "Видалити гілку-джерело"
msgid "Delete this attachment"
-msgstr ""
+msgstr "Видалити це вкладеннÑ"
msgid "Deleted"
msgstr "Видалено"
@@ -3258,7 +3451,7 @@ msgid "DeployTokens|Your new project deploy token has been created."
msgstr "Створено ваш новий токен Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñƒ."
msgid "Deployed"
-msgstr ""
+msgstr "Розгорнуто"
msgid "Deployed to"
msgstr "Розгорнуто на"
@@ -3281,6 +3474,9 @@ msgstr "Шаблони опиÑу дозволÑÑŽÑ‚ÑŒ визначити кон
msgid "Description:"
msgstr "ОпиÑ:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr "Знищити"
@@ -3288,7 +3484,7 @@ msgid "Details"
msgstr "Деталі"
msgid "Details (default)"
-msgstr ""
+msgstr "Деталі (за замовчуваннÑм)"
msgid "Detect host keys"
msgstr "ВиÑÐ²Ð»ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ñ–Ð² хоÑта"
@@ -3321,10 +3517,10 @@ msgid "Disable group Runners"
msgstr "Вимкнути групові Runner'и"
msgid "Disable shared Runners"
-msgstr ""
+msgstr "Вимкнути загальні Runner'и"
msgid "Disabled"
-msgstr ""
+msgstr "Вимкнено"
msgid "Discard"
msgstr "Відхилити"
@@ -3348,22 +3544,25 @@ msgid "Discard review"
msgstr "Відхилити перевірку"
msgid "Discover GitLab Geo"
-msgstr ""
+msgstr "Відкрийте GitLab Geo"
msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr "Відкрийте Ð´Ð»Ñ Ñебе групи, проекти та фрагменти коду. ПоділітьÑÑ Ñвоїми проектами з іншими"
msgid "Discuss a specific suggestion or question"
-msgstr ""
+msgstr "Обговорити конкретну пропозицію чи питаннÑ"
msgid "Discuss a specific suggestion or question that needs to be resolved"
-msgstr ""
+msgstr "Обговорити конкретну пропозицію або питаннÑ, що необхідно вирішити"
+
+msgid "Discussion"
+msgstr "ОбговореннÑ"
msgid "Dismiss"
msgstr "Відхилити"
msgid "Dismiss ConvDev introduction"
-msgstr ""
+msgstr "Відхилити вÑтуп до ConvDev"
msgid "Dismiss Cycle Analytics introduction box"
msgstr "Відхилити блок вÑтупу до Ðналитики Циклу"
@@ -3393,7 +3592,7 @@ msgid "Download"
msgstr "Завантажити"
msgid "Download artifacts"
-msgstr ""
+msgstr "Завантажити артефакти"
msgid "Download asset"
msgstr ""
@@ -3441,13 +3640,16 @@ msgid "Edit"
msgstr "Редагувати"
msgid "Edit %{name}"
-msgstr ""
+msgstr "Редагувати %{name}"
+
+msgid "Edit Deploy Key"
+msgstr "Редагувати ключ Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚ÑƒÐ²Ð°Ð½Ð½Ñ"
msgid "Edit Label"
msgstr "Редагувати мітку"
msgid "Edit Milestone"
-msgstr ""
+msgstr "Редагувати етап"
msgid "Edit Pipeline Schedule %{id}"
msgstr "Редагувати Розклад Конвеєра %{id}"
@@ -3459,10 +3661,13 @@ msgid "Edit application"
msgstr "Редагувати заÑтоÑунок"
msgid "Edit comment"
-msgstr ""
+msgstr "Редагувати коментар"
msgid "Edit environment"
-msgstr ""
+msgstr "Редагувати Ñередовище"
+
+msgid "Edit file"
+msgstr "Редагувати файл"
msgid "Edit files in the editor and commit changes here"
msgstr "Редагуйте файли в редакторі і закомітьте зміни тут"
@@ -3474,6 +3679,9 @@ msgid "Edit identity for %{user_name}"
msgstr "Редагувати ідентифікацію Ð´Ð»Ñ %{user_name}"
msgid "Edit issues"
+msgstr "Редагувати задачі"
+
+msgid "Edit public deploy key"
msgstr ""
msgid "Elasticsearch"
@@ -3494,9 +3702,12 @@ msgstr "ÐдреÑи електронної пошти"
msgid "Embed"
msgstr "Вбудувати"
-msgid "Empty file"
+msgid "Emojis|Something went wrong while loading emojis."
msgstr ""
+msgid "Empty file"
+msgstr "Порожній файл"
+
msgid "Enable"
msgstr "Увімкнути"
@@ -3522,7 +3733,7 @@ msgid "Enable classification control using an external service"
msgstr "Увімкнути контроль за клаÑифікацією за допомогою зовнішньої Ñлужби"
msgid "Enable error tracking"
-msgstr ""
+msgstr "Увімкнути відÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¼Ð¸Ð»Ð¾Ðº"
msgid "Enable for this project"
msgstr "Увімкнути Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
@@ -3530,6 +3741,9 @@ msgstr "Увімкнути Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
msgid "Enable group Runners"
msgstr "Увімкнути групові Runner'и"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr "Увімкнути чи вимкнути збір даних Ð´Ð»Ñ Pseudonymizer."
@@ -3540,16 +3754,16 @@ msgid "Enable reCAPTCHA or Akismet and set IP limits."
msgstr "Увімкнути reCAPTCHA або Akismet та вÑтановити Ð¾Ð±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾ IP."
msgid "Enable self approval of merge requests"
-msgstr ""
+msgstr "Дозволити ÑамоÑтійне Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñ–Ð² на злиттÑ"
msgid "Enable shared Runners"
-msgstr ""
+msgstr "Увімкнути загальні Runner'и"
msgid "Enable the Performance Bar for a given group."
msgstr "Увімкнути панель продуктивноÑÑ‚Ñ– Ð´Ð»Ñ Ð´Ð°Ð½Ð¾Ñ— групи."
msgid "Enable two-factor authentication"
-msgstr ""
+msgstr "Увімкнути двофакторну автентифікацію"
msgid "Enable usage ping"
msgstr "Увімкнути викориÑÑ‚Ð°Ð½Ð½Ñ ping"
@@ -3566,6 +3780,9 @@ msgstr "ЗавершуєтьÑÑ Ð¾ (за Грінвічем)"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3584,26 +3801,23 @@ msgstr "Введіть Ð¾Ð¿Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ"
msgid "Enter the merge request title"
msgstr "Введіть назву запиту на злиттÑ"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
-msgstr ""
+msgstr "Змінні Ñередовища"
msgid "Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use environment variables for passwords, secret keys, or whatever you want."
msgstr ""
msgid "Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default"
-msgstr ""
+msgstr "Змінні Ñередовища налаштовані адмініÑтратором бути %{link_start}захищеними%{link_end} за замовчуваннÑм"
msgid "Environment:"
-msgstr ""
+msgstr "Середовище:"
msgid "Environments"
msgstr "Середовища"
msgid "Environments allow you to track deployments of your application %{link_to_read_more}."
-msgstr ""
+msgstr "Середовища дозволÑÑŽÑ‚ÑŒ відÑтежувати Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ заÑтоÑунку %{link_to_read_more}."
msgid "Environments|An error occurred while fetching the environments."
msgstr "Виникла помилка при завантаженні Ñередовищ."
@@ -3611,6 +3825,12 @@ msgstr "Виникла помилка при завантаженні Ñеред
msgid "Environments|An error occurred while making the request."
msgstr "Під Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·ÑƒÐ¿Ð¸Ð½ÐºÐ¸ Ñередовища, будь лаÑка, Ñпробуйте ще раз"
@@ -3662,15 +3882,33 @@ msgstr "Відкрити працююче Ñередовище"
msgid "Environments|Pod logs from"
msgstr "Журнал Pod’а"
+msgid "Environments|Re-deploy"
+msgstr "Повторно розгорнути"
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "Показати вÑÑ–"
@@ -3681,6 +3919,18 @@ msgid "Environments|Stop environment"
msgstr "Зупинити Ñередовище"
msgid "Environments|Stopping"
+msgstr "Зупинка"
+
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
msgstr ""
msgid "Environments|Updated"
@@ -3705,7 +3955,7 @@ msgid "Epics let you manage your portfolio of projects more efficiently and with
msgstr "Епіки дозволÑÑŽÑ‚ÑŒ керувати вашим портфелем проектів ефективніше та з меншими зуÑиллÑми"
msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
+msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при збереженні дати %{epicDateType}"
msgid "Epics|How can I solve this?"
msgstr "Як Ñ Ð¼Ð¾Ð¶Ñƒ це вирішити?"
@@ -3732,13 +3982,16 @@ msgid "Error Reporting and Logging"
msgstr "Звіти про помилки та логуваннÑ"
msgid "Error Tracking"
-msgstr ""
+msgstr "ВідÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð¿Ð¾Ð¼Ð¸Ð»Ð¾Ðº"
+
+msgid "Error creating a new path"
+msgstr "Помилка при Ñтворенні нового шлÑху"
msgid "Error creating epic"
msgstr "Помилка при Ñтворенні епіку"
msgid "Error deleting %{issuableType}"
-msgstr ""
+msgstr "Помилка при видаленні %{issuableType}"
msgid "Error fetching contributors data."
msgstr "Помилка Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð°Ð½Ð¸Ñ… учаÑників."
@@ -3783,29 +4036,56 @@ msgid "Error occurred when toggling the notification subscription"
msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñки на ÑповіщеннÑ"
msgid "Error rendering markdown preview"
-msgstr ""
+msgstr "Помилка при попередньому переглÑді markdown"
msgid "Error saving label update."
msgstr "Помилка при збереженні мітки."
msgid "Error updating %{issuableType}"
-msgstr ""
+msgstr "Помилка при оновленні %{issuableType}"
msgid "Error updating status for all todos."
-msgstr "Помилка Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑтатуÑу Ð´Ð»Ñ Ð²ÑÑ–Ñ… задач."
+msgstr "Помилка Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑтатуÑу Ð´Ð»Ñ Ð²ÑÑ–Ñ… нагадувань."
msgid "Error updating todo status."
-msgstr "Помилка при оновленні ÑтатуÑу задачі."
+msgstr "Помилка при оновленні ÑтатуÑу нагадуваннÑ."
msgid "Error while loading the merge request. Please try again."
msgstr "Помилка при завантаженні запита на злиттÑ. Будь лаÑка, Ñпробуйте знову."
msgid "Error:"
+msgstr "Помилка:"
+
+msgid "ErrorTracking|Active"
msgstr ""
-msgid "Errors"
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
msgstr ""
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
+msgid "Errors"
+msgstr "Помилки"
+
msgid "Estimated"
msgstr "За оцінками"
@@ -3828,10 +4108,10 @@ msgid "EventFilterBy|Filter by team"
msgstr "Фільтрувати по команді"
msgid "Events"
-msgstr ""
+msgstr "Події"
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
-msgstr ""
+msgstr "УÑÑ– Ñпроби %{action} закінчилиÑÑ Ð½ÐµÐ²Ð´Ð°Ñ‡ÐµÑŽ: %{job_error_message}. Будь лаÑка, Ñпробуйте знову."
msgid "Every day (at 4:00am)"
msgstr "Кожен день (в 4:00 ранку)"
@@ -3843,37 +4123,37 @@ msgid "Every week (Sundays at 4:00am)"
msgstr "Ð©Ð¾Ñ‚Ð¸Ð¶Ð½Ñ (в неділю о 4:00 ранку)"
msgid "Everyone"
-msgstr ""
+msgstr "Будь-хто"
msgid "Everyone can contribute"
msgstr "Кожен може зробити Ñвій внеÑок"
msgid "Everything you need to create a GitLab Pages site using GitBook."
-msgstr ""
+msgstr "Ð’Ñе, що потрібно Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñайту на GitLab Pages за допомогою GitBook."
msgid "Everything you need to create a GitLab Pages site using Hexo."
-msgstr ""
+msgstr "Ð’Ñе, що потрібно Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñайту на GitLab Pages за допомогою Hexo."
msgid "Everything you need to create a GitLab Pages site using Hugo."
-msgstr ""
+msgstr "Ð’Ñе, що потрібно Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñайту на GitLab Pages за допомогою Hugo."
msgid "Everything you need to create a GitLab Pages site using Jekyll."
-msgstr ""
+msgstr "Ð’Ñе, що потрібно Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñайту на GitLab Pages за допомогою Jekyll."
msgid "Everything you need to create a GitLab Pages site using plain HTML."
-msgstr ""
+msgstr "Ð’Ñе, що потрібно Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñайту на GitLab Pages за допомогою проÑтого HTML."
msgid "Except policy:"
msgstr ""
msgid "Existing Git repository"
-msgstr ""
+msgstr "ІÑнуючий репозиторій Git"
msgid "Existing folder"
-msgstr ""
+msgstr "ІÑнуюча папка"
msgid "Existing members and groups"
-msgstr ""
+msgstr "ІÑнуючі учаÑники та групи"
msgid "Expand"
msgstr "Розгорнути"
@@ -3881,6 +4161,9 @@ msgstr "Розгорнути"
msgid "Expand all"
msgstr "Розгорнути вÑе"
+msgid "Expand approvers"
+msgstr "Розгорнути ÑпиÑок затверджуючих оÑіб"
+
msgid "Expand sidebar"
msgstr "Розгорніть бічну панель"
@@ -3915,22 +4198,22 @@ msgid "Explore public groups"
msgstr "ПереглÑнути публічні групи"
msgid "Export as CSV"
-msgstr ""
+msgstr "ЕкÑпортувати Ñк CSV"
msgid "Export issues"
-msgstr ""
+msgstr "ЕкÑпортувати задачі"
msgid "External Classification Policy Authorization"
msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ ÐšÐ»Ð°ÑÐ¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ ÐŸÐ¾Ð»Ñ–Ñ‚Ð¸ÐºÐ¸ Ðвторизації"
msgid "External URL"
-msgstr ""
+msgstr "Зовнішній URL"
msgid "External Wiki"
-msgstr ""
+msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ Ð²Ñ–ÐºÑ–"
msgid "External authentication"
-msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ"
+msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ"
msgid "External authorization denied access to this project"
msgstr "Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ñ–Ñ Ð·Ð°Ð±Ð¾Ñ€Ð¾Ð½Ð¸Ð»Ð° доÑтуп до цього проекту"
@@ -3968,7 +4251,7 @@ msgstr "Ðе вдалоÑÑ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ð½ÑƒÑ‚Ð¸ до"
msgid "Failed to load emoji list."
msgstr "Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ ÑпиÑок Ñмайликів."
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -4005,10 +4288,10 @@ msgid "Feature Flags"
msgstr "Перемикачі функцій"
msgid "FeatureFlags|* (All Environments)"
-msgstr ""
+msgstr "* (УÑÑ– Ñередовища)"
msgid "FeatureFlags|* (All environments)"
-msgstr ""
+msgstr "* (вÑÑ– Ñередовища)"
msgid "FeatureFlags|API URL"
msgstr "URL-адреÑа API"
@@ -4026,49 +4309,46 @@ msgid "FeatureFlags|Create feature flag"
msgstr "Створити перемикач функції"
msgid "FeatureFlags|Delete %{name}?"
-msgstr ""
+msgstr "Видалити %{name}?"
msgid "FeatureFlags|Delete feature flag"
-msgstr ""
+msgstr "Вимкнути перемикач функції"
msgid "FeatureFlags|Description"
msgstr "ОпиÑ"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr "Редагувати %{feature_flag_name}"
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr "Редагувати перемикач функції"
msgid "FeatureFlags|Environment Spec"
-msgstr ""
+msgstr "Ð¡Ð¿ÐµÑ†Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Ñередовища"
msgid "FeatureFlags|Environment Specs"
-msgstr ""
+msgstr "Специфікації Ñередовищ"
msgid "FeatureFlags|Feature Flag"
msgstr "Перемикач функції"
msgid "FeatureFlags|Feature Flag behavior is built up by creating a set of rules to define the status of target environments. A default wildcare rule %{codeStart}*%{codeEnd} for %{boldStart}All Environments%{boldEnd} is set, and you are able to add as many rules as you need by choosing environment specs below. You can toggle the behavior for each of your rules to set them %{boldStart}Active%{boldEnd} or %{boldStart}Inactive%{boldEnd}."
-msgstr ""
+msgstr "Поведінка перемикачів функцій ÑтворюєтьÑÑ ÑˆÐ»Ñхом ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð°Ð±Ð¾Ñ€Ñƒ правил Ð´Ð»Ñ Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñтану цільових Ñередовищ. Правило за замовчуваннÑм %{codeStart}*%{codeEnd} Ð´Ð»Ñ %{boldStart}Ð’ÑÑ–Ñ… Ñередовищ%{boldEnd} вже налаштовано, Ñ– ви можете додати Ñтільки правил, Ñкільки вам потрібно, шлÑхом Ð·Ð°Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ñпецифікацій Ñередовищ нижче. Ви можете перемикати Ñтан Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ð³Ð¾ з ваших правил, щоб зробити Ñ—Ñ… %{boldStart}Ðктивними%{boldEnd} або %{boldStart}Ðеактивними%{boldEnd}."
msgid "FeatureFlags|Feature Flags"
-msgstr ""
+msgstr "Перемикачі функцій"
msgid "FeatureFlags|Feature Flags allow you to configure your code into different flavors by dynamically toggling certain functionality."
-msgstr ""
+msgstr "Перемикачі функцій дозволÑÑŽÑ‚ÑŒ налаштовувати ваш код по-різному за допомогою динамічного ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ‡Ð¸ Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð¿ÐµÐ²Ð½Ð¾Ñ— функціональноÑÑ‚Ñ–."
msgid "FeatureFlags|Feature flag %{name} will be removed. Are you sure?"
-msgstr ""
+msgstr "Перемикач функції %{name} буде видалено. Ви впевнені?"
msgid "FeatureFlags|Get started with Feature Flags"
-msgstr ""
+msgstr "Розпочати роботу з перемикачами функцій"
msgid "FeatureFlags|Inactive"
msgstr "Ðеактивний"
msgid "FeatureFlags|Inactive flag for %{scope}"
-msgstr ""
+msgstr "Ðеактивний перемикач функції Ð´Ð»Ñ %{scope}"
msgid "FeatureFlags|Install a %{docs_link_start}compatible client library%{docs_link_end} and specify the API URL, application name, and instance ID during the configuration setup."
msgstr "Ð’Ñтановіть %{docs_link_start}ÑуміÑну клієнтÑьку бібліотеку%{docs_link_end} Ñ– вкажіть URL адреÑу API, назву заÑтоÑунку та ідентифікатор інÑтанÑа під Ñ‡Ð°Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ."
@@ -4077,10 +4357,10 @@ msgid "FeatureFlags|Instance ID"
msgstr "Ідентифікатор ІнÑтанÑу"
msgid "FeatureFlags|Loading Feature Flags"
-msgstr ""
+msgstr "Ð—Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ð¿ÐµÑ€ÐµÐ¼Ð¸ÐºÐ°Ñ‡Ñ–Ð² функцій"
msgid "FeatureFlags|More Information"
-msgstr ""
+msgstr "Більше інформації"
msgid "FeatureFlags|More information"
msgstr "Більше інформації"
@@ -4094,26 +4374,23 @@ msgstr "Ðовий"
msgid "FeatureFlags|New Feature Flag"
msgstr "Ðовий перемикач функції"
-msgid "FeatureFlags|Save changes"
-msgstr "Зберегти зміни"
-
msgid "FeatureFlags|Status"
msgstr "СтатуÑ"
msgid "FeatureFlags|Target environments"
-msgstr ""
+msgstr "Цільові Ñередовища"
msgid "FeatureFlags|There are no active Feature Flags"
-msgstr ""
+msgstr "Ðемає активних перемикачів функцій"
msgid "FeatureFlags|There are no inactive Feature Flags"
-msgstr ""
+msgstr "Ðемає неактивних перемикачів функцій"
msgid "FeatureFlags|There was an error fetching the feature flags."
-msgstr ""
+msgstr "Помилка при отриманні перемикачів функцій."
msgid "FeatureFlags|Try again in a few moments or contact your support team."
-msgstr ""
+msgstr "Повторіть Ñпробу через деÑкий Ñ‡Ð°Ñ Ð°Ð±Ð¾ звернітьÑÑ Ð´Ð¾ вашої Ñлужби підтримки."
msgid "Feb"
msgstr "лют."
@@ -4126,31 +4403,31 @@ msgstr "ÐŸÐ¾Ð»Ñ Ð½Ð° цій Ñторінці зараз недоÑтупні д
msgid "File"
msgid_plural "Files"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Файл"
+msgstr[1] "Файли"
+msgstr[2] "Файлів"
+msgstr[3] "Файлів"
msgid "File added"
-msgstr ""
+msgstr "Файл додано"
msgid "File browser"
-msgstr ""
+msgstr "Файловий менеджер"
msgid "File deleted"
-msgstr ""
+msgstr "Файл видалено"
msgid "File mode changed from %{a_mode} to %{b_mode}"
msgstr ""
msgid "File moved"
-msgstr ""
+msgstr "Файл переміщено"
msgid "File templates"
msgstr "Шаблони файлів"
msgid "File upload error."
-msgstr ""
+msgstr "Помилка Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ."
msgid "Files"
msgstr "Файли"
@@ -4168,31 +4445,28 @@ msgid "Filter by %{issuable_type} that are currently opened."
msgstr "Фільтрувати відкриті за %{issuable_type}."
msgid "Filter by commit message"
-msgstr "Фільтрувати за коміт-повідомленнÑм"
+msgstr "Фільтрувати за повідомленнÑм Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ"
msgid "Filter by milestone name"
-msgstr ""
+msgstr "Фільтрувати за назвою етапу"
msgid "Filter by two-factor authentication"
-msgstr ""
+msgstr "Фільтрувати за двофакторною автентифікацією"
msgid "Filter results by group"
-msgstr ""
+msgstr "Фільтрувати результати за групою"
msgid "Filter results by project"
-msgstr ""
+msgstr "Фільтрувати результати за проектом"
msgid "Filter..."
msgstr "Фільтр..."
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "Пошук по шлÑху"
msgid "Find existing members by name"
-msgstr ""
+msgstr "Знайти Ñ–Ñнуючих учаÑників за ім'Ñм"
msgid "Find file"
msgstr "Знайти файл"
@@ -4203,11 +4477,14 @@ msgstr "Знайдіть завантажений ZIP-файл і розпаку
msgid "Find the newly extracted <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> file."
msgstr "Знайдіть щойно розпакований <code>Takeout/Google Code Project Hosting/GoogleCodeProjectHosting.json</code> файл."
+msgid "Fingerprint"
+msgstr ""
+
msgid "Fingerprints"
msgstr "Відбитки пальців"
msgid "Finish editing this message first!"
-msgstr ""
+msgstr "Спочатку завершіть Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ повідомленнÑ!"
msgid "Finish review"
msgstr "Завершити перевірку"
@@ -4216,7 +4493,7 @@ msgid "Finished"
msgstr "Завершено"
msgid "First day of the week"
-msgstr ""
+msgstr "Перший день тижнÑ"
msgid "FirstPushedBy|First"
msgstr "Перший"
@@ -4264,7 +4541,7 @@ msgid "For internal projects, any logged in user can view pipelines and access j
msgstr "Ð”Ð»Ñ Ð²Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ–Ñ… проектів будь-Ñкий зареєÑтрований кориÑтувач може переглÑдати конвеєри та отримати доÑтуп до інформації про роботу (логи та артефакти)"
msgid "For more info, read the documentation."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації читайте документацію."
msgid "For more information, go to the "
msgstr "Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації, відвідайте "
@@ -4291,7 +4568,7 @@ msgid "Forking in progress"
msgstr "ВідбуваєтьÑÑ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ„Ð¾Ñ€ÐºÑƒ"
msgid "Forks"
-msgstr ""
+msgstr "Форки"
msgid "Format"
msgstr "Формат"
@@ -4302,8 +4579,8 @@ msgstr "Знайдено помилки у вашому .gitlab-ci.yml:"
msgid "Free Trial of GitLab.com Gold"
msgstr "Безкоштовна пробна верÑÑ–Ñ GitLab.com Gold"
-msgid "From %{provider_title}"
-msgstr "З %{provider_title}"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "З Bitbucket"
@@ -4332,12 +4609,21 @@ msgstr "З етапів:"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "Із Ñторінки деталей Kubernetes-клаÑтера, вÑтановіть runner зі ÑпиÑку заÑтоÑунків"
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "GPG ключі"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "Загальні"
+msgid "General Settings"
+msgstr ""
+
msgid "General pipelines"
msgstr "Загальні конвеєри"
@@ -4345,7 +4631,7 @@ msgid "Generate a default set of labels"
msgstr "Створити Ñтандартний набір міток"
msgid "Generate key"
-msgstr ""
+msgstr "Згенерувати ключ"
msgid "Geo"
msgstr "Geo"
@@ -4362,6 +4648,9 @@ msgstr "Вузол не працює або зламаний."
msgid "GeoNodeSyncStatus|Node is slow, overloaded, or it just recovered after an outage."
msgstr "Вузол працює повільно, перевантажений або тільки що відновивÑÑ Ð¿Ñ–ÑÐ»Ñ Ð·Ð±Ð¾ÑŽ."
+msgid "GeoNodes|Alternate URL"
+msgstr ""
+
msgid "GeoNodes|Checksummed"
msgstr "Із контрольною Ñумою"
@@ -4426,7 +4715,7 @@ msgid "GeoNodes|New node"
msgstr "Ðовий вузол"
msgid "GeoNodes|Node Authentication was successfully repaired."
-msgstr "Ðутентифікацію вузла уÑпішно полагоджено."
+msgstr "Ðвтентифікацію вузла уÑпішно полагоджено."
msgid "GeoNodes|Node was successfully removed."
msgstr "Вузол уÑпішно видалено."
@@ -4438,10 +4727,10 @@ msgid "GeoNodes|Out of sync"
msgstr "РозÑинхронізовані"
msgid "GeoNodes|Removing a primary node stops the sync process for all nodes. Syncing cannot be resumed without losing some data on all secondaries. In this case we would recommend setting up all nodes from scratch. Are you sure?"
-msgstr ""
+msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¾Ñновного вузла зупинÑÑ” Ð¿Ñ€Ð¾Ñ†ÐµÑ Ñинхронізації Ð´Ð»Ñ Ð²ÑÑ–Ñ… вузлів. Синхронізацію неможливо буде відновити без втрати деÑких даних на вÑÑ–Ñ… вторинних вузлах. У цьому випадку рекомендовано налаштувати вÑÑ– вузли з нулÑ. Ви впевнені?"
msgid "GeoNodes|Removing a secondary node stops the sync process. It is not currently possible to add back the same node without losing some data. We only recommend setting up a new secondary node in this case. Are you sure?"
-msgstr ""
+msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ñ‚Ð¾Ñ€Ð¸Ð½Ð½Ð¾Ð³Ð¾ вузла зупинÑÑ” Ð¿Ñ€Ð¾Ñ†ÐµÑ Ñинхронізації. Ðаразі неможливо додати назад той Ñамий вузол без втрати деÑких даних. У цьому випадку рекомендовано Ñтворити новий вторинний вузол. Ви впевнені?"
msgid "GeoNodes|Replication slot WAL"
msgstr "Слот реплікації WAL"
@@ -4516,7 +4805,7 @@ msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr "Вікі перевірено із їхніми копіÑми на первинному вузлі"
msgid "GeoNodes|With %{geo} you can install a special read-only and replicated instance anywhere. Before you add nodes, follow the %{instructions} in the exact order they appear."
-msgstr ""
+msgstr "З %{geo} ви можете вÑтановити будь-де Ñпеціальній реплікований інÑÑ‚Ð°Ð½Ñ Ð»Ð¸ÑˆÐµ Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ. Перед тим, Ñк додавати вузли, дотримайтеÑÑ %{instructions} в точному порÑдку."
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr "Ви налаштували Geo-вузли через незахищене HTTP-з’єднаннÑ. Ми рекомендуємо викориÑтовувати HTTPS."
@@ -4542,9 +4831,24 @@ msgstr "Ð’ÑÑ– проекти плануютьÑÑ Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð½Ð¾Ñ— пÐ
msgid "Geo|All projects are being scheduled for re-sync"
msgstr "Ð’ÑÑ– проекти плануютьÑÑ Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€Ð½Ð¾Ñ— Ñинхронізації"
+msgid "Geo|Alternate URL"
+msgstr ""
+
msgid "Geo|Batch operations"
msgstr "Групові операції"
+msgid "Geo|Choose which groups you wish to synchronize to this secondary node."
+msgstr ""
+
+msgid "Geo|Control the maximum concurrency of LFS/attachment backfill for this secondary node"
+msgstr ""
+
+msgid "Geo|Control the maximum concurrency of verification operations for this Geo node"
+msgstr ""
+
+msgid "Geo|Control the minimum interval in days that a repository should be reverified for this primary node"
+msgstr ""
+
msgid "Geo|Could not remove tracking entry for an existing project."
msgstr "Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ Ð·Ð°Ð¿Ð¸Ñ Ð²Ñ–Ð´ÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ–Ñнуючого проекту."
@@ -4564,7 +4868,7 @@ msgid "Geo|In sync"
msgstr "Синхронізовано"
msgid "Geo|Last repository check run"
-msgstr ""
+msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ Ð·Ð°Ð¿ÑƒÑк перевірки репозиторію"
msgid "Geo|Last successful sync"
msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ ÑƒÑпішна ÑинхронізаціÑ"
@@ -4603,7 +4907,7 @@ msgid "Geo|Projects in certain storage shards"
msgstr "Проекти в певних Ñегментах Ñховищ"
msgid "Geo|Re-verification interval"
-msgstr ""
+msgstr "Інтервал повторної перевірки"
msgid "Geo|Recheck"
msgstr "Повторна перевірка"
@@ -4632,6 +4936,9 @@ msgstr "КількіÑÑ‚ÑŒ Ñпроб"
msgid "Geo|Select groups to replicate."
msgstr "Виберіть групи Ð´Ð»Ñ Ñ€ÐµÐ¿Ð»Ñ–ÐºÐ°Ñ†Ñ–Ñ—."
+msgid "Geo|Selective synchronization"
+msgstr ""
+
msgid "Geo|Shards to synchronize"
msgstr "Сегменти Ð´Ð»Ñ Ñинхронізації"
@@ -4644,12 +4951,21 @@ msgstr "Синхронізовано"
msgid "Geo|Synchronization failed - %{error}"
msgstr "Ð¡Ð¸Ð½Ñ…Ñ€Ð¾Ð½Ñ–Ð·Ð°Ñ†Ñ–Ñ Ð½ÐµÐ²Ð´Ð°Ð»Ð°: %{error}"
+msgid "Geo|This is a primary node"
+msgstr ""
+
+msgid "Geo|To support OAuth logins to this node at a different domain than URL"
+msgstr ""
+
msgid "Geo|Tracking entry for project (%{project_id}) was successfully removed."
msgstr "Ð—Ð°Ð¿Ð¸Ñ Ð²Ñ–Ð´ÑÑ‚ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ñƒ (%{project_id}) уÑпішно видалено."
msgid "Geo|Tracking entry will be removed. Are you sure?"
msgstr "Буде видалено Ð·Ð°Ð¿Ð¸Ñ Ð¿Ñ€Ð¾ відÑтеженнÑ. Ви впевнені?"
+msgid "Geo|URL"
+msgstr ""
+
msgid "Geo|Unknown state"
msgstr "Ðевідомий Ñтан"
@@ -4669,7 +4985,7 @@ msgid "Geo|You are on a secondary, <b>read-only</b> Geo node. You may be able to
msgstr "Ви знаходитеÑÑŒ на вторинному <b>лише Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ</b> Geo-вузлі. Ви зможете вноÑити лише обмежену кількіÑÑ‚ÑŒ змін та виконувати обмежений набір операцій з цієї Ñторінки."
msgid "Geo|You need a different license to use Geo replication"
-msgstr "Вам потрібна інша Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð½Ð° викориÑÑ‚Ð°Ð½Ð½Ñ Ð³ÐµÐ¾Ð³Ñ€Ð°Ñ„Ñ–Ñ‡Ð½Ð¾Ñ— реплікації"
+msgstr "Вам потрібна інша Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð½Ð° викориÑÑ‚Ð°Ð½Ð½Ñ Geo реплікації"
msgid "Geo|misconfigured"
msgstr "неправильно налаштований"
@@ -4684,16 +5000,16 @@ msgid "Get a free instance review"
msgstr "Отримайте безкоштовну оцінку інÑтанÑа"
msgid "Get started with error tracking"
-msgstr ""
+msgstr "Розпочати роботу з відÑтеженнÑм помилок"
msgid "Getting started with releases"
-msgstr ""
+msgstr "Розпочати роботу з релізами"
msgid "Git"
msgstr "Git"
msgid "Git global setup"
-msgstr ""
+msgstr "Глобальні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Git"
msgid "Git repository URL"
msgstr "URL Git-репозиторіÑ"
@@ -4762,20 +5078,29 @@ msgid "Gitea Import"
msgstr "Імпорт з Gitea"
msgid "Given access %{time_ago}"
-msgstr ""
+msgstr "Ðадано доÑтуп %{time_ago}"
msgid "Go Back"
msgstr "ПовернутиÑÑ"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "ПовернутиÑÑ"
+msgid "Go full screen"
+msgstr "Ðа повний екран"
+
msgid "Go to"
msgstr "Перейти до"
msgid "Go to %{link_to_google_takeout}."
msgstr "Перейти до %{link_to_google_takeout}."
+msgid "Go to project"
+msgstr "Перейти до проекту"
+
msgid "Google Code import"
msgstr "Імпорт з Google Code"
@@ -4783,13 +5108,13 @@ 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}. ПопроÑÑ–Ñ‚ÑŒ Ñвого адмініÑтратора GitLab, Ñкщо ви хочете ÑкориÑтатиÑÑ Ñ†Ð¸Ð¼ ÑервіÑом."
+msgstr "ÐÐ²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ Google не %{link_to_documentation}. ПопроÑÑ–Ñ‚ÑŒ Ñвого адмініÑтратора GitLab, Ñкщо ви хочете ÑкориÑтатиÑÑ Ñ†Ð¸Ð¼ ÑервіÑом."
msgid "Got it!"
msgstr "Зрозуміло!"
msgid "Grant access"
-msgstr ""
+msgstr "Ðадати доÑтуп"
msgid "Graph"
msgstr "Графік"
@@ -4806,6 +5131,9 @@ msgstr "Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð³Ñ€ÑƒÐ¿Ð¸ Git LFS:"
msgid "Group ID"
msgstr "Ідентифікатор групи"
+msgid "Group ID: %{group_id}"
+msgstr ""
+
msgid "Group Runners"
msgstr "Групові Runner'и"
@@ -4833,14 +5161,17 @@ msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ групу:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "Керівники групи можуть зареєÑтрувати групові runner'и через %{link}"
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "Ðазва групи"
msgid "Group overview content"
-msgstr ""
+msgstr "ВміÑÑ‚ оглÑдової Ñторінки групи"
msgid "Group:"
-msgstr ""
+msgstr "Група:"
msgid "Group: %{group_name}"
msgstr "Група: %{group_name}"
@@ -4858,33 +5189,48 @@ msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timelin
msgstr "План-графік епіків відображає Ñтан ваших епіків у чаÑÑ–"
msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку, додайте дату початку чи Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків в цій групі або Ñ—Ñ— підгрупах; від %{startDate} до %{endDate}."
msgid "GroupRoadmap|To widen your search, change or remove filters; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "Щоб розширити пошук, змініть або видаліть фільтри; від %{startDate} до %{endDate}."
msgid "GroupRoadmap|Until %{dateWord}"
msgstr "До %{dateWord}"
+msgid "GroupSettings|Auto DevOps pipeline was updated for the group"
+msgstr ""
+
+msgid "GroupSettings|Auto DevOps will automatically build, test and deploy your application based on a predefined Continuous Integration and Delivery configuration. %{auto_devops_start}Learn more about Auto DevOps%{auto_devops_end}"
+msgstr ""
+
msgid "GroupSettings|Badges"
msgstr "Значки"
msgid "GroupSettings|Custom project templates"
-msgstr ""
+msgstr "ВлаÑні шаблони проектів"
msgid "GroupSettings|Customize your group badges."
msgstr "Ðалаштувати значки групи."
+msgid "GroupSettings|Default to Auto DevOps pipeline for all projects within this group"
+msgstr ""
+
msgid "GroupSettings|Learn more about badges."
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про значки."
msgid "GroupSettings|Learn more about group-level project templates."
-msgstr ""
+msgstr "Докладніше про шаблони проектів на рівні групи."
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "Заборонити Ñпільний доÑтуп до проекту в рамках %{group} з іншими групами"
msgid "GroupSettings|Select a sub-group as the custom project template source for this group."
+msgstr "Виберіть підгрупу Ñк джерело влаÑних шаблонів проектів Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи."
+
+msgid "GroupSettings|The Auto DevOps pipeline will run if no alternative CI configuration file is found."
+msgstr ""
+
+msgid "GroupSettings|There was a problem updating Auto DevOps pipeline: %{error_messages}."
msgstr ""
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
@@ -4912,7 +5258,7 @@ msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroup
msgstr "Групи також можуть бути вкладеними при викориÑтанні %{subgroup_docs_link_start}підгруп%{subgroup_docs_link_end}."
msgid "Groups with access to <strong>%{project_name}</strong>"
-msgstr ""
+msgstr "Групи з доÑтупом до <strong>%{project_name}</strong>"
msgid "GroupsDropdown|Frequently visited"
msgstr "ЧаÑто відвідувані"
@@ -5014,7 +5360,7 @@ msgid "Here is the public SSH key that needs to be added to the remote server. F
msgstr "Це відкритий (публічний) SSH ключ, Ñкий потрібно додати на віддалений Ñервер. Ð”Ð»Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÐ¾Ð²Ð¾Ñ— інформації, звернітьÑÑ Ð´Ð¾ документації."
msgid "Hide file browser"
-msgstr ""
+msgstr "Сховати файловий менеджер"
msgid "Hide host keys manual input"
msgstr "Сховати ввід ключів хоÑта"
@@ -5030,7 +5376,7 @@ msgstr[2] "Сховати значень"
msgstr[3] "Сховати значень"
msgid "Hide values"
-msgstr ""
+msgstr "Сховати значеннÑ"
msgid "History"
msgstr "ІÑторіÑ"
@@ -5038,9 +5384,12 @@ msgstr "ІÑторіÑ"
msgid "Housekeeping successfully started"
msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ ÑƒÑпішно розпочато"
-msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
+msgid "Housekeeping, export, path, transfer, remove, archive."
msgstr ""
+msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
+msgstr "Проте ви вже Ñ” учаÑником цього %{member_source}. Увійдіть, викориÑтовуючи інший обліковий запиÑ, щоб прийнÑти запрошеннÑ."
+
msgid "I accept the %{terms_link}"
msgstr "Я приймаю %{terms_link}"
@@ -5120,7 +5469,7 @@ msgid "If you already have files you can push them using the %{link_to_cli} belo
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>."
+msgstr "Якщо ваш HTTP-репозиторій не Ñ” публічним, додайте дані Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— до URL: <code>https://username:password@gitlab.company.com/group/project.git</code>."
msgid "ImageDiffViewer|2-up"
msgstr "2 поруч"
@@ -5132,13 +5481,13 @@ msgid "ImageDiffViewer|Swipe"
msgstr "Ðакладені (проведеннÑ)"
msgid "Impersonation has been disabled"
-msgstr ""
+msgstr "УоÑÐ¾Ð±Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ вимкнено"
msgid "Import"
msgstr "Імпорт"
msgid "Import CSV"
-msgstr ""
+msgstr "Імпортувати CSV"
msgid "Import Projects from Gitea"
msgstr "Імпортувати проекти з Gitea"
@@ -5159,13 +5508,13 @@ msgid "Import in progress"
msgstr "Імпорт триває"
msgid "Import issues"
-msgstr ""
+msgstr "Імпорт задач"
msgid "Import members"
-msgstr ""
+msgstr "Імпортувати учаÑників"
msgid "Import members from another project"
-msgstr ""
+msgstr "Імпортувати учаÑників з іншого проекту"
msgid "Import multiple repositories by uploading a manifest file."
msgstr "Імпортувати кілька репозиторіїв, надіÑлавши файл маніфеÑту."
@@ -5174,7 +5523,7 @@ msgid "Import project"
msgstr "Імпорт проекту"
msgid "Import project members"
-msgstr ""
+msgstr "Імпортувати учаÑників проекту"
msgid "Import projects from Bitbucket"
msgstr "Імпортувати проекти з Bitbucket"
@@ -5203,9 +5552,24 @@ msgstr "Імпорт репозиторію"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr "Підключити репозиторії із"
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr "Покращити дошки обговорень за допомогою верÑÑ–Ñ— GitLab Enterprise Edition."
@@ -5228,19 +5592,19 @@ msgid "Include a Terms of Service agreement and Privacy Policy that all users mu
msgstr "Включити угоду про Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг та правила конфіденційноÑÑ‚Ñ–, Ñкі повинні прийнÑти вÑÑ– кориÑтувачі."
msgid "Include merge request description"
-msgstr ""
+msgstr "Додайте Ð¾Ð¿Ð¸Ñ Ð·Ð°Ð¿Ð¸Ñ‚Ñƒ на злиттÑ"
msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
msgstr "Якщо необхідно додайте ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача в URL: <code>https: //username@gitlab.company.com/group/project.git</code>."
msgid "Includes an MVC structure to help you get started."
-msgstr ""
+msgstr "Включає Ñтруктуру MVC, щоб допомогти вам розпочати роботу."
msgid "Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started."
-msgstr ""
+msgstr "Включає Ñтруктуру MVC, Gemfile, Rakefile, а також багато іншого, щоб допомогти вам розпочати роботу."
msgid "Includes an MVC structure, mvnw and pom.xml to help you get started."
-msgstr ""
+msgstr "Включає Ñтруктуру MVC, mvnw Ñ– pom.xml, щоб допомогти вам розпочати роботу."
msgid "Incompatible Project"
msgstr "ÐеÑуміÑний проект"
@@ -5257,7 +5621,16 @@ msgstr "Введіть ключі хоÑта вручну"
msgid "Input your repository URL"
msgstr "Введіть ваш URL репозиторію"
+msgid "Insert a quote"
+msgstr "Ð’Ñтавити цитату"
+
+msgid "Insert code"
+msgstr "Ð’Ñтавити код"
+
msgid "Insert suggestion"
+msgstr "Додати пропозицію"
+
+msgid "Insights"
msgstr ""
msgid "Install GitLab Runner"
@@ -5292,7 +5665,7 @@ msgid "Interested parties can even contribute by pushing commits if they want to
msgstr "Зацікавлені Ñторони за бажаннÑм можуть навіть робити внеÑки шлÑхом Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð²."
msgid "Internal"
-msgstr ""
+msgstr "Внутрішній"
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "Ð’Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½Ñ â€” будь-Ñкий автентифікований кориÑтувач має доÑтуп до цієї групи та уÑÑ–Ñ… Ñ—Ñ— внутрішніх проектів."
@@ -5312,8 +5685,11 @@ msgstr "ПредÑтавлÑємо аналітику циклу"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr "Ðекорректний ввід, будь лаÑка, уникайте Ñмайликів"
+
msgid "Invitation"
-msgstr ""
+msgstr "ЗапрошеннÑ"
msgid "Invite"
msgstr "ЗапрошеннÑ"
@@ -5322,7 +5698,7 @@ msgid "Invite group"
msgstr ""
msgid "Invite member"
-msgstr ""
+msgstr "ЗапроÑити учаÑника"
msgid "Invoke Count"
msgstr ""
@@ -5330,6 +5706,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr "Задача"
@@ -5342,6 +5721,9 @@ msgstr "Режим фокуÑÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð»Ñ Ð´Ð¾ÑˆÐºÐ¸ задач"
msgid "Issue events"
msgstr "Задачі"
+msgid "Issue settings"
+msgstr ""
+
msgid "IssueBoards|Board"
msgstr "Дошка"
@@ -5349,19 +5731,19 @@ msgid "IssueBoards|Boards"
msgstr "Дошки"
msgid "IssueBoards|Create new board"
-msgstr ""
+msgstr "Створити нову дошку"
msgid "IssueBoards|Delete board"
-msgstr ""
+msgstr "Видалити дошку"
msgid "IssueBoards|No matching boards found"
-msgstr ""
+msgstr "Ðе знайдено відповідних дошок"
msgid "IssueBoards|Some of your boards are hidden, activate a license to see them again."
-msgstr ""
+msgstr "ДеÑкі з ваших дошок Ñ” прихованими, активуйте ліцензію, щоб побачити Ñ—Ñ… знову."
msgid "IssueBoards|Switch board"
-msgstr ""
+msgstr "Перемкнути дошку"
msgid "Issues"
msgstr "Задачі"
@@ -5372,8 +5754,8 @@ msgstr "Задачі можуть бути помилками, нагадуваÐ
msgid "Issues closed"
msgstr "Задачі закриті"
-msgid "Issues, merge requests, pushes and comments."
-msgstr "Задачі, запити на злиттÑ, Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ ÐºÐ¾Ð´Ñƒ Ñ– коментарі."
+msgid "Issues, merge requests, pushes, and comments."
+msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
msgstr "ПіÑÐ»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡ Ð´Ð»Ñ Ð²Ð°ÑˆÐ¸Ñ… проектів, ми зможемо почати відÑтежувати Ñ– відображати метрики Ð´Ð»Ñ Ð½Ð¸Ñ…"
@@ -5400,7 +5782,7 @@ msgid "It must have a header row and at least two columns: the first column is t
msgstr ""
msgid "It's you"
-msgstr ""
+msgstr "Це ви"
msgid "Jaeger URL"
msgstr "URL-адреÑа Jaeger"
@@ -5421,10 +5803,10 @@ msgid "Job has been erased"
msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ Ñтерте"
msgid "Job is stuck. Check runners."
-msgstr ""
+msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ðµ. Перевірте runner'и."
msgid "Job was retried"
-msgstr ""
+msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ»Ð¾ перезапущене"
msgid "Jobs"
msgstr "ЗавданнÑ"
@@ -5466,10 +5848,10 @@ msgid "Job|The artifacts were removed"
msgstr "Ðртефакти були видалені"
msgid "Job|The artifacts will be removed"
-msgstr ""
+msgstr "Ðртефакти будуть видалені"
msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it."
-msgstr ""
+msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ðµ, тому що цей проект не має жодних runner'ів призначених Ð´Ð»Ñ Ð½ÑŒÐ¾Ð³Ð¾."
msgid "Jul"
msgstr "лип."
@@ -5484,7 +5866,7 @@ msgid "June"
msgstr "червень"
msgid "Key (PEM)"
-msgstr ""
+msgstr "Ключ (PEM)"
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -5493,7 +5875,7 @@ msgid "Kubernetes Cluster"
msgstr "КлаÑтер Kubernetes"
msgid "Kubernetes Clusters"
-msgstr ""
+msgstr "КлаÑтери Kubernetes"
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
msgstr "ÐŸÐµÑ€ÐµÐ²Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð»Ñ–Ð¼Ñ–Ñ‚Ñƒ чаÑу при Ñтворенні Kubernetes-клаÑтера; %{timeout}"
@@ -5561,6 +5943,9 @@ msgstr "ПеренеÑти мітку"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr "ПеренеÑÐµÐ½Ð½Ñ %{labelTitle} на рівень групи зробить Ñ—Ñ— доÑтупною Ð´Ð»Ñ Ð²ÑÑ–Ñ… проектів в групі %{groupName}. ІÑнуючі проектні мітки із такими ж іменами будуть об'єднані. Дана Ð´Ñ–Ñ Ð½Ðµ може бути ÑкаÑована."
+msgid "Language"
+msgstr "Мова"
+
msgid "Large File Storage"
msgstr "Сховище великих файлів (LFS)"
@@ -5575,7 +5960,7 @@ msgid "Last Pipeline"
msgstr "ОÑтанній Конвеєр"
msgid "Last activity"
-msgstr ""
+msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–ÑÑ‚ÑŒ"
msgid "Last commit"
msgstr "ОÑтанній коміт"
@@ -5611,7 +5996,7 @@ msgid "Latest changes"
msgstr "ОÑтанні зміни"
msgid "Latest pipeline for this branch"
-msgstr ""
+msgstr "ОÑтанній конвеєр Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— гілки"
msgid "Lead"
msgstr ""
@@ -5626,26 +6011,32 @@ msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про %{issue_boards_url}, щоб Ñтежити за задачами в кількох ÑпиÑках, викориÑтовуючи мітки, виконавців та етапи. Якщо вам чогоÑÑŒ не виÑтачає в дошках обговорень задач, Ñтворіть задачу на %{gitlab_issues_url}."
msgid "Learn more about Auto DevOps"
-msgstr ""
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Auto DevOps"
msgid "Learn more about Kubernetes"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Kubernetes"
msgid "Learn more about Web Terminal"
+msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Веб-термінал"
+
+msgid "Learn more about approvals."
msgstr ""
msgid "Learn more about custom project templates"
-msgstr ""
+msgstr "Докладніше про влаÑні шаблони проектів"
msgid "Learn more about group-level project templates"
-msgstr ""
+msgstr "Докладніше про шаблони проектів на рівні групи"
msgid "Learn more about incoming email addresses"
-msgstr ""
+msgstr "Докладніше про вхідні адреÑи електронної пошти"
msgid "Learn more about protected branches"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про захищені гілки"
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "ДізнайтеÑÑŒ більше"
@@ -5783,10 +6174,10 @@ msgid "Loading..."
msgstr "ЗавантаженнÑ..."
msgid "Loading…"
-msgstr ""
+msgstr "ЗавантаженнÑ…"
msgid "Localization"
-msgstr ""
+msgstr "Регіональні налаштуваннÑ"
msgid "Lock"
msgstr "Блокувати"
@@ -5821,6 +6212,21 @@ msgstr "Вхід за допомогою Ñмарт-картки"
msgid "Logs"
msgstr "Логи"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr "Затверджуючі оÑоби"
+
+msgid "MRApprovals|Pending approvals"
+msgstr ""
+
+msgid "MRDiff|Show changes only"
+msgstr ""
+
+msgid "MRDiff|Show full file"
+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 Ñервера, щоб Ñкоротити Ñ‡Ð°Ñ Ð´Ð»Ñ ÐºÐ»Ð¾Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ñ– Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ з великих репозиторіїв."
@@ -5855,7 +6261,7 @@ msgid "Manage project labels"
msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼Ñ–Ñ‚ÐºÐ°Ð¼Ð¸ проекту"
msgid "Manage two-factor authentication"
-msgstr ""
+msgstr "ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð²Ð¾Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¾ÑŽ автентифікацією"
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr "Керуйте членÑтвом у вашій групі додаючи ще один рівень безпеки із SAML."
@@ -5866,6 +6272,9 @@ msgstr "МаніфеÑÑ‚"
msgid "Manifest file import"
msgstr "Імпортувати файл маніфеÑту"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "Зв’Ñзати обліковий Ð·Ð°Ð¿Ð¸Ñ FogBugz з кориÑтувачем GitLab"
@@ -5888,41 +6297,11 @@ msgid "Mark todo as done"
msgstr "Відмітити Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð¸Ð¼"
msgid "Markdown"
-msgstr ""
+msgstr "Markdown"
msgid "Markdown enabled"
msgstr "Markdown увімкнено"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr "Додати ненумерований ÑпиÑок"
-
-msgid "MarkdownToolbar|Add a link"
-msgstr "Додати поÑиланнÑ"
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr "Додати нумерований ÑпиÑок"
-
-msgid "MarkdownToolbar|Add a table"
-msgstr "Додати таблицю"
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr "Додати ÑпиÑок завдань"
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr "Додати жирний текÑÑ‚"
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr "Додати курÑивний текÑÑ‚"
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr "Повний екран"
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr "Ð’Ñтавити цитату"
-
-msgid "MarkdownToolbar|Insert code"
-msgstr "Ð’Ñтавити код"
-
msgid "Maven Metadata"
msgstr "Maven-метадані"
@@ -5948,10 +6327,10 @@ msgid "Members"
msgstr "КориÑтувачі"
msgid "Members can be added by project <i>Maintainers</i> or <i>Owners</i>"
-msgstr ""
+msgstr "УчаÑники можуть будуть додані <i>Керівниками</i> або <i>ВлаÑниками</i> проекту"
msgid "Members of <strong>%{project_name}</strong>"
-msgstr ""
+msgstr "УчаÑники <strong>%{project_name}</strong>"
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 "УчаÑники будуть перенаправлені Ñюди, коли будуть заходити до вашої групи. Отримайте його від Ñвого провайдера ідентифікації, де вона також може називатиÑÑ \"SSO Service Location\", \"SAML Token Issuance Endpoint\", або \"SAML 2.0/W-Federation URL\"."
@@ -5966,15 +6345,18 @@ msgid "Merge Requests created"
msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð±ÑƒÐ»Ð¾ Ñтворено"
msgid "Merge commit message"
-msgstr ""
+msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñƒ-злиттÑ"
msgid "Merge events"
msgstr "Події злиттÑ"
msgid "Merge immediately"
-msgstr ""
+msgstr "Злити негайно"
msgid "Merge in progress"
+msgstr "Ð—Ð»Ð¸Ñ‚Ñ‚Ñ Ð² процеÑÑ–"
+
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
msgstr ""
msgid "Merge request"
@@ -5990,31 +6372,31 @@ msgid "Merge requests are a place to propose changes you've made to a project an
msgstr "Запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ â€” це ÑпоÑіб запропонувати Ñвої зміни до проекту Ñ– обговорити Ñ—Ñ… із іншими"
msgid "Merge when pipeline succeeds"
-msgstr ""
+msgstr "Злити, коли конвеєр уÑпішно завершитьÑÑ"
msgid "MergeRequests|Add a reply"
-msgstr ""
+msgstr "Додати відповідь"
msgid "MergeRequests|An error occurred while saving the draft comment."
msgstr "Виникла помилка під Ñ‡Ð°Ñ Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ Ñ‡ÐµÑ€Ð½ÐµÑ‚ÐºÐ¸ коментарÑ."
msgid "MergeRequests|Discussion stays resolved"
-msgstr ""
+msgstr "ÐžÐ±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð»Ð¸ÑˆÐ°Ñ”Ñ‚ÑŒÑÑ Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð¸Ð¼"
msgid "MergeRequests|Discussion stays unresolved"
-msgstr ""
+msgstr "ÐžÐ±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð»Ð¸ÑˆÐ°Ñ”Ñ‚ÑŒÑÑ Ð½ÐµÐ²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð¸Ð¼"
msgid "MergeRequests|Discussion will be resolved"
-msgstr ""
+msgstr "ÐžÐ±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ðµ вирішеним"
msgid "MergeRequests|Discussion will be unresolved"
-msgstr ""
+msgstr "ÐžÐ±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð±ÑƒÐ´Ðµ невирішеним"
msgid "MergeRequests|Jump to next unresolved discussion"
-msgstr ""
+msgstr "Перейти до наÑтупного невирішеного обговореннÑ"
msgid "MergeRequests|Reply..."
-msgstr ""
+msgstr "ВідповіÑти..."
msgid "MergeRequests|Resolve this discussion in a new issue"
msgstr "Вирішити це Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð² новій задачі"
@@ -6032,26 +6414,29 @@ msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr "ПереглÑнути замінений файл Ñтаном на %{commitId}"
msgid "MergeRequests|commented on commit %{commitLink}"
-msgstr ""
+msgstr "прокоментував (-ла) коміт %{commitLink}"
msgid "MergeRequests|started a discussion"
-msgstr ""
+msgstr "розпочав (-ла) обговореннÑ"
msgid "MergeRequests|started a discussion on %{linkStart}an old version of the diff%{linkEnd}"
-msgstr ""
+msgstr "розпочав (-ла) Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ %{linkStart}Ñтарої верÑÑ–Ñ— порівнÑÐ½Ð½Ñ (diff)%{linkEnd}"
msgid "MergeRequests|started a discussion on %{linkStart}the diff%{linkEnd}"
-msgstr ""
+msgstr "розпочав (-ла) Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ %{linkStart}порівнÑÐ½Ð½Ñ (diff)%{linkEnd}"
msgid "MergeRequests|started a discussion on an outdated change in commit %{linkStart}%{commitId}%{linkEnd}"
-msgstr ""
+msgstr "розпочав (-ла) Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ñтарілих змін в коміті %{linkStart}%{commitId}%{linkEnd}"
msgid "MergeRequests|started a discussion on commit %{linkStart}%{commitId}%{linkEnd}"
-msgstr ""
+msgstr "розпочав (-ла) коміту %{linkStart}%{commitId}%{linkEnd}"
msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
msgstr "%{paragraphStart} Ð¾Ð¿Ð¸Ñ Ð·Ð¼Ñ–Ð½ÐµÐ½Ð¾ %{descriptionChangedTimes} раз(а,ів) %{timeDifferenceMinutes}%{paragraphEnd}"
+msgid "MergeRequest|Error loading full diff. Please try again."
+msgstr ""
+
msgid "MergeRequest|Filter files"
msgstr "Фільтр файлів"
@@ -6059,7 +6444,7 @@ msgid "MergeRequest|No files found"
msgstr "Файлів не знайдено"
msgid "MergeRequest|Search files"
-msgstr ""
+msgstr "Пошук файлів"
msgid "Merged"
msgstr "Злито"
@@ -6080,7 +6465,7 @@ msgid "Metrics and profiling"
msgstr "Метрики та профілюваннÑ"
msgid "Metrics for environment"
-msgstr ""
+msgstr "Метрики Ð´Ð»Ñ Ñередовища"
msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
msgstr "Перевірте документацію CI/CD щодо Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð² Ñередовищі"
@@ -6089,10 +6474,10 @@ msgid "Metrics|Create metric"
msgstr "Створити метрику"
msgid "Metrics|Delete metric"
-msgstr ""
+msgstr "Видалити метрику"
msgid "Metrics|Delete metric?"
-msgstr ""
+msgstr "Видалити метрику?"
msgid "Metrics|Edit metric"
msgstr "Редагувати метрику"
@@ -6104,7 +6489,7 @@ msgid "Metrics|For grouping similar metrics"
msgstr "Ð”Ð»Ñ Ð³Ñ€ÑƒÐ¿ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð´Ñ–Ð±Ð½Ð¸Ñ… метрик"
msgid "Metrics|Label of the y-axis (usually the unit). The x-axis always represents time."
-msgstr ""
+msgstr "Мітка оÑÑ– - y (зазвичай Ð¾Ð´Ð¸Ð½Ð¸Ñ†Ñ Ð²Ð¸Ð¼Ñ–Ñ€ÑŽÐ²Ð°Ð½Ð½Ñ). ОÑÑ– - x завжди позначає чаÑ."
msgid "Metrics|Learn about environments"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ñередовища"
@@ -6122,14 +6507,11 @@ msgid "Metrics|No deployed environments"
msgstr "Ðемає розгорнутих Ñередовищ"
msgid "Metrics|PromQL query is valid"
-msgstr ""
+msgstr "Запит PromQL Ñ” дійÑним"
msgid "Metrics|Prometheus Query Documentation"
msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð¿Ð¾ запитам Prometheus"
-msgid "Metrics|System"
-msgstr "СиÑтема"
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про Ñередовища. Будь лаÑка, Ñпробуйте ще раз"
@@ -6140,7 +6522,7 @@ msgid "Metrics|There was an error getting environments information."
msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ— про Ñередовища."
msgid "Metrics|There was an error trying to validate your query"
-msgstr ""
+msgstr "Помилка при перевірці вашого запиту"
msgid "Metrics|There was an error while retrieving metrics"
msgstr "ТрапилаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° під Ñ‡Ð°Ñ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ð½Ð½Ñ Ð¼ÐµÑ‚Ñ€Ð¸Ðº"
@@ -6164,7 +6546,7 @@ msgid "Metrics|Y-axis label"
msgstr "Ðазва оÑÑ– Y"
msgid "Metrics|You're about to permanently delete this metric. This cannot be undone."
-msgstr ""
+msgstr "Ви збираєтеÑÑ Ð¾Ñтаточно видалити цю метрику. Це не можна відмінити."
msgid "Metrics|e.g. Throughput"
msgstr "напр. пропуÑкна здатніÑÑ‚ÑŒ"
@@ -6241,17 +6623,20 @@ msgstr "СкаÑувати"
msgid "Modal|Close"
msgstr "Закрити"
+msgid "Modify commit message"
+msgstr "Змінити Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð²"
+
msgid "Modify commit messages"
-msgstr ""
+msgstr "Змінити Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð²"
msgid "Modify merge commit"
-msgstr ""
+msgstr "Змінити коміт-злиттÑ"
msgid "Monday"
-msgstr ""
+msgstr "Понеділок"
msgid "Monitor your errors by integrating with Sentry"
-msgstr ""
+msgstr "Моніторинг ваших помилок шлÑхом інтеграції із Sentry"
msgid "Monitoring"
msgstr "Моніторинг"
@@ -6274,6 +6659,9 @@ msgstr "Детальніше"
msgid "More information is available|here"
msgstr "тут"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr "Ðайбільше в обраних"
@@ -6298,6 +6686,9 @@ msgstr "Ðазвіть ваш індивідуальний ключ за допÐ
msgid "Name:"
msgstr "Ім’Ñ:"
+msgid "Naming, tags, avatar"
+msgstr ""
+
msgid "Naming, visibility"
msgstr "ÐайменуваннÑ, видиміÑÑ‚ÑŒ"
@@ -6314,7 +6705,7 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "Вийти Ñ– зайти під іншим обліковим запиÑом"
msgid "Need help?"
-msgstr ""
+msgstr "Потрібна допомога?"
msgid "Network"
msgstr "Мережа"
@@ -6329,7 +6720,7 @@ msgid "New Application"
msgstr "Ðовий додаток"
msgid "New Environment"
-msgstr ""
+msgstr "Ðове Ñередовище"
msgid "New Group"
msgstr "Ðова група"
@@ -6348,9 +6739,12 @@ msgid "New Label"
msgstr "Ðова мітка"
msgid "New Milestone"
-msgstr ""
+msgstr "Ðовий етап"
msgid "New Pages Domain"
+msgstr "Ðовий домен Pages"
+
+msgid "New Password"
msgstr ""
msgid "New Pipeline Schedule"
@@ -6359,20 +6753,20 @@ msgstr "Ðовий розклад Конвеєра"
msgid "New Snippet"
msgstr "Ðовий Ñніпет"
-msgid "New Snippets"
-msgstr "Ðові Ñніпети"
-
msgid "New branch"
msgstr "Ðова гілка"
msgid "New branch unavailable"
msgstr "Ðова гілка недоÑтупна"
+msgid "New deploy key"
+msgstr ""
+
msgid "New directory"
msgstr "Ðовий каталог"
msgid "New environment"
-msgstr ""
+msgstr "Ðове Ñередовище"
msgid "New epic"
msgstr "Ðовий епік"
@@ -6396,7 +6790,7 @@ msgid "New merge request"
msgstr "Ðовий запит на злиттÑ"
msgid "New milestone"
-msgstr ""
+msgstr "Ðовий етап"
msgid "New pipelines will cancel older, pending pipelines on the same branch"
msgstr "Ðові конвеєри ÑкаÑують Ñтарі, що очікують на тій же гілці"
@@ -6422,12 +6816,18 @@ msgstr "Ðовий..."
msgid "No"
msgstr "ÐÑ–"
+msgid "No %{providerTitle} repositories available to import"
+msgstr "Ðемає репозиторіїв %{providerTitle} Ð´Ð»Ñ Ñ–Ð¼Ð¿Ð¾Ñ€Ñ‚Ñƒ"
+
msgid "No Label"
msgstr "Без Мітки"
-msgid "No activities found"
+msgid "No Tag"
msgstr ""
+msgid "No activities found"
+msgstr "Ðе знайднено активноÑтей"
+
msgid "No assignee"
msgstr "Ðемає виконавцÑ"
@@ -6438,7 +6838,7 @@ msgid "No changes"
msgstr "Ðемає змін"
msgid "No changes between %{ref_start}%{source_branch}%{ref_end} and %{ref_start}%{target_branch}%{ref_end}"
-msgstr ""
+msgstr "Ðемає змін між %{ref_start}%{source_branch}%{ref_end} та %{ref_start}%{target_branch}%{ref_end}"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "Ðеможливо з'єднатиÑÑŒ із Ñервером Gitaly, будь лаÑка, перевірте логи!"
@@ -6452,13 +6852,16 @@ msgstr "ВнеÑки не знайдено"
msgid "No credit card required."
msgstr "Ðе потрібна кредитна картка."
-msgid "No details available"
+msgid "No designs found."
msgstr ""
+msgid "No details available"
+msgstr "Ðемає деталей Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
+
msgid "No due date"
msgstr "Ðемає"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6468,7 +6871,7 @@ msgid "No file chosen"
msgstr "Файл не вибрано"
msgid "No file selected"
-msgstr ""
+msgstr "Файл не вибраний"
msgid "No files found."
msgstr "Ðе знайдено жодного файлу."
@@ -6483,7 +6886,7 @@ msgid "No license. All rights reserved"
msgstr "Ðемає ліцензії. Ð’ÑÑ– права захищені"
msgid "No matching results"
-msgstr ""
+msgstr "Ðемає відповідних результатів"
msgid "No merge requests for the selected time period."
msgstr "Ðемає запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð° вибраний період чаÑу."
@@ -6495,13 +6898,13 @@ msgid "No messages were logged"
msgstr "Ðемає повідомлень у журналі"
msgid "No milestones to show"
-msgstr ""
+msgstr "Ðемає етапів Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ"
msgid "No other labels with such name or description"
msgstr "Ðемає інших міток з таким іменем або опиÑом"
msgid "No preview for this file type"
-msgstr ""
+msgstr "Попереднього переглÑду Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ типу файлів немає"
msgid "No prioritised labels with such name or description"
msgstr "Ðемає пріоритетних міток з таким іменем або опиÑом"
@@ -6522,7 +6925,7 @@ msgid "No schedules"
msgstr "Ðемає розкладів"
msgid "No start date"
-msgstr ""
+msgstr "Ðемає дати початку"
msgid "No, directly import the existing email addresses and usernames."
msgstr "ÐÑ–, безпоÑередньо імпортувати Ñ–Ñнуючі адреÑи електронної пошти та імена кориÑтувачів."
@@ -6554,11 +6957,14 @@ msgstr "ÐедоÑтатньо даних"
msgid "Not now"
msgstr "Пізніше"
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "Майте на увазі, що гілка master захищена автоматично. %{link_to_protected_branches}"
msgid "Note that this invitation was sent to %{mail_to_invite_email}, but you are signed in as %{link_to_current_user} with email %{mail_to_current_user}."
-msgstr ""
+msgstr "Зауважте, що це Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ надіÑлано на %{mail_to_invite_email}, але ви увійшли Ñк %{link_to_current_user} з електронною поштою %{mail_to_current_user}."
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 Ñ– підключати репозиторії без ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¾ÑобиÑтого токену доÑтупу."
@@ -6591,10 +6997,10 @@ msgid "Notification events"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾ події"
msgid "Notification setting"
-msgstr ""
+msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñповіщень"
msgid "Notification setting - %{notification_title}"
-msgstr ""
+msgstr "Параметр ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ - %{notification_title}"
msgid "NotificationEvent|Close issue"
msgstr "Задача закрита"
@@ -6703,13 +7109,13 @@ msgid "Only policy:"
msgstr ""
msgid "Only proceed if you trust %{idp_url} to control your GitLab account sign in."
-msgstr ""
+msgstr "Продовжуйте тільки Ñкщо ви довірÑєте %{idp_url} контроль над входом до вашого облікового запиÑу GitLab."
msgid "Only project members can comment."
msgstr "Тільки учаÑники проекту можуть залишати коментарі."
msgid "Only project members will be imported. Group members will be skipped."
-msgstr ""
+msgstr "Лише учаÑника проекту будуть імпортовані. УчаÑники групи будуть пропущені."
msgid "Oops, are you sure?"
msgstr "Ой, а ви впевнені?"
@@ -6718,13 +7124,13 @@ msgid "Open"
msgstr "Відкриті"
msgid "Open Documentation"
-msgstr ""
+msgstr "Відкрити документацію"
msgid "Open comment type dropdown"
-msgstr ""
+msgstr "Випадаючий ÑпиÑок типу коментарів"
msgid "Open errors"
-msgstr ""
+msgstr "Відкрити помилки"
msgid "Open in Xcode"
msgstr "Відкрити в Xcode"
@@ -6759,6 +7165,9 @@ msgstr "Операції"
msgid "Operations Dashboard"
msgstr "Панель ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñми"
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr "Додайте проект до панелі керуваннÑ"
@@ -6766,6 +7175,9 @@ msgid "OperationsDashboard|The operations dashboard provides a summary of each p
msgstr "Панель ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñми міÑтить інформацію про Ñтан кожного з проектів разом зі Ñтаном його конвеєрів та попереджень."
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
+msgstr "Ðеможливо додати %{invalidProjects}. Панель ÐºÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¾Ð¿ÐµÑ€Ð°Ñ†Ñ–Ñми доÑтупна Ð´Ð»Ñ Ð¿ÑƒÐ±Ð»Ñ–Ñ‡Ð½Ð¸Ñ… та приватних проектів в групах із тарифним планом Gold."
+
+msgid "Optional"
msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
@@ -6814,10 +7226,10 @@ msgid "Pages"
msgstr "Сторінки"
msgid "Pages Domain"
-msgstr ""
+msgstr "Домен Pages"
msgid "Pages Domains"
-msgstr ""
+msgstr "Домени Pages"
msgid "Pagination|Last »"
msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ Â»"
@@ -6832,10 +7244,7 @@ msgid "Pagination|« First"
msgstr "« Перша"
msgid "Parameter"
-msgstr ""
-
-msgid "Parent epic"
-msgstr ""
+msgstr "Параметр"
msgid "Part of merge request changes"
msgstr "ЧаÑтина змін у запиті на злиттÑ"
@@ -6844,13 +7253,16 @@ msgid "Password"
msgstr "Пароль"
msgid "Past due"
+msgstr "ПроÑтрочені"
+
+msgid "Paste a machine public key here. Read more about how to generate it %{link_start}here%{link_end}"
msgstr ""
msgid "Paste epic link"
-msgstr ""
+msgstr "Ð’Ñтавити поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° епік"
msgid "Paste issue link"
-msgstr ""
+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."
@@ -6889,17 +7301,14 @@ msgid "Personal Access Token"
msgstr "Токену перÑонального доÑтупу"
msgid "Personal project creation is not allowed. Please contact your administrator with questions"
-msgstr ""
+msgstr "Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿ÐµÑ€Ñональних проектів не дозволено. Будь лаÑка, звернітьÑÑ Ð´Ð¾ Ñвого адмініÑтратор із питаннÑми"
msgid "Pick a name"
-msgstr ""
+msgstr "Виберіть ім'Ñ"
msgid "Pipeline"
msgstr "Конвеєр"
-msgid "Pipeline Health"
-msgstr "Стан Конвеєра"
-
msgid "Pipeline Schedule"
msgstr "Розклад Конвеєра"
@@ -6960,11 +7369,17 @@ msgstr "Змінні"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "Спеціальні"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr "Коміт: %{ci_status}"
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr "Конвеєр: %{ci_status}"
+
msgid "Pipelines"
msgstr "Конвеєри"
msgid "Pipelines charts"
-msgstr "Чарти Конвеєрів"
+msgstr "СтатиÑтика конвеєрів"
msgid "Pipelines for last month"
msgstr "Конвеєри за оÑтанній міÑÑць"
@@ -6975,6 +7390,9 @@ msgstr "Конвеєри за оÑтанній тиждень"
msgid "Pipelines for last year"
msgstr "Конвеєри за оÑтанній рік"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr "Виконуйте збірки із впевненіÑÑ‚ÑŽ"
@@ -7081,7 +7499,7 @@ msgid "Play"
msgstr "Відтворити"
msgid "Please %{link_to_register} or %{link_to_sign_in} to comment"
-msgstr ""
+msgstr "Будь лаÑка, %{link_to_register} або %{link_to_sign_in} щоб прокоментувати"
msgid "Please accept the Terms of Service before continuing."
msgstr "Будь лаÑка, Ð´Ð»Ñ Ð¿Ñ€Ð¾Ð´Ð¾Ð²Ð¶ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¸Ð¹Ð¼Ñ–Ñ‚ÑŒ умови Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг."
@@ -7095,9 +7513,21 @@ msgstr "Будь лаÑка Ñконвертуйте Ñ—Ñ… в %{link_to_git} Ñ– Ð
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr "Будь лаÑка Ñконвертуйте Ñ—Ñ… в Git на Google Code, Ñ– виконайте знову %{link_to_import_flow}."
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr "Введіть опиÑове ім'Ñ Ð³Ñ€ÑƒÐ¿Ð¸."
@@ -7107,9 +7537,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr "Зверніть увагу, що Ñ†Ñ Ð¿Ñ€Ð¾Ð³Ñ€Ð°Ð¼Ð° не Ñ” чаÑтиною GitLab, Ñ– ви повинні впевнитиÑÑ Ñƒ Ñ—Ñ— безпеці, перш ніж надавати доÑтуп."
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr "Будь лаÑка виберіть хоча б один фільтр, щоб побачити результати"
+msgid "Please set a new password before proceeding."
+msgstr "Будь лаÑка, вÑтановіть пароль перед продовженнÑм."
+
msgid "Please solve the reCAPTCHA"
msgstr "Будь лаÑка, пройдіть reCAPTCHA"
@@ -7117,7 +7556,7 @@ msgid "Please try again"
msgstr "Будь лаÑка, Ñпробуйте ще раз"
msgid "Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version."
-msgstr ""
+msgstr "Будь лаÑка, оновіть PostgreSQL до верÑÑ–Ñ— 9.6 або вище. Стан реплікації не може бути доÑтовірно визначений у поточній верÑÑ–Ñ—."
msgid "Please use this form to report users to GitLab who create spam issues, comments or behave inappropriately."
msgstr "Будь лаÑка, викориÑтовуйте цю форму, щоб повідомлÑти GitLab про кориÑтувачів, Ñкі Ñтворюють задачі Ñ– коментарі зі Ñпамом, або поводÑÑ‚ÑŒÑÑ Ð½ÐµÐ²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð¾."
@@ -7134,6 +7573,9 @@ msgstr "ÐалаштуваннÑ"
msgid "Preferences|Navigation theme"
msgstr "Тема навігації"
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ñ” екÑпериментальною Ñ– переклади ще не завершені"
+
msgid "Press Enter or click to search"
msgstr "Ð”Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ натиÑніть Enter або клікніть"
@@ -7162,7 +7604,7 @@ msgid "Prioritized label"
msgstr "Пріоритетні мітки"
msgid "Private"
-msgstr ""
+msgstr "Приватний"
msgid "Private - Project access must be granted explicitly to each user."
msgstr "Приватний — доÑтуп до проекту повинен надаватиÑÑ ÐºÐ¾Ð¶Ð½Ð¾Ð¼Ñƒ кориÑтувачеві."
@@ -7186,13 +7628,13 @@ msgid "Profiles| You are going to change the username %{currentUsernameBold} to
msgstr "Ви збираєтеÑÑ Ð·Ð¼Ñ–Ð½Ð¸Ñ‚Ð¸ ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача %{currentUsernameBold} на %{newUsernameBold}. Профіль та проекти будуть перенаправлÑтиÑÑ Ð½Ð° проÑÑ‚Ñ–Ñ€ імен %{newUsername}, але таке Ð¿ÐµÑ€ÐµÐ½Ð°Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð·Ð°ÐºÑ–Ð½Ñ‡Ð¸Ñ‚ÑŒÑÑ, коли проÑÑ‚Ñ–Ñ€ імен %{currentUsername} буде зареєÑтровано на іншого кориÑтувача або групу. Будь лаÑка, оновіть віддалені адреÑи в репозиторіÑÑ… Git Ñкомога швидше."
msgid "Profiles|@username"
-msgstr ""
+msgstr "@ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
msgid "Profiles|Account scheduled for removal."
msgstr "Обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð¿Ð»Ð°Ð½Ð¾Ð²Ð°Ð½Ð¸Ð¹ Ð´Ð»Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ."
msgid "Profiles|Activate signin with one of the following services"
-msgstr ""
+msgstr "Ðктивуйте вхід за допомогою однієї з наÑтупних Ñлужб"
msgid "Profiles|Active"
msgstr ""
@@ -7213,28 +7655,28 @@ msgid "Profiles|Change username"
msgstr "Змінити ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
msgid "Profiles|Changing your username can have unintended side effects."
-msgstr ""
+msgstr "Зміна імені кориÑтувача може мати небажані побічні ефекти."
msgid "Profiles|Choose file..."
msgstr "Вибрати файл..."
msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information"
-msgstr ""
+msgstr "Виберіть Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ внеÑків до приватних репозиторіїв у вашому публічному профілі без інформації про проекти, репозиторії або організації"
msgid "Profiles|City, country"
-msgstr ""
+msgstr "МіÑто, країна"
msgid "Profiles|Clear status"
msgstr "ОчиÑтити ÑтатуÑ"
msgid "Profiles|Click on icon to activate signin with one of the following services"
-msgstr ""
+msgstr "Клікніть на іконку, щоб активувати вхід за допомогою одного із наÑтупних ÑервіÑів"
msgid "Profiles|Connect"
-msgstr ""
+msgstr "Приєднати"
msgid "Profiles|Connected Accounts"
-msgstr ""
+msgstr "Підключені облікові запиÑи"
msgid "Profiles|Current path: %{path}"
msgstr "Поточний шлÑÑ…: %{path}"
@@ -7255,7 +7697,7 @@ msgid "Profiles|Deleting an account has the following effects:"
msgstr "Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¾Ð³Ð¾ запиÑу неÑе наÑтупні наÑлідки:"
msgid "Profiles|Disconnect"
-msgstr ""
+msgstr "Від'єднати"
msgid "Profiles|Do not show on profile"
msgstr "Ðе відображати у профілі"
@@ -7267,10 +7709,10 @@ msgid "Profiles|Edit Profile"
msgstr "Редагувати профіль"
msgid "Profiles|Enter your name, so people you know can recognize you"
-msgstr ""
+msgstr "Введіть ваше ім'Ñ, щоб люди, Ñких ви знаєте, могли Ð²Ð°Ñ ÑƒÐ¿Ñ–Ð·Ð½Ð°Ñ‚Ð¸"
msgid "Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)"
-msgstr ""
+msgstr "Підвищити рівень безпеки вашого облікового запиÑу за допомогою ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ð´Ð²Ð¾Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¾Ñ— автентифікації (2FA)"
msgid "Profiles|Invalid password"
msgstr "Ðеправильний пароль"
@@ -7309,13 +7751,13 @@ msgid "Profiles|Set new profile picture"
msgstr "Ð’Ñтановити нове Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ"
msgid "Profiles|Social sign-in"
-msgstr ""
+msgstr "Вхід через Ñоціальні мережі"
msgid "Profiles|Some options are unavailable for LDAP accounts"
msgstr "ДеÑкі параметри недоÑтупні Ð´Ð»Ñ Ð¾Ð±Ð»Ñ–ÐºÐ¾Ð²Ð¸Ñ… запиÑів LDAP"
msgid "Profiles|Tell us about yourself in fewer than 250 characters"
-msgstr ""
+msgstr "Розкажіть про Ñебе в межах 250 Ñимволів"
msgid "Profiles|The maximum file size allowed is 200KB."
msgstr "МакÑимальний розмір файлу 200КБ."
@@ -7324,7 +7766,7 @@ msgid "Profiles|This doesn't look like a public SSH key, are you sure you want t
msgstr "Це не Ñхоже на публічниц ключ SSH. Ви впевнені, що хочете його додати?"
msgid "Profiles|This email will be displayed on your public profile"
-msgstr ""
+msgstr "Ð¦Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð° адреÑа буде відображатиÑÑ Ñƒ вашому публічному профілі"
msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}"
msgstr "Ð¦Ñ Ð°Ð´Ñ€ÐµÑа електронної пошти буде викориÑтовуватиÑÑ Ð´Ð»Ñ Ð±Ñ€Ð°ÑƒÐ·ÐµÑ€Ð½Ð¸Ñ… операцій, таких Ñк Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð° злиттÑ. %{learn_more}"
@@ -7332,14 +7774,11 @@ msgstr "Ð¦Ñ Ð°Ð´Ñ€ÐµÑа електронної пошти буде викорÐ
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr "Цей Ñмайлик та Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð±ÑƒÐ´ÑƒÑ‚ÑŒ показані у вашому профілі та в інтерфейÑÑ–."
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
-msgstr ""
+msgstr "Ð¦Ñ Ñ–Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð±ÑƒÐ´Ðµ відображатиÑÑ Ñƒ вашому профілі"
msgid "Profiles|Two-Factor Authentication"
-msgstr ""
+msgstr "Двофакторна автентифікаціÑ"
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "Введіть ваш %{confirmationValue} Ð´Ð»Ñ Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ:"
@@ -7366,13 +7805,13 @@ msgid "Profiles|Username successfully changed"
msgstr "Ð†Ð¼â€™Ñ ÐºÐ¾Ñ€Ð¸Ñтувача уÑпішно збережено"
msgid "Profiles|Using emojis in names seems fun, but please try to set a status message instead"
-msgstr ""
+msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ Ñмайликів в іменах виглÑдає дотепно, але, будь лаÑка, краще викориÑтовуйте Ñ—Ñ… в повідомленнÑÑ… про ÑтатуÑ"
msgid "Profiles|What's your status?"
msgstr "Який ваш ÑтатуÑ?"
msgid "Profiles|Who you represent or work for"
-msgstr ""
+msgstr "Кого ви предÑтавлÑєте або на кого працюєте"
msgid "Profiles|You can change your avatar here"
msgstr "Тут ви можете змінити Ñвій аватар"
@@ -7393,19 +7832,19 @@ msgid "Profiles|You must transfer ownership or delete these groups before you ca
msgstr "Вам необхідно змінити влаÑника або видалити ці групи перед тим Ñк видалити ваш обліковий запиÑ."
msgid "Profiles|Your LinkedIn profile name from linkedin.com/in/profilename"
-msgstr ""
+msgstr "Ваше ім'Ñ Ð¿Ñ€Ð¾Ñ„Ñ–Ð»ÑŽ LinkedIn з linkedin.com/in/profilename"
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ñ” влаÑником в цих групах:"
msgid "Profiles|Your email address was automatically set based on your %{provider_label} account"
-msgstr ""
+msgstr "Ваша адреÑа електронної пошти була автоматично вÑтановлена на оÑнові вашого облікового запиÑу %{provider_label}"
msgid "Profiles|Your location was automatically set based on your %{provider_label} account"
-msgstr ""
+msgstr "Ваше міÑÑ†ÐµÐ·Ð½Ð°Ñ…Ð¾Ð´Ð¶ÐµÐ½Ð½Ñ Ð±ÑƒÐ»Ð¾ автоматично вÑтановлено на оÑнові вашого облікового запиÑу %{provider_label}"
msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you"
-msgstr ""
+msgstr "Ваше Ñ–Ð¼â€™Ñ Ð±ÑƒÐ»Ð¾ автоматично вÑтановлено на оÑнові вашого облікового запиÑу %{provider_label} щоб люди могли Ð²Ð°Ñ Ð²Ð¿Ñ–Ð·Ð½Ð°Ñ‚Ð¸"
msgid "Profiles|Your status"
msgstr "Ваш ÑтатуÑ"
@@ -7414,10 +7853,10 @@ msgid "Profiles|e.g. My MacBook key"
msgstr "наприклад, мій ключ MacBook"
msgid "Profiles|username"
-msgstr ""
+msgstr "ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
msgid "Profiles|website.com"
-msgstr ""
+msgstr "Ñайт.укр"
msgid "Profiles|your account"
msgstr "ваш обліковий запиÑ"
@@ -7434,6 +7873,9 @@ msgstr "ПрогреÑ"
msgid "Project"
msgstr "Проект"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr "Проект \"%{name}\" більше не доÑтупний. Щоб продовжити виберіть інший проект."
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "Проект '%{project_name}' перебуває в процеÑÑ– видаленнÑ."
@@ -7476,8 +7918,11 @@ msgstr "ЗакінчивÑÑ Ñ‚ÐµÑ€Ð¼Ñ–Ð½ дії поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° проÐ
msgid "Project export started. A download link will be sent by email."
msgstr "Розпочато екÑпорт проекту. ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð´Ð»Ñ ÑÐºÐ°Ñ‡ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ надіÑлана електронною поштою."
+msgid "Project has too many %{label_for_message} to search"
+msgstr "Ð’ проекті занадто багато %{label_for_message} Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ"
+
msgid "Project members"
-msgstr ""
+msgstr "УчаÑники проекту"
msgid "Project name"
msgstr "Ðазва проекту"
@@ -7486,7 +7931,7 @@ msgid "Project slug"
msgstr "ШлÑÑ… проекту"
msgid "Project:"
-msgstr ""
+msgstr "Проект:"
msgid "ProjectActivityRSS|Subscribe"
msgstr "ПідпиÑатиÑÑ"
@@ -7575,12 +8020,18 @@ msgstr "КориÑтувачі можуть відправлÑти в цей Ñ€Ð
msgid "Projects"
msgstr "Проекти"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr "Спільні проекти з %{group_name}"
msgid "Projects that belong to a group are prefixed with the group namespace. Existing projects may be moved into a group."
msgstr "Проекти, що належать до групи, викориÑтовують Ñ—Ñ— ім'Ñ Ñк префікÑ. ІÑнуючі проекти можуть бути переміщені в групу."
+msgid "Projects with write access"
+msgstr ""
+
msgid "ProjectsDropdown|Frequently visited"
msgstr "ЧаÑто відвідувані"
@@ -7657,7 +8108,7 @@ msgid "PrometheusService|Custom metrics"
msgstr "ВлаÑні метрики"
msgid "PrometheusService|Enable Prometheus to define custom metrics, using either option above"
-msgstr ""
+msgstr "Увімкнути Prometheus Ð´Ð»Ñ Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð²Ð»Ð°Ñних метрик, викориÑтовуючи будь-Ñкий з варіантів вище"
msgid "PrometheusService|Finding and configuring metrics..."
msgstr "Пошук та Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¼ÐµÑ‚Ñ€Ð¸Ðº..."
@@ -7789,7 +8240,7 @@ msgid "Pseudonymizer data collection"
msgstr "Збір даних Pseudonymizer"
msgid "Public"
-msgstr ""
+msgstr "Публічний"
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "Публічна — група та вÑÑ– публічні проекти можуть переглÑдатиÑÑ Ð±ÐµÐ· автентифікації."
@@ -7797,6 +8248,9 @@ msgstr "Публічна — група та вÑÑ– публічні проекÑ
msgid "Public - The project can be accessed without any authentication."
msgstr "Публічний — проект может переглÑдатиÑÑ Ð±ÐµÐ· автентифікації."
+msgid "Public deploy keys (%{deploy_keys_count})"
+msgstr ""
+
msgid "Public pipelines"
msgstr "Публічні конвеєри"
@@ -7831,19 +8285,19 @@ msgid "Quarters"
msgstr "Квартали"
msgid "Query"
-msgstr ""
+msgstr "Запит"
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "Швидкі дії можна викориÑтовувати в опиÑах задач Ñ– коментарÑÑ…."
msgid "README"
-msgstr ""
+msgstr "ІнÑÑ‚Ñ€ÑƒÐºÑ†Ñ–Ñ (README)"
msgid "Read more"
msgstr "Докладніше"
msgid "Read more about environments"
-msgstr ""
+msgstr "Читати більше про Ñередовища"
msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про права доÑтупу в проекті <strong>%{link_to_help}</strong>"
@@ -7852,6 +8306,12 @@ msgid "Real-time features"
msgstr "Фунції реального чаÑу"
msgid "Receive alerts from manually configured Prometheus servers."
+msgstr "Отримувати Ð¿Ð¾Ð¿ÐµÑ€ÐµÐ´Ð¶ÐµÐ½Ð½Ñ Ð²Ñ–Ð´ налаштованого вручну Ñервера Prometheus."
+
+msgid "Recent"
+msgstr ""
+
+msgid "Recent Project Activity"
msgstr ""
msgid "Recent searches"
@@ -7883,7 +8343,7 @@ msgid "Register / Sign In"
msgstr "ЗареєÑтруватиÑÑ / Увійти"
msgid "Register U2F device"
-msgstr ""
+msgstr "ЗареєÑтрувати приÑтрій U2F"
msgid "Register and see your runners for this group."
msgstr "ЗареєÑтруйте Ñ– переглÑдайте ваші Runner’и Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— групи."
@@ -7916,7 +8376,7 @@ msgid "Related merge requests"
msgstr "Пов'Ñзані запити на злиттÑ"
msgid "Releases"
-msgstr ""
+msgstr "Релізи"
msgid "Releases mark specific points in a project's development history, communicate information about the type of change, and deliver on prepared, often compiled, versions of the software to be reused elsewhere. Currently, releases can only be created through the API."
msgstr ""
@@ -7931,10 +8391,16 @@ msgid "Remove Runner"
msgstr "Видалити Runner"
msgid "Remove all approvals in a merge request when new commits are pushed to its source branch"
-msgstr ""
+msgstr "ВидалÑти вÑÑ– Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ñƒ запитах на злиттÑ, коли у гілці-джерелі з'ÑвлÑÑŽÑ‚ÑŒÑÑ Ð½Ð¾Ð²Ñ– коміти"
msgid "Remove approver"
-msgstr ""
+msgstr "Видалити затверджуючу оÑобу"
+
+msgid "Remove approvers"
+msgstr "Видалити затверджуючих оÑіб"
+
+msgid "Remove approvers?"
+msgstr "Видалити затверджуючих оÑіб?"
msgid "Remove avatar"
msgstr "Видалити аватар"
@@ -7967,13 +8433,13 @@ msgid "Reopen epic"
msgstr "Повторне Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ ÐµÐ¿Ñ–ÐºÑƒ"
msgid "Reopen milestone"
-msgstr ""
+msgstr "Повторне Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ ÐµÑ‚Ð°Ð¿Ñƒ"
msgid "Repair authentication"
-msgstr "Відновити аутентифікацію"
+msgstr "Відновити автентифікацію"
msgid "Reply to comment"
-msgstr ""
+msgstr "ВідповіÑти на коментар"
msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr "ВідповіÑти на це електронне Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð±ÐµÐ·Ð¿Ð¾Ñередньо або %{view_it_on_gitlab}."
@@ -8023,6 +8489,12 @@ msgstr "Результати Ð´Ð»Ñ Ñ‚ÐµÑтового звіту оброблÑ
msgid "Reports|Vulnerability"
msgstr "ВразливіÑÑ‚ÑŒ"
+msgid "Reports|issue"
+msgstr ""
+
+msgid "Reports|merge request"
+msgstr ""
+
msgid "Reports|no changed test results"
msgstr "результати теÑтів не змінилиÑÑ"
@@ -8036,10 +8508,10 @@ msgid "Repository URL"
msgstr "URL репозиторіÑ"
msgid "Repository cleanup"
-msgstr ""
+msgstr "ÐžÑ‡Ð¸Ñ‰ÐµÐ½Ð½Ñ Ñ€ÐµÐ¿Ð¾Ð·Ð¸Ñ‚Ð¾Ñ€Ñ–ÑŽ"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
-msgstr ""
+msgstr "ОчиÑтку репозиторію розпочато. Ви отримаєте Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾ електронній пошті піÑÐ»Ñ Ñ—Ñ— завершеннÑ."
msgid "Repository has no locks."
msgstr "Репозиторій не має блокувань."
@@ -8071,11 +8543,28 @@ msgstr "Вимагати від вÑÑ–Ñ… кориÑтувачів цієї грÑ
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "Вимагати від уÑÑ–Ñ… кориÑтувачів приймати умови Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг та політику конфіденційноÑÑ‚Ñ–, коли вони отримують доÑтуп до GitLab."
-msgid "Resend invite"
+msgid "Require approval from code owners"
msgstr ""
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "Resend invite"
+msgstr "Повторно надіÑлати запрошеннÑ"
+
msgid "Reset authorization key"
-msgstr ""
+msgstr "Скинути ключ авторизації"
msgid "Reset authorization key?"
msgstr ""
@@ -8102,10 +8591,10 @@ msgid "Resolve discussion"
msgstr "Завершити обговореннÑ"
msgid "Resolved"
-msgstr ""
+msgstr "Вирішено"
msgid "Response"
-msgstr ""
+msgstr "Відповідь"
msgid "Response metrics (AWS ELB)"
msgstr "Метрики відповідей (AWS ELB)"
@@ -8117,7 +8606,7 @@ msgid "Response metrics (HA Proxy)"
msgstr "Метрики відповідей (HA Proxy)"
msgid "Response metrics (NGINX Ingress VTS)"
-msgstr ""
+msgstr "Метрики відповідей (NGINX Ingress VTS)"
msgid "Response metrics (NGINX Ingress)"
msgstr "Метрики відповідей (NGINX Ingress)"
@@ -8126,7 +8615,7 @@ msgid "Response metrics (NGINX)"
msgstr "Метрики відповідей (NGINX)"
msgid "Restart Terminal"
-msgstr ""
+msgstr "ПерезапуÑтити термінал"
msgid "Resume"
msgstr "Продовжити"
@@ -8148,7 +8637,7 @@ msgstr[2] "Показати значень"
msgstr[3] "Показати значень"
msgid "Reveal values"
-msgstr ""
+msgstr "Показати значеннÑ"
msgid "Revert this commit"
msgstr "Ðнулювати цей коміт"
@@ -8178,7 +8667,7 @@ msgid "Run CI/CD pipelines for external repositories"
msgstr "ЗапуÑтити CI/CD конвеєри Ð´Ð»Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ–Ñ… репозиторіїв"
msgid "Run tests against your code live using the Web Terminal"
-msgstr ""
+msgstr "ПротеÑтувати ваш запущений код за допомогою Веб-терміналу"
msgid "Run untagged jobs"
msgstr "Виконати Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÐµÐ· тегів"
@@ -8208,7 +8697,7 @@ msgid "Runners API"
msgstr "API Runner’ів"
msgid "Runners activated for this project"
-msgstr ""
+msgstr "Runner'и активовані Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проекту"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr "Runner’и можуть розміщуватиÑÑ Ñƒ різних кориÑтувачів, на Ñерверах Ñ– навіть на вашій локальній машині."
@@ -8231,6 +8720,9 @@ msgstr "Ви викориÑтали уÑÑ– виділенні хвилини дÐ
msgid "Running"
msgstr "ВиконуєтьÑÑ"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "Єдиний вхід SAML"
@@ -8244,7 +8736,7 @@ msgid "SAML Single Sign On Settings"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ”Ð´Ð¸Ð½Ð¾Ð³Ð¾ входу SAML"
msgid "SAML for %{group_name}"
-msgstr ""
+msgstr "SAML Ð´Ð»Ñ %{group_name}"
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr "Відбиток SHA1 Ñертифікату Ð´Ð»Ñ Ð¿Ñ–Ð´Ð¿Ð¸ÑÑƒÐ²Ð°Ð½Ð½Ñ Ñ‚Ð¾ÐºÐµÐ½Ñ–Ð² SAML. Отримайте його від провайдера ідентифікації, де він також може називатиÑÑ \"Thumbprint\"."
@@ -8261,11 +8753,14 @@ msgstr "Відкритий SSH-ключ"
msgid "SSL Verification"
msgstr "Перевірка SSL"
+msgid "Saturday"
+msgstr "Субота"
+
msgid "Save"
msgstr "Зберегти"
msgid "Save Changes"
-msgstr ""
+msgstr "Зберегти зміни"
msgid "Save application"
msgstr "Зберегти заÑтоÑунок"
@@ -8277,7 +8772,7 @@ msgid "Save changes before testing"
msgstr "Зберегти зміни перед теÑтуваннÑм"
msgid "Save comment"
-msgstr ""
+msgstr "Зберегти коментар"
msgid "Save pipeline schedule"
msgstr "Зберегти розклад конвеєра"
@@ -8294,6 +8789,9 @@ msgstr "Заплановано"
msgid "Schedules"
msgstr "Розклади"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "ÐŸÐ»Ð°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ð²ÐµÑ”Ñ€Ñ–Ð²"
@@ -8316,7 +8814,7 @@ msgid "Search"
msgstr "Пошук"
msgid "Search an environment spec"
-msgstr ""
+msgstr "Пошук Ñпецифікації Ñередовища"
msgid "Search branches"
msgstr "Пошук у гілках"
@@ -8331,7 +8829,7 @@ msgid "Search for projects, issues, etc."
msgstr "Пошук в проектах, задачах і т. д."
msgid "Search groups"
-msgstr ""
+msgstr "Пошук в групах"
msgid "Search merge requests"
msgstr "Пошук у запитах на злиттÑ"
@@ -8354,6 +8852,9 @@ msgstr "Пошук проектів"
msgid "Search users"
msgstr "Пошук кориÑтувачів"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr "Пошук у ваших проектах"
@@ -8403,7 +8904,7 @@ msgid "Security Dashboard|Issue Created"
msgstr "Створено задачу"
msgid "Security Reports|At this time, the security dashboard only supports SAST and dependency scanning."
-msgstr ""
+msgstr "Ðа даний момент, панель безпеки підтримує тільки SAST та ÑÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ñ€Ð°Ð·Ð»Ð¸Ð²Ð¾Ñтей."
msgid "Security Reports|Create issue"
msgstr "Створити задачу"
@@ -8412,13 +8913,13 @@ msgid "Security Reports|Dismiss vulnerability"
msgstr "Відхилити вразливіÑÑ‚ÑŒ"
msgid "Security Reports|Learn more about setting up your dashboard"
-msgstr ""
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð°ÑˆÐ¾Ñ— панелі"
msgid "Security Reports|More info"
msgstr "Детальніше"
msgid "Security Reports|No Vulnerabilities"
-msgstr ""
+msgstr "Ðемає вразливоÑтей"
msgid "Security Reports|Security dashboard documentation"
msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð´Ð¾ панелі безпеки"
@@ -8426,6 +8927,9 @@ msgstr "Ð”Ð¾ÐºÑƒÐ¼ÐµÐ½Ñ‚Ð°Ñ†Ñ–Ñ Ð´Ð¾ панелі безпеки"
msgid "Security Reports|There was an error creating the issue."
msgstr "Помилка при Ñтворенні задачі."
+msgid "Security Reports|There was an error creating the merge request."
+msgstr ""
+
msgid "Security Reports|There was an error dismissing the vulnerability."
msgstr "Помилка при відхиленні вразливоÑÑ‚Ñ–."
@@ -8436,16 +8940,16 @@ msgid "Security Reports|There was an error reverting this dismissal."
msgstr "Помилка при анулюванні цього відхиленнÑ."
msgid "Security Reports|Undo dismiss"
-msgstr ""
+msgstr "Відмінити відхиленнÑ"
msgid "Security Reports|We've found no vulnerabilities for your group"
-msgstr ""
+msgstr "Ми не виÑвили вразливоÑтей Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— групи"
msgid "Security Reports|While it's rare to have no vulnerabilities for your group, it can happen. In any event, we ask that you please double check your settings to make sure you've set up your dashboard correctly."
-msgstr ""
+msgstr "Хоча й рідко, але можливо, що ваша група не має вразливоÑтей. Ð’ будь-Ñкому разі, ми проÑимо Ð²Ð°Ñ Ð¿ÐµÑ€ÐµÐ²Ñ–Ñ€Ð¸Ñ‚Ð¸ ваші налаштуваннÑ, щоб впевнитиÑÑ, що ваша панель налаштована правильно."
msgid "Security dashboard"
-msgstr ""
+msgstr "Панель безпеки"
msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr "Ðа панелі безпеки відображаєтьÑÑ Ð¾Ñтанній звіт про безпеку. ВикориÑтовуйте його Ð´Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ та Ð²Ð¸Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð²Ñ€Ð°Ð·Ð»Ð¸Ð²Ð¾Ñтей."
@@ -8457,7 +8961,7 @@ msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
msgstr "Конвеєр %{pipelineLink} запущено"
msgid "See metrics"
-msgstr ""
+msgstr "ПереглÑнути метрики"
msgid "See the affected projects in the GitLab admin panel"
msgstr ""
@@ -8474,6 +8978,12 @@ msgstr "Виберіть групу Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ"
msgid "Select a namespace to fork the project"
msgstr "Виберіть проÑÑ‚Ñ–Ñ€ імен Ð´Ð»Ñ Ñ„Ð¾Ñ€ÐºÑƒ проекту"
+msgid "Select a project to read Insights configuration file"
+msgstr ""
+
+msgid "Select a repository"
+msgstr ""
+
msgid "Select a template repository"
msgstr "Вибрати шаблон репозиторію"
@@ -8490,7 +9000,7 @@ msgid "Select branch/tag"
msgstr "Виберіть гілку або тег"
msgid "Select members to invite"
-msgstr ""
+msgstr "Виберіть учаÑників Ð´Ð»Ñ Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ"
msgid "Select project"
msgstr "Вибрати проект"
@@ -8519,20 +9029,17 @@ 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 "При виборі кориÑтувача Gitlab поÑÐ¸Ð»Ð°Ð½Ð½Ñ Ð½Ð° нього буде додане до опиÑу задачі та коментарів (напр. \"<a href=\"#\"> @johnsmith</a>\"). Також це призведе до аÑоціації та/або Ð¿Ñ€Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ†Ð¸Ñ… задач та коментарів на вибраного кориÑтувача."
-msgid "Selective synchronization"
-msgstr "Вибіркова ÑинхронізаціÑ"
-
msgid "Send email"
msgstr "ÐадіÑлати лиÑта"
msgid "Send report"
-msgstr ""
+msgstr "ÐадіÑлати звіт"
msgid "Send usage data"
msgstr "Відправити дані про викориÑтаннÑ"
msgid "Sentry API URL"
-msgstr ""
+msgstr "URL-адреÑа Sentry API"
msgid "Sep"
msgstr "вер."
@@ -8544,46 +9051,46 @@ msgid "Server version"
msgstr "ВерÑÑ–Ñ Ñервера"
msgid "Serverless"
-msgstr ""
+msgstr "Serverless"
msgid "ServerlessDetails|Kubernetes Pods"
-msgstr ""
+msgstr "Pod'и Kubernetes"
msgid "ServerlessDetails|Number of Kubernetes pods in use over time based on necessity."
-msgstr ""
+msgstr "КількіÑÑ‚ÑŒ pod'ів Kubernetes у викориÑтанні на оÑнові необхідноÑÑ‚Ñ– протÑгом періоду чаÑу."
msgid "ServerlessDetails|pod in use"
-msgstr ""
+msgstr "pod у викориÑтанні"
msgid "ServerlessDetails|pods in use"
-msgstr ""
+msgstr "pod'и у викориÑтанні"
msgid "ServerlessURL|Copy URL to clipboard"
-msgstr ""
+msgstr "Скопіювати URL-адреÑу в буфер обміну"
msgid "Serverless| In order to start using functions as a service, you must first install Knative on your Kubernetes cluster."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб почати викориÑтовувати функції Ñк ÑервіÑ, необхідно Ñпочатку вÑтановити Knative на ваш клаÑтер Kubernetes."
msgid "Serverless|An error occurred while retrieving serverless components"
-msgstr ""
+msgstr "СталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при отриманні компонентів serverless"
msgid "Serverless|Getting started with serverless"
-msgstr ""
+msgstr "Початок роботи із serverless"
msgid "Serverless|If you believe none of these apply, please check back later as the function data may be in the process of becoming available."
-msgstr ""
+msgstr "Якщо ви вважаєте, що жодна з них не підходить, будь лаÑка перевірте пізніше, тому що дані про функції можуть бути в процеÑÑ– отриманнÑ."
msgid "Serverless|Install Knative"
-msgstr ""
+msgstr "Ð’Ñтановити Knative"
msgid "Serverless|Learn more about Serverless"
-msgstr ""
+msgstr "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про Serverless"
msgid "Serverless|No functions available"
-msgstr ""
+msgstr "Ðемає доÑтупних функцій"
msgid "Serverless|There is currently no function data available from Knative. This could be for a variety of reasons including:"
-msgstr ""
+msgstr "Ðаразі немає даних про функції від Knative. Це може бути викликано різними причинами, зокрема:"
msgid "Service Desk"
msgstr "Service Desk"
@@ -8597,6 +9104,9 @@ msgstr "URL ÑервіÑу"
msgid "Session expiration, projects limit and attachment size."
msgstr "Термін дії ÑеÑÑ–Ñ—, проектні ліміти та розміри вкладень."
+msgid "Set a number of approvals required, the approvers and other approval settings."
+msgstr ""
+
msgid "Set a password on your account to pull or push via %{protocol}."
msgstr "Ð’Ñтановіть пароль Ð´Ð»Ñ Ñвого облікового запиÑу, щоб мати можливіÑÑ‚ÑŒ відправлÑти та отримувати через %{protocol}."
@@ -8612,11 +9122,14 @@ msgstr "Ð’Ñтановити репозиторій шаблонів Ð´Ð»Ñ Ð²Ñ
msgid "Set max session time for web terminal."
msgstr "МакÑимальний термін дії ÑеÑÑ–Ñ— Ð´Ð»Ñ Ð²ÐµÐ±-терміналу."
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "Ðалаштувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ð¾ електронній пошті Ð´Ð»Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ про зловживаннÑ."
msgid "Set number of approvers required before open merge requests can be merged"
-msgstr ""
+msgstr "Ð’Ñтановити необхідну кількіÑÑ‚ÑŒ затверджуючих оÑіб перед тим, Ñк відкритий запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¼Ð¾Ð¶Ðµ бути злитий"
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
msgstr "Ð’Ñтановіть вимоги Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ кориÑтувачів. Увімкніть обов’Ñзкову двофакторну автентифікацію."
@@ -8634,6 +9147,9 @@ msgid "Set up assertions/attributes/claims (email, first_name, last_name) and Na
msgstr "Ðалаштуйте твердженнÑ/атрибути (email, ім'Ñ, прізвище) Ñ– NameID відповідно до %{docsLinkStart} документації %{icon}%{docsLinkEnd}"
msgid "Set up new U2F device"
+msgstr "Ðалаштувати новий приÑтрій U2F"
+
+msgid "Set up new password"
msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
@@ -8693,14 +9209,20 @@ msgstr "Обнулити викориÑтані хвилини в конвеєр
msgid "Sherlock Transactions"
msgstr "Sherlock транзакції"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "Показати команду"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "Показати повний неформатований журнал"
msgid "Show file browser"
-msgstr ""
+msgstr "Показати файловий менеджер"
msgid "Show latest version"
msgstr "Показати оÑтанню верÑÑ–ÑŽ"
@@ -8743,19 +9265,19 @@ msgid "Sign in / Register"
msgstr "Увійти або зареєÑтруватиÑÑ"
msgid "Sign in to \"%{group_name}\""
-msgstr ""
+msgstr "Увійти до \"%{group_name}\""
msgid "Sign in using smart card"
-msgstr ""
+msgstr "Увійти за допомогою Ñмарт-карти"
msgid "Sign in via 2FA code"
-msgstr ""
+msgstr "Увійти за допомогою коду двофакторної автентифікції"
msgid "Sign in with Single Sign-On"
msgstr "Увійти за допомогою єдиного входу"
msgid "Sign in with smart card"
-msgstr ""
+msgstr "Увійти за допомогою Ñмарт-карти"
msgid "Sign out"
msgstr "Вийти"
@@ -8767,7 +9289,7 @@ msgid "Sign-up restrictions"
msgstr "ÐžÐ±Ð¼ÐµÐ¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ€ÐµÑ”Ñтрації"
msgid "Similar issues"
-msgstr ""
+msgstr "Подібні задачі"
msgid "Size"
msgstr "Розмір"
@@ -8791,17 +9313,35 @@ msgid "Smartcard authentication failed: client certificate header is missing."
msgstr "Ðе вдалоÑÑ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÑƒÐ²Ð°Ñ‚Ð¸ Ñмарт-карту: відÑутній заголовок в Ñертифікаті клієнта."
msgid "Snippet Contents"
-msgstr ""
+msgstr "ВміÑÑ‚ Ñніпета"
msgid "Snippets"
msgstr "Сніпети"
-msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
+msgid "SnippetsEmptyState|Explore public snippets"
msgstr ""
-msgid "Someone edited this merge request at the same time you did. Please refresh the page to see changes."
+msgid "SnippetsEmptyState|New snippet"
msgstr ""
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
+msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
+msgstr "ХтоÑÑŒ відредагував цю %{issueType} одночаÑно з вами. ÐžÐ¿Ð¸Ñ Ð±ÑƒÐ»Ð¾ оновлено, тому вам знову треба внеÑти Ñвої зміни."
+
+msgid "Someone edited this merge request at the same time you did. Please refresh the page to see changes."
+msgstr "ХтоÑÑŒ відредагував цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¾Ð´Ð½Ð¾Ñ‡Ð°Ñно з вами. Будь лаÑка, оновіть Ñторінку, щоб побачити зміни."
+
msgid "Something went wrong on our end"
msgstr "ЩоÑÑŒ пішло не так з нашого боку"
@@ -8812,7 +9352,7 @@ msgid "Something went wrong on our end. Please try again!"
msgstr "ЩоÑÑŒ пішло не так на нашій Ñтороні. Будь-лаÑка, Ñпробуйте ще раз!"
msgid "Something went wrong on our end. Please try again."
-msgstr ""
+msgstr "ЩоÑÑŒ пішло не так на нашій Ñтороні. Будь-лаÑка, Ñпробуйте ще раз."
msgid "Something went wrong trying to change the confidentiality of this issue"
msgstr "Помилка при зміні конфіденційноÑÑ‚Ñ– цієї задачі"
@@ -8824,13 +9364,13 @@ msgid "Something went wrong when toggling the button"
msgstr "Помилка при перемиканні кнопки"
msgid "Something went wrong while applying the suggestion. Please try again."
-msgstr ""
+msgstr "Помилка при заÑтоÑуванні пропозиції. Будь лаÑка, Ñпробуйте знову."
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr "Помилка при закритті %{issuable}. Будь лаÑка, Ñпробуйте пізніше"
msgid "Something went wrong while deleting the source branch. Please try again."
-msgstr ""
+msgstr "Помилка при видаленні гілки-джерела. Будь лаÑка, Ñпробуйте знову."
msgid "Something went wrong while fetching %{listType} list"
msgstr "Помилка при отриманні ÑпиÑку %{listType}"
@@ -8851,7 +9391,7 @@ msgid "Something went wrong while fetching the registry list."
msgstr "ЩоÑÑŒ пішло не так при отриманні ÑпиÑку із реєÑтру."
msgid "Something went wrong while merging this merge request. Please try again."
-msgstr ""
+msgstr "Помилка при заÑтоÑуванні цього запиту на злиттÑ. Будь лаÑка, Ñпробуйте знову."
msgid "Something went wrong while reopening the %{issuable}. Please try again later"
msgstr "Помилка при повторному відкритті %{issuable}. Будь лаÑка, Ñпробуйте пізніше"
@@ -8878,13 +9418,13 @@ msgid "Sorry, no projects matched your search"
msgstr "Ðа жаль жоден проект не задовольнÑÑ” критеріÑм вашого пошуку"
msgid "Sorry, your filter produced no results"
-msgstr ""
+msgstr "Ðа жаль, немає результатів, Ñкі відповідають фільтру"
msgid "Sort by"
msgstr "Сортувати за"
msgid "Sort direction"
-msgstr ""
+msgstr "ПорÑдок ÑортуваннÑ"
msgid "SortOptions|Access level, ascending"
msgstr "Рівень доÑтупу, в порÑдку зроÑтаннÑ"
@@ -8932,7 +9472,7 @@ msgid "SortOptions|Less weight"
msgstr "Менша вага"
msgid "SortOptions|Milestone due date"
-msgstr ""
+msgstr "Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ ÐµÑ‚Ð°Ð¿Ñƒ"
msgid "SortOptions|Milestone due later"
msgstr "Етап запланований на пізніше"
@@ -8965,7 +9505,7 @@ msgid "SortOptions|Oldest joined"
msgstr "Приєднаний найраніше"
msgid "SortOptions|Oldest last activity"
-msgstr ""
+msgstr "ÐайÑтаріша оÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–ÑÑ‚ÑŒ"
msgid "SortOptions|Oldest sign in"
msgstr "Залогінений найраніше"
@@ -8980,7 +9520,7 @@ msgid "SortOptions|Priority"
msgstr "Пріоритет"
msgid "SortOptions|Recent last activity"
-msgstr ""
+msgstr "Ðайновіша оÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–ÑÑ‚ÑŒ"
msgid "SortOptions|Recent sign in"
msgstr "Ðещодавно зареєÑтровані"
@@ -9010,7 +9550,7 @@ msgid "Source is not available"
msgstr "Джерело недоÑтупне"
msgid "Source project cannot be found."
-msgstr ""
+msgstr "Проект-джерело не знайдено."
msgid "Spam Logs"
msgstr "Спам-журнал"
@@ -9028,7 +9568,7 @@ msgid "Specify the following URL during the Runner setup:"
msgstr "Зазначте наÑтупний URL під Ñ‡Ð°Ñ Ð²ÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Runner-а:"
msgid "Squash commit message"
-msgstr ""
+msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ð¾Ð±'єднаного (squash) коміту"
msgid "Squash commits"
msgstr "Виконати об'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ (squash) комітів"
@@ -9066,12 +9606,18 @@ msgstr "ÐктивніÑÑ‚ÑŒ в обраних проектах"
msgid "Starred projects"
msgstr "Обрані проекти"
-msgid "Stars"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
msgstr ""
-msgid "Start Web Terminal"
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
msgstr ""
+msgid "Stars"
+msgstr "У обраному"
+
+msgid "Start Web Terminal"
+msgstr "ЗапуÑтити Веб-Термінал"
+
msgid "Start a %{new_merge_request} with these changes"
msgstr "Почати %{new_merge_request} з цими змінами"
@@ -9082,19 +9628,19 @@ msgid "Start and due date"
msgstr "Дата початку та завершеннÑ"
msgid "Start cleanup"
-msgstr ""
+msgstr "Почати очищеннÑ"
msgid "Start date"
msgstr "Дата початку"
msgid "Start discussion"
-msgstr ""
+msgstr "Розпочати диÑкуÑÑ–ÑŽ"
msgid "Start discussion & close %{noteable_name}"
-msgstr ""
+msgstr "Розпочати Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‚Ð° закрити %{noteable_name}"
msgid "Start discussion & reopen %{noteable_name}"
-msgstr ""
+msgstr "Розпочати Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ– повторно відкрити %{noteable_name}"
msgid "Start the Runner!"
msgstr "ЗапуÑÑ‚Ñ–Ñ‚ÑŒ Runner!"
@@ -9106,13 +9652,13 @@ msgid "Started"
msgstr "Запущений"
msgid "Started %{startsIn}"
-msgstr ""
+msgstr "Запущено %{startsIn}"
msgid "Starting..."
-msgstr ""
+msgstr "ЗапуÑк..."
msgid "Starts %{startsIn}"
-msgstr ""
+msgstr "Буде запущено %{startsIn}"
msgid "Starts at (UTC)"
msgstr "ПочинаєтьÑÑ Ð¾ (за Грінвічем)"
@@ -9124,10 +9670,10 @@ msgid "Status"
msgstr "СтатуÑ"
msgid "Status:"
-msgstr ""
+msgstr "СтатуÑ:"
msgid "Stop Terminal"
-msgstr ""
+msgstr "Зупинити термінал"
msgid "Stop environment"
msgstr "Зупинити Ñередовище"
@@ -9145,7 +9691,7 @@ msgid "Stopping this environment is currently not possible as a deployment is in
msgstr "Зупинка Ñередовища наразі неможлива, тому що відбуваєтьÑÑ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
msgid "Stopping..."
-msgstr ""
+msgstr "ЗупиненнÑ..."
msgid "Storage"
msgstr "Сховище"
@@ -9163,7 +9709,7 @@ msgid "Submit as spam"
msgstr "Позначити Ñк Ñпам"
msgid "Submit feedback"
-msgstr ""
+msgstr "ÐадіÑлати відгук"
msgid "Submit review"
msgstr "ÐадіÑлати перевірку"
@@ -9181,19 +9727,19 @@ msgid "Subscribe at project level"
msgstr "ПідпиÑатиÑÑ Ð½Ð° рівні проекту"
msgid "Subscribe to RSS feed"
-msgstr ""
+msgstr "ПідпиÑатиÑÑ Ð½Ð° RSS-канал"
msgid "Subscribe to calendar"
-msgstr ""
+msgstr "ПідпиÑатиÑÑ Ð½Ð° календар"
msgid "Subscribed"
msgstr "Ви підпиÑані"
msgid "SubscriptionTable|Billing"
-msgstr ""
+msgstr "Білінг"
msgid "SubscriptionTable|Free"
-msgstr ""
+msgstr "Безкоштовно"
msgid "SubscriptionTable|GitLab allows you to continue using your subscription even if you exceed the number of seats you purchased. You will be required to pay for these seats upon renewal."
msgstr ""
@@ -9208,7 +9754,7 @@ msgid "SubscriptionTable|Loading subscriptions"
msgstr ""
msgid "SubscriptionTable|Manage"
-msgstr ""
+msgstr "УправліннÑ"
msgid "SubscriptionTable|Max seats used"
msgstr ""
@@ -9226,10 +9772,10 @@ msgid "SubscriptionTable|Seats owed"
msgstr ""
msgid "SubscriptionTable|Subscription end date"
-msgstr ""
+msgstr "Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¿Ñ–Ð´Ð¿Ð¸Ñки"
msgid "SubscriptionTable|Subscription start date"
-msgstr ""
+msgstr "Дата початку підпиÑки"
msgid "SubscriptionTable|This is the last time the GitLab.com team was in contact with you to settle any outstanding balances."
msgstr ""
@@ -9244,28 +9790,28 @@ msgid "SubscriptionTable|This is the number of seats you will be required to pur
msgstr ""
msgid "SubscriptionTable|Trial"
-msgstr ""
+msgstr "Пробна верÑÑ–Ñ"
msgid "SubscriptionTable|Trial end date"
-msgstr ""
+msgstr "Дата Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾Ð±Ð½Ð¾Ñ— верÑÑ–Ñ—"
msgid "SubscriptionTable|Trial start date"
-msgstr ""
+msgstr "Дата початку пробної верÑÑ–Ñ—"
msgid "SubscriptionTable|Upgrade"
-msgstr ""
+msgstr "Підвищити"
msgid "SubscriptionTable|Usage"
-msgstr ""
+msgstr "ВикориÑтаннÑ"
msgid "SubscriptionTable|Usage count is performed once a day at 12:00 PM."
msgstr ""
msgid "Suggested change"
-msgstr ""
+msgstr "Пропонована зміна"
msgid "Sunday"
-msgstr ""
+msgstr "ÐеділÑ"
msgid "Support for custom certificates is disabled. Ask your system's administrator to enable it."
msgstr ""
@@ -9276,6 +9822,9 @@ msgstr "Перейти в гілку/тег"
msgid "Sync information"
msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ Ñинхронізацію"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "СиÑтемні гуки"
@@ -9286,7 +9835,7 @@ msgid "System default (%{default})"
msgstr ""
msgid "System header and footer"
-msgstr ""
+msgstr "СиÑтемний заголовок та футер"
msgid "System metrics (Custom)"
msgstr "СиÑтемні метрики (ВлаÑні)"
@@ -9295,10 +9844,10 @@ msgid "System metrics (Kubernetes)"
msgstr "СиÑтемні метрики (Kubernetes)"
msgid "Tag"
-msgstr ""
+msgstr "Тег"
msgid "Tag list:"
-msgstr ""
+msgstr "СпиÑок тегів:"
msgid "Tags"
msgstr "Теги"
@@ -9394,10 +9943,10 @@ msgid "Templates"
msgstr "Шаблони"
msgid "Terminal"
-msgstr ""
+msgstr "Термінал"
msgid "Terminal for environment"
-msgstr ""
+msgstr "Термінал Ð´Ð»Ñ Ñередовища"
msgid "Terms of Service Agreement and Privacy Policy"
msgstr "Угода про Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñлуг Ñ– політика конфіденційноÑÑ‚Ñ–"
@@ -9421,7 +9970,7 @@ msgid "The Advanced Global Search in GitLab is a powerful search service that sa
msgstr "Розширений глобальний пошук в GitLab — це потужний інÑтрумент Ñкий заощаджує ваш чаÑ. ЗаміÑÑ‚ÑŒ Ð´ÑƒÐ±Ð»ÑŽÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð´Ñƒ Ñ– витрати чаÑу, ви можете шукати код інших команд, Ñкий може допомогти у вашому проекті."
msgid "The CSV export will be created in the background. Once finished, it will be sent to <strong>%{email}</strong> in an attachment."
-msgstr ""
+msgstr "ЕкÑпорт CSV буде Ñтворено у фоновому режимі. ПіÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð¹Ð¾Ð³Ð¾ буде надіÑлано на <strong>%{email}</strong> у вкладенні."
msgid "The Git LFS objects will <strong>not</strong> be synced."
msgstr "Об'єкти Git LFS <strong>не</strong> будуть ÑинхронізуватиÑÑ."
@@ -9435,6 +9984,9 @@ msgstr "Трекер задач — це міÑце, де можна додатÐ
msgid "The X509 Certificate to use when mutual TLS is required to communicate with the external authorization service. If left blank, the server certificate is still validated when accessing over HTTPS."
msgstr "Сертифікат X509 викориÑтовуєтьÑÑ Ð´Ð»Ñ Ð²Ð·Ð°Ñ”Ð¼Ð½Ð¾Ñ— перевірки автентичноÑÑ‚Ñ– TLS Ñ– необхідний Ð´Ð»Ñ Ð·Ð²'Ñзку з зовнішньою Ñлужбою авторизації. Якщо залишити порожнім, Ñертифікат Ñервера буде перевірÑтиÑÑŒ при доÑтупі через HTTPS."
+msgid "The branch for this project has no active pipeline configuration."
+msgstr ""
+
msgid "The character highlighter helps you keep the subject line to %{titleLength} characters and wrap the body at %{bodyLength} so they are readable in git."
msgstr "ПідÑвітка Ñимволів дозволÑÑ” обмежувати заголовок до %{titleLength} Ñимволів Ñ– обмежувати довжину Ñ€Ñдків тіла %{bodyLength} Ñимволами Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб вони залишаютьÑÑ Ñ‡Ð¸Ñ‚Ð°Ð±ÐµÐ»ÑŒÐ½Ð¸Ð¼Ð¸ в git."
@@ -9460,11 +10012,14 @@ msgid "The issue stage shows the time it takes from creating an issue to assigni
msgstr "Ð¡Ñ‚Ð°Ð´Ñ–Ñ Ð—Ð°Ð´Ð°Ñ‡Ð° показує, Ñкільки чаÑу потрібно від ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– до Ð²ÐºÐ»ÑŽÑ‡ÐµÐ½Ð½Ñ Ñ—Ñ— до ÑкогоÑÑŒ етапу, або Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ– на дошку. Почніть Ñтворювати задачі, щоб переглÑдати дані Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
msgid "The maximum file size allowed is %{size}."
-msgstr ""
+msgstr "МакÑимальний розмір файлу — %{size}."
msgid "The maximum file size allowed is 200KB."
msgstr "МакÑимальний розмір файлу — 200 Кб."
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "Пароль, Ñкий потрібен Ð´Ð»Ñ Ð´ÐµÑˆÐ¸Ñ„Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¸Ð²Ð°Ñ‚Ð½Ð¾Ð³Ð¾ ключа. Він Ñ” необов’Ñзковим Ñ– зберігаєтьÑÑ Ñƒ зашифрованому виглÑді."
@@ -9541,19 +10096,19 @@ msgid "The value lying at the midpoint of a series of observed values. E.g., bet
msgstr "Середнє Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð² Ñ€Ñдку. Приклад: між 3, 5, 9, Ñередніми 5, між 3, 5, 7, 8, Ñередніми (5 + 7) / 2 = 6."
msgid "There are no approvers"
-msgstr ""
+msgstr "Ðемає затверджуючих оÑіб"
msgid "There are no archived projects yet"
msgstr "Ðаразі немає жодного архівованого проекту"
msgid "There are no closed issues"
-msgstr ""
+msgstr "Закритих задач немає"
msgid "There are no closed merge requests"
-msgstr ""
+msgstr "Закритих запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½ÐµÐ¼Ð°Ñ”"
msgid "There are no custom project templates set up for this GitLab instance. They are enabled from GitLab's Admin Area. Contact your GitLab instance administrator to setup custom project templates."
-msgstr ""
+msgstr "ВлаÑні шаблони проектів не налаштовані Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ Ñерверу GitLab. Вони активуютьÑÑ Ð½Ð° Ñторінці адмініÑÑ‚Ñ€ÑƒÐ²Ð°Ð½Ð½Ñ GitLab. ЗвернітьÑÑ Ð´Ð¾ адмініÑтратора GitLab Ð´Ð»Ñ Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð»Ð°Ñних шаблонів проектів."
msgid "There are no issues to show"
msgstr "Ðемає задач Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ"
@@ -9562,10 +10117,10 @@ msgid "There are no labels yet"
msgstr "Тут ще немає міток"
msgid "There are no open issues"
-msgstr ""
+msgstr "Відкритих задач немає"
msgid "There are no open merge requests"
-msgstr ""
+msgstr "Відкритих запитів на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð½ÐµÐ¼Ð°Ñ”"
msgid "There are no packages yet"
msgstr ""
@@ -9580,7 +10135,7 @@ msgid "There are no unstaged changes"
msgstr "Ðемає неіндекÑованих змін"
msgid "There was an error adding a todo."
-msgstr "Помилка при додаванні задачі."
+msgstr "Помилка при додаванні нагадуваннÑ."
msgid "There was an error deleting the todo."
msgstr "Помилка при видаленні задачі."
@@ -9588,6 +10143,9 @@ msgstr "Помилка при видаленні задачі."
msgid "There was an error loading users activity calendar."
msgstr "Помилка при завантаженні ÐºÐ°Ð»ÐµÐ½Ð´Ð°Ñ€Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾ÑÑ‚Ñ– кориÑтувачів."
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr "Помилка при збереженні ваших налаштувань Ñповіщень."
@@ -9604,7 +10162,7 @@ msgid "There was an error when unsubscribing from this label."
msgstr "Помилка при відпиÑці від цієї мітки."
msgid "These existing issues have a similar title. It might be better to comment there instead of creating another similar issue."
-msgstr ""
+msgstr "Ці Ñ–Ñнуючі проблеми мають подібні заголовки. Можливо, краще додати коментар до однієї з них заміÑÑ‚ÑŒ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¾Ñ—."
msgid "They can be managed using the %{link}."
msgstr "Ðими можна керувати за допомогою %{link}."
@@ -9613,10 +10171,10 @@ msgid "Third party offers"
msgstr "Сторонні пропозиції"
msgid "This %{issuable} is locked. Only <strong>project members</strong> can comment."
-msgstr ""
+msgstr "Ð¦Ñ %{issuable} заблокована. Лише <strong>учаÑники проекту</strong> можуть коментувати."
msgid "This %{viewer} could not be displayed because %{reason}. You can %{options} instead."
-msgstr ""
+msgstr "Цей %{viewer} не може бути відображено через %{reason}. ЗаміÑÑ‚ÑŒ цього можна %{options}."
msgid "This GitLab instance does not provide any shared Runners yet. Instance administrators can register shared Runners in the admin area."
msgstr "Цей інÑÑ‚Ð°Ð½Ñ GitLab ще немає загальних Runner'ів. ÐдмініÑтратори можуть Ñ—Ñ… зареєÑтрувати у Ñпеціальному розділі конфігурації."
@@ -9633,6 +10191,21 @@ msgstr "ВидиміÑÑ‚ÑŒ цієї дошки обмежена"
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr "Ð¦Ñ Ð³Ñ–Ð»ÐºÐ° була змінена піÑÐ»Ñ Ñ‚Ð¾Ð³Ð¾ моменту, коли ви почали Ñ—Ñ— редагувати. Ви хотіли б Ñтворити нову?"
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr "Заплановане Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ñ†ÑŒÐ¾Ð³Ð¾ реєÑтру контейнерів."
@@ -9652,6 +10225,9 @@ msgid "This directory"
msgstr "Цей каталог"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
+msgstr "Цей домен не підтверджено. Щоб увімкнути доÑтуп, потрібно підтвердити право влаÑноÑÑ‚Ñ–."
+
+msgid "This field is required."
msgstr ""
msgid "This group"
@@ -9718,10 +10294,10 @@ msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ±ÑƒÐ²Ð°Ñ” в Ñтані Ð¾Ñ‡Ñ–ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ñ– чекає на запуÑк Runner"
msgid "This job is stuck because you don't have any active runners online with any of these tags assigned to them:"
-msgstr ""
+msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ðµ, тому що немає жодних runner'ів з будь Ñким із цих тегів:"
msgid "This job is stuck because you don't have any active runners that can run this job."
-msgstr ""
+msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ðµ, тому що немає активних runner'ів, Ñкі могли б його виконати."
msgid "This job is the most recent deployment to %{link}."
msgstr "Це Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñ” оÑтаннім розгортаннÑм на %{link}."
@@ -9730,7 +10306,7 @@ msgid "This job requires a manual action"
msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼Ð°Ð³Ð°Ñ” ручних дій"
msgid "This job will automatically run after its timer finishes. Often they are used for incremental roll-out deploys to production environments. When unscheduled it converts into a manual action."
-msgstr ""
+msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð±ÑƒÐ´Ðµ запущено автоматично піÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ñ‚Ð°Ð¹Ð¼ÐµÑ€Ð°. Зазвичай вони викориÑтовуютьÑÑ Ð´Ð»Ñ Ñ–Ð½ÐºÑ€ÐµÐ¼ÐµÐ½Ñ‚Ð°Ð»ÑŒÐ½Ð¾Ð³Ð¾ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð² production Ñередовище. У разі ÑкаÑÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¾Ð½Ð¾ перетворитьÑÑ Ð½Ð° ручну операцію."
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "Це означає, що ви не можете відправлÑти код, поки не Ñтворите порожній репозиторій або не імпортуєте Ñ–Ñнуючий."
@@ -9738,6 +10314,12 @@ msgstr "Це означає, що ви не можете відправлÑти
msgid "This merge request is locked."
msgstr "Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾."
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr "Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¼Ð°Ñ” бути затверджений учаÑниками цих груп. Ви можете перевизначити проектні налаштуваннÑ, вÑтановивши ваш влаÑний ÑпиÑок затверджуючих оÑіб."
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr "Цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¼Ð°Ñ” бути затверджений цими кориÑтувачами. Ви можете перевизначити проектні налаштуваннÑ, вÑтановивши ваш влаÑний ÑпиÑок затверджуючих оÑіб."
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr "Цей параметр вимкнено, тому що у Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” дозволу на Ð·Ð°Ð¿Ð¸Ñ Ñƒ поточну гілку"
@@ -9751,13 +10333,13 @@ msgid "This page will be removed in a future release."
msgstr "Цю Ñторінку буде видалено у майбутній верÑÑ–Ñ—."
msgid "This pipeline is run in a merge request context"
-msgstr ""
+msgstr "Цей конвеєр виконуєтьÑÑ Ð² контекÑÑ‚Ñ– запиту злиттÑ"
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{strongStart}Auto DevOps.%{strongEnd}"
-msgstr ""
+msgstr "Цей конвеєр викориÑтовує попередньо визначену конфігурацію CI / CD, увімкнену за допомогою %{strongStart}Auto DevOps.%{strongEnd}"
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b>"
-msgstr ""
+msgstr "Цей конвеєр викориÑтовує попередньо визначену конфігурацію CI / CD, увімкнену за допомогою <b>Auto DevOps</b>"
msgid "This project"
msgstr "Цей проект"
@@ -9799,7 +10381,7 @@ msgid "This user will be the author of all events in the activity feed that are
msgstr "Цей кориÑтувач буде автором вÑÑ–Ñ… подій в каналі активноÑÑ‚Ñ–, Ñкі Ñ” результатом оновленнÑ, наприклад ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¸Ñ… гілок або Ð²Ñ–Ð´Ð¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ð¾Ð²Ð¸Ñ… комітів до Ñ–Ñнуючих гілок. При Ñтворенні або перепризначенні ви зможете призначити лише Ñебе кориÑтувачем Ð´Ð»Ñ Ð²Ñ–Ð´Ð´Ð·ÐµÑ€ÐºÐ°Ð»ÐµÐ½Ð½Ñ."
msgid "This will redirect you to an external sign in page."
-msgstr ""
+msgstr "Це перенаправить Ð²Ð°Ñ Ð½Ð° зовнішню Ñторінку входу."
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr "Ці Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти автоматично Ñтануть обговореннÑми задач, Ñкі відображатимутьÑÑ Ñ‚ÑƒÑ‚ (причому коментарі Ñтануть чаÑтиною перепиÑки)."
@@ -9996,7 +10578,7 @@ msgid "Title"
msgstr "Заголовок"
msgid "Titles and Filenames"
-msgstr ""
+msgstr "Заголовки та імена файлів"
msgid "To %{link_to_help} of your domain, add the above key to a TXT record within to your DNS configuration."
msgstr ""
@@ -10005,7 +10587,7 @@ msgid "To GitLab"
msgstr "Ð’ GitLab"
msgid "To access this domain create a new DNS record"
-msgstr ""
+msgstr "Щоб отримати доÑтуп до цього домену, Ñтворіть новий Ð·Ð°Ð¿Ð¸Ñ DNS"
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}."
@@ -10056,7 +10638,7 @@ msgid "To keep this project going, create a new merge request"
msgstr ""
msgid "To link Sentry to GitLab, enter your Sentry URL and Auth Token."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ñ–Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ñ–Ñ— Sentry із Gitlab введіть URL-адреÑу Sentry та токен автентифікації."
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 "Щоб переміÑтити або Ñкопіювати веÑÑŒ проект GitLab з іншої інÑталÑції GitLab до цього, перейдіть на Ñторінку налаштувань оригіналу проекту, Ñтворіть файл екÑпорту та надішліть його Ñюди."
@@ -10068,19 +10650,19 @@ msgid "To open Jaeger and easily view tracing from GitLab, link the %{link} page
msgstr ""
msgid "To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed."
-msgstr ""
+msgstr "Ð”Ð»Ñ Ð·Ð±ÐµÑ€ÐµÐ¶ÐµÐ½Ð½Ñ ÑˆÐ²Ð¸Ð´ÐºÐ¾Ð´Ñ–Ñ— відображаютьÑÑ Ð»Ð¸ÑˆÐµ <strong>%{display_size} із %{real_size}</strong> файлів."
msgid "To receive alerts from manually configured Prometheus services, add the following URL and Authorization key to your Prometheus webhook config file. Learn more about %{linkStart}configuring Prometheus%{linkEnd} to send alerts to GitLab."
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 "Щоб налаштувати аутентифікацію SAML Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— групи через провайдера ідентифікації такої Ñк Azure, Okta, Onelogin, Ping Identity або вашого влаÑного поÑтачальника SAML 2.0:"
+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 start serving your jobs you can either add specific Runners to your project or use shared Runners"
-msgstr ""
+msgstr "Щоб почати ваші Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð²Ð¸ÐºÐ¾Ð½ÑƒÐ²Ð°Ð»Ð¸ÑÑ, ви можете або додати Ñпеціальні Runner’и до вашого проекту, або викориÑтовувати загальні"
msgid "To this GitLab instance"
msgstr "До цього інÑтанÑу GitLab"
@@ -10092,7 +10674,7 @@ msgid "To view the roadmap, add a start or due date to one of your epics in this
msgstr "Ð”Ð»Ñ Ð¿ÐµÑ€ÐµÐ³Ð»Ñду плану-графіку, додайте дату початку чи Ð·Ð°ÐºÑ–Ð½Ñ‡ÐµÐ½Ð½Ñ Ð´Ð¾ одного з ваших епіків в цій групі або Ñ—Ñ— підгрупах. При поміÑÑчному переглÑді показуютьÑÑ Ñ‚Ñ–Ð»ÑŒÐºÐ¸ епіки за попередній, поточний, та наÑтупні 5 міÑÑців."
msgid "To widen your search, change or remove filters above"
-msgstr ""
+msgstr "Щоб розширити пошук, змініть або видаліть фільтри вище"
msgid "To widen your search, change or remove filters."
msgstr "Щоб розширити пошук, змініть або видаліть фільтри."
@@ -10101,22 +10683,22 @@ msgid "Today"
msgstr "Сьогодні"
msgid "Todo"
-msgstr "Задача"
+msgstr "ÐагдуваннÑ"
msgid "Todos"
-msgstr "Задачі"
+msgstr "ÐагадуваннÑ"
msgid "Toggle Sidebar"
msgstr "Перемикач бічної панелі"
msgid "Toggle comments for this file"
-msgstr ""
+msgstr "Увімкнути або вимкнути коментарі Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ файлу"
msgid "Toggle commit description"
msgstr "Перемкнути Ð¾Ð¿Ð¸Ñ ÐºÐ¾Ð¼Ñ–Ñ‚Ñ–Ð²"
msgid "Toggle commit list"
-msgstr ""
+msgstr "Відкрити або закрити ÑпиÑок комітів"
msgid "Toggle discussion"
msgstr "Перемикач диÑкуÑÑ–Ñ—"
@@ -10182,9 +10764,12 @@ msgid "Trigger this manual action"
msgstr "ЗапуÑтити цю ручну дію"
msgid "Trigger token:"
-msgstr ""
+msgstr "Токен тригера:"
msgid "Trigger variables:"
+msgstr "Змінні тригера:"
+
+msgid "Triggerer"
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."
@@ -10197,13 +10782,13 @@ msgid "Try again"
msgstr "Спробуйте ще раз"
msgid "Try again?"
-msgstr ""
+msgstr "Спробуйте ще раз?"
msgid "Try all GitLab has to offer for 30 days."
msgstr "Спробуйте вÑÑ– функції GitLab протÑгом 30 днів."
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
-msgstr ""
+msgstr "ВідбуваєтьÑÑ Ð·'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ñ–Ð· вашим приÑтроєм. Підключіть його (Ñкщо ви цього ще не зробили) Ñ– натиÑніть кнопку на ньому зараз."
msgid "Turn on Service Desk"
msgstr "Ввімкнути Service Desk"
@@ -10218,7 +10803,7 @@ msgid "Type"
msgstr "Тип"
msgid "URL"
-msgstr ""
+msgstr "URL"
msgid "Unable to load the diff. %{button_try_again}"
msgstr "Ðеможливо завантажити порівнÑÐ½Ð½Ñ (diff). %{button_try_again}"
@@ -10230,13 +10815,13 @@ msgid "Unable to update this epic at this time."
msgstr "Ðеможливо оновити цей епік в даний момент."
msgid "Unblock"
-msgstr ""
+msgstr "Розблокувати"
msgid "Undo"
msgstr "СкаÑувати"
msgid "Unfortunately, your email message to GitLab could not be processed."
-msgstr ""
+msgstr "Ðа жаль, ваше Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾Ñ— пошти до GitLab не може бути оброблено."
msgid "Unknown"
msgstr "Ðевідомо"
@@ -10287,7 +10872,7 @@ msgid "Unsubscribe at project level"
msgstr "ВідпиÑатиÑÑ Ð½Ð° рівні проекту"
msgid "Unsubscribe from %{type}"
-msgstr ""
+msgstr "ВідпиÑатиÑÑ Ð²Ñ–Ð´ %{type}"
msgid "Unverified"
msgstr "Ðепідтверджено"
@@ -10301,8 +10886,11 @@ msgstr "Ðезабаром"
msgid "Update"
msgstr "Оновити"
+msgid "Update approvers"
+msgstr "Оновити затверджуючих оÑіб"
+
msgid "Update failed"
-msgstr ""
+msgstr "ÐžÐ½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ Ð½Ðµ вдалоÑÑ"
msgid "Update now"
msgstr "Оновити зараз"
@@ -10310,11 +10898,17 @@ msgstr "Оновити зараз"
msgid "Update your group name, description, avatar, and visibility."
msgstr "Оновіть Ñ–Ð¼â€™Ñ Ð³Ñ€ÑƒÐ¿Ð¸, опиÑ, аватар та видиміÑÑ‚ÑŒ."
+msgid "Update your project name, tags, description and avatar."
+msgstr ""
+
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "ОновленнÑ"
msgid "Upgrade plan to unlock Canary Deployments feature"
-msgstr ""
+msgstr "Перейдіть на вищий тарифний план, щоб отримати доÑтуп до функціональноÑÑ‚Ñ– Canary Deployments"
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "Перейдіть на вищий тарифний план щоб активувати Покращений Глобальний Пошук."
@@ -10335,16 +10929,16 @@ msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
msgstr "ÐадіÑлати <code>GoogleCodeProjectHosting.json</code> тут:"
msgid "Upload CSV file"
-msgstr ""
+msgstr "Завантажити CSV файл"
msgid "Upload New File"
msgstr "ÐадіÑлати новий файл"
msgid "Upload a certificate for your domain with all intermediates"
-msgstr ""
+msgstr "Завантажити Ñертифікат Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ домену з уÑіма проміжними"
msgid "Upload a private key for your certificate"
-msgstr ""
+msgstr "Завантажити приватний ключ Ð´Ð»Ñ Ñертифіката"
msgid "Upload file"
msgstr "ÐадіÑлати файл"
@@ -10389,13 +10983,13 @@ msgid "Use your global notification setting"
msgstr "ВикориÑтовуютьÑÑ Ð³Ð»Ð¾Ð±Ð°Ð»ÑŒÐ½Ñ– Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½ÑŒ"
msgid "Use your smart card to authenticate with the LDAP server."
-msgstr ""
+msgstr "ВикориÑтовуйте Ñмарт-карту Ð´Ð»Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ— на Ñервері LDAP."
msgid "Used by members to sign in to your group in GitLab"
msgstr "ВикориÑтовуєтьÑÑ ÑƒÑ‡Ð°Ñниками Ð´Ð»Ñ Ð²Ñ…Ð¾Ð´Ñƒ у вашу групу в GitLab"
msgid "Used to help configure your identity provider"
-msgstr ""
+msgstr "ВикориÑтовуєтьÑÑ Ð´Ð»Ñ Ð´Ð¾Ð¿Ð¾Ð¼Ð¾Ð³Ð¸ в налаштуванні провайдера ідентифікації"
msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
msgstr "Когорти КориÑтувачів показуютьÑÑ Ð»Ð¸ÑˆÐµ тоді, коли увімкнено %{usage_ping_link_start}збір даних про викориÑтаннÑ%{usage_ping_link_end}."
@@ -10422,22 +11016,22 @@ msgid "UserProfile|Edit profile"
msgstr "Редагувати профіль"
msgid "UserProfile|Explore public groups to find projects to contribute to."
-msgstr ""
+msgstr "ПереглÑдайте публічні групи та знаходьте проекти Ð´Ð»Ñ Ñвоїх внеÑків."
msgid "UserProfile|Groups"
msgstr "Групи"
msgid "UserProfile|Groups are the best way to manage projects and members."
-msgstr ""
+msgstr "Групи — це найкращий ÑпоÑіб керувати проектами та учаÑниками."
msgid "UserProfile|Join or create a group to start contributing by commenting on issues or submitting merge requests!"
-msgstr ""
+msgstr "ПриєднуйтеÑÑŒ до або Ñтворіть групу, щоб почати робити внеÑки через коментарі до задач, а також надÑилаючи запити на злиттÑ!"
msgid "UserProfile|Most Recent Activity"
msgstr "ОÑÑ‚Ð°Ð½Ð½Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ñ–ÑÑ‚ÑŒ"
msgid "UserProfile|No snippets found."
-msgstr ""
+msgstr "Сніпетів не знайдено."
msgid "UserProfile|Overview"
msgstr "ОглÑд"
@@ -10452,19 +11046,19 @@ msgid "UserProfile|Snippets"
msgstr "Сніпети"
msgid "UserProfile|Snippets in GitLab can either be private, internal, or public."
-msgstr ""
+msgstr "Сніпети в GitLab можуть бути приватними, внутрішніми та публічними."
msgid "UserProfile|Subscribe"
msgstr "ПідпиÑатиÑÑ"
msgid "UserProfile|This user doesn't have any personal projects"
-msgstr ""
+msgstr "Цей кориÑтувач не має оÑобиÑтих проектів"
msgid "UserProfile|This user has a private profile"
msgstr "Цей кориÑтувач має приватний профіль"
msgid "UserProfile|This user hasn't contributed to any projects"
-msgstr ""
+msgstr "Цей кориÑтувач не робив внеÑків до жодного із проектів"
msgid "UserProfile|View all"
msgstr "ПереглÑнути вÑе"
@@ -10473,31 +11067,31 @@ msgid "UserProfile|View user in admin area"
msgstr "ПереглÑнути кориÑтувача в адмінці"
msgid "UserProfile|You can create a group for several dependent projects."
-msgstr ""
+msgstr "Ви можете Ñтворити групу Ð´Ð»Ñ Ð´ÐµÐºÑ–Ð»ÑŒÐºÐ¾Ñ… залежних проектів."
msgid "UserProfile|You haven't created any personal projects."
-msgstr ""
+msgstr "Ви ще не Ñтворили жодного оÑобиÑтого проекту."
msgid "UserProfile|You haven't created any snippets."
-msgstr ""
+msgstr "Ви ще не Ñтворили жодних Ñніпетів."
msgid "UserProfile|Your projects can be available publicly, internally, or privately, at your choice."
-msgstr ""
+msgstr "За вашим вибором ваші проекти можуть бути доÑтупні публічно, внутрішньо або приватно."
msgid "Users"
msgstr "КориÑтувачі"
msgid "Users requesting access to"
-msgstr ""
+msgstr "КориÑтувачі, Ñкі запитують доÑтуп до"
msgid "Validate"
-msgstr ""
+msgstr "Перевірити"
msgid "Validate your GitLab CI configuration file"
-msgstr ""
+msgstr "Перевірити ваш файл конфігурації Gitlab CI"
msgid "Value"
-msgstr ""
+msgstr "ЗначеннÑ"
msgid "Various container registry settings."
msgstr "Різноманітні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñ€ÐµÑ”Ñтру контейнерів."
@@ -10506,7 +11100,7 @@ msgid "Various email settings."
msgstr "Різноманітні Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾ÑˆÑ‚Ð¸."
msgid "Various localization settings."
-msgstr ""
+msgstr "Різні параметри локалізації."
msgid "Various settings that affect GitLab performance."
msgstr "Різноманітні налаштуваннÑ, що впливають на продуктивніÑÑ‚ÑŒ GitLab."
@@ -10515,7 +11109,7 @@ msgid "Verification information"
msgstr "Ð†Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ñ–Ñ Ð¿Ñ€Ð¾ перевірку"
msgid "Verification status"
-msgstr ""
+msgstr "Стан перевірки"
msgid "Verified"
msgstr "Підтверджено"
@@ -10530,14 +11124,17 @@ msgid "View app"
msgstr "ПереглÑнути заÑтоÑунок"
msgid "View deployment"
-msgstr ""
+msgstr "ПереглÑнути розгортаннÑ"
msgid "View details: %{details_url}"
-msgstr ""
+msgstr "ПереглÑнути деталі: %{details_url}"
msgid "View documentation"
msgstr "ПереглÑнути документацію"
+msgid "View eligible approvers"
+msgstr "ПереглÑнути доÑтупних оÑіб Ð´Ð»Ñ Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ"
+
msgid "View epics list"
msgstr "ПереглÑнути ÑпиÑок епіків"
@@ -10547,8 +11144,8 @@ msgstr "ПереглÑд файла @ "
msgid "View group labels"
msgstr "ПереглÑнути мітки групи"
-msgid "View issue"
-msgstr "ПереглÑнути задачу"
+msgid "View in Sentry"
+msgstr ""
msgid "View it on GitLab"
msgstr "ПереглÑнути це на GitLab"
@@ -10575,7 +11172,7 @@ msgid "View the documentation"
msgstr "ПереглÑнути документацію"
msgid "Viewing commit"
-msgstr ""
+msgstr "ПереглÑд коміту"
msgid "Visibility and access controls"
msgstr "ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð¸Ð¼Ð¾ÑÑ‚Ñ– та доÑтупу"
@@ -10586,6 +11183,9 @@ msgstr "Рівень видимоÑÑ‚Ñ–"
msgid "Visibility level:"
msgstr "Рівень видимоÑÑ‚Ñ–:"
+msgid "Visibility, project features, permissions"
+msgstr ""
+
msgid "Visibility:"
msgstr "ВидиміÑÑ‚ÑŒ:"
@@ -10602,10 +11202,10 @@ msgid "VisibilityLevel|Unknown"
msgstr "Ðевідомий"
msgid "Vulnerability Chart"
-msgstr ""
+msgstr "СтатиÑтика вразливоÑтей"
msgid "Vulnerability List"
-msgstr ""
+msgstr "СпиÑок вразливоÑтей"
msgid "Vulnerability|Class"
msgstr "КлаÑ"
@@ -10632,7 +11232,7 @@ msgid "Vulnerability|Project"
msgstr "Проект"
msgid "Vulnerability|Report Type"
-msgstr ""
+msgstr "Тип звіту"
msgid "Vulnerability|Severity"
msgstr "Рівень"
@@ -10641,19 +11241,19 @@ msgid "Want to see the data? Please ask an administrator for access."
msgstr "Хочете побачити дані? Будь лаÑка, попроÑить у адмініÑтратора доÑтуп."
msgid "We can't find an epic that matches what you are looking for."
-msgstr ""
+msgstr "Ми не можемо знайти епік, Ñкий відповідає тому, що ви шукаєте."
msgid "We can't find an issue that matches what you are looking for."
-msgstr ""
+msgstr "Ми не можемо знайти задачу, Ñка відповідає тому, що ви шукаєте."
msgid "We could not determine the path to remove the epic"
-msgstr ""
+msgstr "Ми не змогли визначити шлÑÑ… до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ ÐµÐ¿Ñ–ÐºÑƒ"
msgid "We could not determine the path to remove the issue"
-msgstr ""
+msgstr "Ми не змогли визначити шлÑÑ… до Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ–"
msgid "We couldn't find any results matching"
-msgstr ""
+msgstr "Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ відповідні результати"
msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr "Ми виÑвили потенційний Ñпам у %{humanized_resource_name}. Будь лаÑка, введіть цей код Ð¿Ñ–Ð´Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ reCAPTCHA, щоб продовжити."
@@ -10662,7 +11262,7 @@ msgid "We don't have enough data to show this stage."
msgstr "Ми не маємо доÑтатньо даних Ð´Ð»Ñ Ð²Ñ–Ð´Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ñ†Ñ–Ñ”Ñ— Ñтадії."
msgid "We heard back from your U2F device. You have been authenticated."
-msgstr ""
+msgstr "Ми отримали відповідь від вашого приÑтрою U2F. Ви пройшли автентифікацію."
msgid "We want to be sure it is you, please confirm you are not a robot."
msgstr "Ми хочемо бути впевнені, що це ви, будь лаÑка, підтвердіть, що ви не робот."
@@ -10671,7 +11271,7 @@ msgid "Web IDE"
msgstr "Веб-IDE"
msgid "Web Terminal"
-msgstr ""
+msgstr "Веб-термінал"
msgid "Web terminal"
msgstr "Веб-термінал"
@@ -10698,7 +11298,7 @@ msgid "When leaving the URL blank, classification labels can still be specified
msgstr "Якщо залишити URL порожнім, можна вÑтановлювати мітки клаÑифікації без Ð²Ð¸Ð¼ÐºÐ½ÐµÐ½Ð½Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ð¹ проекту та Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð¾Ð²Ð½Ñ–ÑˆÐ½ÑŒÐ¾Ñ— авторизації."
msgid "When:"
-msgstr ""
+msgstr "Коли:"
msgid "Who can see this group?"
msgstr "Хто може бачити цю групу?"
@@ -10842,7 +11442,7 @@ msgid "Wiki|Wiki Pages"
msgstr "Вікі-Ñторінки"
msgid "Will deploy to"
-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 "З аналітикою контриб’юторів ви може вивчати активніÑÑ‚ÑŒ в задачах, запитах на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ñ– подій відправки коду Ð´Ð»Ñ Ð²Ð°ÑˆÐ¾Ñ— організації Ñ– Ñ—Ñ— учаÑників."
@@ -10850,11 +11450,17 @@ msgstr "З аналітикою контриб’юторів ви може ви
msgid "Withdraw Access Request"
msgstr "СкаÑувати запит доÑтупу"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
+msgstr "Ðапишіть коментар або перетÑгніть файли Ñюди…"
+
+msgid "Write access allowed"
msgstr ""
msgid "Write milestone description..."
-msgstr ""
+msgstr "Створити Ð¾Ð¿Ð¸Ñ ÐµÑ‚Ð°Ð¿Ñƒ..."
msgid "Yes"
msgstr "Так"
@@ -10869,7 +11475,7 @@ msgid "Yesterday"
msgstr "Вчора"
msgid "You"
-msgstr ""
+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 "Ви — адмініÑтратор, а це означає, що Ð½Ð°Ð´Ð°Ð½Ð½Ñ Ð´Ð¾Ñтупу Ð´Ð»Ñ <strong>%{client_name}</strong> дозволить їм взаємодіÑти з GitLab Ñк адмініÑтратору. Продовжуйте обережно."
@@ -10890,7 +11496,7 @@ msgid "You are on a read-only GitLab instance."
msgstr "Ви знаходитеÑÑ Ð½Ð° інÑтанÑÑ– Gitlab \"тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ\"."
msgid "You are receiving this message because you are a GitLab administrator for %{url}."
-msgstr ""
+msgstr "Ви отримуєте це повідомленнÑ, бо ви Ñ” адмініÑтратором GitLab Ð´Ð»Ñ %{url}."
msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr "ЗаміÑÑ‚ÑŒ цього ви можете %{linkStart}переглÑнути бінарні дані%{linkEnd}."
@@ -10917,7 +11523,7 @@ msgid "You can only edit files when you are on a branch"
msgstr "Ви можете редагувати файли, лише перебуваючи у ÑкійÑÑŒ гілці"
msgid "You can only merge once the items above are resolved"
-msgstr ""
+msgstr "Ви можете зливати лише коли вищезгадані пункти будуть вирішені"
msgid "You can 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}"
@@ -10938,7 +11544,7 @@ msgid "You do not have any subscriptions yet"
msgstr "У Ð²Ð°Ñ Ñ‰Ðµ немає підпиÑок"
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
-msgstr ""
+msgstr "Ви не маєте дозволу запуÑкати Веб-термінал. Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора проекту."
msgid "You do not have the correct permissions to override the settings from the LDAP group sync."
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” необхідних прав доÑтупу, щоб перевизначити Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ñинхронізації LDAP-груп."
@@ -10950,16 +11556,19 @@ msgid "You don't have any authorized applications"
msgstr "Ви не маєте ніÑких авторизованих заÑтоÑунків"
msgid "You don't have any deployments right now."
-msgstr ""
+msgstr "Ðаразі у Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” ніÑких розгортань."
msgid "You have no permissions"
msgstr "У Ð²Ð°Ñ Ð½ÐµÐ¼Ð°Ñ” прав доÑтупу"
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr "Ви не додали жодної затверджуючої оÑоби. Почніть з Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувачів або груп."
+
msgid "You have reached your project limit"
msgstr "Ви доÑÑгли Ñвого ліміту по кількоÑÑ‚Ñ– проектів"
msgid "You may also add variables that are made available to the running application by prepending the variable key with <code>K8S_SECRET_</code>."
-msgstr ""
+msgstr "Ви також можете додати змінні, що будуть доÑтупними Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑ‰ÐµÐ½Ð¾Ð³Ð¾ заÑтоÑунку шлÑхом Ð´Ð¾Ð´Ð°Ð²Ð°Ð½Ð½Ñ Ð¿Ñ€ÐµÑ„Ñ–ÐºÑу <code>K8S_SECRET_</code> до Ñ—Ñ… імен."
msgid "You must accept our Terms of Service and privacy policy in order to register an account"
msgstr "Ви повинні прийнÑти правила кориÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑервіÑом Ñ– політику конфіденційноÑÑ‚Ñ– Ð´Ð»Ñ Ñ‚Ð¾Ð³Ð¾, щоб Ñтворити обліковий запиÑ"
@@ -10971,7 +11580,7 @@ msgid "You need a different license to enable FileLocks feature"
msgstr "Ð”Ð»Ñ Ð°ÐºÑ‚Ð¸Ð²Ð°Ñ†Ñ–Ñ— функції Ð‘Ð»Ð¾ÐºÑƒÐ²Ð°Ð½Ð½Ñ Ð¤Ð°Ð¹Ð»Ñ–Ð² вам потрібна інша ліцензіÑ"
msgid "You need a different license to enable Geo replication."
-msgstr ""
+msgstr "Вам потрібна інша Ð»Ñ–Ñ†ÐµÐ½Ð·Ñ–Ñ Ð½Ð° викориÑÑ‚Ð°Ð½Ð½Ñ Geo реплікації."
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"
@@ -10980,13 +11589,13 @@ msgid "You need permission."
msgstr "Вам потрібен дозвіл"
msgid "You need to register a two-factor authentication app before you can set up a U2F device."
-msgstr ""
+msgstr "Вам треба зареєÑтрувати програму Ð´Ð»Ñ Ð´Ð²Ð¾Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¾Ñ— автентифікації перед налаштуваннÑм приÑтрою U2F."
msgid "You will lose all changes you've made to this file. This action cannot be undone."
-msgstr ""
+msgstr "Ви втратите вÑÑ– зміни, внеÑені вами в цей файл. Цю дію не можна ÑкаÑувати."
msgid "You will lose all the unstaged changes you've made in this project. This action cannot be undone."
-msgstr ""
+msgstr "Ви втратите вÑÑ– неіндекÑовані зміни, внеÑені вами в цей проект. Цю дію не можна ÑкаÑувати."
msgid "You will not get any notifications via email"
msgstr "Ви не отримаєте ніÑких повідомлень по електронній пошті"
@@ -11015,6 +11624,9 @@ msgstr "Ви не зможете відправлÑти та отримуватÐ
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "Вам необхідно викориÑтовувати різні імена гілок Ð´Ð»Ñ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾Ð³Ð¾ порівнÑннÑ."
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr "Ви отримали цей електронний лиÑÑ‚, оÑкільки %{reason}."
@@ -11043,10 +11655,10 @@ msgid "Your Projects' Activity"
msgstr "ÐктивніÑÑ‚ÑŒ ваших проектів"
msgid "Your Todos"
-msgstr "Ваші Задачі"
+msgstr "Ваші ÐагадуваннÑ"
msgid "Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left."
-msgstr ""
+msgstr "Ваш приÑтрій U2F має бути налаштований. Підключіть його (Ñкщо ви цього ще не зробили) Ñ– натиÑніть кнопку зліва."
msgid "Your applications (%{size})"
msgstr "Ваші заÑтоÑунки (%{size})"
@@ -11055,7 +11667,7 @@ msgid "Your authorized applications"
msgstr "Ваші авторизовані заÑтоÑунки"
msgid "Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer)."
-msgstr ""
+msgstr "Ваш браузер не підтримує U2F. Будь лаÑка, викориÑтовйте Google Chrome Ð´Ð»Ñ ÐºÐ¾Ð¼Ð¿'ютера (верÑÑ–ÑŽ 41 або новішу)."
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr "Ваші зміни можуть бути закомічені до %{branch_name}, оÑкільки запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸Ð¹."
@@ -11070,22 +11682,22 @@ msgid "Your comment will not be visible to the public."
msgstr "Ваш коментар не буде видимим Ð´Ð»Ñ Ð²ÑÑ–Ñ…."
msgid "Your device was successfully set up! Give it a name and register it with the GitLab server."
-msgstr ""
+msgstr "Ваш приÑтрій уÑпішно налаштовано! Дайте йому ім'Ñ Ñ‚Ð° зареєÑтруйте його на Ñервері GitLab."
msgid "Your groups"
msgstr "Ваші групи"
msgid "Your issues are being imported. Once finished, you'll get a confirmation email."
-msgstr ""
+msgstr "Ваші задачі імпортуютьÑÑ. ПіÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð²Ð¸ отримаєте Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾ електронній пошті із підтвердженнÑм."
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
-msgstr ""
+msgstr "Ваші задачі будуть імпортовані в фоні. ПіÑÐ»Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð½Ñ Ð²Ð¸ отримаєте Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð¿Ð¾ електронній пошті із підтвердженнÑм."
msgid "Your name"
msgstr "Ваше ім'Ñ"
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
-msgstr ""
+msgstr "Ваш ліміт проектів Ñкладає %{limit}! Будь лаÑка, звернітьÑÑ Ð´Ð¾ адмініÑтратора, щоб його збільшити"
msgid "Your projects"
msgstr "Ваші проекти"
@@ -11093,6 +11705,9 @@ msgstr "Ваші проекти"
msgid "a deleted user"
msgstr "видалений кориÑтувач"
+msgid "added %{created_at_timeago}"
+msgstr ""
+
msgid "ago"
msgstr "тому"
@@ -11105,7 +11720,13 @@ msgstr "тощо"
msgid "assign yourself"
msgstr "призначити Ñебе"
+msgid "at"
+msgstr ""
+
msgid "attach a new file"
+msgstr "прикріпити новий файл"
+
+msgid "authored"
msgstr ""
msgid "branch name"
@@ -11132,6 +11753,20 @@ msgstr "%{vulnerability} впливає на %{namespace}."
msgid "ciReport|%{remainingPackagesCount} more"
msgstr "%{remainingPackagesCount} більше"
+msgid "ciReport|%{reportType} %{status} detected %{dismissedCount} dismissed vulnerability"
+msgid_plural "ciReport|%{reportType} %{status} detected %{dismissedCount} dismissed vulnerabilities"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+msgid "ciReport|%{reportType} %{status} detected %{dismissedCount} dismissed vulnerability for the source branch only"
+msgid_plural "ciReport|%{reportType} %{status} detected %{dismissedCount} dismissed vulnerabilities for the source branch only"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerability"
msgid_plural "ciReport|%{reportType} %{status} detected %{fixedCount} fixed vulnerabilities"
msgstr[0] "%{reportType} %{status} виÑвив %{fixedCount} виправлену вразливіÑÑ‚ÑŒ"
@@ -11139,6 +11774,9 @@ msgstr[1] "%{reportType} %{status} виÑвив %{fixedCount} виправлен
msgstr[2] "%{reportType} %{status} виÑвив %{fixedCount} виправлених вразливоÑтей"
msgstr[3] "%{reportType} %{status} виÑвив %{fixedCount} виправлених вразливоÑтей"
+msgid "ciReport|%{reportType} %{status} detected %{fixedCount} fixed, and %{dismissedCount} dismissed vulnerabilities"
+msgstr ""
+
msgid "ciReport|%{reportType} %{status} detected %{newCount} new vulnerability"
msgid_plural "ciReport|%{reportType} %{status} detected %{newCount} new vulnerabilities"
msgstr[0] "%{reportType} %{status} виÑвив %{newCount} нову вразливіÑÑ‚ÑŒ"
@@ -11146,6 +11784,15 @@ msgstr[1] "%{reportType} %{status} виÑвив %{newCount} нові вразлÐ
msgstr[2] "%{reportType} %{status} виÑвив %{newCount} нових вразливоÑтей"
msgstr[3] "%{reportType} %{status} виÑвив %{newCount} нових вразливоÑтей"
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, %{fixedCount} fixed, and %{dismissedCount} dismissed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{dismissedCount} dismissed vulnerabilities"
+msgstr ""
+
+msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{dismissedCount} dismissed vulnerabilities for the source branch only"
+msgstr ""
+
msgid "ciReport|%{reportType} %{status} detected %{newCount} new, and %{fixedCount} fixed vulnerabilities"
msgstr "%{reportType} %{status} виÑвив %{newCount} нових та %{fixedCount} виправлених вразливоÑтей"
@@ -11199,17 +11846,29 @@ msgstr "ЯкіÑÑ‚ÑŒ коду"
msgid "ciReport|Confidence"
msgstr "ВпевненіÑÑ‚ÑŒ"
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ð°"
msgid "ciReport|Container scanning detects known vulnerabilities in your docker images."
msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ð½Ñ‚ÐµÐ¹Ð½ÐµÑ€Ñ–Ð² виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у ваших Docker образах."
+msgid "ciReport|Create issue"
+msgstr ""
+
+msgid "ciReport|Create merge request"
+msgstr ""
+
+msgid "ciReport|Created %{eventType}"
+msgstr ""
+
msgid "ciReport|DAST"
msgstr "DAST"
msgid "ciReport|Dependency Scanning"
-msgstr ""
+msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей"
msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr "Ð¡ÐºÐ°Ð½ÑƒÐ²Ð°Ð½Ð½Ñ Ð·Ð°Ð»ÐµÐ¶Ð½Ð¾Ñтей виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у залежноÑÑ‚ÑÑ… вашого коду."
@@ -11227,10 +11886,10 @@ msgid "ciReport|Dismissed by"
msgstr "Відхилено"
msgid "ciReport|Download and apply the patch to fix this vulnerability."
-msgstr ""
+msgstr "Завантажити або заÑтоÑувати патч, щоб виправити цю вразливіÑÑ‚ÑŒ."
msgid "ciReport|Download patch"
-msgstr ""
+msgstr "Завантажити патч"
msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr "Динамічне теÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ заÑтоÑунків (DAST) виÑвлÑÑ” відомі вразливоÑÑ‚Ñ– у вашому веб-заÑтоÑунку."
@@ -11247,9 +11906,15 @@ msgstr "Виправлено:"
msgid "ciReport|Identifiers"
msgstr "Ідентифікатори"
+msgid "ciReport|Implement this solution by creating a merge request"
+msgstr ""
+
msgid "ciReport|Instances"
msgstr "ІнÑтанÑи"
+msgid "ciReport|Investigate this vulnerability by creating an issue"
+msgstr ""
+
msgid "ciReport|Learn more about interacting with security reports (Alpha)."
msgstr "ДізнатиÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ про взаємодію з звітами безпеки (Ðльфа)."
@@ -11297,9 +11962,6 @@ msgstr "Ðемає змін у показниках продуктивноÑÑ‚Ñ–
msgid "ciReport|Performance metrics"
msgstr "Показники продуктивноÑÑ‚Ñ–"
-msgid "ciReport|Revert dismissal"
-msgstr "Відмінити відхиленнÑ"
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11321,6 +11983,9 @@ msgstr "Статичне теÑÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð±ÐµÐ·Ð¿ÐµÐºÐ¸ заÑтоÑункÑ
msgid "ciReport|There was an error creating the issue. Please try again."
msgstr "Помилка при Ñтворенні задачі. Будь лаÑка Ñпробуйте знову."
+msgid "ciReport|There was an error creating the merge request. Please try again."
+msgstr ""
+
msgid "ciReport|There was an error dismissing the vulnerability. Please try again."
msgstr "Помилка при відхиленні вразливоÑÑ‚Ñ–. Будь лаÑка, Ñпробуйте знову."
@@ -11339,6 +12004,9 @@ msgstr "Помилка при завантаженні звіту по ÑканÑ
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "Помилка при відміні відхиленнÑ. Будь лаÑка, Ñпробуйте знову."
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "Оновити %{name} з %{version} до %{fixed}."
@@ -11359,7 +12027,7 @@ msgid "command line instructions"
msgstr "інÑтрукції Ð´Ð»Ñ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð½Ð¾Ð³Ð¾ Ñ€Ñдка"
msgid "commented on %{link_to_project}"
-msgstr ""
+msgstr "прокоментовано в %{link_to_project}"
msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
msgstr "Ви вимикаєте конфіденційніÑÑ‚ÑŒ. Це означає, що <strong>будь-хто</strong> зможе бачити Ñ– залишати коментарі Ð´Ð»Ñ Ñ†Ñ–Ñ”Ñ— задачі."
@@ -11384,7 +12052,7 @@ msgstr[2] "днів"
msgstr[3] "днів"
msgid "deleted"
-msgstr ""
+msgstr "видалено"
msgid "deploy token"
msgstr "токен Ð´Ð»Ñ Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ"
@@ -11394,10 +12062,10 @@ msgstr "вимкнено"
msgid "discussion resolved"
msgid_plural "discussions resolved"
-msgstr[0] ""
-msgstr[1] ""
-msgstr[2] ""
-msgstr[3] ""
+msgstr[0] "Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¾"
+msgstr[1] "Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¾"
+msgstr[2] "обговорень завершено"
+msgstr[3] "обговорень завершено"
msgid "done"
msgstr "готово"
@@ -11413,13 +12081,13 @@ msgid "enabled"
msgstr "увімкнено"
msgid "epic"
-msgstr ""
+msgstr "епік"
msgid "error"
-msgstr ""
+msgstr "помилка"
msgid "error code:"
-msgstr ""
+msgstr "код помилки:"
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr "%{slash_command} перезапиÑує запланований Ñ‡Ð°Ñ Ð¾Ñтаннім значеннÑм."
@@ -11431,7 +12099,7 @@ msgid "from"
msgstr "від"
msgid "group"
-msgstr ""
+msgstr "група"
msgid "help"
msgstr "допомога"
@@ -11439,9 +12107,6 @@ msgstr "допомога"
msgid "here"
msgstr "тут"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://your-bitbucket-server"
@@ -11455,10 +12120,10 @@ msgid "importing"
msgstr "імпорт"
msgid "in group %{link_to_group}"
-msgstr ""
+msgstr "в групі %{link_to_group}"
msgid "in project %{link_to_project}"
-msgstr ""
+msgstr "в проекті %{link_to_project}"
msgid "index"
msgstr ""
@@ -11483,7 +12148,7 @@ msgid "is out of the hierarchy of the Group owning the template"
msgstr ""
msgid "issue"
-msgstr ""
+msgstr "задача"
msgid "issue boards"
msgstr "дошки Ð¾Ð±Ð³Ð¾Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡"
@@ -11492,13 +12157,13 @@ msgid "it is stored externally"
msgstr ""
msgid "it is stored in LFS"
-msgstr ""
+msgstr "зберігаєтьÑÑ Ð² LFS"
msgid "it is too large"
msgstr ""
msgid "latest"
-msgstr ""
+msgstr "оÑтанній"
msgid "latest deployment"
msgstr "оÑтаннє розгортаннÑ"
@@ -11523,19 +12188,22 @@ msgstr[2] "запитів на злиттÑ"
msgstr[3] "запитів на злиттÑ"
msgid "missing"
-msgstr ""
+msgstr "відÑутні"
msgid "mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}."
+msgstr "%{commitCount} і %{mergeCommitCount} буде додано до %{targetBranch}."
+
+msgid "mrWidgetCommitsAdded|%{commitCount} will be added to %{targetBranch}."
msgstr ""
msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr ""
+msgstr "1 коміт-злиттÑ"
msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
msgstr "Будь лаÑка відновіть Ñ—Ñ— або викориÑтовуйте іншу %{missingBranchName} гілку"
msgid "mrWidget|%{link_start}Learn more about resolving conflicts%{link_end}"
-msgstr ""
+msgstr "%{link_start}Докладніше про Ð²Ð¸Ñ€Ñ–ÑˆÐµÐ½Ð½Ñ ÐºÐ¾Ð½Ñ„Ð»Ñ–ÐºÑ‚Ñ–Ð²%{link_end}"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
msgstr "ВикориÑÑ‚Ð°Ð½Ð½Ñ %{metricsLinkStart} пам’ÑÑ‚Ñ– %{metricsLinkEnd} %{emphasisStart} впало %{emphasisEnd} з %{memoryFrom}Мб до %{memoryTo}Мб"
@@ -11553,10 +12221,10 @@ msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr "ДозволÑÑ” коміти від учаÑників, Ñкі можуть зливати до цільової гілки"
msgid "mrWidget|An error occurred while removing your approval."
-msgstr ""
+msgstr "Під Ñ‡Ð°Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð²Ð°ÑˆÐ¾Ð³Ð¾ Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ ÑталаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ°."
msgid "mrWidget|An error occurred while retrieving approval data for this merge request."
-msgstr ""
+msgstr "Помилка при отриманні даних про Ð·Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ запиту на злиттÑ."
msgid "mrWidget|An error occurred while submitting your approval."
msgstr "Помилка при обробці вашого затвердженнÑ."
@@ -11564,6 +12232,9 @@ msgstr "Помилка при обробці вашого затвердженн
msgid "mrWidget|Approve"
msgstr "Затвердити"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "Затверджено"
@@ -11595,7 +12266,7 @@ msgid "mrWidget|Create an issue to resolve them later"
msgstr "Створіть задачу, щоб вирішити їх пізніше"
msgid "mrWidget|Delete source branch"
-msgstr ""
+msgstr "Видалити гілку-джерело"
msgid "mrWidget|Deployment statistics are not available currently"
msgstr "СтатиÑтика Ñ€Ð¾Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð½Ð°Ñ€Ð°Ð·Ñ– недоÑтупна"
@@ -11636,17 +12307,20 @@ msgstr "Злити локально"
msgid "mrWidget|Merge request approved"
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"
+msgstr ""
-msgid "mrWidget|No Approval required; you can still approve"
-msgstr "Ð—Ð°Ñ‚Ð²ÐµÑ€Ð´Ð¶ÐµÐ½Ð½Ñ Ð½Ðµ Ñ” обов’Ñзковим; але ви вÑе одно можете це зробити"
+msgid "mrWidget|No approval required; you can still approve"
+msgstr ""
msgid "mrWidget|Open in Web IDE"
msgstr "Відкрити у Web IDE"
@@ -11701,6 +12375,9 @@ msgstr "Ðнулювати"
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr "Ðнулювати цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð·Ð° допомогою нового запиту на злиттÑ"
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "Ð’Ñтановлено"
@@ -11720,19 +12397,19 @@ msgid "mrWidget|The source branch HEAD has recently changed. Please reload the p
msgstr "HEAD гілки-джерела нещодавно було змінено. Будь лаÑка оновіть Ñторінку Ñ– переглÑньте зміни перед злиттÑм"
msgid "mrWidget|The source branch has been deleted"
-msgstr ""
+msgstr "Гілку-джерело видалено"
msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
msgstr "Гілка-джерело на %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} цільової гілки"
msgid "mrWidget|The source branch is being deleted"
-msgstr ""
+msgstr "Гілка-джерело в процеÑÑ– видаленнÑ"
msgid "mrWidget|The source branch will be deleted"
-msgstr ""
+msgstr "Гілку-джерело буде видалено"
msgid "mrWidget|The source branch will not be deleted"
-msgstr ""
+msgstr "Гілку-джерело не буде видалено"
msgid "mrWidget|There are merge conflicts"
msgstr "Ñ–Ñнують конфлікти при злитті"
@@ -11741,7 +12418,7 @@ msgid "mrWidget|There are unresolved discussions. Please resolve these discussio
msgstr "ПриÑутні незавершені обговореннÑ. Будь лаÑка завершіть Ñ—Ñ…"
msgid "mrWidget|This feature merges changes from the target branch to the source branch. You cannot use this feature since the source branch is protected."
-msgstr ""
+msgstr "Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð·Ð»Ð¸Ð²Ð°Ñ” зміни з цільової гілки до гілки-джерела. Її не можна викориÑтовувати, оÑкільки гілка-джерело Ñ” захищеною."
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr "ВідбулаÑÑ Ð¿Ð¾Ð¼Ð¸Ð»ÐºÐ° при автоматичному злитті цього запиту"
@@ -11756,7 +12433,7 @@ msgid "mrWidget|You are not allowed to edit this project directly. Please fork t
msgstr "Ви не можете безпоÑередньо редагувати цей проект. Будь лаÑка, зробіть форк, щоб внеÑти зміни."
msgid "mrWidget|You can delete the source branch now"
-msgstr ""
+msgstr "Тепер ви можете видалити гілку-джерело"
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "Ви можете прийнÑти цей запит на Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð²Ñ€ÑƒÑ‡Ð½Ñƒ за допомогою"
@@ -11780,19 +12457,19 @@ msgid "new merge request"
msgstr "Ðовий запит на злиттÑ"
msgid "none"
-msgstr ""
+msgstr "немає"
msgid "notification emails"
msgstr "ÐŸÐ¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ ÐµÐ»ÐµÐºÑ‚Ñ€Ð¾Ð½Ð½Ð¾ÑŽ поштою"
msgid "nounSeries|%{firstItem} and %{lastItem}"
-msgstr ""
+msgstr "%{firstItem} Ñ– %{lastItem}"
msgid "nounSeries|%{item}, %{nextItem}"
-msgstr ""
+msgstr "%{item}, %{nextItem}"
msgid "nounSeries|%{item}, and %{lastItem}"
-msgstr ""
+msgstr "%{item}, Ñ– %{lastItem}"
msgid "or"
msgstr "або"
@@ -11818,7 +12495,7 @@ msgid "personal access token"
msgstr "оÑобиÑтий токен доÑтупу"
msgid "private"
-msgstr ""
+msgstr "приватний"
msgid "private key does not match certificate."
msgstr "приватний ключ не відповідає Ñертифікату."
@@ -11831,10 +12508,10 @@ msgstr[2] "проектів"
msgstr[3] "проектів"
msgid "quick actions"
-msgstr ""
+msgstr "швидкі дії"
msgid "register"
-msgstr ""
+msgstr "зареєÑтруватиÑÑ"
msgid "remaining"
msgstr "залишилоÑÑŒ"
@@ -11859,16 +12536,19 @@ msgstr[2] "відповідей"
msgstr[3] "відповідей"
msgid "score"
+msgstr "результат"
+
+msgid "security Reports|There was an error creating the merge request"
msgstr ""
msgid "should be higher than %{access} inherited membership from group %{group_name}"
-msgstr ""
+msgstr "має бути вищим за %{access}, уÑпадкованого з групи %{group_name}"
msgid "show less"
-msgstr ""
+msgstr "показати менше"
msgid "sign in"
-msgstr ""
+msgstr "увійти"
msgid "source"
msgstr "джерело"
@@ -11883,13 +12563,13 @@ msgid "started"
msgstr "розпочато"
msgid "stuck"
-msgstr ""
+msgstr "заблоковано"
msgid "syntax is correct"
-msgstr ""
+msgstr "ÑинтакÑÐ¸Ñ Ð¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹"
msgid "syntax is incorrect"
-msgstr ""
+msgstr "ÑинтакÑÐ¸Ñ Ð½ÐµÐ¿Ñ€Ð°Ð²Ð¸Ð»ÑŒÐ½Ð¸Ð¹"
msgid "this document"
msgstr "цей документ"
@@ -11898,10 +12578,10 @@ msgid "to help your contributors communicate effectively!"
msgstr "щоб допомогти вашим контриб’юторам ефективно ÑпілкуватиÑÑ!"
msgid "triggered"
-msgstr ""
+msgstr "запущено"
msgid "updated"
-msgstr ""
+msgstr "оновлено"
msgid "username"
msgstr "ім'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача"
diff --git a/locale/zh_CN/gitlab.po b/locale/zh_CN/gitlab.po
index 522946d739f..d89914c9775 100644
--- a/locale/zh_CN/gitlab.po
+++ b/locale/zh_CN/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-CN\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:12\n"
+"PO-Revision-Date: 2019-03-06 17:46\n"
msgid " Status"
msgstr "状æ€"
@@ -30,17 +30,21 @@ msgid_plural " improved on %d points"
msgstr[0] " æ高 %d 点"
msgid " or "
-msgstr ""
+msgstr " 或 "
msgid " or <#epic id>"
-msgstr ""
+msgstr " 或<#epic id>"
msgid " or <#issue id>"
-msgstr ""
+msgstr " 或<#issue id>"
msgid "\"%{query}\" in projects"
msgstr "在项目中\"%{query}\""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d 次æ交"
@@ -50,7 +54,7 @@ msgid_plural "%d commits behind"
msgstr[0] "è½åŽ %d 个æ交"
msgid "%d commits"
-msgstr ""
+msgstr "%d 次æ交"
msgid "%d exporter"
msgid_plural "%d exporters"
@@ -70,7 +74,7 @@ msgstr[0] "%d 个议题"
msgid "%d issue selected"
msgid_plural "%d issues selected"
-msgstr[0] ""
+msgstr[0] "已选择%d个议题"
msgid "%d layer"
msgid_plural "%d layers"
@@ -111,12 +115,25 @@ msgstr "%{counter_storage} (%{counter_repositories} 个存储库, %{counter_bu
msgid "%{count} %{alerts}"
msgstr "%{count}个%{alerts}"
-msgid "%{count} more"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+
+msgid "%{count} approvals from %{name}"
msgstr ""
+msgid "%{count} more"
+msgstr "其余 %{count} 项"
+
msgid "%{count} more assignees"
msgstr "åŠå…¶ä»–%{count}å指派人"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} ä½å‚与者"
@@ -131,18 +148,18 @@ msgstr "%{filePath} 已删除"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel} +%{labelCount} 更多"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{group_docs_link_start}群组%{group_docs_link_end} å…许您管ç†ã€å作多个项目。群组的æˆå‘˜å¯ä»¥è®¿é—®ç¾¤ç»„下的所有项目。"
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "%{issuableType} 将被删除ï¼æ‚¨ç¡®å®šå—?"
-msgid "%{link_start}Read more%{link_end} about role permissions"
+msgid "%{label_for_message} unavailable"
msgstr ""
+msgid "%{link_start}Read more%{link_end} about role permissions"
+msgstr "%{link_start}查看更多%{link_end} 关于角色æƒé™"
+
msgid "%{loadingIcon} Started"
msgstr "%{loadingIcon} 已开始"
@@ -162,22 +179,22 @@ msgid "%{percent}%% complete"
msgstr "å·²å®Œæˆ %{percent}%%"
msgid "%{state} epics"
-msgstr ""
+msgstr "epic %{state}"
msgid "%{strong_start}%{branch_count}%{strong_end} Branch"
msgid_plural "%{strong_start}%{branch_count}%{strong_end} Branches"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{branch_count}%{strong_end} 个分支"
msgid "%{strong_start}%{commit_count}%{strong_end} Commit"
msgid_plural "%{strong_start}%{commit_count}%{strong_end} Commits"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{commit_count}%{strong_end} 次æ交"
msgid "%{strong_start}%{human_size}%{strong_end} Files"
-msgstr ""
+msgstr "%{strong_start}%{human_size}%{strong_end} 个文件"
msgid "%{strong_start}%{tag_count}%{strong_end} Tag"
msgid_plural "%{strong_start}%{tag_count}%{strong_end} Tags"
-msgstr[0] ""
+msgstr[0] "%{strong_start}%{tag_count}%{strong_end} 个标签"
msgid "%{text} %{files}"
msgid_plural "%{text} %{files} files"
@@ -196,10 +213,10 @@ msgid "%{usage_ping_link_start}Learn more%{usage_ping_link_end} about what infor
msgstr "%{usage_ping_link_start}了解更多%{usage_ping_link_end}关于GitLab Inc.的共享信æ¯ã€‚"
msgid "%{user_name} profile page"
-msgstr ""
+msgstr "%{user_name}的个人资料"
msgid "(external source)"
-msgstr ""
+msgstr "(外部资æº)"
msgid "+ %{count} more"
msgstr "+ 其余 %{count} 项"
@@ -207,9 +224,12 @@ msgstr "+ 其余 %{count} 项"
msgid "+ %{moreCount} more"
msgstr "+ 其余 %{moreCount} 项"
-msgid ", or "
+msgid "+%{extraOptionCount} more"
msgstr ""
+msgid ", or "
+msgstr ",或"
+
msgid "- Runner is active and can process any new jobs"
msgstr "- Runnerå·²å¯ç”¨ï¼Œéšæ—¶å¯ä»¥å¤„ç†æ–°ä½œä¸š"
@@ -227,6 +247,10 @@ msgid "1 %{type} modification"
msgid_plural "%{count} %{type} modifications"
msgstr[0] "%{count} 个 %{type} 的修改"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "%d 个已关闭的议题"
@@ -267,13 +291,13 @@ msgid "1st contribution!"
msgstr "最高贡献"
msgid "2FA"
-msgstr ""
+msgstr "åŒå› å­éªŒè¯"
msgid "2FA enabled"
msgstr "å¯ç”¨åŒé‡è®¤è¯"
msgid "403|Please contact your GitLab administrator to get permission."
-msgstr ""
+msgstr "请è”系您的 GitLab 管ç†å‘˜èŽ·å–访问æƒé™ã€‚"
msgid "403|You don't have the permission to access this page."
msgstr "您没有æƒé™è®¿é—®æ­¤é¡µé¢ã€‚"
@@ -315,11 +339,26 @@ msgid "<strong>%{pushes}</strong> pushes, more than <strong>%{commits}</strong>
msgstr "<strong>%{pushes}</strong>次推é€ï¼Œå«æ¥è‡ª<strong>%{people}</strong> å贡献者的<strong>%{commits}</strong>次以上æ交。"
msgid "<strong>Deletes</strong> source branch"
-msgstr ""
+msgstr "<strong>删除</strong>æºåˆ†æ”¯"
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "Runner是一个执行任务的进程。您å¯ä»¥æ ¹æ®éœ€è¦é…置任æ„æ•°é‡çš„Runner。"
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "æŒç»­é›†æˆæ•°æ®å›¾"
@@ -335,6 +374,9 @@ msgstr "GitLab滥用审查团队将会尽快查看您的报告。"
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "将在派生(fork)项目中中创建一个新的分支, 并开å¯ä¸€ä¸ªæ–°çš„åˆå¹¶è¯·æ±‚。"
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A 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}。"
@@ -363,7 +405,7 @@ msgid "Abuse reports"
msgstr "滥用报告"
msgid "Accept invitation"
-msgstr ""
+msgstr "接å—邀请"
msgid "Accept terms"
msgstr "接å—æ¡æ¬¾"
@@ -396,16 +438,16 @@ msgid "Active Sessions"
msgstr "活动会è¯"
msgid "Activity"
-msgstr "活动"
+msgstr "动æ€"
msgid "Add"
msgstr "添加"
msgid "Add CHANGELOG"
-msgstr ""
+msgstr "添加更新日志"
msgid "Add CONTRIBUTING"
-msgstr ""
+msgstr "添加贡献信æ¯"
msgid "Add Group Webhooks and GitLab Enterprise Edition."
msgstr "添加组 Webhooks å’Œ GitLab ä¼ä¸šç‰ˆã€‚"
@@ -417,26 +459,53 @@ msgid "Add Kubernetes cluster"
msgstr "添加 Kubernetes 集群"
msgid "Add README"
+msgstr "添加自述文件"
+
+msgid "Add a bullet list"
msgstr ""
msgid "Add a general comment to this %{noteable_name}."
-msgstr ""
+msgstr "添加一般评论 %{noteable_name}。"
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "在wiki中添加一个主页,其中包å«æœ‰å…³é¡¹ç›®çš„ä¿¡æ¯ï¼ŒGitLab将在此处显示该主页,而ä¸æ˜¯æ­¤æ¶ˆæ¯ã€‚"
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "添加表格"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "添加包å«åœ¨æ‰€æœ‰ç”µå­é‚®ä»¶ä¸­çš„附加文本。 长度ä¸è¶…过%{character_limit} 字符"
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "ç«‹å³æ·»åŠ è¯„论"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "添加图片评论"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "添加许å¯è¯"
@@ -453,7 +522,7 @@ msgid "Add reaction"
msgstr "添加回应"
msgid "Add to project"
-msgstr ""
+msgstr "添加到项目"
msgid "Add to review"
msgstr "添加到评审"
@@ -465,7 +534,7 @@ msgid "Add user(s) to the group:"
msgstr "å‘群组添加用户"
msgid "Add users or groups who are allowed to approve every merge request"
-msgstr ""
+msgstr "添加å…许批准æ¯ä¸ªåˆå¹¶è¯·æ±‚的用户或群组"
msgid "Add users to group"
msgstr "将用户加入群组"
@@ -516,40 +585,40 @@ msgid "AdminProjects|Delete project"
msgstr "删除项目"
msgid "AdminSettings|Auto DevOps domain"
-msgstr ""
+msgstr "Auto DevOps 域"
msgid "AdminSettings|Environment variables are protected by default"
-msgstr ""
+msgstr "环境å˜é‡é»˜è®¤å—ä¿æŠ¤"
msgid "AdminSettings|Specify a domain to use by default for every project's Auto Review Apps and Auto Deploy stages."
msgstr "为æ¯ä¸ªé¡¹ç›®çš„自动审阅应用 (Auto Review Apps) 和自动部署 (Auto Deploy) 阶段指定一个默认使用的域。"
msgid "AdminSettings|When creating a new environment variable it will be protected by default."
-msgstr ""
+msgstr "创建一个新的环境å˜é‡æ—¶ï¼Œå®ƒä¼šè¢«é»˜è®¤ä¿æŠ¤ã€‚"
msgid "AdminUsers|2FA Disabled"
-msgstr ""
+msgstr "ç¦ç”¨åŒå› å­éªŒè¯"
msgid "AdminUsers|2FA Enabled"
-msgstr ""
+msgstr "å¯ç”¨åŒå› å­éªŒè¯"
msgid "AdminUsers|Active"
-msgstr ""
+msgstr "活跃"
msgid "AdminUsers|Admin"
-msgstr ""
+msgstr "管ç†å‘˜"
msgid "AdminUsers|Admins"
-msgstr ""
+msgstr "管ç†å‘˜"
msgid "AdminUsers|Block user"
msgstr "ç¦ç”¨ç”¨æˆ·"
msgid "AdminUsers|Blocked"
-msgstr ""
+msgstr "å·²å±è”½"
msgid "AdminUsers|Cannot unblock LDAP blocked users"
-msgstr ""
+msgstr "无法解除已å±è”½çš„ LDAP 用户"
msgid "AdminUsers|Delete User %{username} and contributions?"
msgstr "删除用户 %{username} 以åŠç›¸å…³è´¡çŒ®å—?"
@@ -564,28 +633,28 @@ msgid "AdminUsers|Delete user and contributions"
msgstr "删除用户åŠç›¸å…³è´¡çŒ®"
msgid "AdminUsers|External"
-msgstr ""
+msgstr "外部"
msgid "AdminUsers|It's you!"
-msgstr ""
+msgstr "自己ï¼"
msgid "AdminUsers|New user"
-msgstr ""
+msgstr "新用户"
msgid "AdminUsers|No users found"
-msgstr ""
+msgstr "未找到用户"
msgid "AdminUsers|Search by name, email or username"
-msgstr ""
+msgstr "按åå­—ã€ç”µå­é‚®ä»¶æˆ–用户åæœç´¢"
msgid "AdminUsers|Search users"
-msgstr ""
+msgstr "æœç´¢ç”¨æˆ·"
msgid "AdminUsers|Send email to users"
-msgstr ""
+msgstr "å‘用户å‘é€ç”µå­é‚®ä»¶"
msgid "AdminUsers|Sort by"
-msgstr ""
+msgstr "排åºæ–¹å¼"
msgid "AdminUsers|To confirm, type %{projectName}"
msgstr "请输入 %{projectName} æ¥ç¡®è®¤"
@@ -594,10 +663,10 @@ msgid "AdminUsers|To confirm, type %{username}"
msgstr "请输入 %{username} æ¥ç¡®è®¤"
msgid "AdminUsers|User will be blocked"
-msgstr ""
+msgstr "用户将被å±è”½"
msgid "AdminUsers|Without projects"
-msgstr ""
+msgstr "没有项目"
msgid "Advanced permissions, Large File Storage and Two-Factor authentication settings."
msgstr "高级æƒé™ï¼Œå¤§æ–‡ä»¶å­˜å‚¨å’ŒåŒé‡è®¤è¯è®¾ç½®ã€‚"
@@ -605,16 +674,22 @@ msgstr "高级æƒé™ï¼Œå¤§æ–‡ä»¶å­˜å‚¨å’ŒåŒé‡è®¤è¯è®¾ç½®ã€‚"
msgid "Advanced settings"
msgstr "高级设置"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "警报"
msgid "Alerts"
-msgstr ""
+msgstr "警报"
msgid "All"
msgstr "全部"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr "所有更改å‡å·²æ交"
@@ -622,13 +697,13 @@ msgid "All features are enabled for blank projects, from templates, or when impo
msgstr "从模æ¿æˆ–导入时为空白项目将å¯ç”¨æ‰€æœ‰åŠŸèƒ½ï¼Œä½†å¯ä»¥åœ¨é¡¹ç›®è®¾ç½®ä¸­å°†å…¶ç¦ç”¨ã€‚"
msgid "All issues for this milestone are closed. You may close this milestone now."
-msgstr ""
+msgstr "这个里程碑的所有议题都已关闭。现在您å¯ä»¥å…³é—­è¿™ä¸ªé‡Œç¨‹ç¢‘。"
msgid "All users"
msgstr "所有用户"
msgid "Allow \"%{group_name}\" to sign you in"
-msgstr ""
+msgstr "å…许“%{group_name}â€ä»¥æ‚¨çš„身份登录"
msgid "Allow commits from members who can merge to the target branch."
msgstr "å…许具有åˆå¹¶åˆ°ç›®æ ‡åˆ†æ”¯æƒé™çš„æˆå‘˜æ交"
@@ -652,7 +727,7 @@ msgid "Allow users to request access if visibility is public or internal."
msgstr "如果是公开或内部å¯è§æ€§ï¼Œåˆ™å…许用户请求访问æƒé™ã€‚"
msgid "Allowed to fail"
-msgstr ""
+msgstr "å…许失败"
msgid "Allows you to add and manage Kubernetes clusters."
msgstr "这里å¯ä»¥æ·»åŠ å’Œç®¡ç† Kubernetes 集群。"
@@ -684,7 +759,7 @@ msgstr "空GitLab用户字段将在所有问题和注释的æ述中添加FogBug
msgid "An error has occurred"
msgstr "å‘生错误"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -694,6 +769,15 @@ msgid "An error occurred adding a new draft."
msgstr "添加新è‰ç¨¿æ—¶å‡ºé”™ã€‚"
msgid "An error occurred creating the new branch."
+msgstr "创建新分支时å‘生错误。"
+
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
msgstr ""
msgid "An error occurred previewing the blob"
@@ -708,6 +792,9 @@ msgstr "更新议题æƒé‡æ—¶å‘生错误"
msgid "An error occurred while adding approver"
msgstr "添加审批者时出错"
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "删除评论时å‘生错误"
@@ -744,6 +831,9 @@ msgstr "获å–作业列表时å‘生错误"
msgid "An error occurred while fetching the pipeline."
msgstr "获å–æµæ°´çº¿æ—¶å‘生错误"
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "获å–项目时å‘生错误"
@@ -769,7 +859,7 @@ msgid "An error occurred while loading the file"
msgstr "加载文件时å‘生错误"
msgid "An error occurred while loading the subscription details."
-msgstr ""
+msgstr "加载订阅信æ¯æ—¶å‘生错误。"
msgid "An error occurred while making the request."
msgstr "å‘é€è¯·æ±‚æ—¶å‘生错误。"
@@ -778,10 +868,10 @@ msgid "An error occurred while removing approver"
msgstr "删除批准者时å‘生错误"
msgid "An error occurred while removing epics."
-msgstr ""
+msgstr "删除 epic æ—¶å‘生错误。"
msgid "An error occurred while removing issues."
-msgstr ""
+msgstr "删除议题时å‘生错误。"
msgid "An error occurred while rendering KaTeX"
msgstr "渲染KaTeXæ—¶å‘生错误"
@@ -790,7 +880,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 "获å–差异时å‘生错误"
@@ -801,12 +891,18 @@ msgstr "ä¿å­˜LDAP覆盖状æ€æ—¶å‘生错误。请å†è¯•ä¸€æ¬¡ã€‚"
msgid "An error occurred while saving assignees"
msgstr "ä¿å­˜è¢«æŒ‡æ´¾äººæ—¶å‡ºçŽ°é”™è¯¯ã€‚"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr "订阅通知时å‘生错误。"
msgid "An error occurred while unsubscribing to notifications."
msgstr "å–消订阅通知时å‘生错误。"
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "更新评论时å‘生错误"
@@ -814,52 +910,52 @@ msgid "An error occurred while validating username"
msgstr "验è¯ç”¨æˆ·åæ—¶å‘生错误"
msgid "An error occurred whilst committing your changes."
-msgstr ""
+msgstr "æ交更改时å‘生错误。"
msgid "An error occurred whilst fetching the job trace."
-msgstr ""
+msgstr "获å–作业跟踪时å‘生错误。"
msgid "An error occurred whilst fetching the latest pipeline."
-msgstr ""
+msgstr "获å–最新æµæ°´çº¿æ—¶å‘生错误。"
msgid "An error occurred whilst loading all the files."
-msgstr ""
+msgstr "加载所有文件时å‘生错误。"
msgid "An error occurred whilst loading the file content."
-msgstr ""
+msgstr "加载文件内容时å‘生错误。"
msgid "An error occurred whilst loading the file."
-msgstr ""
+msgstr "加载文件时å‘生错误。"
msgid "An error occurred whilst loading the merge request changes."
-msgstr ""
+msgstr "加载åˆå¹¶è¯·æ±‚çš„å˜æ›´å†…容时å‘生错误。"
msgid "An error occurred whilst loading the merge request version data."
-msgstr ""
+msgstr "加载åˆå¹¶è¯·æ±‚的版本数æ®æ—¶å‘生错误。"
msgid "An error occurred whilst loading the merge request."
-msgstr ""
+msgstr "加载åˆå¹¶è¯·æ±‚æ—¶å‘生错误。"
msgid "An error occurred whilst loading the pipelines jobs."
-msgstr ""
+msgstr "加载æµæ°´çº¿ä½œä¸šæ—¶å‘生错误。"
msgid "An error occurred. Please try again."
msgstr "å‘生了错误,请å†è¯•ä¸€æ¬¡ã€‚"
msgid "An unexpected error occurred while checking the project environment."
-msgstr ""
+msgstr "检查项目环境时å‘生æ„外错误。"
msgid "An unexpected error occurred while checking the project runners."
-msgstr ""
+msgstr "检查项目è¿è¡Œå™¨æ—¶å‘生æ„外错误。"
msgid "An unexpected error occurred while communicating with the Web Terminal."
-msgstr ""
+msgstr "与Web终端通信时å‘生æ„外错误。"
msgid "An unexpected error occurred while starting the Web Terminal."
-msgstr ""
+msgstr "å¯åŠ¨Web终端时å‘生æ„外错误。"
msgid "An unexpected error occurred while stopping the Web Terminal."
-msgstr ""
+msgstr "åœæ­¢Web终端时å‘生æ„外错误。"
msgid "Analytics"
msgstr "分æž"
@@ -892,20 +988,54 @@ msgid "Applications"
msgstr "应用"
msgid "Applied"
-msgstr ""
+msgstr "已应用"
msgid "Apply suggestion"
+msgstr "应用建议"
+
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
msgstr ""
-msgid "Approvals"
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
msgstr ""
-msgid "Approvals required"
+msgid "ApprovalRule|Members"
msgstr ""
-msgid "Approvers"
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
msgstr ""
+msgid "Approvals"
+msgstr "核准"
+
+msgid "Approvals required"
+msgstr "需è¦æ ¸å‡†"
+
+msgid "Approvers"
+msgstr "核准人"
+
msgid "Apr"
msgstr "4月"
@@ -919,7 +1049,7 @@ msgid "Archived projects"
msgstr "归档项目"
msgid "Are you sure"
-msgstr ""
+msgstr "您确定å—"
msgid "Are you sure you want to delete this pipeline schedule?"
msgstr "确定è¦åˆ é™¤æ­¤æµæ°´çº¿è®¡åˆ’å—?"
@@ -931,7 +1061,7 @@ msgid "Are you sure you want to lose unsaved changes?"
msgstr "确定è¦æ”¾å¼ƒæœªä¿å­˜çš„更改å—?"
msgid "Are you sure you want to lose your issue information?"
-msgstr ""
+msgstr "您确定è¦ä¸¢å¼ƒè®®é¢˜ä¿¡æ¯å—?"
msgid "Are you sure you want to regenerate the public key? You will have to update the public key on the remote server before mirroring will work again."
msgstr "您确定è¦é‡æ–°ç”Ÿæˆå…¬é’¥å—?在镜åƒå†æ¬¡è¿è¡Œä¹‹å‰ï¼Œæ‚¨å¿…须更新远程æœåŠ¡å™¨ä¸Šçš„公钥。"
@@ -939,17 +1069,23 @@ msgstr "您确定è¦é‡æ–°ç”Ÿæˆå…¬é’¥å—?在镜åƒå†æ¬¡è¿è¡Œä¹‹å‰ï¼Œæ‚¨å¿…
msgid "Are you sure you want to remove %{group_name}?"
msgstr "确定移除群组 %{group_name} å—?"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
+msgstr "您确定è¦åˆ é™¤æ ¸å‡†è€… %{name} å—?"
+
+msgid "Are you sure you want to remove group %{name}"
msgstr ""
msgid "Are you sure you want to remove group %{name}?"
-msgstr ""
+msgstr "您确定è¦åˆ é™¤ %{name} 群组å—?"
msgid "Are you sure you want to remove the attachment?"
-msgstr ""
+msgstr "您确定è¦åˆ é™¤æ­¤é™„件?"
msgid "Are you sure you want to remove this identity?"
-msgstr "你确定è¦åˆ é™¤è¿™ä¸ªèº«ä»½æ ‡è¯†å—?"
+msgstr "您确定è¦åˆ é™¤è¿™ä¸ªèº«ä»½æ ‡è¯†å—?"
msgid "Are you sure you want to reset registration token?"
msgstr "确定è¦é‡ç½®æ³¨å†Œä»¤ç‰Œå—?"
@@ -961,10 +1097,10 @@ msgid "Are you sure you want to stop this environment?"
msgstr "是å¦ç¡®å®šç»ˆæ­¢å½“å‰çŽ¯å¢ƒï¼Ÿ"
msgid "Are you sure you want to unlock %{path_lock_path}?"
-msgstr "你确定è¦è§£é” %{path_lock_path} å—?"
+msgstr "您确定è¦è§£é” %{path_lock_path} å—?"
msgid "Are you sure you want to unsubscribe from the %{type}: %{link_to_noteable_text}?"
-msgstr ""
+msgstr "您确定è¦å–消订阅 %{type}:%{link_to_noteable_text}å—?"
msgid "Are you sure?"
msgstr "确定å—?"
@@ -985,7 +1121,7 @@ msgid "Assertion consumer service URL"
msgstr "断言消费者æœåŠ¡ URL"
msgid "Assets"
-msgstr ""
+msgstr "资æº"
msgid "Assign custom color like #FF0000"
msgstr "分é…自定义颜色,如FF0000"
@@ -997,7 +1133,7 @@ msgid "Assign milestone"
msgstr "分é…里程碑"
msgid "Assign some issues to this milestone."
-msgstr ""
+msgstr "为此里程碑分é…一些议题。"
msgid "Assign to"
msgstr "分é…到"
@@ -1026,9 +1162,12 @@ msgstr "指派列表显示分é…给选定用户的所有议题。"
msgid "Assignee(s)"
msgstr "指派"
-msgid "Attach a file"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
msgstr ""
+msgid "Attach a file"
+msgstr "附加文件"
+
msgid "Attach a file by drag &amp; drop or %{upload_link}"
msgstr "拖放文件到此处或者 %{upload_link}"
@@ -1041,9 +1180,6 @@ msgstr "8月"
msgid "August"
msgstr "8 月"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "认è¯æ—¥å¿—"
@@ -1060,7 +1196,7 @@ msgid "Authorization code:"
msgstr "授æƒç ï¼š"
msgid "Authorization key"
-msgstr ""
+msgstr "授æƒå¯†é’¥"
msgid "Authorization was granted by entering your username and password in the application."
msgstr "在应用中输入您的用户å和密ç å³å®ŒæˆæŽˆæƒã€‚"
@@ -1123,25 +1259,25 @@ msgid "Automatically marked as default internal user"
msgstr "自动标记为默认内部用户"
msgid "Automatically resolved"
-msgstr ""
+msgstr "自动解决"
msgid "Available"
msgstr "å¯ç”¨çš„"
msgid "Available group Runners: %{runners}"
-msgstr ""
+msgstr "å¯ç”¨çš„群组 Runner: %{runners}"
msgid "Available shared Runners:"
-msgstr ""
+msgstr "å¯ç”¨çš„共享è¿è¡Œå™¨ï¼š"
msgid "Available specific runners"
-msgstr ""
+msgstr "å¯ç”¨çš„特定è¿è¡Œå™¨"
msgid "Avatar for %{assigneeName}"
-msgstr ""
+msgstr "%{assigneeName} 头åƒ"
msgid "Avatar for %{name}"
-msgstr ""
+msgstr "%{name} 头åƒ"
msgid "Avatar will be removed. Are you sure?"
msgstr "å³å°†åˆ é™¤å¤´åƒã€‚确定继续å—?"
@@ -1327,10 +1463,10 @@ msgid "Bitbucket import"
msgstr "从 Bitbucket 导入"
msgid "Block"
-msgstr ""
+msgstr "å±è”½"
msgid "Blocked"
-msgstr ""
+msgstr "å·²å±è”½"
msgid "Blog"
msgstr "åšå®¢"
@@ -1495,34 +1631,34 @@ msgid "Browse files"
msgstr "æµè§ˆæ–‡ä»¶"
msgid "Built-in"
-msgstr ""
+msgstr "内置"
msgid "Business"
-msgstr ""
+msgstr "商业"
msgid "Business metrics (Custom)"
msgstr "业务指标(自定义)"
msgid "By %{user_name}"
-msgstr ""
+msgstr "ç”± %{user_name}"
msgid "ByAuthor|by"
msgstr "作者:"
msgid "CHANGELOG"
-msgstr ""
+msgstr "更新日志"
msgid "CI / CD"
msgstr "CI / CD"
msgid "CI / CD Charts"
-msgstr ""
+msgstr "CI/CD 统计图"
msgid "CI / CD Settings"
msgstr "CI/CD 设置"
msgid "CI Lint"
-msgstr ""
+msgstr "CI Lint"
msgid "CI will run using the credentials assigned above."
msgstr "CI将使用上é¢æŒ‡å®šçš„凭æ®è¿è¡Œã€‚"
@@ -1570,25 +1706,25 @@ msgid "CICD|The Auto DevOps pipeline will run if no alternative CI configuration
msgstr "在未找到备用CIé…置文件时使用Auto DevOpsæµæ°´çº¿ã€‚"
msgid "CICD|You must add a %{kubernetes_cluster_start}Kubernetes cluster integration%{kubernetes_cluster_end} to this project with a domain in order for your deployment strategy to work correctly."
-msgstr ""
+msgstr "您必须添加一个%{kubernetes_cluster_start}Kubernetes集群集æˆ%{kubernetes_cluster_end}域å到此项目,以便您的部署策略正常工作。"
msgid "CICD|instance enabled"
msgstr "å·²å¯ç”¨çš„实例"
msgid "CONTRIBUTING"
-msgstr ""
+msgstr "贡献信æ¯"
msgid "Callback URL"
msgstr "回调 URL"
msgid "Can override approvers and approvals required per merge request"
-msgstr ""
+msgstr "å¯ä»¥è¦†ç›–æ¯ä¸ªåˆå¹¶è¯·æ±‚所需的核准人和核准信æ¯"
msgid "Can't find HEAD commit for this branch"
msgstr "无法找到此分支的 HEAD æ交"
msgid "Canary Deployments is a popular CI strategy, where a small portion of the fleet is updated to the new version of your application."
-msgstr ""
+msgstr "金ä¸é›€éƒ¨ç½²æ˜¯ä¸€é¡¹æµè¡Œçš„CI策略,其中一å°éƒ¨åˆ†èŠ‚点更新到您的新版本应用。"
msgid "Cancel"
msgstr "å–消"
@@ -1602,11 +1738,14 @@ msgstr "无法自动åˆå¹¶"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "无法修改托管的 Kubernetes 集群"
-msgid "Certificate"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
msgstr ""
+msgid "Certificate"
+msgstr "è¯ä¹¦"
+
msgid "Certificate (PEM)"
-msgstr ""
+msgstr "è¯ä¹¦(PEM)"
msgid "Certificate fingerprint"
msgstr "è¯ä¹¦æŒ‡çº¹"
@@ -1615,7 +1754,7 @@ msgid "Change Weight"
msgstr "更改æƒé‡"
msgid "Change permissions"
-msgstr ""
+msgstr "更改æƒé™"
msgid "Change template"
msgstr "更改模æ¿"
@@ -1638,11 +1777,14 @@ msgstr "还原"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "这将创建一个新的æ交, æ¥è¿˜åŽŸçŽ°æœ‰çš„更改。"
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "差异显示方å¼ä¾<b>æº</b>版本åˆå¹¶åˆ°<b>目标</b>版本的形å¼ã€‚"
msgid "Changes suppressed. Click to show."
-msgstr ""
+msgstr "仅显示部分。点此显示全部。"
msgid "Charts"
msgstr "统计图"
@@ -1650,17 +1792,20 @@ msgstr "统计图"
msgid "Chat"
msgstr "å³æ—¶é€šè®¯"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "查看%{docs_link_start}文档%{docs_link_end}。"
msgid "Check your .gitlab-ci.yml"
-msgstr ""
+msgstr "检查您的.gitlab-ci.yml"
msgid "Checking %{text} availability…"
msgstr "正在检查%{text}çš„å¯ç”¨æ€§..."
msgid "Checking approval status"
-msgstr ""
+msgstr "检查批准状æ€"
msgid "Checking branch availability..."
msgstr "正在检查分支的å¯ç”¨æ€§..."
@@ -1684,10 +1829,10 @@ msgid "Choose a branch/tag (e.g. %{master}) or enter a commit (e.g. %{sha}) to s
msgstr "选择分支/标签(例如%{master})或输入æ交(例如%{sha})以查看更改内容或创建åˆå¹¶è¯·æ±‚。"
msgid "Choose a file"
-msgstr ""
+msgstr "选择一个文件"
msgid "Choose a role permission"
-msgstr ""
+msgstr "选择角色æƒé™"
msgid "Choose a template..."
msgstr "选择模æ¿..."
@@ -1708,7 +1853,7 @@ msgid "Choose the top-level group for your repository imports."
msgstr "选择存储库导入的顶级群组。"
msgid "Choose what content you want to see on a group’s overview page"
-msgstr ""
+msgstr "选择è¦åœ¨ç¾¤ç»„概述页é¢ä¸Šçœ‹åˆ°çš„内容"
msgid "Choose which groups you wish to synchronize to this secondary node."
msgstr "选择您希望与此次è¦èŠ‚点åŒæ­¥çš„群组。"
@@ -1716,9 +1861,6 @@ msgstr "选择您希望与此次è¦èŠ‚点åŒæ­¥çš„群组。"
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr "请选择è¦è¿žæŽ¥å¹¶è¿è¡Œ CI/CD æµæ°´çº¿çš„代ç ä»“库。"
-msgid "Choose which repositories you want to import."
-msgstr "选择è¦å¯¼å…¥çš„仓库"
-
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr "选择è¦åŒæ­¥åˆ°æ­¤æ¬¡èŠ‚点的切片。"
@@ -1804,7 +1946,7 @@ msgid "CiVariable|Create wildcard"
msgstr "创建通é…符"
msgid "CiVariable|Error occurred while saving variables"
-msgstr ""
+msgstr "ä¿å­˜å˜é‡æ—¶å‡ºé”™"
msgid "CiVariable|New environment"
msgstr "新建环境"
@@ -1825,10 +1967,10 @@ msgid "ClassificationLabelUnavailable|is unavailable: %{reason}"
msgstr "ä¸å¯ç”¨: %{reason}"
msgid "Clear"
-msgstr ""
+msgstr "清除"
msgid "Clear input"
-msgstr ""
+msgstr "清除输入"
msgid "Clear search"
msgstr "清除æœç´¢"
@@ -1870,43 +2012,43 @@ msgid "Clients"
msgstr "客户端"
msgid "Clone"
-msgstr ""
+msgstr "克隆"
msgid "Clone repository"
msgstr "克隆仓库"
msgid "Clone with %{http_label}"
+msgstr "使用 %{http_label} 克隆"
+
+msgid "Clone with KRB5"
msgstr ""
msgid "Clone with SSH"
-msgstr ""
+msgstr "使用 SSH 克隆"
msgid "Close"
msgstr "关闭"
msgid "Close epic"
-msgstr "关闭å²è¯—故事"
+msgstr "关闭epic"
msgid "Close milestone"
-msgstr ""
+msgstr "关闭里程碑"
msgid "Closed"
msgstr "已关闭"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "已关闭议题"
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
-msgstr ""
+msgstr "%{custom_domain_start}更多信æ¯%{custom_domain_end}。"
msgid "ClusterIntegration| can be used instead of a custom domain."
-msgstr ""
+msgstr "å¯ä»¥ç”¨æ¥ä»£æ›¿è‡ªå®šä¹‰åŸŸã€‚"
msgid "ClusterIntegration| is the default environment scope for this cluster. This means that all jobs, regardless of their environment, will use this cluster. %{environment_scope_start}More information%{environment_scope_end}"
-msgstr ""
+msgstr "是此群集的默认环境范围。这æ„味ç€æ‰€æœ‰ä½œä¸šéƒ½å°†ä½¿ç”¨æ­¤ç¾¤é›†ã€‚%{environment_scope_start}更多信æ¯%{environment_scope_end}"
msgid "ClusterIntegration|%{appList} was successfully installed on your Kubernetes cluster"
msgstr "%{appList} å·²æˆåŠŸå®‰è£…到Kubernetes集群上"
@@ -1915,7 +2057,7 @@ msgid "ClusterIntegration|%{boldNotice} This will add some extra resources like
msgstr "%{boldNotice} 这将添加一些é¢å¤–的资æºï¼Œå¦‚è´Ÿè½½å‡è¡¡å™¨ï¼Œè¿™å¯èƒ½ä¼šäº§ç”Ÿé¢å¤–çš„æˆæœ¬ï¼Œå…·ä½“å–决于您安装Kubernetes集群的托管æœåŠ¡æ供商。如果您使用的是Google Kubernetes Engine,则å¯ä»¥ %{pricingLink}。"
msgid "ClusterIntegration|%{title} upgraded successfully."
-msgstr ""
+msgstr "%{title} å‡çº§æˆåŠŸã€‚"
msgid "ClusterIntegration|API URL"
msgstr "API地å€"
@@ -1939,40 +2081,40 @@ msgid "ClusterIntegration|After installing Ingress, you will need to point your
msgstr "安装IngressåŽï¼Œæ‚¨éœ€è¦åœ¨ç”Ÿæˆçš„外部IP地å€ä¸ŠæŒ‡å‘DNS,以便在部署åŽæŸ¥çœ‹æ‚¨çš„应用程åºã€‚ %{ingressHelpLink}"
msgid "ClusterIntegration|Alternatively"
-msgstr ""
-
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "å°è¯•èŽ·å–项目地域时å‘生错误:%{error}"
+msgstr "除外"
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "å°è¯•è”ç³»Google Cloud APIæ—¶å‘生错误。请ç¨åŽå†è¯•ã€‚"
-msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
+msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
+msgstr "å°è¯•èŽ·å–您的项目时å‘生错误:%{error}"
+
msgid "ClusterIntegration|An error occurred while trying to fetch zone machine types: %{error}"
-msgstr ""
+msgstr "å°è¯•èŽ·å–设备类型时å‘生错误:%{error}"
msgid "ClusterIntegration|Applications"
msgstr "应用程åº"
msgid "ClusterIntegration|Apply for credit"
-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 "确定è¦åˆ é™¤æ­¤Kubernetes集群的集æˆå—?注æ„这并ä¸ä¼šåˆ é™¤å®žé™…çš„Kubernetes集群本身。"
msgid "ClusterIntegration|Base domain"
-msgstr ""
+msgstr "基础域"
msgid "ClusterIntegration|CA Certificate"
msgstr "CAè¯ä¹¦"
msgid "ClusterIntegration|Cert-Manager"
-msgstr ""
+msgstr "Cert-Manager"
msgid "ClusterIntegration|Cert-Manager is a native Kubernetes certificate management controller that helps with issuing certificates. Installing Cert-Manager on your cluster will issue a certificate by %{letsEncrypt} and ensure that certificates are valid and up-to-date."
-msgstr ""
+msgstr "Cert-Manager 是一个本地的 Kubernetes è¯ä¹¦ç®¡ç†æŽ§åˆ¶å™¨ï¼Œå¯å¸®åŠ©é¢å‘è¯ä¹¦ã€‚在群集上安装 Cert-Manager 将通过 %{letsEncrypt} é¢å‘è¯ä¹¦ï¼Œå¹¶ç¡®ä¿è¯ä¹¦æœ‰æ•ˆä¸”是最新的。"
msgid "ClusterIntegration|Certificate Authority bundle (PEM format)"
msgstr "è¯ä¹¦æŽˆæƒåŒ…(PEMæ ¼å¼)"
@@ -1983,9 +2125,12 @@ msgstr "选择è¦åœ¨ Kubernetes 群集上安装的应用程åºã€‚安装以下任
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "请选择使用此Kubernetes群集的环境。"
-msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
+msgid "ClusterIntegration|Cluster health"
msgstr ""
+msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
+msgstr "通过选择匹é…的环境范围的最近集群。例如,项目群集将优先于群组群集。"
+
msgid "ClusterIntegration|Copy API URL"
msgstr "å¤åˆ¶API地å€"
@@ -1999,7 +2144,7 @@ msgid "ClusterIntegration|Copy Jupyter Hostname to clipboard"
msgstr "å°†Jupyter主机åå¤åˆ¶åˆ°å‰ªè´´æ¿"
msgid "ClusterIntegration|Copy Knative IP Address to clipboard"
-msgstr ""
+msgstr "å¤åˆ¶ Knative IP 地å€åˆ°å‰ªè´´æ¿"
msgid "ClusterIntegration|Copy Kubernetes cluster name"
msgstr "å¤åˆ¶Kubernetes集群å称"
@@ -2029,13 +2174,13 @@ msgid "ClusterIntegration|Every new Google Cloud Platform (GCP) account receives
msgstr "æ¯ä¸ªæ–°çš„ Google äº‘å¹³å° (GCP) å¸æˆ·ä½¿ç”¨æ­¤é“¾æŽ¥ %{sign_up_link} å¯ä»¥æ”¶åˆ° 价值300美元的å…è´¹é¢åº¦ã€‚通过与Google çš„åˆä½œï¼ŒGitLabå¯ä»¥ä¸ºæ–°æ³¨å†Œçš„和现有的 GCP å¸æˆ·æä¾›é¢å¤– 200 美元的é¢åº¦ï¼Œä»¥æ–¹ä¾¿å®žçŽ°GitLabå’Œ Google Kubernetes引擎的集æˆã€‚"
msgid "ClusterIntegration|Failed to configure Google Kubernetes Engine Cluster: %{message}"
-msgstr ""
+msgstr "é…置谷歌Kubernetes引擎(GKE)集群失败: %{message}"
msgid "ClusterIntegration|Failed to request to Google Cloud Platform: %{message}"
-msgstr ""
+msgstr "请求Google云平å°å¤±è´¥: %{message}"
msgid "ClusterIntegration|Failed to run Kubeclient: %{message}"
-msgstr ""
+msgstr "è¿è¡Œ Kubeclient 失败: %{message}"
msgid "ClusterIntegration|Fetching machine types"
msgstr "正在获å–实例类型"
@@ -2079,8 +2224,8 @@ 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|In order to view the health of your cluster, you must first install Prometheus below."
+msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr "Ingress"
@@ -2094,9 +2239,6 @@ msgstr "Ingress为您æ供了一ç§åŸºäºŽè¯·æ±‚主机或路径将请求路由åˆ
msgid "ClusterIntegration|Install"
msgstr "安装"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr "安装Prometheus"
-
msgid "ClusterIntegration|Installed"
msgstr "已安装"
@@ -2110,10 +2252,10 @@ msgid "ClusterIntegration|Integration status"
msgstr "集æˆçŠ¶æ€"
msgid "ClusterIntegration|Issuer Email"
-msgstr ""
+msgstr "ç­¾å‘人电å­é‚®ä»¶"
msgid "ClusterIntegration|Issuers represent a certificate authority. You must provide an email address for your Issuer. "
-msgstr ""
+msgstr "ç­¾å‘人代表è¯ä¹¦é¢å‘机构。您必须为您的签å‘人æ供电å­é‚®ä»¶åœ°å€ã€‚ "
msgid "ClusterIntegration|Jupyter Hostname"
msgstr "Jupyter主机å"
@@ -2131,10 +2273,10 @@ msgid "ClusterIntegration|Knative Domain Name:"
msgstr "Knative域å:"
msgid "ClusterIntegration|Knative IP Address:"
-msgstr ""
+msgstr "Knative IP地å€ï¼š"
msgid "ClusterIntegration|Knative extends Kubernetes to provide a set of middleware components that are essential to build modern, source-centric, and container-based applications that can run anywhere: on premises, in the cloud, or even in a third-party data center."
-msgstr ""
+msgstr "Knative扩展了Kubernetes,æ供了一组中间件组件,这些组件对于构建å¯åœ¨ä»»ä½•åœ°æ–¹è¿è¡ŒçŽ°ä»£çš„ã€ä»¥å¼€æºä¸ºä¸­å¿ƒå’ŒåŸºäºŽå®¹å™¨çš„应用程åºè‡³å…³é‡è¦ï¼šåœ¨æœ¬åœ°ï¼Œåœ¨äº‘中,甚至在第三方数æ®ä¸­å¿ƒã€‚"
msgid "ClusterIntegration|Kubernetes cluster"
msgstr "Kubernetes 集群"
@@ -2142,9 +2284,6 @@ msgstr "Kubernetes 集群"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Kubernetes集群详细信æ¯"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Kubernetes集群è¿è¡ŒçŠ¶å†µ"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "正在Google Kubernetes Engine上创建Kubernetes集群..."
@@ -2173,7 +2312,7 @@ msgid "ClusterIntegration|Learn more about group Kubernetes clusters"
msgstr "了解更多的群组级Kubernetes集群信æ¯"
msgid "ClusterIntegration|Let's Encrypt"
-msgstr ""
+msgstr "Let's Encrypt"
msgid "ClusterIntegration|Machine type"
msgstr "机器类型"
@@ -2254,7 +2393,7 @@ msgid "ClusterIntegration|Request to begin installing failed"
msgstr "请求安装失败"
msgid "ClusterIntegration|Retry upgrade"
-msgstr ""
+msgstr "é‡è¯•å‡çº§"
msgid "ClusterIntegration|Save changes"
msgstr "ä¿å­˜æ›´æ”¹"
@@ -2299,7 +2438,7 @@ msgid "ClusterIntegration|Something went wrong on our end."
msgstr "å‘生了内部错误"
msgid "ClusterIntegration|Something went wrong when upgrading %{title}. Please check the logs and try again."
-msgstr ""
+msgstr "å‡çº§ %{title} 时出错。请检查日志并é‡è¯•ã€‚"
msgid "ClusterIntegration|Something went wrong while creating your Kubernetes cluster on Google Kubernetes Engine"
msgstr "在 Google Kubernetes Engine 上创建Kubernetes集群时å‘生错误"
@@ -2308,7 +2447,7 @@ msgid "ClusterIntegration|Something went wrong while installing %{title}"
msgstr "安装 %{title} æ—¶å‘生故障"
msgid "ClusterIntegration|Specifying a domain will allow you to use Auto Review Apps and Auto Deploy stages for %{auto_devops_start}Auto DevOps%{auto_devops_end}. The domain should have a wildcard DNS configured matching the domain."
-msgstr ""
+msgstr "指定域将å…许您使用自动评审应用程åºå’Œè‡ªåŠ¨éƒ¨ç½²é˜¶æ®µè¿›è¡Œ %{auto_devops_start}Auto DevOps%{auto_devops_end}。域应é…置与域匹é…的通é…符DNS。"
msgid "ClusterIntegration|The IP address is in the process of being assigned. Please check your Kubernetes cluster or Quotas on Google Kubernetes Engine if it takes a long time."
msgstr "IP地å€æ­£åœ¨åˆ†é…中。如果花费时间过长,请检查您的Kubernetes集群或谷歌Kubernetes引擎(GKE) 上的é…é¢ã€‚"
@@ -2326,16 +2465,16 @@ msgid "ClusterIntegration|Token"
msgstr "令牌"
msgid "ClusterIntegration|Upgrade"
-msgstr ""
+msgstr "å‡çº§"
msgid "ClusterIntegration|Upgrade failed"
-msgstr ""
+msgstr "å‡çº§å¤±è´¥"
msgid "ClusterIntegration|Upgraded"
-msgstr ""
+msgstr "å·²å‡çº§"
msgid "ClusterIntegration|Upgrading"
-msgstr ""
+msgstr "å‡çº§ä¸­"
msgid "ClusterIntegration|Validating project billing status"
msgstr "验è¯é¡¹ç›®è´¦å•çŠ¶æ€"
@@ -2350,7 +2489,7 @@ msgid "ClusterIntegration|You must first install Helm Tiller before installing t
msgstr "在安装以下应用程åºä¹‹å‰ï¼Œå¿…须先安装Helm Tiller"
msgid "ClusterIntegration|You must have an RBAC-enabled cluster to install Knative."
-msgstr ""
+msgstr "您必须具有已å¯ç”¨RBAC的群集æ‰èƒ½å®‰è£…Knative。"
msgid "ClusterIntegration|Your account must have %{link_to_kubernetes_engine}"
msgstr "您的å¸æˆ·å¿…须有æƒé™%{link_to_kubernetes_engine}"
@@ -2380,31 +2519,43 @@ msgid "ClusterIntegration|sign up"
msgstr "注册"
msgid "Code"
+msgstr "代ç "
+
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
msgstr ""
msgid "Code owners"
msgstr "代ç æ‰€æœ‰è€…"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr "世代表"
msgid "Collapse"
msgstr "收起"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "收起侧边æ "
msgid "Command line instructions"
-msgstr ""
+msgstr "命令行指引"
msgid "Comment"
msgstr "添加评论"
msgid "Comment & close %{noteable_name}"
-msgstr ""
+msgstr "评论并关闭 %{noteable_name}"
msgid "Comment & reopen %{noteable_name}"
-msgstr ""
+msgstr "评论并é‡æ–°å¼€å¯ %{noteable_name}"
msgid "Comment & resolve discussion"
msgstr "添加评论并解决讨论"
@@ -2423,13 +2574,13 @@ msgid_plural "Commits"
msgstr[0] "æ交"
msgid "Commit %{commit_id}"
-msgstr ""
+msgstr "æ交 %{commit_id}"
msgid "Commit Message"
msgstr "æ交消æ¯"
msgid "Commit deleted"
-msgstr ""
+msgstr "æ交已删除"
msgid "Commit duration in minutes for last 30 commits"
msgstr "最近30次æ交相应æŒç»­é›†æˆèŠ±è´¹çš„时间(分钟)"
@@ -2495,7 +2646,7 @@ msgid "Compare Revisions"
msgstr "比较版本"
msgid "Compare changes"
-msgstr ""
+msgstr "比较å˜æ›´"
msgid "Compare changes with the last commit"
msgstr "与上个æ交比较å˜æ›´å†…容"
@@ -2525,7 +2676,7 @@ msgid "Confidentiality"
msgstr "ä¿å¯†æ€§"
msgid "Configure GitLab runners to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
-msgstr ""
+msgstr "é…ç½® GitLab Runner 以开始使用Web终端。 %{helpStart}了解更多。%{helpEnd}"
msgid "Configure Gitaly timeouts."
msgstr "é…ç½®Gitaly超时时间。"
@@ -2534,7 +2685,7 @@ msgid "Configure Tracing"
msgstr "é…置跟踪"
msgid "Configure a <code>.gitlab-webide.yml</code> file in the <code>.gitlab</code> directory to start using the Web Terminal. %{helpStart}Learn more.%{helpEnd}"
-msgstr ""
+msgstr "在 <code>.gitlab</code> 目录中é…ç½® <code>.gitlab-webide.yml</code> 文件以开始使用Web终端。 %{helpStart}了解更多。%{helpEnd}"
msgid "Configure automatic git checks and housekeeping on repositories."
msgstr "在仓库上é…置自动git检查和仓库整ç†ã€‚"
@@ -2570,7 +2721,7 @@ msgid "Connecting..."
msgstr "正在连接..."
msgid "Contact sales to upgrade"
-msgstr ""
+msgstr "è”系销售人员进行å‡çº§"
msgid "Container Registry"
msgstr "容器注册表"
@@ -2621,7 +2772,7 @@ msgid "ContainerRegistry|You can also use a %{deploy_token} for read-only access
msgstr "您也å¯ä»¥ä½¿ç”¨ %{deploy_token} 以åªè¯»æ–¹å¼è®¿é—®é•œåƒåº“çš„é•œåƒã€‚"
msgid "Contents of .gitlab-ci.yml"
-msgstr ""
+msgstr ".gitlab-ci.yml的内容"
msgid "Continue"
msgstr "继续"
@@ -2639,7 +2790,7 @@ msgid "Contribution"
msgstr "贡献"
msgid "Contribution Charts"
-msgstr ""
+msgstr "贡献图表"
msgid "Contributions for <strong>%{calendar_date}</strong>"
msgstr "<strong>%{calendar_date}</strong>的贡献"
@@ -2675,13 +2826,13 @@ msgid "Control the maximum concurrency of verification operations for this Geo n
msgstr "控制此Geo节点的校验æ“作的最大并å‘性"
msgid "Control the minimum interval in days that a repository should be reverified for this primary node"
-msgstr ""
+msgstr "控制此主节点é‡æ–°éªŒè¯å­˜å‚¨åº“的最å°æ—¶é—´é—´éš”"
msgid "ConvDev Index"
msgstr "ConvDev指数"
msgid "Copy %{http_label} clone URL"
-msgstr ""
+msgstr "å¤åˆ¶ %{http_label} 克隆地å€"
msgid "Copy %{protocol} clone URL"
msgstr "å¤åˆ¶ %{protocol} 克隆URL"
@@ -2689,11 +2840,14 @@ msgstr "å¤åˆ¶ %{protocol} 克隆URL"
msgid "Copy ID to clipboard"
msgstr "å¤åˆ¶ID到剪贴æ¿"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr "å¤åˆ¶SSH克隆URL"
msgid "Copy SSH public key"
-msgstr ""
+msgstr "å¤åˆ¶SSH公钥"
msgid "Copy SSH public key to clipboard"
msgstr "å°† SSH 公钥å¤åˆ¶åˆ°å‰ªè´´æ¿"
@@ -2741,7 +2895,7 @@ msgid "Create New Directory"
msgstr "创建新目录"
msgid "Create New Domain"
-msgstr ""
+msgstr "创建新域"
msgid "Create a new branch"
msgstr "创建一个新分支"
@@ -2753,7 +2907,7 @@ msgid "Create a new issue"
msgstr "创建新议题"
msgid "Create a new repository"
-msgstr ""
+msgstr "创建一个新存储库"
msgid "Create a personal access token on your account to pull or push via %{protocol}."
msgstr "在å¸æˆ·ä¸Šåˆ›å»ºä¸ªäººè®¿é—®ä»¤ç‰Œï¼Œä»¥é€šè¿‡ %{protocol} æ¥æ‹‰å–或推é€ã€‚"
@@ -2771,7 +2925,7 @@ msgid "Create empty repository"
msgstr "创建空的仓库"
msgid "Create epic"
-msgstr "创建å²è¯—故事"
+msgstr "创建epic"
msgid "Create file"
msgstr "创建文件"
@@ -2795,7 +2949,7 @@ msgid "Create merge request and branch"
msgstr "创建åˆå¹¶è¯·æ±‚åŠåˆ†æ”¯"
msgid "Create milestone"
-msgstr ""
+msgstr "创建里程碑"
msgid "Create new branch"
msgstr "创建新分支"
@@ -2843,7 +2997,7 @@ msgid "Created on:"
msgstr "创建于:"
msgid "Creating epic"
-msgstr "创建å²è¯—故事中"
+msgstr "创建epic中"
msgid "Cron Timezone"
msgstr "Cron 时区"
@@ -2879,7 +3033,7 @@ msgid "Custom project templates"
msgstr "自定义项目模æ¿"
msgid "Custom project templates have not been set up for groups that you are a member of. They are enabled from a group’s settings page. Contact your group’s Owner or Maintainer to setup custom project templates."
-msgstr ""
+msgstr "尚未为您所属的群组设置自定义项目模æ¿ã€‚请在群组的设置页é¢å¯ç”¨å®ƒã€‚è¦è®¾ç½®è‡ªå®šä¹‰é¡¹ç›®æ¨¡æ¿ï¼Œè¯·è”系群组的所有者或维护者。"
msgid "Customize colors"
msgstr "自定义颜色"
@@ -2891,10 +3045,10 @@ msgid "Customize how Google Code email addresses and usernames are imported into
msgstr "自定义如何将Google Code电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å导入GitLab。下一步将选择è¦å¯¼å…¥çš„项目。"
msgid "Customize language and region related settings."
-msgstr ""
+msgstr "自定义语言和区域相关设置。"
msgid "Customize your merge request approval settings."
-msgstr ""
+msgstr "自定义您的åˆå¹¶è¯·æ±‚核准设置。"
msgid "Customize your pipeline configuration, view your pipeline status and coverage report."
msgstr "自定义æµæ°´çº¿é…置,查看æµæ°´çº¿çŠ¶æ€å’Œè¦†ç›–率报告。"
@@ -2927,7 +3081,7 @@ msgid "CycleAnalyticsStage|Test"
msgstr "测试"
msgid "DNS"
-msgstr ""
+msgstr "DNS"
msgid "Dashboard"
msgstr "仪表æ¿"
@@ -2939,7 +3093,7 @@ msgid "DashboardProjects|Personal"
msgstr "个人"
msgid "Data is still calculating..."
-msgstr ""
+msgstr "æ•°æ®ä»åœ¨è®¡ç®—中……"
msgid "Date picker"
msgstr "日期选择器"
@@ -2954,7 +3108,7 @@ msgid "December"
msgstr "12月"
msgid "Decline"
-msgstr ""
+msgstr "æ‹’ç»"
msgid "Decline and sign out"
msgstr "æ‹’ç»å¹¶é€€å‡º"
@@ -2966,10 +3120,10 @@ msgid "Default classification label"
msgstr "默认分类标记"
msgid "Default first day of the week"
-msgstr ""
+msgstr "æ¯å‘¨çš„默认起始日"
msgid "Default first day of the week in calendars and date pickers."
-msgstr ""
+msgstr "在日期选择器中显示æ¯å‘¨é»˜è®¤çš„起始日。"
msgid "Default: Directly import the Google Code email address or username"
msgstr "默认:直接导入Google Code电å­é‚®ä»¶åœ°å€æˆ–用户å"
@@ -2981,7 +3135,7 @@ msgid "Define a custom pattern with cron syntax"
msgstr "使用 Cron 语法定义自定义模å¼"
msgid "Define environments in the deploy stage(s) in <code>.gitlab-ci.yml</code> to track deployments here."
-msgstr ""
+msgstr "在<code>.githab-ci.yml</code>çš„ deploy 阶段中定义 environment æ¥è·Ÿè¸ªéƒ¨ç½²ã€‚"
msgid "DelayedJobs|Are you sure you want to run %{jobName} immediately? Otherwise this job will run automatically after it's timer finishes."
msgstr "您确定è¦ç«‹å³è¿è¡Œ %{jobName} å—?å¦åˆ™çš„è¯ï¼Œè¯¥ä½œä¸šå°†åœ¨è®¡æ—¶ç»“æŸåŽè‡ªåŠ¨è¿è¡Œã€‚"
@@ -3014,10 +3168,10 @@ msgid "Delete list"
msgstr "删除列表"
msgid "Delete source branch"
-msgstr ""
+msgstr "删除æºåˆ†æ”¯"
msgid "Delete this attachment"
-msgstr ""
+msgstr "删除此附件"
msgid "Deleted"
msgstr "已删除"
@@ -3153,7 +3307,7 @@ msgid "DeployTokens|Your new project deploy token has been created."
msgstr "新项目部署令牌已创建。"
msgid "Deployed"
-msgstr ""
+msgstr "已部署"
msgid "Deployed to"
msgstr "已部署到"
@@ -3176,6 +3330,9 @@ msgstr "æ述模æ¿å…许您为项目的问题和åˆå¹¶è¯·æ±‚定义æ述字段
msgid "Description:"
msgstr "æè¿°:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr "删除"
@@ -3183,7 +3340,7 @@ msgid "Details"
msgstr "详情"
msgid "Details (default)"
-msgstr ""
+msgstr "详细信æ¯(默认)"
msgid "Detect host keys"
msgstr "检测主机密钥"
@@ -3216,10 +3373,10 @@ msgid "Disable group Runners"
msgstr "ç¦ç”¨ç¾¤ç»„Runner"
msgid "Disable shared Runners"
-msgstr ""
+msgstr "ç¦ç”¨å…±äº«è¿è¡Œå™¨"
msgid "Disabled"
-msgstr ""
+msgstr "å·²ç¦ç”¨"
msgid "Discard"
msgstr "放弃"
@@ -3243,22 +3400,25 @@ msgid "Discard review"
msgstr "放弃评审"
msgid "Discover GitLab Geo"
-msgstr ""
+msgstr "å‘现GitLab Geo"
msgid "Discover projects, groups and snippets. Share your projects with others"
msgstr "æµè§ˆé¡¹ç›®ï¼Œç¾¤ç»„和代ç ç‰‡æ®µã€‚与他人分享您的项目"
msgid "Discuss a specific suggestion or question"
-msgstr ""
+msgstr "讨论具体的建议或议题"
msgid "Discuss a specific suggestion or question that needs to be resolved"
+msgstr "讨论需è¦è§£å†³çš„具体建议或议题"
+
+msgid "Discussion"
msgstr ""
msgid "Dismiss"
msgstr "忽略"
msgid "Dismiss ConvDev introduction"
-msgstr ""
+msgstr "å–消 ConvDev 导入"
msgid "Dismiss Cycle Analytics introduction box"
msgstr "关闭循环分æžä»‹ç»æ¡†"
@@ -3288,10 +3448,10 @@ msgid "Download"
msgstr "下载"
msgid "Download artifacts"
-msgstr ""
+msgstr "下载工件"
msgid "Download asset"
-msgstr ""
+msgstr "下载资æº"
msgid "Download tar"
msgstr "下载 tar"
@@ -3318,7 +3478,7 @@ msgid "DownloadSource|Download"
msgstr "下载"
msgid "Downstream"
-msgstr ""
+msgstr "下游"
msgid "Downvotes"
msgstr "踩"
@@ -3336,13 +3496,13 @@ msgid "Edit"
msgstr "编辑"
msgid "Edit %{name}"
-msgstr ""
+msgstr "编辑%{name}"
msgid "Edit Label"
msgstr "编辑标签"
msgid "Edit Milestone"
-msgstr ""
+msgstr "编辑里程碑"
msgid "Edit Pipeline Schedule %{id}"
msgstr "编辑 %{id} æµæ°´çº¿è®¡åˆ’"
@@ -3354,10 +3514,10 @@ msgid "Edit application"
msgstr "编辑应用"
msgid "Edit comment"
-msgstr ""
+msgstr "编辑评论"
msgid "Edit environment"
-msgstr ""
+msgstr "编辑环境"
msgid "Edit files in the editor and commit changes here"
msgstr "在编辑器中编辑文件并在这里​​æ交å˜æ›´å†…容"
@@ -3369,7 +3529,7 @@ msgid "Edit identity for %{user_name}"
msgstr "编辑 %{user_name} 的身份信æ¯"
msgid "Edit issues"
-msgstr ""
+msgstr "编辑议题"
msgid "Elasticsearch"
msgstr "Elasticsearch"
@@ -3390,7 +3550,7 @@ msgid "Embed"
msgstr "嵌入"
msgid "Empty file"
-msgstr ""
+msgstr "空文件"
msgid "Enable"
msgstr "å¯ç”¨"
@@ -3417,7 +3577,7 @@ msgid "Enable classification control using an external service"
msgstr "使用外部æœåŠ¡å¯ç”¨åˆ†ç±»æŽ§åˆ¶"
msgid "Enable error tracking"
-msgstr ""
+msgstr "å¯ç”¨é”™è¯¯è·Ÿè¸ª"
msgid "Enable for this project"
msgstr "在此项目中å¯ç”¨"
@@ -3425,6 +3585,9 @@ msgstr "在此项目中å¯ç”¨"
msgid "Enable group Runners"
msgstr "å¯ç”¨ç¾¤ç»„Runner"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr "å¯ç”¨æˆ–ç¦ç”¨åŒ¿å化数æ®æ”¶é›†."
@@ -3435,16 +3598,16 @@ msgid "Enable reCAPTCHA or Akismet and set IP limits."
msgstr "å¯ç”¨reCAPTCHA或Akismet并设置IPé™åˆ¶ã€‚"
msgid "Enable self approval of merge requests"
-msgstr ""
+msgstr "å¯ç”¨è‡ªæˆ‘核准åˆå¹¶è¯·æ±‚"
msgid "Enable shared Runners"
-msgstr ""
+msgstr "å¯ç”¨å…±äº«çš„è¿è¡Œå™¨"
msgid "Enable the Performance Bar for a given group."
msgstr "对指定群组å¯ç”¨æ€§èƒ½æ ã€‚"
msgid "Enable two-factor authentication"
-msgstr ""
+msgstr "å¯ç”¨åŒå› ç´ èº«ä»½éªŒè¯"
msgid "Enable usage ping"
msgstr "å¯ç”¨ä½¿ç”¨æƒ…况检测(usage ping)"
@@ -3459,10 +3622,13 @@ msgid "Ends at (UTC)"
msgstr "结æŸäºŽ(UTC)"
msgid "Enforce SSO-only authentication for this group"
+msgstr "对该群组强制执行仅SSO身份验è¯"
+
+msgid "Enforce users to have dedicated group managed accounts for this group"
msgstr ""
msgid "Enforced SSO"
-msgstr ""
+msgstr "已强制执行SSO"
msgid "Enter in your Bitbucket Server URL and personal access token below"
msgstr "输入您的BitbucketæœåŠ¡å™¨URL和个人访问令牌"
@@ -3479,26 +3645,23 @@ msgstr "输入åˆå¹¶è¯·æ±‚说明"
msgid "Enter the merge request title"
msgstr "输入åˆå¹¶è¯·æ±‚标题"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
-msgstr ""
+msgstr "环境å˜é‡"
msgid "Environment variables are applied to environments via the runner. They can be protected by only exposing them to protected branches or tags. You can use environment variables for passwords, secret keys, or whatever you want."
-msgstr ""
+msgstr "环境å˜é‡ä½œç”¨äºŽè¿è¡Œå™¨çš„环境中。å¯å°†å˜é‡é™åˆ¶ä¸ºä»…å—ä¿æŠ¤çš„分支或标签å¯ä»¥è®¿é—®ã€‚您å¯ä»¥ä¿å­˜å¯†ç ã€å¯†é’¥æˆ–任何你想è¦çš„å˜é‡ã€‚"
msgid "Environment variables are configured by your administrator to be %{link_start}protected%{link_end} by default"
-msgstr ""
+msgstr "环境å˜é‡å·²è¢«ç®¡ç†å‘˜é…置为%{link_start}å—ä¿æŠ¤çš„(默认)%{link_end}"
msgid "Environment:"
-msgstr ""
+msgstr "环境:"
msgid "Environments"
msgstr "环境"
msgid "Environments allow you to track deployments of your application %{link_to_read_more}."
-msgstr ""
+msgstr "环境å…许您跟踪应用程åºçš„部署 %{link_to_read_more}。"
msgid "Environments|An error occurred while fetching the environments."
msgstr "获å–环境时å‘生错误。"
@@ -3506,6 +3669,12 @@ msgstr "获å–环境时å‘生错误。"
msgid "Environments|An error occurred while making the request."
msgstr "å‘é€è¯·æ±‚æ—¶å‘生错误。"
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr "终止环境时å‘生错误,请ç¨åŽé‡è¯•"
@@ -3557,15 +3726,33 @@ msgstr "打开è¿è¡Œä¸­çš„环境"
msgid "Environments|Pod logs from"
msgstr "Pod日志æ¥è‡ªäºŽ"
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "显示全部"
@@ -3576,6 +3763,18 @@ msgid "Environments|Stop environment"
msgstr "终止环境"
msgid "Environments|Stopping"
+msgstr "åœæ­¢ä¸­"
+
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
msgstr ""
msgid "Environments|Updated"
@@ -3588,19 +3787,19 @@ msgid "Environments|protected"
msgstr "å—ä¿æŠ¤çš„"
msgid "Epic"
-msgstr "å²è¯—故事"
+msgstr "Epic"
msgid "Epics"
-msgstr "å²è¯—故事"
+msgstr "Epic"
msgid "Epics Roadmap"
-msgstr "å²è¯—故事路线图"
+msgstr "Epic路线图"
msgid "Epics let you manage your portfolio of projects more efficiently and with less effort"
-msgstr "利用å²è¯—故事(Epics),产å“线管ç†ä¼šå˜å¾—æ›´è½»æ¾ä¸”更高效"
+msgstr "利用 Epic,产å“线管ç†ä¼šå˜å¾—æ›´è½»æ¾ä¸”更高效"
msgid "Epics|An error occurred while saving the %{epicDateType} date"
-msgstr ""
+msgstr "ä¿å­˜ %{epicDateType} 日期时å‘生错误"
msgid "Epics|How can I solve this?"
msgstr "我该如何解决该问题?"
@@ -3609,10 +3808,10 @@ msgid "Epics|More information"
msgstr "更多信æ¯"
msgid "Epics|These dates affect how your epics appear in the roadmap. Dates from milestones come from the milestones assigned to issues in the epic. You can also set fixed dates or remove them entirely."
-msgstr "这些日期会影å“å²è¯—故事在路线图中的显示方å¼ã€‚里程碑日期æ¥è‡ªäºŽå²è¯—故事中的议题所属的里程碑。您还å¯ä»¥è®¾ç½®å›ºå®šæ—¥æœŸæˆ–完全删除它们。"
+msgstr "è¿™äº›æ—¥æœŸä¼šå½±å“ epic 在路线图中的显示方å¼ã€‚里程碑日期æ¥è‡ªäºŽå²è¯—故事中的议题所属的里程碑。您还å¯ä»¥è®¾ç½®å›ºå®šæ—¥æœŸæˆ–完全删除它们。"
msgid "Epics|To schedule your epic's %{epicDateType} date based on milestones, assign a milestone with a %{epicDateType} date to any issue in the epic."
-msgstr "如需根æ®é‡Œç¨‹ç¢‘æ¥å®‰æŽ’å²è¯—故事的 %{epicDateType} 日期,请为å²è¯—故事中的任一议题指定带有 %{epicDateType} 日期的里程碑。"
+msgstr "如需根æ®é‡Œç¨‹ç¢‘æ¥å®‰æŽ’ epic çš„ %{epicDateType} 日期,请为 epic 中的任一议题指定带有 %{epicDateType} 日期的里程碑。"
msgid "Epics|due"
msgstr "到期"
@@ -3627,13 +3826,16 @@ msgid "Error Reporting and Logging"
msgstr "错误报告和日志记录"
msgid "Error Tracking"
+msgstr "错误跟踪"
+
+msgid "Error creating a new path"
msgstr ""
msgid "Error creating epic"
-msgstr "创建å²è¯—故事时出错"
+msgstr "创建 epic 时出错"
msgid "Error deleting %{issuableType}"
-msgstr ""
+msgstr "删除 %{issuableType} 时出错"
msgid "Error fetching contributors data."
msgstr "获å–贡献者数æ®æ—¶å‡ºé”™ã€‚"
@@ -3678,13 +3880,13 @@ msgid "Error occurred when toggling the notification subscription"
msgstr "切æ¢é€šçŸ¥è®¢é˜…æ—¶å‘生错误"
msgid "Error rendering markdown preview"
-msgstr ""
+msgstr "渲染Markdown预览时出错"
msgid "Error saving label update."
msgstr "ä¿å­˜æ ‡è®°æ›´æ–°æ—¶å‡ºé”™ã€‚"
msgid "Error updating %{issuableType}"
-msgstr ""
+msgstr "更新 %{issuableType} 时出错"
msgid "Error updating status for all todos."
msgstr "更新所有待办事项的状æ€æ—¶å‡ºé”™ã€‚"
@@ -3696,11 +3898,38 @@ msgid "Error while loading the merge request. Please try again."
msgstr "加载åˆå¹¶è¯·æ±‚时出错。请å†è¯•ä¸€æ¬¡ã€‚"
msgid "Error:"
+msgstr "错误:"
+
+msgid "ErrorTracking|Active"
msgstr ""
-msgid "Errors"
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
msgstr ""
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
+msgid "Errors"
+msgstr "错误"
+
msgid "Estimated"
msgstr "预计"
@@ -3723,10 +3952,10 @@ msgid "EventFilterBy|Filter by team"
msgstr "åªæ˜¾ç¤ºå›¢é˜Ÿäº‹ä»¶"
msgid "Events"
-msgstr ""
+msgstr "事件"
msgid "Every %{action} attempt has failed: %{job_error_message}. Please try again."
-msgstr ""
+msgstr "所有 %{action} å°è¯•éƒ½å·²å¤±è´¥ï¼š %{job_error_message}。请å†è¯•ä¸€æ¬¡ã€‚"
msgid "Every day (at 4:00am)"
msgstr "æ¯æ—¥æ‰§è¡Œï¼ˆå‡Œæ™¨ 4 点)"
@@ -3738,37 +3967,37 @@ msgid "Every week (Sundays at 4:00am)"
msgstr "æ¯å‘¨æ‰§è¡Œï¼ˆå‘¨æ—¥å‡Œæ™¨ 4 点)"
msgid "Everyone"
-msgstr ""
+msgstr "所有人"
msgid "Everyone can contribute"
msgstr "人人皆å¯è´¡çŒ®"
msgid "Everything you need to create a GitLab Pages site using GitBook."
-msgstr ""
+msgstr "使用GitBook创建GitLab Pages站点所需的一切。"
msgid "Everything you need to create a GitLab Pages site using Hexo."
-msgstr ""
+msgstr "使用Hexo创建GitLab Pages站点所需的一切。"
msgid "Everything you need to create a GitLab Pages site using Hugo."
-msgstr ""
+msgstr "使用Hugo创建GitLab Pages站点所需的一切。"
msgid "Everything you need to create a GitLab Pages site using Jekyll."
-msgstr ""
+msgstr "使用Jekyll创建GitLab Pages站点所需的一切。"
msgid "Everything you need to create a GitLab Pages site using plain HTML."
-msgstr ""
+msgstr "使用纯HTML创建GitLab Pages网站所需的一切。"
msgid "Except policy:"
-msgstr ""
+msgstr "策略以外:"
msgid "Existing Git repository"
-msgstr ""
+msgstr "现有的Git存储库"
msgid "Existing folder"
-msgstr ""
+msgstr "现有的文件夹"
msgid "Existing members and groups"
-msgstr ""
+msgstr "现有的æˆå‘˜å’Œç¾¤ç»„"
msgid "Expand"
msgstr "展开"
@@ -3776,6 +4005,9 @@ msgstr "展开"
msgid "Expand all"
msgstr "展开全部"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "展开侧边æ "
@@ -3783,10 +4015,10 @@ msgid "Expiration date"
msgstr "到期时间"
msgid "Expired %{expiredOn}"
-msgstr ""
+msgstr "已在 %{expiredOn} 过期"
msgid "Expires in %{expires_at}"
-msgstr ""
+msgstr "在 %{expires_at} 过期"
msgid "Explain the problem. If appropriate, provide a link to the relevant issue or comment."
msgstr "请解释此问题。如适用,å¯æ供相关议题或评论的链接。"
@@ -3810,19 +4042,19 @@ msgid "Explore public groups"
msgstr "æœç´¢å…¬å¼€ç¾¤ç»„"
msgid "Export as CSV"
-msgstr ""
+msgstr "导出为 CSV"
msgid "Export issues"
-msgstr ""
+msgstr "导出议题"
msgid "External Classification Policy Authorization"
msgstr "外部分类政策授æƒ"
msgid "External URL"
-msgstr ""
+msgstr "外部URL"
msgid "External Wiki"
-msgstr ""
+msgstr "外部Wiki"
msgid "External authentication"
msgstr "外部身份验è¯"
@@ -3863,7 +4095,7 @@ msgstr "无法部署到"
msgid "Failed to load emoji list."
msgstr "无法加载表情列表。"
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3876,7 +4108,7 @@ msgid "Failed to remove the pipeline schedule"
msgstr "无法删除æµæ°´çº¿è®¡åˆ’"
msgid "Failed to reset key. Please try again."
-msgstr ""
+msgstr "é‡ç½®å¯†é’¥å¤±è´¥ã€‚请é‡è¯•ã€‚"
msgid "Failed to signing using smartcard authentication"
msgstr "无法使用智能å¡èº«ä»½éªŒè¯è¿›è¡Œç™»å½•"
@@ -3885,13 +4117,13 @@ msgid "Failed to update issues, please try again."
msgstr "更新议题失败, 请é‡è¯•"
msgid "Failed to upload object map file"
-msgstr ""
+msgstr "上传对象映射文件失败"
msgid "Failure"
msgstr "失败"
msgid "Fast-forward merge without a merge commit"
-msgstr ""
+msgstr "没有åˆå¹¶æ交的快进åˆå¹¶"
msgid "Faster as it re-uses the project workspace (falling back to clone if it doesn't exist)"
msgstr "速度更快,因其é‡ç”¨äº†é¡¹ç›®çš„工作空间(如果它ä¸å­˜åœ¨ï¼Œå°†å›žé€€åˆ°å…‹éš†ï¼‰"
@@ -3900,10 +4132,10 @@ msgid "Feature Flags"
msgstr "功能标志"
msgid "FeatureFlags|* (All Environments)"
-msgstr ""
+msgstr "*(所有环境)"
msgid "FeatureFlags|* (All environments)"
-msgstr ""
+msgstr "*(所有环境)"
msgid "FeatureFlags|API URL"
msgstr "API URL"
@@ -3921,49 +4153,46 @@ msgid "FeatureFlags|Create feature flag"
msgstr "创建功能标志"
msgid "FeatureFlags|Delete %{name}?"
-msgstr ""
+msgstr "删除 %{name}?"
msgid "FeatureFlags|Delete feature flag"
-msgstr ""
+msgstr "删除功能标志"
msgid "FeatureFlags|Description"
msgstr "æè¿°"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr "编辑 %{feature_flag_name}"
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr "编辑功能标志"
msgid "FeatureFlags|Environment Spec"
-msgstr ""
+msgstr "环境规格"
msgid "FeatureFlags|Environment Specs"
-msgstr ""
+msgstr "环境规格"
msgid "FeatureFlags|Feature Flag"
msgstr "功能标志"
msgid "FeatureFlags|Feature Flag behavior is built up by creating a set of rules to define the status of target environments. A default wildcare rule %{codeStart}*%{codeEnd} for %{boldStart}All Environments%{boldEnd} is set, and you are able to add as many rules as you need by choosing environment specs below. You can toggle the behavior for each of your rules to set them %{boldStart}Active%{boldEnd} or %{boldStart}Inactive%{boldEnd}."
-msgstr ""
+msgstr "功能标记是通过创建一组规则æ¥å®šä¹‰ç›®æ ‡çŽ¯å¢ƒçš„状æ€æ¥æž„建的行为。默认的规则%{codeStart} *%{codeEnd}用于%{boldStart}所有环境%{boldEnd},您å¯ä»¥é€šè¿‡ä¸‹é¢çš„环境规范添加任æ„æ•°é‡çš„规则。您å¯ä»¥åˆ‡æ¢æ¯ä¸ªè§„则的行为以设置它们%{boldStart}活动%{boldEnd}或%{boldStart}éžæ´»åŠ¨%{boldEnd}。"
msgid "FeatureFlags|Feature Flags"
-msgstr ""
+msgstr "功能标志"
msgid "FeatureFlags|Feature Flags allow you to configure your code into different flavors by dynamically toggling certain functionality."
-msgstr ""
+msgstr "功能标志å…许您通过动æ€åˆ‡æ¢æŸäº›åŠŸèƒ½å°†ä»£ç é…置为ä¸åŒçš„风格。"
msgid "FeatureFlags|Feature flag %{name} will be removed. Are you sure?"
-msgstr ""
+msgstr "功能标志 %{name} 将被删除。您确定å—?"
msgid "FeatureFlags|Get started with Feature Flags"
-msgstr ""
+msgstr "功能标志入门"
msgid "FeatureFlags|Inactive"
msgstr "未å¯ç”¨"
msgid "FeatureFlags|Inactive flag for %{scope}"
-msgstr ""
+msgstr "%{scope} çš„éžæ´»åŠ¨æ ‡å¿—"
msgid "FeatureFlags|Install a %{docs_link_start}compatible client library%{docs_link_end} and specify the API URL, application name, and instance ID during the configuration setup."
msgstr "安装 %{docs_link_start}兼容的客户端库%{docs_link_end} ,并在é…置指定API URLã€åº”用å称和实例ID。"
@@ -3972,10 +4201,10 @@ msgid "FeatureFlags|Instance ID"
msgstr "实例ID"
msgid "FeatureFlags|Loading Feature Flags"
-msgstr ""
+msgstr "加载功能标志"
msgid "FeatureFlags|More Information"
-msgstr ""
+msgstr "更多信æ¯"
msgid "FeatureFlags|More information"
msgstr "更多信æ¯"
@@ -3989,26 +4218,23 @@ msgstr "新建"
msgid "FeatureFlags|New Feature Flag"
msgstr "新建功能标志"
-msgid "FeatureFlags|Save changes"
-msgstr "ä¿å­˜æ›´æ”¹"
-
msgid "FeatureFlags|Status"
msgstr "状æ€"
msgid "FeatureFlags|Target environments"
-msgstr ""
+msgstr "目标环境"
msgid "FeatureFlags|There are no active Feature Flags"
-msgstr ""
+msgstr "没有活动的功能标志"
msgid "FeatureFlags|There are no inactive Feature Flags"
-msgstr ""
+msgstr "没有éžæ´»åŠ¨çš„功能标志"
msgid "FeatureFlags|There was an error fetching the feature flags."
-msgstr ""
+msgstr "获å–功能标志时出错。"
msgid "FeatureFlags|Try again in a few moments or contact your support team."
-msgstr ""
+msgstr "请ç¨åŽé‡è¯•æˆ–è”系您的支æŒå›¢é˜Ÿã€‚"
msgid "Feb"
msgstr "2月"
@@ -4021,28 +4247,28 @@ msgstr "当å‰é¡µé¢ä¸Šçš„字段ä¸å¯ç¼–辑,å¯ä»¥é…ç½®"
msgid "File"
msgid_plural "Files"
-msgstr[0] ""
+msgstr[0] "文件"
msgid "File added"
-msgstr ""
+msgstr "文件已添加"
msgid "File browser"
-msgstr ""
+msgstr "文件æµè§ˆ"
msgid "File deleted"
-msgstr ""
+msgstr "文件已删除"
msgid "File mode changed from %{a_mode} to %{b_mode}"
-msgstr ""
+msgstr "文件模å¼ä»Ž %{a_mode} 更改为 %{b_mode}"
msgid "File moved"
-msgstr ""
+msgstr "文件已移动"
msgid "File templates"
msgstr "文件模æ¿"
msgid "File upload error."
-msgstr ""
+msgstr "文件上传错误。"
msgid "Files"
msgstr "文件"
@@ -4063,28 +4289,25 @@ msgid "Filter by commit message"
msgstr "按æ交消æ¯è¿‡æ»¤"
msgid "Filter by milestone name"
-msgstr ""
+msgstr "按里程碑å称过滤"
msgid "Filter by two-factor authentication"
-msgstr ""
+msgstr "按åŒé‡èº«ä»½éªŒè¯è¿‡æ»¤"
msgid "Filter results by group"
-msgstr ""
+msgstr "按群组过滤结果"
msgid "Filter results by project"
-msgstr ""
+msgstr "按项目过滤结果"
msgid "Filter..."
msgstr "过滤..."
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "按路径查找"
msgid "Find existing members by name"
-msgstr ""
+msgstr "按å称查找现有æˆå‘˜"
msgid "Find file"
msgstr "查找文件"
@@ -4099,7 +4322,7 @@ msgid "Fingerprints"
msgstr "指纹"
msgid "Finish editing this message first!"
-msgstr ""
+msgstr "先完æˆæ­¤æ¶ˆæ¯çš„编辑ï¼"
msgid "Finish review"
msgstr "完æˆè¯„审"
@@ -4108,7 +4331,7 @@ msgid "Finished"
msgstr "已完æˆ"
msgid "First day of the week"
-msgstr ""
+msgstr "æ¯å‘¨çš„起始日"
msgid "FirstPushedBy|First"
msgstr "首次推é€"
@@ -4156,7 +4379,7 @@ msgid "For internal projects, any logged in user can view pipelines and access j
msgstr "对于内部项目,任何已登录的用户都å¯ä»¥æŸ¥çœ‹æµæ°´çº¿å¹¶è®¿é—®ä½œä¸šè¯¦æƒ…(输出日志和工件)"
msgid "For more info, read the documentation."
-msgstr ""
+msgstr "有关详细信æ¯ï¼Œè¯·é˜…读文档。"
msgid "For more information, go to the "
msgstr "如需了解详细信æ¯ï¼Œè¯·å‚阅"
@@ -4183,7 +4406,7 @@ msgid "Forking in progress"
msgstr "派生(Fork)中"
msgid "Forks"
-msgstr ""
+msgstr "派生"
msgid "Format"
msgstr "æ ¼å¼"
@@ -4194,8 +4417,8 @@ msgstr "在.gitlab-ci.yml中å‘现错误:"
msgid "Free Trial of GitLab.com Gold"
msgstr "å…费试用gitlab.comçš„Gold计划"
-msgid "From %{provider_title}"
-msgstr "æ¥è‡ª %{provider_title}"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "æ¥è‡ªBitbucket"
@@ -4224,20 +4447,26 @@ msgstr "æ¥è‡ªé‡Œç¨‹ç¢‘:"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "在Kubernetes集群详细信æ¯è§†å›¾ä¸­ï¼Œä»Žåº”用程åºåˆ—表中安装Runner"
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "GPG 密钥"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "通用"
msgid "General pipelines"
-msgstr "一般æµæ°´çº¿"
+msgstr "æµæ°´çº¿é€šç”¨è®¾ç½®"
msgid "Generate a default set of labels"
msgstr "生æˆä¸€ç»„默认的标记"
msgid "Generate key"
-msgstr ""
+msgstr "生æˆå¯†é’¥"
msgid "Geo"
msgstr "Geo"
@@ -4330,10 +4559,10 @@ msgid "GeoNodes|Out of sync"
msgstr "ä¸åŒæ­¥"
msgid "GeoNodes|Removing a primary node stops the sync process for all nodes. Syncing cannot be resumed without losing some data on all secondaries. In this case we would recommend setting up all nodes from scratch. Are you sure?"
-msgstr ""
+msgstr "删除主节点会åœæ­¢æ‰€æœ‰èŠ‚点的åŒæ­¥è¿›ç¨‹ã€‚如果ä¸ä¸¢å¤±æ‰€æœ‰è¾…助节点上的æŸäº›æ•°æ®ï¼Œåˆ™æ— æ³•æ¢å¤åŒæ­¥ã€‚在这ç§æƒ…况下,我们建议é‡æ–°è®¾ç½®æ‰€æœ‰èŠ‚点。你确定å—?"
msgid "GeoNodes|Removing a secondary node stops the sync process. It is not currently possible to add back the same node without losing some data. We only recommend setting up a new secondary node in this case. Are you sure?"
-msgstr ""
+msgstr "删除辅助节点会åœæ­¢åŒæ­¥è¿›ç¨‹ã€‚ç›®å‰æ— æ³•åœ¨ä¸ä¸¢å¤±æŸäº›æ•°æ®çš„情况下添加相åŒçš„节点。我们åªå»ºè®®åœ¨è¿™ç§æƒ…况下设置一个新的辅助节点。你确定å—?"
msgid "GeoNodes|Replication slot WAL"
msgstr "å¤åˆ¶æ§½ WAL"
@@ -4408,7 +4637,7 @@ msgid "GeoNodes|Wikis verified with their counterparts on the Primary node"
msgstr "已与主节点上对应项验è¯çš„Wiki"
msgid "GeoNodes|With %{geo} you can install a special read-only and replicated instance anywhere. Before you add nodes, follow the %{instructions} in the exact order they appear."
-msgstr ""
+msgstr "使用 %{geo} ,您å¯ä»¥åœ¨ä»»ä½•åœ°æ–¹å®‰è£…特殊的åªè¯»å’Œå¤åˆ¶å®žä¾‹ã€‚在添加节点之å‰ï¼Œè¯·æŒ‰ç…§ %{instructions} 出现的确切顺åºæ‰§è¡Œ 。"
msgid "GeoNodes|You have configured Geo nodes using an insecure HTTP connection. We recommend the use of HTTPS."
msgstr "当å‰Geo节点é…置使用ä¸å®‰å…¨çš„HTTP连接, 建议使用HTTPS。"
@@ -4456,7 +4685,7 @@ msgid "Geo|In sync"
msgstr "å·²åŒæ­¥"
msgid "Geo|Last repository check run"
-msgstr ""
+msgstr "上次存储库的è¿è¡Œæ£€æŸ¥"
msgid "Geo|Last successful sync"
msgstr "最近一次æˆåŠŸçš„åŒæ­¥"
@@ -4495,7 +4724,7 @@ msgid "Geo|Projects in certain storage shards"
msgstr "特定存储片中的项目"
msgid "Geo|Re-verification interval"
-msgstr ""
+msgstr "é‡æ–°éªŒè¯é—´éš”"
msgid "Geo|Recheck"
msgstr "é‡æ–°æ£€æŸ¥"
@@ -4576,16 +4805,16 @@ msgid "Get a free instance review"
msgstr "获得å…费的实例评估"
msgid "Get started with error tracking"
-msgstr ""
+msgstr "开始使用错误跟踪"
msgid "Getting started with releases"
-msgstr ""
+msgstr "开始使用版本"
msgid "Git"
msgstr "Git"
msgid "Git global setup"
-msgstr ""
+msgstr "Git 全局设置"
msgid "Git repository URL"
msgstr "Git仓库URL"
@@ -4615,13 +4844,13 @@ msgid "GitLab Import"
msgstr "GitLab导入"
msgid "GitLab Shared Runners execute code of different projects on the same Runner unless you configure GitLab Runner Autoscale with MaxBuilds 1 (which it is on GitLab.com)."
-msgstr ""
+msgstr "GitLab 共享è¿è¡Œå™¨å°†åœ¨åŒä¸€ä¸ªè¿è¡Œå™¨ä¸Šæ‰§è¡Œä¸åŒé¡¹ç›®çš„代ç ï¼Œé™¤éžæ‚¨åœ¨GitLab上设置 MaxBuilds为1å’Œ GitLab è¿è¡Œå™¨è‡ªåŠ¨ç¼©æ”¾ã€‚"
msgid "GitLab User"
msgstr "GitLab用户"
msgid "GitLab metadata URL"
-msgstr ""
+msgstr "GitLab å…ƒæ•°æ® URL"
msgid "GitLab project export"
msgstr "GitLab项目导出"
@@ -4654,20 +4883,29 @@ msgid "Gitea Import"
msgstr "从Gitea导入"
msgid "Given access %{time_ago}"
-msgstr ""
+msgstr "%{time_ago} 之å‰æŽˆæƒè®¿é—®"
msgid "Go Back"
msgstr "返回"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "返回"
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr "转到"
msgid "Go to %{link_to_google_takeout}."
msgstr "转至 %{link_to_google_takeout}。"
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr "从Google Code导入"
@@ -4681,7 +4919,7 @@ msgid "Got it!"
msgstr "了解ï¼"
msgid "Grant access"
-msgstr ""
+msgstr "å…许访问"
msgid "Graph"
msgstr "分支图"
@@ -4725,14 +4963,17 @@ msgstr "群组信æ¯"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "群组维护者å¯ä»¥åœ¨é€šè¿‡ %{link} 注册群组级 Runner"
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "群组å称"
msgid "Group overview content"
-msgstr ""
+msgstr "群组概述内容"
msgid "Group:"
-msgstr ""
+msgstr "群组:"
msgid "Group: %{group_name}"
msgstr "群组:%{group_name}"
@@ -4741,19 +4982,19 @@ msgid "GroupRoadmap|From %{dateWord}"
msgstr "从 %{dateWord} 起"
msgid "GroupRoadmap|Something went wrong while fetching epics"
-msgstr "读å–å²è¯—故事时出错"
+msgstr "è¯»å– epic 时出错"
msgid "GroupRoadmap|Sorry, no epics matched your search"
-msgstr "对ä¸èµ·ï¼Œæœªæœç´¢åˆ°ä»»ä½•ç¬¦åˆæ¡ä»¶çš„å²è¯—故事"
+msgstr "对ä¸èµ·ï¼Œæœªæœç´¢åˆ°ä»»ä½•ç¬¦åˆæ¡ä»¶çš„ epic"
msgid "GroupRoadmap|The roadmap shows the progress of your epics along a timeline"
-msgstr "路线图显示了å²è¯—故事沿ç€æ—¶é—´çº¿çš„进展情况"
+msgstr "路线图显示了epic 沿ç€æ—¶é—´çº¿çš„进展情况"
msgid "GroupRoadmap|To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "è¦æŸ¥çœ‹è·¯çº¿å›¾ï¼Œè¯·åœ¨æ­¤ç¾¤ç»„或其å­ç¾¤ç»„中的一个 epic 中添加开始日期或截止日期;从 %{startDate} 到 %{endDate}。"
msgid "GroupRoadmap|To widen your search, change or remove filters; from %{startDate} to %{endDate}."
-msgstr ""
+msgstr "è¦æ‰©å¤§æ‚¨çš„æœç´¢ï¼Œè¯·æ›´æ”¹æˆ–移除过滤器,从 %{startDate} 到 %{endDate}。"
msgid "GroupRoadmap|Until %{dateWord}"
msgstr "直到 %{dateWord}"
@@ -4762,7 +5003,7 @@ msgid "GroupSettings|Badges"
msgstr "徽章"
msgid "GroupSettings|Custom project templates"
-msgstr ""
+msgstr "自定义项目模æ¿"
msgid "GroupSettings|Customize your group badges."
msgstr "自定义群组徽章。"
@@ -4771,13 +5012,13 @@ msgid "GroupSettings|Learn more about badges."
msgstr "了解有关徽章的更多信æ¯ã€‚"
msgid "GroupSettings|Learn more about group-level project templates."
-msgstr ""
+msgstr "了解更多关于群组级项目模æ¿"
msgid "GroupSettings|Prevent sharing a project within %{group} with other groups"
msgstr "ç¦æ­¢ä¸Žå…¶ä»–群组共享 %{group} 中的项目"
msgid "GroupSettings|Select a sub-group as the custom project template source for this group."
-msgstr ""
+msgstr "选择一个å­ç¾¤ç»„作为此群组的自定义项目模æ¿æºã€‚"
msgid "GroupSettings|This setting is applied on %{ancestor_group} and has been overridden on this subgroup."
msgstr "此设置已ç»åº”用于 %{ancestor_group},并已覆盖此å­ç»„的设置。"
@@ -4804,7 +5045,7 @@ msgid "Groups can also be nested by creating %{subgroup_docs_link_start}subgroup
msgstr "也å¯ä»¥é€šè¿‡åˆ›å»º %{subgroup_docs_link_start}å­ç¾¤ç»„æ¥åµŒå¥—群组%{subgroup_docs_link_end}。"
msgid "Groups with access to <strong>%{project_name}</strong>"
-msgstr ""
+msgstr "å¯ä»¥è®¿é—® <strong>%{project_name}</strong>"
msgid "GroupsDropdown|Frequently visited"
msgstr "ç»å¸¸è®¿é—®çš„群组"
@@ -4906,7 +5147,7 @@ msgid "Here is the public SSH key that needs to be added to the remote server. F
msgstr "以下是需è¦æ·»åŠ åˆ°è¿œç¨‹æœåŠ¡å™¨çš„SSH公钥。有关更多信æ¯ï¼Œè¯·å‚阅文档。"
msgid "Hide file browser"
-msgstr ""
+msgstr "éšè—文件æµè§ˆå™¨"
msgid "Hide host keys manual input"
msgstr "éšè—主机密钥手动输入"
@@ -4919,7 +5160,7 @@ msgid_plural "Hide values"
msgstr[0] "éšè—值"
msgid "Hide values"
-msgstr ""
+msgstr "éšè—值"
msgid "History"
msgstr "历å²"
@@ -4928,7 +5169,7 @@ msgid "Housekeeping successfully started"
msgstr "已开始维护"
msgid "However, you are already a member of this %{member_source}. Sign in using a different account to accept the invitation."
-msgstr ""
+msgstr "但是,您已ç»æ˜¯ %{member_source} çš„æˆå‘˜ã€‚请使用其他å¸æˆ·ç™»å½•ä»¥æŽ¥å—邀请。"
msgid "I accept the %{terms_link}"
msgstr "æˆ‘æŽ¥å— %{terms_link}"
@@ -4988,7 +5229,7 @@ msgid "Identity provider single sign on URL"
msgstr "身份验è¯æ供商å•ç‚¹ç™»å½•URL"
msgid "If any job surpasses this timeout threshold, it will be marked as failed. Human readable time input language is accepted like \"1 hour\". Values without specification represent seconds."
-msgstr ""
+msgstr "如果任何作业超过这个超时阈值,它将被标记为失败。å¯è¾“入英文语å¥ï¼Œå¦‚ “1 hourâ€ã€‚默认å•ä½ä¸ºç§’。"
msgid "If disabled, a diverged local branch will not be automatically updated with commits from its remote counterpart, to prevent local data loss. If the default branch (%{default_branch}) has diverged and cannot be updated, mirroring will fail. Other diverged branches are silently ignored."
msgstr "如果ç¦ç”¨ï¼Œåˆ™ä¸ä¼šä½¿ç”¨è¿œç¨‹å‰¯æœ¬çš„æ交自动更新分å‰çš„本地分支,以防止本地数æ®ä¸¢å¤±ã€‚如果默认分支 (%{default_branch}) 已分å‰ä¸”无法更新,则镜åƒå°†å¤±è´¥ã€‚其他分å‰çš„分支默默被忽略。"
@@ -5021,13 +5262,13 @@ msgid "ImageDiffViewer|Swipe"
msgstr "滑动"
msgid "Impersonation has been disabled"
-msgstr ""
+msgstr "模拟已被ç¦ç”¨"
msgid "Import"
msgstr "导入"
msgid "Import CSV"
-msgstr ""
+msgstr "导入CSV"
msgid "Import Projects from Gitea"
msgstr "从Gitea导入项目"
@@ -5048,13 +5289,13 @@ msgid "Import in progress"
msgstr "正在导入"
msgid "Import issues"
-msgstr ""
+msgstr "导入议题"
msgid "Import members"
-msgstr ""
+msgstr "导入æˆå‘˜"
msgid "Import members from another project"
-msgstr ""
+msgstr "从å¦ä¸€ä¸ªé¡¹ç›®å¯¼å…¥æˆå‘˜"
msgid "Import multiple repositories by uploading a manifest file."
msgstr "通过上传manifest文件导入多个仓库"
@@ -5063,7 +5304,7 @@ msgid "Import project"
msgstr "导入项目"
msgid "Import project members"
-msgstr ""
+msgstr "导入项目æˆå‘˜"
msgid "Import projects from Bitbucket"
msgstr "从Bitbucket导入项目"
@@ -5090,11 +5331,26 @@ msgid "Import repository"
msgstr "导入仓库"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
+msgstr "导入超时。耗时已超过 %{import_jobs_expiration} 秒"
+
+msgid "Import/Export illustration"
msgstr ""
msgid "ImportButtons|Connect repositories from"
msgstr "用以下方å¼è¿žæŽ¥å‚¨å­˜åº“"
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr "使用 GitLab ä¼ä¸šç‰ˆçš„增强议题看æ¿ã€‚"
@@ -5108,7 +5364,7 @@ msgid "In order to enable instance-level analytics, please ask an admin to enabl
msgstr "è¦å¯ç”¨å®žä¾‹çº§åˆ†æžï¼Œè¯·è¦æ±‚管ç†å‘˜å¯ç”¨ %{usage_ping_link_start}使用情况检测(usage ping)%{usage_ping_link_end}。"
msgid "In order to gather accurate feature usage data, it can take 1 to 2 weeks to see your index."
-msgstr ""
+msgstr "为了收集准确的功能使用数æ®ï¼Œå¯èƒ½éœ€è¦1到2周æ‰èƒ½çœ‹åˆ°æ‚¨çš„指数。"
msgid "In the next step, you'll be able to select the projects you want to import."
msgstr "继续下一步,选择想è¦å¯¼å…¥çš„项目"
@@ -5117,19 +5373,19 @@ msgid "Include a Terms of Service agreement and Privacy Policy that all users mu
msgstr "包括所有用户必须接å—çš„æœåŠ¡æ¡æ¬¾å议和éšç§æ”¿ç­–。"
msgid "Include merge request description"
-msgstr ""
+msgstr "包å«åˆå¹¶è¯·æ±‚æè¿°"
msgid "Include the username in the URL if required: <code>https://username@gitlab.company.com/group/project.git</code>."
msgstr "如果需è¦ï¼Œè¯·åœ¨URL中包å«ç”¨æˆ·å: <code>https://username@gitlab.company.com/group/project.git</code>。"
msgid "Includes an MVC structure to help you get started."
-msgstr ""
+msgstr "包å«ä¸€ä¸ª MVC 结构æ¥å¸®åŠ©æ‚¨å¼€å§‹ã€‚"
msgid "Includes an MVC structure, Gemfile, Rakefile, along with many others, to help you get started."
-msgstr ""
+msgstr "包括一个 MVC 结构,Gemfile,Rakefile,以åŠå…¶ä»–许多æ¥å¸®åŠ©æ‚¨å¼€å§‹ã€‚"
msgid "Includes an MVC structure, mvnw and pom.xml to help you get started."
-msgstr ""
+msgstr "包括一个MVC 结构,mvw å’Œ pom.xml æ¥å¸®åŠ©æ‚¨å¼€å§‹ã€‚"
msgid "Incompatible Project"
msgstr "ä¸å…¼å®¹çš„项目"
@@ -5146,9 +5402,15 @@ msgstr "手动输入主机密钥"
msgid "Input your repository URL"
msgstr "输入您的存储库URL"
-msgid "Insert suggestion"
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
msgstr ""
+msgid "Insert suggestion"
+msgstr "æ’入建议"
+
msgid "Install GitLab Runner"
msgstr "安装GitLab Runner"
@@ -5178,7 +5440,7 @@ msgid "Interested parties can even contribute by pushing commits if they want to
msgstr "相关人员甚至å¯ä»¥é€šè¿‡æŽ¨é€æ交æ¥ä¸ºé¡¹ç›®ä½œå‡ºè´¡çŒ®ã€‚"
msgid "Internal"
-msgstr ""
+msgstr "内部"
msgid "Internal - The group and any internal projects can be viewed by any logged in user."
msgstr "内部 - 任何登录的用户都å¯ä»¥æŸ¥çœ‹è¯¥ç¾¤ç»„和任何内部项目。"
@@ -5196,24 +5458,30 @@ msgid "Introducing Cycle Analytics"
msgstr "周期分æžç®€ä»‹"
msgid "Introducing Your Conversational Development Index"
+msgstr "介ç»æ‚¨çš„会è¯å‘展指数"
+
+msgid "Invalid input, please avoid emojis"
msgstr ""
msgid "Invitation"
-msgstr ""
+msgstr "邀请"
msgid "Invite"
msgstr "邀请"
msgid "Invite group"
-msgstr ""
+msgstr "邀请群组"
msgid "Invite member"
-msgstr ""
+msgstr "邀请æˆå‘˜"
msgid "Invoke Count"
-msgstr ""
+msgstr "调用计数"
msgid "Invoke Time"
+msgstr "调用时间"
+
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
msgstr ""
msgid "Issue"
@@ -5235,19 +5503,19 @@ msgid "IssueBoards|Boards"
msgstr "看æ¿"
msgid "IssueBoards|Create new board"
-msgstr ""
+msgstr "创建新看æ¿"
msgid "IssueBoards|Delete board"
-msgstr ""
+msgstr "删除看æ¿"
msgid "IssueBoards|No matching boards found"
-msgstr ""
+msgstr "未找到匹é…的看æ¿"
msgid "IssueBoards|Some of your boards are hidden, activate a license to see them again."
-msgstr ""
+msgstr "您的一些看æ¿è¢«éšè—,请激活许å¯è¯åŽå†æ¬¡æŸ¥çœ‹ã€‚"
msgid "IssueBoards|Switch board"
-msgstr ""
+msgstr "切æ¢çœ‹æ¿"
msgid "Issues"
msgstr "议题"
@@ -5258,8 +5526,8 @@ msgstr "议题å¯ä»¥æ˜¯ç¼ºé™·ï¼Œä»»åŠ¡æˆ–è¦è®¨è®ºçš„想法。此外,å¯ä»¥é€š
msgid "Issues closed"
msgstr "关闭议题"
-msgid "Issues, merge requests, pushes and comments."
-msgstr "议题,åˆå¹¶è¯·æ±‚,推é€å’Œè¯„论。"
+msgid "Issues, merge requests, pushes, and comments."
+msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
msgstr "在您为项目创建议题åŽï¼Œæˆ‘们就会开始跟踪并显示它们的指标"
@@ -5283,10 +5551,10 @@ msgid "IssuesAnalytics|To widen your search, change or remove filters in the fil
msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除上é¢çš„过滤æ¡ä»¶"
msgid "It must have a header row and at least two columns: the first column is the issue title and the second column is the issue description. The separator is automatically detected."
-msgstr ""
+msgstr "它必须有标题行和至少有两列:第一æ æ˜¯è®®é¢˜æ ‡é¢˜ï¼Œç¬¬äºŒæ æ˜¯è®®é¢˜æ述。自动检测分隔符。"
msgid "It's you"
-msgstr ""
+msgstr "这是您"
msgid "Jaeger URL"
msgstr "Jaeger 地å€"
@@ -5307,10 +5575,10 @@ msgid "Job has been erased"
msgstr "作业已被删除"
msgid "Job is stuck. Check runners."
-msgstr ""
+msgstr "作业å¡ä½äº†ã€‚请检查è¿è¡Œå™¨ã€‚"
msgid "Job was retried"
-msgstr ""
+msgstr "作业已é‡è¯•"
msgid "Jobs"
msgstr "作业"
@@ -5352,10 +5620,10 @@ msgid "Job|The artifacts were removed"
msgstr "作业产物已被删除"
msgid "Job|The artifacts will be removed"
-msgstr ""
+msgstr "工件将被删除"
msgid "Job|This job is stuck because the project doesn't have any runners online assigned to it."
-msgstr ""
+msgstr "此作业å¡ä½äº†ï¼Œå› ä¸ºè¯¥é¡¹ç›®æ²¡æœ‰åœ¨çº¿åˆ†é…任何è¿è¡Œå™¨ã€‚"
msgid "Jul"
msgstr "7月"
@@ -5370,7 +5638,7 @@ msgid "June"
msgstr "6月"
msgid "Key (PEM)"
-msgstr ""
+msgstr "秘钥 (PEM)"
msgid "Kubernetes"
msgstr "Kubernetes"
@@ -5379,7 +5647,7 @@ msgid "Kubernetes Cluster"
msgstr "Kubernetes集群"
msgid "Kubernetes Clusters"
-msgstr ""
+msgstr "Kubernetes集群"
msgid "Kubernetes cluster creation time exceeds timeout; %{timeout}"
msgstr "Kubernetes集群创建时间超过超时; %{timeout}"
@@ -5447,6 +5715,9 @@ msgstr "å‡çº§æ ‡è®°"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr "æå‡ %{labelTitle} 将使其å¯ç”¨äºŽ %{groupName} 内的所有项目。现有的åŒå项目标记将被åˆå¹¶ã€‚该æ“作ä¸å¯æ’¤é”€ã€‚"
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr "大文件存储"
@@ -5458,7 +5729,7 @@ msgid "Last Pipeline"
msgstr "最新æµæ°´çº¿"
msgid "Last activity"
-msgstr ""
+msgstr "上次活动"
msgid "Last commit"
msgstr "最åŽæ交"
@@ -5476,7 +5747,7 @@ msgid "Last reply by"
msgstr "最åŽå›žå¤æ¥è‡ªäºŽ"
msgid "Last seen"
-msgstr ""
+msgstr "上次查看"
msgid "Last update"
msgstr "最åŽæ›´æ–°"
@@ -5494,13 +5765,13 @@ msgid "Latest changes"
msgstr "最新更改"
msgid "Latest pipeline for this branch"
-msgstr ""
+msgstr "该分支的最新æµæ°´çº¿"
msgid "Lead"
-msgstr ""
+msgstr "超å‰"
msgid "Learn how to %{no_packages_link_start}publish and share your packages%{no_packages_link_end} with GitLab."
-msgstr ""
+msgstr "了解GitLab如何 %{no_packages_link_start}å‘布和共享您的包%{no_packages_link_end}。"
msgid "Learn more"
msgstr "进一步了解"
@@ -5509,26 +5780,29 @@ msgid "Learn more about %{issue_boards_url}, to keep track of issues in multiple
msgstr "为了了解更多关于 %{issue_boards_url},在多个列表中ä¿æŒå¯¹è®®é¢˜çš„追踪,您å¯ä»¥ä½¿ç”¨ä½¿ç”¨æ ‡è®°ï¼ŒæŒ‡æ´¾äººï¼Œå’Œé‡Œç¨‹ç¢‘。 如果你å‘现议题看æ¿ä¸Šä¸¢å¤±äº†ä¸€äº›ä¿¡æ¯ï¼Œè¯·åœ¨ %{gitlab_issues_url} 创建一个议题。"
msgid "Learn more about Auto DevOps"
-msgstr ""
+msgstr "了解更多关于Auto DevOps"
msgid "Learn more about Kubernetes"
msgstr "进一步了解关于Kubernetesçš„ä¿¡æ¯"
msgid "Learn more about Web Terminal"
-msgstr ""
+msgstr "了解更多关于 Web 终端"
msgid "Learn more about custom project templates"
-msgstr ""
+msgstr "了解更多关于自定义项目模æ¿"
msgid "Learn more about group-level project templates"
-msgstr ""
+msgstr "了解更多关于群组级项目模æ¿"
msgid "Learn more about incoming email addresses"
-msgstr ""
+msgstr "了解更多关于用于接收的电å­é‚®ä»¶åœ°å€"
msgid "Learn more about protected branches"
msgstr "进一步了解ä¿æŠ¤åˆ†æ”¯"
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "了解更多"
@@ -5663,10 +5937,10 @@ msgid "Loading..."
msgstr "正在加载..."
msgid "Loading…"
-msgstr ""
+msgstr "正在加载..."
msgid "Localization"
-msgstr ""
+msgstr "本土化"
msgid "Lock"
msgstr "é”定"
@@ -5701,6 +5975,15 @@ msgstr "使用智能å¡ç™»å½•"
msgid "Logs"
msgstr "日志"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 实例的åªè¯»é•œåƒ, 使得从远端克隆和拉å–大型代ç ä»“库的时间大大缩短,从而æ高团队æˆå‘˜çš„工作效率。"
@@ -5735,7 +6018,7 @@ msgid "Manage project labels"
msgstr "管ç†é¡¹ç›®æ ‡è®°"
msgid "Manage two-factor authentication"
-msgstr ""
+msgstr "管ç†åŒå› ç´ èº«ä»½éªŒè¯"
msgid "Manage your group’s membership while adding another level of security with SAML."
msgstr "通过SAML管ç†ç¾¤ç»„æˆå‘˜ï¼Œè¿›ä¸€æ­¥æ高安全性。"
@@ -5746,6 +6029,9 @@ msgstr "Manifest"
msgid "Manifest file import"
msgstr "Manifest文件导入"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "å°†FogBugzå¸æˆ·ID映射为GitLab用户"
@@ -5768,41 +6054,11 @@ msgid "Mark todo as done"
msgstr "标记为已完æˆ"
msgid "Markdown"
-msgstr ""
+msgstr "Markdown"
msgid "Markdown enabled"
msgstr "支æŒMarkdownæ ¼å¼"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr "添加一个符å·åˆ—表"
-
-msgid "MarkdownToolbar|Add a link"
-msgstr "添加一个链接"
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr "添加一个编å·åˆ—表"
-
-msgid "MarkdownToolbar|Add a table"
-msgstr "添加一个表格"
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr "添加一个任务列表"
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr "添加粗体文本"
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr "添加斜体文本"
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr "å…¨å±æ˜¾ç¤º"
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr "æ’入一个引用"
-
-msgid "MarkdownToolbar|Insert code"
-msgstr "æ’入代ç "
-
msgid "Maven Metadata"
msgstr "Maven 元数æ®"
@@ -5828,10 +6084,10 @@ msgid "Members"
msgstr "æˆå‘˜"
msgid "Members can be added by project <i>Maintainers</i> or <i>Owners</i>"
-msgstr ""
+msgstr "项目 <i>维护者</i> 或 <i>所有者</i>å¯ä»¥æ·»åŠ æˆå‘˜"
msgid "Members of <strong>%{project_name}</strong>"
-msgstr ""
+msgstr "<strong>%{project_name}</strong>çš„æˆå‘˜"
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 "群组在登录您的群组时会跳转到此处。请从您的身份认è¯æ供商处获得该信æ¯ã€‚它å¯èƒ½å«åšâ€œSSOæœåŠ¡ä½ç½®ï¼ˆSSO Service Location)â€ï¼Œâ€œSAML令牌é¢å‘点(SAML Token Issuance Endpoint)â€æˆ–“SAML 2.0/W-Federation URLâ€ã€‚"
@@ -5846,15 +6102,18 @@ msgid "Merge Requests created"
msgstr "创建åˆå¹¶è¯·æ±‚"
msgid "Merge commit message"
-msgstr ""
+msgstr "åˆå¹¶æ交消æ¯"
msgid "Merge events"
msgstr "åˆå¹¶äº‹ä»¶"
msgid "Merge immediately"
-msgstr ""
+msgstr "ç«‹å³åˆå¹¶"
msgid "Merge in progress"
+msgstr "正在åˆå¹¶"
+
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
msgstr ""
msgid "Merge request"
@@ -5870,31 +6129,31 @@ msgid "Merge requests are a place to propose changes you've made to a project an
msgstr "åˆå¹¶è¯·æ±‚用于æ出对项目的更改与他人讨论"
msgid "Merge when pipeline succeeds"
-msgstr ""
+msgstr "当æµæ°´çº¿æˆåŠŸæ—¶åˆå¹¶"
msgid "MergeRequests|Add a reply"
-msgstr ""
+msgstr "添加回å¤"
msgid "MergeRequests|An error occurred while saving the draft comment."
msgstr "ä¿å­˜è¯„论è‰ç¨¿æ—¶å‘生错误。"
msgid "MergeRequests|Discussion stays resolved"
-msgstr ""
+msgstr "讨论并解决"
msgid "MergeRequests|Discussion stays unresolved"
-msgstr ""
+msgstr "讨论并未解决"
msgid "MergeRequests|Discussion will be resolved"
-msgstr ""
+msgstr "讨论并解决"
msgid "MergeRequests|Discussion will be unresolved"
-msgstr ""
+msgstr "讨论并未解决"
msgid "MergeRequests|Jump to next unresolved discussion"
-msgstr ""
+msgstr "跳转到下一个未解决的讨论"
msgid "MergeRequests|Reply..."
-msgstr ""
+msgstr "回å¤..."
msgid "MergeRequests|Resolve this discussion in a new issue"
msgstr "在新议题中解决此讨论"
@@ -5912,22 +6171,22 @@ msgid "MergeRequests|View replaced file @ %{commitId}"
msgstr "查看已替æ¢æ–‡ä»¶ @ %{commitId}"
msgid "MergeRequests|commented on commit %{commitLink}"
-msgstr ""
+msgstr "讨论%{commitLink}æ交"
msgid "MergeRequests|started a discussion"
-msgstr ""
+msgstr "开始讨论"
msgid "MergeRequests|started a discussion on %{linkStart}an old version of the diff%{linkEnd}"
-msgstr ""
+msgstr "开始讨论 %{linkStart}旧版本的差异%{linkEnd}"
msgid "MergeRequests|started a discussion on %{linkStart}the diff%{linkEnd}"
-msgstr ""
+msgstr "开始讨论 %{linkStart}差异%{linkEnd}"
msgid "MergeRequests|started a discussion on an outdated change in commit %{linkStart}%{commitId}%{linkEnd}"
-msgstr ""
+msgstr "开始讨论一个过时的å˜æ›´æ交%{linkStart}%{commitId}%{linkEnd}"
msgid "MergeRequests|started a discussion on commit %{linkStart}%{commitId}%{linkEnd}"
-msgstr ""
+msgstr "开始讨论æ交%{linkStart}%{commitId}%{linkEnd}"
msgid "MergeRequest| %{paragraphStart}changed the description %{descriptionChangedTimes} times %{timeDifferenceMinutes}%{paragraphEnd}"
msgstr "%{paragraphStart}%{timeDifferenceMinutes}%{descriptionChangedTimes}次更改了æè¿°%{paragraphEnd}"
@@ -5939,7 +6198,7 @@ msgid "MergeRequest|No files found"
msgstr "未找到任何文件"
msgid "MergeRequest|Search files"
-msgstr ""
+msgstr "æœç´¢æ–‡ä»¶"
msgid "Merged"
msgstr "å·²åˆå¹¶"
@@ -5960,7 +6219,7 @@ msgid "Metrics and profiling"
msgstr "指标和分æž"
msgid "Metrics for environment"
-msgstr ""
+msgstr "环境指标"
msgid "Metrics|Check out the CI/CD documentation on deploying to an environment"
msgstr "查看有关部署到环境的CI/CD文档"
@@ -5969,10 +6228,10 @@ msgid "Metrics|Create metric"
msgstr "创建指标"
msgid "Metrics|Delete metric"
-msgstr ""
+msgstr "删除指标"
msgid "Metrics|Delete metric?"
-msgstr ""
+msgstr "删除指标?"
msgid "Metrics|Edit metric"
msgstr "编辑指标"
@@ -5984,7 +6243,7 @@ msgid "Metrics|For grouping similar metrics"
msgstr "用于分组类似指标"
msgid "Metrics|Label of the y-axis (usually the unit). The x-axis always represents time."
-msgstr ""
+msgstr "Y轴是标签(通常是å•ä½)。X轴总是代表时间。"
msgid "Metrics|Learn about environments"
msgstr "了解环境"
@@ -6002,14 +6261,11 @@ msgid "Metrics|No deployed environments"
msgstr "应用未部署到任何环境"
msgid "Metrics|PromQL query is valid"
-msgstr ""
+msgstr "PromotQL 查询有效"
msgid "Metrics|Prometheus Query Documentation"
msgstr "Prometheus查询文档"
-msgid "Metrics|System"
-msgstr "系统"
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr "获å–环境数æ®æ—¶å‡ºé”™ï¼Œè¯·é‡è¯•"
@@ -6020,7 +6276,7 @@ msgid "Metrics|There was an error getting environments information."
msgstr "获å–环境信æ¯æ—¶å‡ºé”™ã€‚"
msgid "Metrics|There was an error trying to validate your query"
-msgstr ""
+msgstr "å°è¯•éªŒè¯æ‚¨çš„查询时出错"
msgid "Metrics|There was an error while retrieving metrics"
msgstr "读å–指标时出错"
@@ -6044,7 +6300,7 @@ msgid "Metrics|Y-axis label"
msgstr "Y轴标签"
msgid "Metrics|You're about to permanently delete this metric. This cannot be undone."
-msgstr ""
+msgstr "您å³å°†æ°¸ä¹…删除此指标且无法撤消。"
msgid "Metrics|e.g. Throughput"
msgstr "例如:åžåé‡"
@@ -6122,16 +6378,16 @@ msgid "Modal|Close"
msgstr "关闭"
msgid "Modify commit messages"
-msgstr ""
+msgstr "修改æ交消æ¯"
msgid "Modify merge commit"
-msgstr ""
+msgstr "修改åˆå¹¶æ交"
msgid "Monday"
-msgstr ""
+msgstr "星期一"
msgid "Monitor your errors by integrating with Sentry"
-msgstr ""
+msgstr "通过与Sentry集æˆæ¥ç›‘控您的错误"
msgid "Monitoring"
msgstr "监控"
@@ -6154,6 +6410,9 @@ msgstr "更多信æ¯"
msgid "More information is available|here"
msgstr "帮助文档"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr "最多星标"
@@ -6194,7 +6453,7 @@ msgid "Nav|Sign out and sign in with a different account"
msgstr "退出并登录到其他账å·"
msgid "Need help?"
-msgstr ""
+msgstr "需è¦å¸®åŠ©å—?"
msgid "Network"
msgstr "网络"
@@ -6209,7 +6468,7 @@ msgid "New Application"
msgstr "新建应用"
msgid "New Environment"
-msgstr ""
+msgstr "新环境"
msgid "New Group"
msgstr "新建群组"
@@ -6225,9 +6484,12 @@ msgid "New Label"
msgstr "新标签"
msgid "New Milestone"
-msgstr ""
+msgstr "新里程碑"
msgid "New Pages Domain"
+msgstr "新页é¢åŸŸå"
+
+msgid "New Password"
msgstr ""
msgid "New Pipeline Schedule"
@@ -6236,9 +6498,6 @@ msgstr "创建æµæ°´çº¿è®¡åˆ’"
msgid "New Snippet"
msgstr "新建代ç ç‰‡æ®µ"
-msgid "New Snippets"
-msgstr "新建代ç ç‰‡æ®µ"
-
msgid "New branch"
msgstr "新建分支"
@@ -6249,10 +6508,10 @@ msgid "New directory"
msgstr "新建目录"
msgid "New environment"
-msgstr ""
+msgstr "新环境"
msgid "New epic"
-msgstr "新建å²è¯—故事"
+msgstr "æ–°epic"
msgid "New file"
msgstr "新建文件"
@@ -6273,7 +6532,7 @@ msgid "New merge request"
msgstr "新建åˆå¹¶è¯·æ±‚"
msgid "New milestone"
-msgstr ""
+msgstr "新里程碑"
msgid "New pipelines will cancel older, pending pipelines on the same branch"
msgstr "æ–°æµæ°´çº¿å°†å–消åŒä¸€åˆ†æ”¯ä¸Šè¾ƒæ—§çš„待处ç†æµæ°´çº¿"
@@ -6299,12 +6558,18 @@ msgstr "新建..."
msgid "No"
msgstr "å¦"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr "无标记"
-msgid "No activities found"
+msgid "No Tag"
msgstr ""
+msgid "No activities found"
+msgstr "没有å‘现任何活动"
+
msgid "No assignee"
msgstr "未指派"
@@ -6315,7 +6580,7 @@ msgid "No changes"
msgstr "æ— å˜æ›´å†…容"
msgid "No changes between %{ref_start}%{source_branch}%{ref_end} and %{ref_start}%{target_branch}%{ref_end}"
-msgstr ""
+msgstr "%{ref_start}%{source_branch}%{ref_end} å’Œ %{ref_start}%{target_branch}%{ref_end} 之间没有产生å˜åŒ–"
msgid "No connection could be made to a Gitaly Server, please check your logs!"
msgstr "无法连接到GitalyæœåŠ¡å™¨ï¼Œè¯·æ£€æŸ¥ç›¸å…³æ—¥å¿—ï¼"
@@ -6329,13 +6594,16 @@ msgstr "未找到任何贡献者"
msgid "No credit card required."
msgstr "无需信用å¡ã€‚"
-msgid "No details available"
+msgid "No designs found."
msgstr ""
+msgid "No details available"
+msgstr "没有å¯ç”¨çš„详细信æ¯"
+
msgid "No due date"
msgstr "无截止日期"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6345,7 +6613,7 @@ msgid "No file chosen"
msgstr "未选定任何文件"
msgid "No file selected"
-msgstr ""
+msgstr "未选择任何文件"
msgid "No files found."
msgstr "未找到文件。"
@@ -6360,7 +6628,7 @@ msgid "No license. All rights reserved"
msgstr "未设定许å¯è¯ã€‚版æƒæ‰€æœ‰ã€‚"
msgid "No matching results"
-msgstr ""
+msgstr "没有匹é…的结果"
msgid "No merge requests for the selected time period."
msgstr "所选时间段无åˆå¹¶è¯·æ±‚。"
@@ -6372,13 +6640,13 @@ msgid "No messages were logged"
msgstr "未记录任何消æ¯"
msgid "No milestones to show"
-msgstr ""
+msgstr "没有è¦æ˜¾ç¤ºçš„里程碑"
msgid "No other labels with such name or description"
msgstr "没有其他具有此类å称或æ述的标记"
msgid "No preview for this file type"
-msgstr ""
+msgstr "无法预览此类型文件"
msgid "No prioritised labels with such name or description"
msgstr "无具有此类å称或æ述的优先标记"
@@ -6399,7 +6667,7 @@ msgid "No schedules"
msgstr "无计划"
msgid "No start date"
-msgstr ""
+msgstr "没有开始日期"
msgid "No, directly import the existing email addresses and usernames."
msgstr "å¦, 请直接导入现有电å­é‚®ä»¶åœ°å€å’Œç”¨æˆ·å。"
@@ -6431,6 +6699,9 @@ msgstr "æ•°æ®ä¸è¶³"
msgid "Not now"
msgstr "æš‚ä¸"
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "请注æ„,master分支自动å—ä¿æŠ¤ã€‚%{link_to_protected_branches}"
@@ -6468,10 +6739,10 @@ msgid "Notification events"
msgstr "通知事件"
msgid "Notification setting"
-msgstr ""
+msgstr "无法预览此类型文件"
msgid "Notification setting - %{notification_title}"
-msgstr ""
+msgstr "通知设置 - %{notification_title}"
msgid "NotificationEvent|Close issue"
msgstr "关闭议题"
@@ -6486,7 +6757,7 @@ msgid "NotificationEvent|Merge merge request"
msgstr "åˆå¹¶è¯·æ±‚被åˆå¹¶"
msgid "NotificationEvent|New epic"
-msgstr "新建å²è¯—"
+msgstr "æ–°epic"
msgid "NotificationEvent|New issue"
msgstr "新建议题"
@@ -6574,16 +6845,16 @@ msgid "Only mirror protected branches"
msgstr "åªé•œåƒå—ä¿æŠ¤çš„分支"
msgid "Only policy:"
-msgstr ""
+msgstr "仅策略:"
msgid "Only proceed if you trust %{idp_url} to control your GitLab account sign in."
-msgstr ""
+msgstr "åªæœ‰åœ¨æ‚¨ä¿¡ä»» %{idp_url} æ—¶æ‰èƒ½ç»§ç»­æŽ§åˆ¶GitLabå¸æˆ·ç™»å½•ã€‚"
msgid "Only project members can comment."
msgstr "åªæœ‰é¡¹ç›®æˆå‘˜å¯ä»¥å‘表评论。"
msgid "Only project members will be imported. Group members will be skipped."
-msgstr ""
+msgstr "仅导入项目æˆå‘˜ã€‚群组æˆå‘˜å°†è¢«è·³è¿‡ã€‚"
msgid "Oops, are you sure?"
msgstr "å•Š~~, 确定å—?"
@@ -6592,13 +6863,13 @@ msgid "Open"
msgstr "展开"
msgid "Open Documentation"
-msgstr ""
+msgstr "打开文档"
msgid "Open comment type dropdown"
-msgstr ""
+msgstr "打开评论类型下拉列表"
msgid "Open errors"
-msgstr ""
+msgstr "打开错误"
msgid "Open in Xcode"
msgstr "用Xcode打开"
@@ -6633,6 +6904,9 @@ msgstr "è¿ç»´"
msgid "Operations Dashboard"
msgstr "è¿ç»´ä»ªè¡¨æ¿"
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr "将项目添加到仪表æ¿"
@@ -6640,6 +6914,9 @@ msgid "OperationsDashboard|The operations dashboard provides a summary of each p
msgstr "è¿ç»´ä»ªè¡¨æ¿æä¾›æ¯ä¸ªé¡¹ç›®çš„è¿è¡ŒçŠ¶å†µçš„摘è¦ï¼ŒåŒ…括æµæ°´çº¿å’Œè­¦æŠ¥çŠ¶æ€ã€‚"
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
+msgstr "无法添加 %{invalidProjects}。 æ“作仪表æ¿å¯ç”¨äºŽå…¬å…±é¡¹ç›®ä»¥åŠå…·æœ‰Gold方案的群组中的ç§æœ‰é¡¹ç›®ã€‚"
+
+msgid "Optional"
msgstr ""
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
@@ -6688,10 +6965,10 @@ msgid "Pages"
msgstr "Pages"
msgid "Pages Domain"
-msgstr ""
+msgstr "Pages域å"
msgid "Pages Domains"
-msgstr ""
+msgstr "Pages域å"
msgid "Pagination|Last »"
msgstr "尾页 »"
@@ -6706,10 +6983,10 @@ msgid "Pagination|« First"
msgstr "« 首页"
msgid "Parameter"
-msgstr ""
+msgstr "å‚æ•°"
msgid "Parent epic"
-msgstr ""
+msgstr "父级epic"
msgid "Part of merge request changes"
msgstr "包å«äºŽåˆå¹¶è¯·æ±‚å˜æ›´ä¸­"
@@ -6718,13 +6995,13 @@ msgid "Password"
msgstr "密ç "
msgid "Past due"
-msgstr ""
+msgstr "逾期"
msgid "Paste epic link"
-msgstr ""
+msgstr "粘贴epic链接"
msgid "Paste issue link"
-msgstr ""
+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 ç§é’¥ã€‚"
@@ -6763,10 +7040,10 @@ msgid "Personal Access Token"
msgstr "个人访问凭è¯"
msgid "Personal project creation is not allowed. Please contact your administrator with questions"
-msgstr ""
+msgstr "ä¸å…许创建个人项目。如有疑问,请è”系您的管ç†å‘˜"
msgid "Pick a name"
-msgstr ""
+msgstr "选择一个å称"
msgid "Pipeline"
msgstr "æµæ°´çº¿"
@@ -6834,6 +7111,12 @@ msgstr "å˜é‡"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "自定义"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "æµæ°´çº¿"
@@ -6849,6 +7132,9 @@ msgstr "上周的æµæ°´çº¿"
msgid "Pipelines for last year"
msgstr "去年的æµæ°´çº¿"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr "自信地构建"
@@ -6955,7 +7241,7 @@ msgid "Play"
msgstr "è¿è¡Œ"
msgid "Please %{link_to_register} or %{link_to_sign_in} to comment"
-msgstr ""
+msgstr "想è¦è¯„论请 %{link_to_register} 或 %{link_to_sign_in}"
msgid "Please accept the Terms of Service before continuing."
msgstr "请接å—æœåŠ¡æ¡æ¬¾ä»¥ç»§ç»­ã€‚"
@@ -6969,21 +7255,42 @@ msgstr "请将它们先%{link_to_git}, 然åŽå†æ¬¡ä½¿ç”¨%{link_to_import_flow}ã
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr "请将它们先在Google Code中转为Git, 然åŽå†æ¬¡ä½¿ç”¨%{link_to_import_flow}。"
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
+msgstr "请å¯ç”¨å¹¶è¿ç§»åˆ°æ•£åˆ—存储以é¿å…安全问题并确ä¿æ•°æ®å®Œæ•´æ€§ã€‚ %{migrate_link}"
+
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
msgstr ""
msgid "Please fill in a descriptive name for your group."
msgstr "请为您的群组填写æ述性å称。"
msgid "Please migrate all existing projects to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
-msgstr ""
+msgstr "请将所有现有项目è¿ç§»åˆ°æ•£åˆ—存储,以é¿å…安全问题并确ä¿æ•°æ®å®Œæ•´æ€§ã€‚ %{migrate_link}"
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr "请注æ„,GitLabä¸æ供此应用程åºï¼Œæ‚¨åº”该在å…许访问之å‰éªŒè¯å…¶çœŸå®žæ€§ã€‚"
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr "请至少选择一个过滤器æ¥æŸ¥çœ‹ç»“æžœ"
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "请填写验è¯ç ã€‚"
@@ -6991,7 +7298,7 @@ msgid "Please try again"
msgstr "请å†è¯•ä¸€æ¬¡"
msgid "Please upgrade PostgreSQL to version 9.6 or greater. The status of the replication cannot be determined reliably with the current version."
-msgstr ""
+msgstr "请将PostgreSQLå‡çº§åˆ°9.6或更高版本。使用当å‰ç‰ˆæœ¬æ— æ³•å¯é åœ°ç¡®å®šå¤åˆ¶çš„状æ€ã€‚"
msgid "Please use this form to report users to GitLab who create spam issues, comments or behave inappropriately."
msgstr "请使用此表å•å‘GitLab报告创建垃圾议题ã€è¯„论或有ä¸å½“行为的用户。"
@@ -7008,6 +7315,9 @@ msgstr "å好设置"
msgid "Preferences|Navigation theme"
msgstr "导航主题"
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr "按 回车键或å•å‡»ä»¥æœç´¢"
@@ -7036,7 +7346,7 @@ msgid "Prioritized label"
msgstr "优先标记"
msgid "Private"
-msgstr ""
+msgstr "ç§æœ‰"
msgid "Private - Project access must be granted explicitly to each user."
msgstr "ç§äºº - å¿…é¡»å‘æ¯ä¸ªç”¨æˆ·æ˜Žç¡®æŽˆäºˆé¡¹ç›®è®¿é—®æƒé™ã€‚"
@@ -7060,16 +7370,16 @@ msgid "Profiles| You are going to change the username %{currentUsernameBold} to
msgstr "您将更改用户å %{currentUsernameBold} 为 %{newUsernameBold}。é…置文件和项目将é‡å®šå‘到 %{newUsername} 命å空间,但是一旦 %{currentUsername} 命å空间被å¦ä¸€ä¸ªç”¨æˆ·æˆ–组注册,此é‡å®šå‘将过期。请尽快更新您的远端Git仓库。"
msgid "Profiles|@username"
-msgstr ""
+msgstr "@用户å"
msgid "Profiles|Account scheduled for removal."
msgstr "å¸æˆ·å·²å®‰æŽ’被删除。"
msgid "Profiles|Activate signin with one of the following services"
-msgstr ""
+msgstr "使用以下æœåŠ¡ä¹‹ä¸€æ¿€æ´»ç™»å½•"
msgid "Profiles|Active"
-msgstr ""
+msgstr "活动"
msgid "Profiles|Add key"
msgstr "添加密钥"
@@ -7087,28 +7397,28 @@ msgid "Profiles|Change username"
msgstr "更改用户å"
msgid "Profiles|Changing your username can have unintended side effects."
-msgstr ""
+msgstr "更改您的用户åå¯èƒ½ä¼šäº§ç”Ÿæ„想ä¸åˆ°çš„副作用。"
msgid "Profiles|Choose file..."
msgstr "选择文件..."
msgid "Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information"
-msgstr ""
+msgstr "选择在公开个人资料中显示ç§æœ‰é¡¹ç›®çš„贡献,但ä¸æ˜¾ç¤ºä»»ä½•é¡¹ç›®ï¼Œå­˜å‚¨åº“或组织信æ¯"
msgid "Profiles|City, country"
-msgstr ""
+msgstr "城市,国家"
msgid "Profiles|Clear status"
msgstr "清除状æ€"
msgid "Profiles|Click on icon to activate signin with one of the following services"
-msgstr ""
+msgstr "å•å‡»å›¾æ ‡ä»¥ä½¿ç”¨ä»¥ä¸‹æœåŠ¡ä¹‹ä¸€æ¿€æ´»ç™»å½•"
msgid "Profiles|Connect"
-msgstr ""
+msgstr "连接"
msgid "Profiles|Connected Accounts"
-msgstr ""
+msgstr "å…³è”账户"
msgid "Profiles|Current path: %{path}"
msgstr "当å‰è·¯å¾„: %{path}"
@@ -7129,7 +7439,7 @@ msgid "Profiles|Deleting an account has the following effects:"
msgstr "删除å¸æˆ·å…·æœ‰ä»¥ä¸‹æ•ˆæžœï¼š"
msgid "Profiles|Disconnect"
-msgstr ""
+msgstr "æ–­å¼€"
msgid "Profiles|Do not show on profile"
msgstr "ä¸åœ¨ä¸ªäººèµ„料中显示"
@@ -7141,10 +7451,10 @@ msgid "Profiles|Edit Profile"
msgstr "编辑个人资料"
msgid "Profiles|Enter your name, so people you know can recognize you"
-msgstr ""
+msgstr "输入您的姓å,以便大家认识您"
msgid "Profiles|Increase your account's security by enabling Two-Factor Authentication (2FA)"
-msgstr ""
+msgstr "å¯ç”¨åŒé‡èº«ä»½è®¤è¯ï¼ˆ2FA),æ高账户安全性"
msgid "Profiles|Invalid password"
msgstr "密ç æ— æ•ˆ"
@@ -7183,13 +7493,13 @@ msgid "Profiles|Set new profile picture"
msgstr "设置新个人资料图片"
msgid "Profiles|Social sign-in"
-msgstr ""
+msgstr "社交登录"
msgid "Profiles|Some options are unavailable for LDAP accounts"
msgstr "æŸäº›é€‰é¡¹å¯¹äºŽ LDAP å¸æˆ·ä¸å¯ç”¨"
msgid "Profiles|Tell us about yourself in fewer than 250 characters"
-msgstr ""
+msgstr "请用少于250字符æ述您自己"
msgid "Profiles|The maximum file size allowed is 200KB."
msgstr "å…许的最大文件大å°ä¸º200KB。"
@@ -7198,7 +7508,7 @@ msgid "Profiles|This doesn't look like a public SSH key, are you sure you want t
msgstr "这看起æ¥ä¸åƒ SSH 公钥,确定è¦æ·»åŠ å®ƒå—?"
msgid "Profiles|This email will be displayed on your public profile"
-msgstr ""
+msgstr "此电å­é‚®ä»¶å°†æ˜¾ç¤ºåœ¨æ‚¨çš„公开个人资料中。"
msgid "Profiles|This email will be used for web based operations, such as edits and merges. %{learn_more}"
msgstr "此电å­é‚®ä»¶å°†ç”¨äºŽåŸºäºŽWebçš„æ“作,例如编辑和åˆå¹¶ã€‚ %{learn_more}"
@@ -7206,14 +7516,11 @@ msgstr "此电å­é‚®ä»¶å°†ç”¨äºŽåŸºäºŽWebçš„æ“作,例如编辑和åˆå¹¶ã€‚ %{
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr "此表情符å·å’Œæ¶ˆæ¯ä¼šæ˜¾ç¤ºåœ¨æ‚¨çš„个人资料和界é¢ä¸­ã€‚"
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
-msgstr ""
+msgstr "此信æ¯å°†æ˜¾ç¤ºåœ¨æ‚¨çš„个人资料中"
msgid "Profiles|Two-Factor Authentication"
-msgstr ""
+msgstr "åŒå› ç´ èº«ä»½éªŒè¯"
msgid "Profiles|Type your %{confirmationValue} to confirm:"
msgstr "键入您的 %{confirmationValue} 以确认:"
@@ -7240,13 +7547,13 @@ msgid "Profiles|Username successfully changed"
msgstr "用户å更改æˆåŠŸ"
msgid "Profiles|Using emojis in names seems fun, but please try to set a status message instead"
-msgstr ""
+msgstr "姓å中使用表情符å·è™½ç„¶æœ‰è¶£ï¼Œä½†è¯·è½¬åˆ°çŠ¶æ€ä¿¡æ¯é‡Œä½¿ç”¨å®ƒ"
msgid "Profiles|What's your status?"
-msgstr "你当å‰çš„状æ€ï¼Ÿ"
+msgstr "您当å‰çš„状æ€ï¼Ÿ"
msgid "Profiles|Who you represent or work for"
-msgstr ""
+msgstr "您代表è°æˆ–为è°å·¥ä½œ"
msgid "Profiles|You can change your avatar here"
msgstr "å¯ä»¥åœ¨è¿™é‡Œä¿®æ”¹æ‚¨çš„头åƒ"
@@ -7267,31 +7574,31 @@ msgid "Profiles|You must transfer ownership or delete these groups before you ca
msgstr "您必须转移所有æƒæˆ–删除这些群组,然åŽæ‰èƒ½åˆ é™¤æ‚¨çš„å¸æˆ·ã€‚"
msgid "Profiles|Your LinkedIn profile name from linkedin.com/in/profilename"
-msgstr ""
+msgstr "您的LinkedInå称,å‚è§linkedin.com/in/profilename"
msgid "Profiles|Your account is currently an owner in these groups:"
msgstr "您的å¸æˆ·ç›®å‰æ˜¯è¿™äº›ç¾¤ç»„的所有者:"
msgid "Profiles|Your email address was automatically set based on your %{provider_label} account"
-msgstr ""
+msgstr "您的电å­é‚®ä»¶åœ°å€æ˜¯æ ¹æ®æ‚¨çš„ %{provider_label} å¸æˆ·è‡ªåŠ¨è®¾ç½®çš„"
msgid "Profiles|Your location was automatically set based on your %{provider_label} account"
-msgstr ""
+msgstr "您的ä½ç½®æ˜¯åŸºäºŽæ‚¨çš„ %{provider_label} å¸æˆ·è‡ªåŠ¨è®¾ç½®çš„。"
msgid "Profiles|Your name was automatically set based on your %{provider_label} account, so people you know can recognize you"
-msgstr ""
+msgstr "您的姓å是根æ®æ‚¨çš„ %{provider_label} å¸æˆ·è‡ªåŠ¨è®¾ç½®çš„,以便大家认识您"
msgid "Profiles|Your status"
-msgstr "你的状æ€"
+msgstr "您当å‰çš„状æ€ï¼Ÿ"
msgid "Profiles|e.g. My MacBook key"
msgstr "例如: My MacBook Key"
msgid "Profiles|username"
-msgstr ""
+msgstr "用户å"
msgid "Profiles|website.com"
-msgstr ""
+msgstr "website.com"
msgid "Profiles|your account"
msgstr "您的å¸æˆ·"
@@ -7308,6 +7615,9 @@ msgstr "进度"
msgid "Project"
msgstr "项目"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "项目 “%{project_name}†正在被删除。"
@@ -7350,9 +7660,12 @@ msgstr "项目导出链接已过期。请从项目设置中é‡æ–°ç”Ÿæˆé¡¹ç›®å¯¼
msgid "Project export started. A download link will be sent by email."
msgstr "项目导出已开始。下载链接将通过电å­é‚®ä»¶å‘é€ã€‚"
-msgid "Project members"
+msgid "Project has too many %{label_for_message} to search"
msgstr ""
+msgid "Project members"
+msgstr "项目æˆå‘˜"
+
msgid "Project name"
msgstr "项目å称"
@@ -7360,7 +7673,7 @@ msgid "Project slug"
msgstr "项目标识串"
msgid "Project:"
-msgstr ""
+msgstr "项目:"
msgid "ProjectActivityRSS|Subscribe"
msgstr "订阅"
@@ -7420,7 +7733,7 @@ msgid "ProjectSettings|Contact an admin to change this setting."
msgstr "è”系管ç†å‘˜æ›´æ”¹æ­¤è®¾ç½®ã€‚"
msgid "ProjectSettings|Customize your project badges."
-msgstr "自定义你的项目徽章。"
+msgstr "自定义您的项目徽章。"
msgid "ProjectSettings|Failed to protect the tag"
msgstr "ä¿æŠ¤æ ‡ç­¾å¤±è´¥"
@@ -7449,6 +7762,9 @@ msgstr "用户åªèƒ½é€šè¿‡è‡ªå·±å·²éªŒè¯çš„电å­é‚®ä»¶åœ°å€å°†æ交到此存
msgid "Projects"
msgstr "项目"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr "与 %{group_name} 共享的项目"
@@ -7531,7 +7847,7 @@ msgid "PrometheusService|Custom metrics"
msgstr "自定义指标"
msgid "PrometheusService|Enable Prometheus to define custom metrics, using either option above"
-msgstr ""
+msgstr "使用上é¢ä»»ä¸€é€‰é¡¹ä»¥å¯ç”¨ Prometheus æ¥å®šä¹‰è‡ªå®šä¹‰åº¦é‡æ ‡å‡†ï¼Œ"
msgid "PrometheusService|Finding and configuring metrics..."
msgstr "查找和é…置指标..."
@@ -7597,7 +7913,7 @@ msgid "Promotions|Don't show me this again"
msgstr "ä¸å†æ˜¾ç¤º"
msgid "Promotions|Epics let you manage your portfolio of projects more efficiently and with less effort by tracking groups of issues that share a theme, across projects and milestones."
-msgstr "å²è¯—故事(Epics) 让您å¯ä»¥é€šè¿‡è¿½è¸ªè·¨é¡¹ç›®å’Œé‡Œç¨‹ç¢‘共享主题的问题组,更有效地管ç†é¡¹ç›®ç»„åˆã€‚"
+msgstr "Epic 让您å¯ä»¥é€šè¿‡è¿½è¸ªè·¨é¡¹ç›®å’Œé‡Œç¨‹ç¢‘共享主题的问题组,更有效地管ç†é¡¹ç›®ç»„åˆã€‚"
msgid "Promotions|This feature is locked."
msgstr "此功能已é”定"
@@ -7663,7 +7979,7 @@ msgid "Pseudonymizer data collection"
msgstr "匿å化数æ®æ”¶é›†"
msgid "Public"
-msgstr ""
+msgstr "公有"
msgid "Public - The group and any public projects can be viewed without any authentication."
msgstr "公开 - 群组和任何公开项目å¯ä»¥å¼€æ”¾æŸ¥çœ‹ï¼Œæ— éœ€èº«ä»½éªŒè¯ã€‚"
@@ -7705,19 +8021,19 @@ msgid "Quarters"
msgstr "季度"
msgid "Query"
-msgstr ""
+msgstr "查询"
msgid "Quick actions can be used in the issues description and comment boxes."
msgstr "快速æ“作å¯ç”¨äºŽè®®é¢˜æ述和评论框。"
msgid "README"
-msgstr ""
+msgstr "自述文件"
msgid "Read more"
msgstr "进一步了解"
msgid "Read more about environments"
-msgstr ""
+msgstr "阅读更多有关环境"
msgid "Read more about project permissions <strong>%{link_to_help}</strong>"
msgstr "了解有关项目æƒé™çš„æ›´å¤šä¿¡æ¯ <strong>%{link_to_help}</strong>"
@@ -7726,7 +8042,7 @@ msgid "Real-time features"
msgstr "实时功能"
msgid "Receive alerts from manually configured Prometheus servers."
-msgstr ""
+msgstr "从手动é…置的 Prometheus æœåŠ¡å™¨æŽ¥æ”¶è­¦æŠ¥ã€‚"
msgid "Recent searches"
msgstr "最近的æœç´¢"
@@ -7754,7 +8070,7 @@ msgid "Register / Sign In"
msgstr "注册/登录"
msgid "Register U2F device"
-msgstr ""
+msgstr "注册U2F设备"
msgid "Register and see your runners for this group."
msgstr "注册并查看当å‰ç¾¤ç»„çš„Runner。"
@@ -7787,10 +8103,10 @@ msgid "Related merge requests"
msgstr "相关åˆå¹¶è¯·æ±‚"
msgid "Releases"
-msgstr ""
+msgstr "版本"
msgid "Releases mark specific points in a project's development history, communicate information about the type of change, and deliver on prepared, often compiled, versions of the software to be reused elsewhere. Currently, releases can only be created through the API."
-msgstr ""
+msgstr "版本标志ç€é¡¹ç›®çš„å‘展历å²ä¸­çš„具体节点,传达相关的å˜æ›´ä¿¡æ¯ï¼Œå¹¶æä¾›ç»è¿‡ç¼–译并且已准备好交付的软件版本ã€ä»¥ä¾¿åœ¨å…¶å®ƒåœ°æ–¹é‡ç”¨ã€‚ç›®å‰ï¼Œåªèƒ½é€šè¿‡ API 创建版本。"
msgid "Remind later"
msgstr "ç¨åŽæ醒"
@@ -7802,9 +8118,15 @@ msgid "Remove Runner"
msgstr "移除Runner"
msgid "Remove all approvals in a merge request when new commits are pushed to its source branch"
-msgstr ""
+msgstr "将新的æ交推é€åˆ°å…¶æºåˆ†æ”¯æ—¶ï¼Œåˆ é™¤åˆå¹¶è¯·æ±‚中的所有核准"
msgid "Remove approver"
+msgstr "删除核准人"
+
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
msgstr ""
msgid "Remove avatar"
@@ -7835,16 +8157,16 @@ msgid "Rename folder"
msgstr "é‡å‘½å文件夹"
msgid "Reopen epic"
-msgstr "é‡æ–°å¼€å¯å²è¯—"
+msgstr "é‡æ–°å¼€å¯epic"
msgid "Reopen milestone"
-msgstr ""
+msgstr "é‡æ–°æ‰“开里程碑"
msgid "Repair authentication"
msgstr "ä¿®å¤è®¤è¯"
msgid "Reply to comment"
-msgstr ""
+msgstr "回å¤è¯„论"
msgid "Reply to this email directly or %{view_it_on_gitlab}."
msgstr "直接回å¤æ­¤é‚®ä»¶æˆ– %{view_it_on_gitlab}。"
@@ -7907,10 +8229,10 @@ msgid "Repository URL"
msgstr "仓库地å€"
msgid "Repository cleanup"
-msgstr ""
+msgstr "存储库清ç†"
msgid "Repository cleanup has started. You will receive an email once the cleanup operation is complete."
-msgstr ""
+msgstr "存储库清ç†å·²ç»å¼€å§‹ã€‚一旦清ç†å®Œæˆï¼Œæ‚¨å°†æ”¶åˆ°ä¸€å°ç”µå­é‚®ä»¶ã€‚"
msgid "Repository has no locks."
msgstr "当å‰ä»“库无加é”文件。"
@@ -7931,7 +8253,7 @@ msgid "Request Access"
msgstr "申请æƒé™"
msgid "Requested %{time_ago}"
-msgstr ""
+msgstr "请求的 %{time_ago}"
msgid "Requests Profiles"
msgstr "请求分æž"
@@ -7942,26 +8264,37 @@ msgstr "è¦æ±‚此群组中的所有用户都å¯ç”¨åŒé‡è®¤è¯"
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "è¦æ±‚所有用户在访问GitLab时接å—æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–。"
-msgid "Resend invite"
+msgid "Require approval from code owners"
msgstr ""
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+
+msgid "Resend invite"
+msgstr "é‡æ–°å‘é€é‚€è¯·"
+
msgid "Reset authorization key"
-msgstr ""
+msgstr "é‡ç½®æŽˆæƒå¯†é’¥"
msgid "Reset authorization key?"
-msgstr ""
+msgstr "é‡ç½®æŽˆæƒå¯†é’¥ï¼Ÿ"
msgid "Reset health check access token"
msgstr "é‡ç½®è¿è¡ŒçŠ¶å†µæ£€æŸ¥è®¿é—®ä»¤ç‰Œ"
msgid "Reset key"
-msgstr ""
+msgstr "é‡ç½®å¯†é’¥"
msgid "Reset runners registration token"
msgstr "é‡ç½® Runner 注册令牌"
msgid "Resetting the authorization key will invalidate the previous key. Existing alert configurations will need to be updated with the new key."
-msgstr ""
+msgstr "é‡ç½®æŽˆæƒå¯†é’¥å°†ä¼šä½¿å…ˆå‰çš„密钥无效。现有警报é…置需è¦é€šè¿‡æ–°çš„密钥更新。"
msgid "Resolve all discussions in new issue"
msgstr "在新议题中解决所有讨论"
@@ -7973,10 +8306,10 @@ msgid "Resolve discussion"
msgstr "解决讨论"
msgid "Resolved"
-msgstr ""
+msgstr "已解决"
msgid "Response"
-msgstr ""
+msgstr "å“应"
msgid "Response metrics (AWS ELB)"
msgstr "å“应指标(AWS ELB)"
@@ -7988,7 +8321,7 @@ msgid "Response metrics (HA Proxy)"
msgstr "å“应指标(HA Proxy)"
msgid "Response metrics (NGINX Ingress VTS)"
-msgstr ""
+msgstr "å“应指标(NGINX Ingress VTS)"
msgid "Response metrics (NGINX Ingress)"
msgstr "å“应指标(NGINX Ingress)"
@@ -7997,7 +8330,7 @@ msgid "Response metrics (NGINX)"
msgstr "å“应指标(NGINX)"
msgid "Restart Terminal"
-msgstr ""
+msgstr "é‡å¯ç»ˆç«¯"
msgid "Resume"
msgstr "æ¢å¤"
@@ -8016,7 +8349,7 @@ msgid_plural "Reveal values"
msgstr[0] "显示值"
msgid "Reveal values"
-msgstr ""
+msgstr "显示值"
msgid "Revert this commit"
msgstr "还原此æ交"
@@ -8046,7 +8379,7 @@ msgid "Run CI/CD pipelines for external repositories"
msgstr "使用外部仓库的CI/CDæµæ°´çº¿"
msgid "Run tests against your code live using the Web Terminal"
-msgstr ""
+msgstr "使用Web终端对您的代ç è¿›è¡Œå®žæ—¶æµ‹è¯•"
msgid "Run untagged jobs"
msgstr "è¿è¡Œæœªæ ‡è®°çš„作业"
@@ -8076,7 +8409,7 @@ msgid "Runners API"
msgstr "Runners API"
msgid "Runners activated for this project"
-msgstr ""
+msgstr "此项目已激活的è¿è¡Œå™¨"
msgid "Runners can be placed on separate users, servers, and even on your local machine."
msgstr "Runnerå¯ä»¥æ”¾åœ¨ä¸åŒçš„用户ã€æœåŠ¡å™¨ï¼Œç”šè‡³æœ¬åœ°æœºå™¨ä¸Šã€‚"
@@ -8099,6 +8432,9 @@ msgstr "您已ç»ä½¿ç”¨äº†æ‰€æœ‰å…±äº«Runnerçš„æµæ°´çº¿æ—¶é—´ã€‚"
msgid "Running"
msgstr "è¿è¡Œä¸­"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "SAML SSO(å•ç‚¹ç™»å½•)"
@@ -8112,7 +8448,7 @@ msgid "SAML Single Sign On Settings"
msgstr "SAML å•ç‚¹ç™»å½•è®¾ç½®"
msgid "SAML for %{group_name}"
-msgstr ""
+msgstr "%{group_name} çš„ SAML"
msgid "SHA1 fingerprint of the SAML token signing certificate. Get this from your identity provider, where it can also be called \"Thumbprint\"."
msgstr "SAML令牌签åè¯ä¹¦çš„SHA1指纹。请从身份验è¯æ供商处获å–(也å¯ä»¥è¢«ç§°ä¸ºâ€œæŒ‡çº¹â€ï¼‰ã€‚"
@@ -8129,11 +8465,14 @@ msgstr "SSH公钥"
msgid "SSL Verification"
msgstr "SSL验è¯"
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "ä¿å­˜"
msgid "Save Changes"
-msgstr ""
+msgstr "ä¿å­˜ä¿®æ”¹"
msgid "Save application"
msgstr "ä¿å­˜åº”用"
@@ -8145,7 +8484,7 @@ msgid "Save changes before testing"
msgstr "测试å‰ä¿å­˜æ›´æ”¹"
msgid "Save comment"
-msgstr ""
+msgstr "ä¿å­˜è¯„论"
msgid "Save pipeline schedule"
msgstr "ä¿å­˜æµæ°´çº¿è®¡åˆ’"
@@ -8162,6 +8501,9 @@ msgstr "已加入日程"
msgid "Schedules"
msgstr "计划"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "æµæ°´çº¿è®¡åˆ’"
@@ -8184,7 +8526,7 @@ msgid "Search"
msgstr "æœç´¢"
msgid "Search an environment spec"
-msgstr ""
+msgstr "æœç´¢çŽ¯å¢ƒè§„范"
msgid "Search branches"
msgstr "æœç´¢åˆ†æ”¯"
@@ -8199,7 +8541,7 @@ msgid "Search for projects, issues, etc."
msgstr "æœç´¢é¡¹ç›®ã€è®®é¢˜ç­‰ç­‰â€¦"
msgid "Search groups"
-msgstr ""
+msgstr "æœç´¢ç¾¤ç»„"
msgid "Search merge requests"
msgstr "æœç´¢åˆå¹¶è¯·æ±‚"
@@ -8222,6 +8564,9 @@ msgstr "æœç´¢é¡¹ç›®"
msgid "Search users"
msgstr "æœç´¢ç”¨æˆ·"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr "æœç´¢æ‚¨çš„项目"
@@ -8271,7 +8616,7 @@ msgid "Security Dashboard|Issue Created"
msgstr "已创建议题"
msgid "Security Reports|At this time, the security dashboard only supports SAST and dependency scanning."
-msgstr ""
+msgstr "现在的安全仪表æ¿ä»…支æŒSASTå’Œä¾èµ–扫æ。"
msgid "Security Reports|Create issue"
msgstr "创建议题"
@@ -8280,13 +8625,13 @@ msgid "Security Reports|Dismiss vulnerability"
msgstr "忽略æ¼æ´ž"
msgid "Security Reports|Learn more about setting up your dashboard"
-msgstr ""
+msgstr "了解更多关于您的仪表æ¿è®¾ç½®"
msgid "Security Reports|More info"
msgstr "更多信æ¯"
msgid "Security Reports|No Vulnerabilities"
-msgstr ""
+msgstr "æ— æ¼æ´ž"
msgid "Security Reports|Security dashboard documentation"
msgstr "安全仪表æ¿æ–‡æ¡£"
@@ -8304,16 +8649,16 @@ msgid "Security Reports|There was an error reverting this dismissal."
msgstr "å–消忽略时出错。"
msgid "Security Reports|Undo dismiss"
-msgstr ""
+msgstr "撤消解除"
msgid "Security Reports|We've found no vulnerabilities for your group"
-msgstr ""
+msgstr "我们没有在您的群组中å‘现æ¼æ´ž"
msgid "Security Reports|While it's rare to have no vulnerabilities for your group, it can happen. In any event, we ask that you please double check your settings to make sure you've set up your dashboard correctly."
-msgstr ""
+msgstr "虽然您的群组中没有æ¼æ´žï¼Œä½†å®ƒå¯èƒ½ä¼šå‡ºçŽ°ã€‚无论如何,我们都建议您仔细检查您的设置以确ä¿æ‚¨å·²æ­£ç¡®çš„设置了您的仪表æ¿ã€‚"
msgid "Security dashboard"
-msgstr ""
+msgstr "安全仪表盘"
msgid "SecurityDashboard| The security dashboard displays the latest security report. Use it to find and fix vulnerabilities."
msgstr "安全仪表æ¿æ˜¾ç¤ºæœ€æ–°çš„安全报告。用它æ¥æŸ¥æ‰¾å’Œä¿®å¤æ¼æ´žã€‚"
@@ -8325,10 +8670,10 @@ msgid "SecurityDashboard|Pipeline %{pipelineLink} triggered"
msgstr "æµæ°´çº¿ %{pipelineLink} 已触å‘"
msgid "See metrics"
-msgstr ""
+msgstr "查看指标"
msgid "See the affected projects in the GitLab admin panel"
-msgstr ""
+msgstr "查看 GitLab 管ç†é¢æ¿ä¸­çš„å—å½±å“项目"
msgid "Select"
msgstr "选择"
@@ -8358,7 +8703,7 @@ msgid "Select branch/tag"
msgstr "选择分支/标签"
msgid "Select members to invite"
-msgstr ""
+msgstr "选择è¦é‚€è¯·çš„æˆå‘˜"
msgid "Select project"
msgstr "选择项目"
@@ -8394,13 +8739,13 @@ msgid "Send email"
msgstr "å‘é€ç”µå­é‚®ä»¶"
msgid "Send report"
-msgstr ""
+msgstr "å‘é€æŠ¥å‘Š"
msgid "Send usage data"
msgstr "å‘é€çš„使用情况数æ®"
msgid "Sentry API URL"
-msgstr ""
+msgstr "Sentry API URL"
msgid "Sep"
msgstr "9月"
@@ -8412,46 +8757,46 @@ msgid "Server version"
msgstr "æœåŠ¡å™¨ç‰ˆæœ¬"
msgid "Serverless"
-msgstr ""
+msgstr "Serverless"
msgid "ServerlessDetails|Kubernetes Pods"
-msgstr ""
+msgstr "Kubernestes Pods"
msgid "ServerlessDetails|Number of Kubernetes pods in use over time based on necessity."
-msgstr ""
+msgstr "æ ¹æ®éœ€è¦éšæ—¶é—´æŽ¨ç§»ä½¿ç”¨çš„Kubernetes Podæ•°é‡ã€‚"
msgid "ServerlessDetails|pod in use"
-msgstr ""
+msgstr "Pod正在使用"
msgid "ServerlessDetails|pods in use"
-msgstr ""
+msgstr "Pods正在使用"
msgid "ServerlessURL|Copy URL to clipboard"
-msgstr ""
+msgstr "å¤åˆ¶é“¾æŽ¥åˆ°å‰ªè´´æ¿"
msgid "Serverless| In order to start using functions as a service, you must first install Knative on your Kubernetes cluster."
-msgstr ""
+msgstr "è¦å¼€å§‹å°†åŠŸèƒ½ä½œä¸ºä¸€ç§æœåŠ¡ï¼Œå¿…须先在Kubernetes群集上安装Knative。"
msgid "Serverless|An error occurred while retrieving serverless components"
-msgstr ""
+msgstr "检索Serverless组件时å‘生错误"
msgid "Serverless|Getting started with serverless"
-msgstr ""
+msgstr "开始使用Serverless"
msgid "Serverless|If you believe none of these apply, please check back later as the function data may be in the process of becoming available."
-msgstr ""
+msgstr "如果您认为这些都ä¸é€‚用,请ç¨åŽå†æŸ¥çœ‹ï¼Œå› ä¸ºåŠŸèƒ½æ•°æ®å¯èƒ½æ­£åœ¨å˜ä¸ºå¯ç”¨ã€‚"
msgid "Serverless|Install Knative"
-msgstr ""
+msgstr "安装Knative"
msgid "Serverless|Learn more about Serverless"
-msgstr ""
+msgstr "了解更多关于 Serverless"
msgid "Serverless|No functions available"
-msgstr ""
+msgstr "没有å¯ç”¨çš„功能"
msgid "Serverless|There is currently no function data available from Knative. This could be for a variety of reasons including:"
-msgstr ""
+msgstr " Knativeç›®å‰æ²¡æœ‰å¯ç”¨çš„功能数æ®ã€‚è¿™å¯èƒ½æœ‰å¤šç§åŽŸå› ï¼ŒåŒ…括:"
msgid "Service Desk"
msgstr "æœåŠ¡å°"
@@ -8480,11 +8825,14 @@ msgstr "设置实例范围的模æ¿å­˜å‚¨åº“"
msgid "Set max session time for web terminal."
msgstr "为Web终端设置最长会è¯æ—¶é—´ã€‚"
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "为滥用报告设置通知电å­é‚®ä»¶ã€‚"
msgid "Set number of approvers required before open merge requests can be merged"
-msgstr ""
+msgstr "设置åˆå¹¶æœªå…³é—­çš„åˆå¹¶è¯·æ±‚之å‰æ‰€éœ€çš„核准人数é‡"
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
msgstr "设定用户登录的æ¡ä»¶ã€‚å¯ç”¨å¼ºåˆ¶åŒé‡è®¤è¯ã€‚"
@@ -8502,6 +8850,9 @@ msgid "Set up assertions/attributes/claims (email, first_name, last_name) and Na
msgstr "æ ¹æ®%{docsLinkStart}文档%{icon}%{docsLinkEnd}设置断言/属性/声明(email,first_name,last_name)和NameID"
msgid "Set up new U2F device"
+msgstr "设置新的U2F设备"
+
+msgid "Set up new password"
msgstr ""
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
@@ -8532,7 +8883,7 @@ msgid "SetStatusModal|Sorry, we weren't able to set your status. Please try agai
msgstr "对ä¸èµ·ï¼Œæˆ‘们无法设置您的状æ€ã€‚请ç¨åŽå†è¯•ã€‚"
msgid "SetStatusModal|What's your status?"
-msgstr "你的状æ€æ˜¯ä»€ä¹ˆï¼Ÿ"
+msgstr "您的状æ€æ˜¯ä»€ä¹ˆï¼Ÿ"
msgid "Settings"
msgstr "设置"
@@ -8561,14 +8912,20 @@ msgstr "é‡ç½®å·²ç”¨æµæ°´çº¿åˆ†é’Ÿæ•°"
msgid "Sherlock Transactions"
msgstr "Sherlock事物"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "显示相关命令"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "显示完整的原始日志"
msgid "Show file browser"
-msgstr ""
+msgstr "显示文件æµè§ˆå™¨"
msgid "Show latest version"
msgstr "显示最新版本"
@@ -8608,19 +8965,19 @@ msgid "Sign in / Register"
msgstr "登录/注册"
msgid "Sign in to \"%{group_name}\""
-msgstr ""
+msgstr "登录到 \"%{group_name}\""
msgid "Sign in using smart card"
-msgstr ""
+msgstr "使用智能å¡ç™»å½•"
msgid "Sign in via 2FA code"
-msgstr ""
+msgstr "通过2FA代ç ç™»å½•"
msgid "Sign in with Single Sign-On"
msgstr "使用å•ç‚¹ç™»å½•(SSO)进行登录"
msgid "Sign in with smart card"
-msgstr ""
+msgstr "使用智能å¡ç™»å½•"
msgid "Sign out"
msgstr "退出"
@@ -8632,7 +8989,7 @@ msgid "Sign-up restrictions"
msgstr "注册é™åˆ¶"
msgid "Similar issues"
-msgstr ""
+msgstr "相似议题"
msgid "Size"
msgstr "大å°"
@@ -8656,17 +9013,35 @@ msgid "Smartcard authentication failed: client certificate header is missing."
msgstr "智能å¡èº«ä»½éªŒè¯å¤±è´¥ï¼šç¼ºå°‘客户端è¯ä¹¦æŠ¥å¤´ã€‚"
msgid "Snippet Contents"
-msgstr ""
+msgstr "代ç æ®µå†…容"
msgid "Snippets"
msgstr "代ç ç‰‡æ®µ"
-msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
+msgid "SnippetsEmptyState|Explore public snippets"
msgstr ""
-msgid "Someone edited this merge request at the same time you did. Please refresh the page to see changes."
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
msgstr ""
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
+msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
+msgstr "有人与您åŒæ—¶ç¼–辑了%{issueType} 。æ述已被更新,您需è¦é‡æ–°ä¿®æ”¹ã€‚"
+
+msgid "Someone edited this merge request at the same time you did. Please refresh the page to see changes."
+msgstr "有人您åŒæ—¶ç¼–辑了这一åˆå¹¶è¯·æ±‚。请刷新页é¢æŸ¥çœ‹æ›´æ”¹ã€‚"
+
msgid "Something went wrong on our end"
msgstr "出错了,抱歉。"
@@ -8677,7 +9052,7 @@ msgid "Something went wrong on our end. Please try again!"
msgstr "æœåŠ¡å™¨ç«¯å‡ºçŽ°é—®é¢˜ï¼Œè¯·é‡è¯•ã€‚"
msgid "Something went wrong on our end. Please try again."
-msgstr ""
+msgstr "æœåŠ¡å™¨é”™è¯¯ã€‚ 请ç¨åŽå†è¯•ã€‚"
msgid "Something went wrong trying to change the confidentiality of this issue"
msgstr "试图改å˜è¿™ä¸ªè®®é¢˜çš„ç§å¯†æ€§æ—¶å‡ºçŽ°é”™è¯¯"
@@ -8689,13 +9064,13 @@ msgid "Something went wrong when toggling the button"
msgstr "点击按钮时出错"
msgid "Something went wrong while applying the suggestion. Please try again."
-msgstr ""
+msgstr "应用åŒæ„时出了点问题。请å†è¯•ä¸€æ¬¡ã€‚"
msgid "Something went wrong while closing the %{issuable}. Please try again later"
msgstr "关闭 %{issuable} 时出错。请ç¨åŽé‡è¯•"
msgid "Something went wrong while deleting the source branch. Please try again."
-msgstr ""
+msgstr "删除æºåˆ†æ”¯æ—¶å‡ºé”™ã€‚请é‡è¯•ã€‚"
msgid "Something went wrong while fetching %{listType} list"
msgstr "åœ¨èŽ·å– %{listType} 列表时出错了"
@@ -8716,7 +9091,7 @@ msgid "Something went wrong while fetching the registry list."
msgstr "拉å–注册表列表时å‘生错误。"
msgid "Something went wrong while merging this merge request. Please try again."
-msgstr ""
+msgstr "åˆå¹¶æ­¤åˆå¹¶è¯·æ±‚时出错。请é‡è¯•ã€‚"
msgid "Something went wrong while reopening the %{issuable}. Please try again later"
msgstr "é‡æ–°å¼€å¯ %{issuable} 时出错。请ç¨åŽå†è¯•"
@@ -8737,19 +9112,19 @@ msgid "Something went wrong. Please try again."
msgstr "出现错误。请é‡è¯•ã€‚"
msgid "Sorry, no epics matched your search"
-msgstr "对ä¸èµ·ï¼Œæœªæœç´¢åˆ°ä»»ä½•ç¬¦åˆæ¡ä»¶çš„å²è¯—故事"
+msgstr "对ä¸èµ·ï¼Œæœªæœç´¢åˆ°ä»»ä½•ç¬¦åˆæ¡ä»¶çš„epic"
msgid "Sorry, no projects matched your search"
msgstr "对ä¸èµ·ï¼Œæœªæœç´¢åˆ°ä»»ä½•ç¬¦åˆæ¡ä»¶çš„项目"
msgid "Sorry, your filter produced no results"
-msgstr ""
+msgstr "抱歉,您的过滤器没有产生结果"
msgid "Sort by"
msgstr "排åº"
msgid "Sort direction"
-msgstr ""
+msgstr "排åºæ–¹å‘"
msgid "SortOptions|Access level, ascending"
msgstr "访问级别,å‡åºæŽ’列"
@@ -8797,7 +9172,7 @@ msgid "SortOptions|Less weight"
msgstr "é™ä½Žæƒé‡"
msgid "SortOptions|Milestone due date"
-msgstr ""
+msgstr "里程碑截止日期"
msgid "SortOptions|Milestone due later"
msgstr "里程碑截止日期"
@@ -8830,7 +9205,7 @@ msgid "SortOptions|Oldest joined"
msgstr "最早的加入"
msgid "SortOptions|Oldest last activity"
-msgstr ""
+msgstr "最新的活动"
msgid "SortOptions|Oldest sign in"
msgstr "最早的登录"
@@ -8845,7 +9220,7 @@ msgid "SortOptions|Priority"
msgstr "优先"
msgid "SortOptions|Recent last activity"
-msgstr ""
+msgstr "最近一次活动"
msgid "SortOptions|Recent sign in"
msgstr "最近登录"
@@ -8875,7 +9250,7 @@ msgid "Source is not available"
msgstr "æºä¸å¯ç”¨"
msgid "Source project cannot be found."
-msgstr ""
+msgstr "找ä¸åˆ°æºé¡¹ç›®ã€‚"
msgid "Spam Logs"
msgstr "垃圾信æ¯æ—¥å¿—"
@@ -8893,7 +9268,7 @@ msgid "Specify the following URL during the Runner setup:"
msgstr "在 Runner 设置时指定以下 URL:"
msgid "Squash commit message"
-msgstr ""
+msgstr "Squashæ交消æ¯"
msgid "Squash commits"
msgstr "åˆå¹¶æ交"
@@ -8923,20 +9298,26 @@ msgid "StarProject|Star"
msgstr "星标"
msgid "Starred Projects"
-msgstr "已星标项目"
+msgstr "星标项目"
msgid "Starred Projects' Activity"
-msgstr "已星标项目的活动"
+msgstr "星标项目动æ€"
msgid "Starred projects"
-msgstr "已星标项目"
+msgstr "星标项目"
-msgid "Stars"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
msgstr ""
-msgid "Start Web Terminal"
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
msgstr ""
+msgid "Stars"
+msgstr "星标"
+
+msgid "Start Web Terminal"
+msgstr "å¯åŠ¨Web终端"
+
msgid "Start a %{new_merge_request} with these changes"
msgstr "由此更改 %{new_merge_request}"
@@ -8947,19 +9328,19 @@ msgid "Start and due date"
msgstr "开始和截止日期"
msgid "Start cleanup"
-msgstr ""
+msgstr "开始清ç†"
msgid "Start date"
msgstr "开始日期"
msgid "Start discussion"
-msgstr ""
+msgstr "开始讨论"
msgid "Start discussion & close %{noteable_name}"
-msgstr ""
+msgstr "开始讨论并关闭 %{noteable_name}"
msgid "Start discussion & reopen %{noteable_name}"
-msgstr ""
+msgstr "开始讨论并é‡æ–°æ‰“å¼€ %{noteable_name}"
msgid "Start the Runner!"
msgstr "å¯åŠ¨ Runner!"
@@ -8971,13 +9352,13 @@ msgid "Started"
msgstr "å·²å¯åŠ¨"
msgid "Started %{startsIn}"
-msgstr ""
+msgstr "已开始 %{startsIn}"
msgid "Starting..."
-msgstr ""
+msgstr "正在å¯åŠ¨..."
msgid "Starts %{startsIn}"
-msgstr ""
+msgstr "开始 %{startsIn}"
msgid "Starts at (UTC)"
msgstr "开始于(UTC)"
@@ -8989,10 +9370,10 @@ msgid "Status"
msgstr "状æ€"
msgid "Status:"
-msgstr ""
+msgstr "状æ€:"
msgid "Stop Terminal"
-msgstr ""
+msgstr "åœæ­¢ç»ˆç«¯"
msgid "Stop environment"
msgstr "终止环境"
@@ -9010,7 +9391,7 @@ msgid "Stopping this environment is currently not possible as a deployment is in
msgstr "由于部署正在进行,目å‰æ— æ³•ç»ˆæ­¢æ­¤çŽ¯å¢ƒ"
msgid "Stopping..."
-msgstr ""
+msgstr "正在åœæ­¢â€¦â€¦"
msgid "Storage"
msgstr "存储"
@@ -9028,7 +9409,7 @@ msgid "Submit as spam"
msgstr "垃圾信æ¯ä¸¾æŠ¥"
msgid "Submit feedback"
-msgstr ""
+msgstr "æ交å馈"
msgid "Submit review"
msgstr "æ交评审"
@@ -9046,94 +9427,94 @@ msgid "Subscribe at project level"
msgstr "在项目级别订阅"
msgid "Subscribe to RSS feed"
-msgstr ""
+msgstr "订阅 RSS "
msgid "Subscribe to calendar"
-msgstr ""
+msgstr "订阅日历"
msgid "Subscribed"
msgstr "已订阅"
msgid "SubscriptionTable|Billing"
-msgstr ""
+msgstr "è´¦å•"
msgid "SubscriptionTable|Free"
-msgstr ""
+msgstr "å…è´¹"
msgid "SubscriptionTable|GitLab allows you to continue using your subscription even if you exceed the number of seats you purchased. You will be required to pay for these seats upon renewal."
-msgstr ""
+msgstr "GitLab å…许您继续使用您的订阅,å³ä½¿æ‚¨å·²è¶…过您购买的最大使用人数。您必须在续订时支付这些订阅。"
msgid "SubscriptionTable|GitLab.com %{planName} %{suffix}"
-msgstr ""
+msgstr "GitLab.com %{planName} %{suffix}"
msgid "SubscriptionTable|Last invoice"
-msgstr ""
+msgstr "最åŽä¸€ä¸ªå‘票"
msgid "SubscriptionTable|Loading subscriptions"
-msgstr ""
+msgstr "加载订阅"
msgid "SubscriptionTable|Manage"
-msgstr ""
+msgstr "管ç†"
msgid "SubscriptionTable|Max seats used"
-msgstr ""
+msgstr "最大的使用人数"
msgid "SubscriptionTable|Next invoice"
-msgstr ""
+msgstr "下一张å‘票"
msgid "SubscriptionTable|Seats currently in use"
-msgstr ""
+msgstr "当å‰æ­£åœ¨ä½¿ç”¨çš„人数"
msgid "SubscriptionTable|Seats in subscription"
-msgstr ""
+msgstr "订阅中的人数"
msgid "SubscriptionTable|Seats owed"
-msgstr ""
+msgstr "欠款人数"
msgid "SubscriptionTable|Subscription end date"
-msgstr ""
+msgstr "订阅结æŸæ—¥æœŸ"
msgid "SubscriptionTable|Subscription start date"
-msgstr ""
+msgstr "订阅开始日期"
msgid "SubscriptionTable|This is the last time the GitLab.com team was in contact with you to settle any outstanding balances."
-msgstr ""
+msgstr "这是 GitLab.com 团队最近一次与您è”系的时间,关于您的欠款。"
msgid "SubscriptionTable|This is the maximum number of users that have existed at the same time since this subscription started."
-msgstr ""
+msgstr "这是自订阅开始以æ¥åŒæ—¶å­˜åœ¨çš„最大用户数é‡ã€‚"
msgid "SubscriptionTable|This is the next date when the GitLab.com team is scheduled to get in contact with you to settle any outstanding balances."
-msgstr ""
+msgstr "这是 GitLab.com 团队下一次与您è”系的时间,关于您的欠款。"
msgid "SubscriptionTable|This is the number of seats you will be required to purchase if you update to a paid plan."
-msgstr ""
+msgstr "这是您更新到付费方案时需è¦è´­ä¹°çš„最大使用人数。"
msgid "SubscriptionTable|Trial"
-msgstr ""
+msgstr "试用"
msgid "SubscriptionTable|Trial end date"
-msgstr ""
+msgstr "试用结æŸæ—¥æœŸ"
msgid "SubscriptionTable|Trial start date"
-msgstr ""
+msgstr "试用开始日期"
msgid "SubscriptionTable|Upgrade"
-msgstr ""
+msgstr "å‡çº§"
msgid "SubscriptionTable|Usage"
-msgstr ""
+msgstr "使用情况"
msgid "SubscriptionTable|Usage count is performed once a day at 12:00 PM."
-msgstr ""
+msgstr "使用次数将在æ¯å¤©ä¸­åˆ12:00进行更新。"
msgid "Suggested change"
-msgstr ""
+msgstr "å˜æ›´å»ºè®®"
msgid "Sunday"
-msgstr ""
+msgstr "星期日"
msgid "Support for custom certificates is disabled. Ask your system's administrator to enable it."
-msgstr ""
+msgstr "对自定义è¯ä¹¦çš„支æŒå·²ç¦ç”¨ã€‚请è”系系统管ç†å‘˜æ¥å¯ç”¨å®ƒã€‚"
msgid "Switch branch/tag"
msgstr "切æ¢åˆ†æ”¯/标签"
@@ -9141,6 +9522,9 @@ msgstr "切æ¢åˆ†æ”¯/标签"
msgid "Sync information"
msgstr "åŒæ­¥ä¿¡æ¯"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "系统钩å­"
@@ -9148,10 +9532,10 @@ msgid "System Info"
msgstr "系统信æ¯"
msgid "System default (%{default})"
-msgstr ""
+msgstr "系统默认值(%{default})"
msgid "System header and footer"
-msgstr ""
+msgstr "系统页头和页脚"
msgid "System metrics (Custom)"
msgstr "系统指标(自定义)"
@@ -9160,10 +9544,10 @@ msgid "System metrics (Kubernetes)"
msgstr "系统指标(Kubernetes)"
msgid "Tag"
-msgstr ""
+msgstr "标签"
msgid "Tag list:"
-msgstr ""
+msgstr "标签列表:"
msgid "Tags"
msgstr "标签"
@@ -9259,10 +9643,10 @@ msgid "Templates"
msgstr "模æ¿"
msgid "Terminal"
-msgstr ""
+msgstr "终端"
msgid "Terminal for environment"
-msgstr ""
+msgstr "环境终端"
msgid "Terms of Service Agreement and Privacy Policy"
msgstr "æœåŠ¡æ¡æ¬¾å议和éšç§æ”¿ç­–"
@@ -9280,13 +9664,13 @@ msgid "Thanks! Don't show me this again"
msgstr "ä¸å†æ˜¾ç¤º"
msgid "The \"%{group_path}\" group allows you to sign in with your Single Sign-On Account"
-msgstr ""
+msgstr "群组“%{group_path}â€å…许您使用SSO以登录å¸æˆ·"
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 中的高级全局æœç´¢åŠŸèƒ½æ˜¯ä¸€ä¸ªå¼ºå¤§ä¸”节çœæ‚¨çš„时间的æœç´¢æœåŠ¡ã€‚您å¯ä»¥æœç´¢å…¶ä»–团队的代ç ä»¥å¸®åŠ©æ‚¨å®Œå–„自己项目中的代ç ã€‚从而é¿å…创建é‡å¤çš„代ç æˆ–浪费时间。"
msgid "The CSV export will be created in the background. Once finished, it will be sent to <strong>%{email}</strong> in an attachment."
-msgstr ""
+msgstr "CSV导出将在åŽå°åˆ›å»ºã€‚完æˆåŽï¼Œå®ƒå°†ä»¥é™„件形å¼å‘é€åˆ°<strong>%{email}</strong>。"
msgid "The Git LFS objects will <strong>not</strong> be synced."
msgstr "Git LFS对象将<strong>ä¸ä¼š</strong>被åŒæ­¥ã€‚"
@@ -9325,11 +9709,14 @@ msgid "The issue stage shows the time it takes from creating an issue to assigni
msgstr "议题阶段概述了从创建议题到将议题添加到里程碑或议题看æ¿æ‰€èŠ±è´¹çš„时间。创建第一个议题åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„.。"
msgid "The maximum file size allowed is %{size}."
-msgstr ""
+msgstr "å…许的最大文件大å°ä¸º %{size}。"
msgid "The maximum file size allowed is 200KB."
msgstr "文件大å°é™åˆ¶ä¸º 200KB。"
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "解密ç§é’¥æ‰€éœ€çš„密ç çŸ­è¯­ã€‚该项为å¯é€‰é¡¹, 并且内容被加密存储。"
@@ -9352,7 +9739,7 @@ msgid "The production stage shows the total time it takes between creating an is
msgstr "生产阶段概述了从创建一个议题到将代ç éƒ¨ç½²åˆ°ç”Ÿäº§çŽ¯å¢ƒçš„总时间。当完æˆæƒ³æ³•åˆ°éƒ¨ç½²ç”Ÿäº§çš„循环,数æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
msgid "The project can be accessed by any logged in user."
-msgstr "该项目å…许已登录的用户访问。"
+msgstr "该项目å…许所有已登录到当å‰GitLabæœåŠ¡å™¨çš„用户访问。"
msgid "The project can be accessed without any authentication."
msgstr "该项目å…许任何人访问。"
@@ -9376,7 +9763,7 @@ msgid "The review stage shows the time from creating the merge request to mergin
msgstr "审阅阶段概述了从创建åˆå¹¶è¯·æ±‚到被åˆå¹¶çš„时间。当创建第一个åˆå¹¶è¯·æ±‚åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
msgid "The roadmap shows the progress of your epics along a timeline"
-msgstr "路线图显示了å²è¯—故事沿ç€æ—¶é—´çº¿çš„进展情况"
+msgstr "路线图显示了 epic 沿ç€æ—¶é—´çº¿çš„进展情况"
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 "预å‘布阶段概述了从åˆå¹¶è¯·æ±‚被åˆå¹¶åˆ°éƒ¨ç½²è‡³ç”Ÿäº§çŽ¯å¢ƒçš„总时间。首次部署到生产环境åŽï¼Œæ•°æ®å°†è‡ªåŠ¨æ·»åŠ åˆ°æ­¤å¤„。"
@@ -9406,19 +9793,19 @@ msgid "The value lying at the midpoint of a series of observed values. E.g., bet
msgstr "中ä½æ•°æ˜¯ä¸€ä¸ªæ•°åˆ—中最中间的值。例如在 3ã€5ã€9 之间,中ä½æ•°æ˜¯ 5。在 3ã€5ã€7ã€8 之间,中ä½æ•°æ˜¯ (5 + 7)/ 2 = 6。"
msgid "There are no approvers"
-msgstr ""
+msgstr "没有核准人"
msgid "There are no archived projects yet"
msgstr "当å‰å°šæ— å·²å½’档的项目"
msgid "There are no closed issues"
-msgstr ""
+msgstr "没有已关闭的议题"
msgid "There are no closed merge requests"
-msgstr ""
+msgstr "没有已关闭的åˆå¹¶è¯·æ±‚"
msgid "There are no custom project templates set up for this GitLab instance. They are enabled from GitLab's Admin Area. Contact your GitLab instance administrator to setup custom project templates."
-msgstr ""
+msgstr "没有为此GitLab实例设置自定义项目模æ¿ã€‚它们是从GitLab的管ç†åŒºåŸŸå¯ç”¨çš„。请与您的GitLab实例管ç†å‘˜è”系以设置自定义项目模æ¿ã€‚"
msgid "There are no issues to show"
msgstr "当å‰æ— è®®é¢˜"
@@ -9427,13 +9814,13 @@ msgid "There are no labels yet"
msgstr "ç›®å‰æ— æ ‡è®°"
msgid "There are no open issues"
-msgstr ""
+msgstr "没有未关闭的议题"
msgid "There are no open merge requests"
-msgstr ""
+msgstr "没有未关闭的åˆå¹¶è¯·æ±‚"
msgid "There are no packages yet"
-msgstr ""
+msgstr "还没有包"
msgid "There are no projects shared with this group yet"
msgstr "当å‰å°šæ— åˆ†äº«ç»™è¯¥ç¾¤ç»„的项目"
@@ -9453,6 +9840,9 @@ msgstr "删除待办事项时出现错误。"
msgid "There was an error loading users activity calendar."
msgstr "加载用户活动日历时出错。"
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr "ä¿å­˜é€šçŸ¥è®¾ç½®æ—¶å‘生错误。"
@@ -9469,7 +9859,7 @@ msgid "There was an error when unsubscribing from this label."
msgstr "å–消订阅此标记时出错。"
msgid "These existing issues have a similar title. It might be better to comment there instead of creating another similar issue."
-msgstr ""
+msgstr "这些现有的议题具有类似的标题。在那里评论å¯èƒ½æ›´å¥½ï¼Œè€Œä¸æ˜¯åˆ›å»ºå¦ä¸€ä¸ªç±»ä¼¼çš„问题。"
msgid "They can be managed using the %{link}."
msgstr "他们å¯ä»¥é€šè¿‡ %{link} 进行管ç†ã€‚"
@@ -9478,10 +9868,10 @@ msgid "Third party offers"
msgstr "第三方优惠"
msgid "This %{issuable} is locked. Only <strong>project members</strong> can comment."
-msgstr ""
+msgstr "这个 %{issuable} 已被é”定。åªæœ‰ <strong>项目æˆå‘˜</strong> å¯ä»¥å‘表评论。"
msgid "This %{viewer} could not be displayed because %{reason}. You can %{options} instead."
-msgstr ""
+msgstr "因为 %{reason}无法显示 %{viewer} 。您å¯ä»¥æ”¹ä¸º %{options}。"
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。"
@@ -9498,25 +9888,43 @@ msgstr "此看æ¿èŒƒå›´ç¼©å°äº†"
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr "自您开始编辑åŽ, 此分支已更改。您想创建一个新的分支å—?"
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr "此容器注册表已安排删除。"
msgid "This date is after the due date, so this epic won't appear in the roadmap."
-msgstr "此日期在截止日期之åŽï¼Œå› æ­¤è¯¥å²è¯—故事ä¸ä¼šå‡ºçŽ°åœ¨è·¯çº¿å›¾ä¸­ã€‚"
+msgstr "此日期在截止日期之åŽï¼Œå› æ­¤è¯¥ epic ä¸ä¼šå‡ºçŽ°åœ¨è·¯çº¿å›¾ä¸­ã€‚"
msgid "This date is before the start date, so this epic won't appear in the roadmap."
-msgstr "此日期在开始日期之å‰ï¼Œå› æ­¤è¯¥å²è¯—故事ä¸ä¼šå‡ºçŽ°åœ¨è·¯çº¿å›¾ä¸­ã€‚"
+msgstr "此日期在开始日期之å‰ï¼Œå› æ­¤è¯¥ epic ä¸ä¼šå‡ºçŽ°åœ¨è·¯çº¿å›¾ä¸­ã€‚"
msgid "This diff is collapsed."
msgstr "此差异已折å ã€‚"
msgid "This diff was suppressed by a .gitattributes entry."
-msgstr ""
+msgstr "此差异由.gitattributes 抑制。"
msgid "This directory"
msgstr "当å‰ç›®å½•"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
+msgstr "此域å未ç»è¿‡éªŒè¯ã€‚在å¯ç”¨è®¿é—®æƒé™ä¹‹å‰ï¼Œæ‚¨éœ€è¦éªŒè¯æ‰€æœ‰æƒã€‚"
+
+msgid "This field is required."
msgstr ""
msgid "This group"
@@ -9583,10 +9991,10 @@ msgid "This job is in pending state and is waiting to be picked by a runner"
msgstr "作业挂起中,等待进入队列"
msgid "This job is stuck because you don't have any active runners online with any of these tags assigned to them:"
-msgstr ""
+msgstr "此作业被å¡ä½äº†ï¼Œå› ä¸ºæ‚¨æ²¡æœ‰ä»»ä½•åœ¨çº¿æ´»è·ƒçš„在è¿è¡Œå™¨ï¼Œå¹¶åˆ†é…了这些标签:"
msgid "This job is stuck because you don't have any active runners that can run this job."
-msgstr ""
+msgstr "由于您没有任何å¯ä»¥è¿è¡Œæ­¤ä½œä¸šçš„活跃è¿è¡Œå™¨ï¼Œå› æ­¤ä½œä¸šå¡ä½äº†ã€‚"
msgid "This job is the most recent deployment to %{link}."
msgstr "此作业最近部署到 %{link}。"
@@ -9595,7 +10003,7 @@ msgid "This job requires a manual action"
msgstr "作业需手动æ“作"
msgid "This job will automatically run after its timer finishes. Often they are used for incremental roll-out deploys to production environments. When unscheduled it converts into a manual action."
-msgstr ""
+msgstr "该作业将在计时器完æˆåŽè‡ªåŠ¨è¿è¡Œã€‚它们通常用于生产环境的增é‡éƒ¨ç½²ã€‚未计划时,它会转æ¢ä¸ºæ‰‹åŠ¨æ“作。"
msgid "This means you can not push code until you create an empty repository or import existing one."
msgstr "在创建一个空的仓库或导入现有仓库之å‰ï¼Œå°†æ— æ³•æŽ¨é€ä»£ç ã€‚"
@@ -9603,6 +10011,12 @@ msgstr "在创建一个空的仓库或导入现有仓库之å‰ï¼Œå°†æ— æ³•æŽ¨é€
msgid "This merge request is locked."
msgstr "æ­¤åˆå¹¶è¯·æ±‚å·²é”定。"
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr "由于您没有当å‰åˆ†æ”¯çš„写入æƒé™ï¼Œå› æ­¤ç¦ç”¨æ­¤é€‰é¡¹"
@@ -9616,13 +10030,13 @@ msgid "This page will be removed in a future release."
msgstr "此页é¢å°†åœ¨å°†æ¥çš„版本中删除。"
msgid "This pipeline is run in a merge request context"
-msgstr ""
+msgstr "æ­¤æµæ°´çº¿åœ¨åˆå¹¶è¯·æ±‚上下文中è¿è¡Œ"
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by %{strongStart}Auto DevOps.%{strongEnd}"
-msgstr ""
+msgstr "æ­¤æµæ°´çº¿ä½¿ç”¨äº† %{strongStart}Auto DevOps 预先定义的并已å¯ç”¨çš„ CI/CD é…置。%{strongEnd}"
msgid "This pipeline makes use of a predefined CI/CD configuration enabled by <b>Auto DevOps.</b>"
-msgstr ""
+msgstr "æ­¤æµæ°´çº¿ä½¿ç”¨äº† <b>Auto DevOps 预先定义的并已å¯ç”¨çš„ CI/CD é…置。</b>"
msgid "This project"
msgstr "当å‰é¡¹ç›®"
@@ -9652,7 +10066,7 @@ msgid "This source diff could not be displayed because it is too large."
msgstr "此代ç å·®å¼‚无法显示,因为它太大了。"
msgid "This timeout will take precedence when lower than project-defined timeout and accepts a human readable time input language like \"1 hour\". Values without specification represent seconds."
-msgstr ""
+msgstr "当低于项目定义的超时时,此超时将优先,并å¯æŽ¥å—英文语å¥ï¼Œå¦‚“1 hourâ€ã€‚默认å•ä½ä¸ºç§’。"
msgid "This user has no identities"
msgstr "该用户无身份标识"
@@ -9664,7 +10078,7 @@ msgid "This user will be the author of all events in the activity feed that are
msgstr "此用户将æˆä¸ºæ´»åŠ¨æµä¸­æ‰€æœ‰äº‹ä»¶çš„作者,例如创建新分支或者推é€æ–°æ交到现有分支。在创建或é‡æ–°æŒ‡å®šæ—¶æ‚¨ä»…å¯å°†è‡ªå·±æŒ‡å®šä¸ºé•œåƒç”¨æˆ·ã€‚"
msgid "This will redirect you to an external sign in page."
-msgstr ""
+msgstr "这会将您é‡å®šå‘到外部登录页é¢ã€‚"
msgid "Those emails automatically become issues (with the comments becoming the email conversation) listed here."
msgstr "这些电å­é‚®ä»¶è‡ªåŠ¨ç”Ÿæˆä¸ºè®®é¢˜(评论生æˆä¸ºç”µå­é‚®ä»¶ä¼šè¯)并在此处列出。"
@@ -9772,7 +10186,7 @@ msgid "Timeago|1 month ago"
msgstr "1个月å‰"
msgid "Timeago|1 month remaining"
-msgstr "剩余 1 个月"
+msgstr "剩余1个月"
msgid "Timeago|1 week ago"
msgstr "1星期å‰"
@@ -9799,7 +10213,7 @@ msgid "Timeago|in %s minutes"
msgstr " %s 分钟åŽ"
msgid "Timeago|in %s months"
-msgstr " %s 个月åŽ"
+msgstr " %s个月åŽ"
msgid "Timeago|in %s seconds"
msgstr " %s 秒åŽ"
@@ -9855,16 +10269,16 @@ msgid "Title"
msgstr "标题"
msgid "Titles and Filenames"
-msgstr ""
+msgstr "标题和文件å称"
msgid "To %{link_to_help} of your domain, add the above key to a TXT record within to your DNS configuration."
-msgstr ""
+msgstr "è¦%{link_to_help}到您的域å,请将上述密钥添加到DNSé…置中的TXT记录。"
msgid "To GitLab"
msgstr "到GitLab"
msgid "To access this domain create a new DNS record"
-msgstr ""
+msgstr "è¦è®¿é—®æ­¤åŸŸï¼Œè¯·åˆ›å»ºæ–°çš„DNS记录"
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}。"
@@ -9909,13 +10323,13 @@ msgid "To import an SVN repository, check out %{svn_link}."
msgstr "è¦å¯¼å…¥SVN仓库,请查看 %{svn_link}。"
msgid "To keep this project going, create a new issue"
-msgstr ""
+msgstr "为了ä¿æŒè¿™ä¸ªé¡¹ç›®çš„进行,请创建一个新的议题"
msgid "To keep this project going, create a new merge request"
-msgstr ""
+msgstr "为了ä¿æŒè¿™ä¸ªé¡¹ç›®çš„进行,请创建一个新的åˆå¹¶è¯·æ±‚"
msgid "To link Sentry to GitLab, enter your Sentry URL and Auth Token."
-msgstr ""
+msgstr "è¦å°†Sentry链接到GitLab,请输入您的Sentry URLå’ŒAuth Token。"
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 "如需将整个GitLab项目从å¦ä¸€ä¸ªGitLabæœåŠ¡å™¨ç§»åŠ¨æˆ–å¤åˆ¶åˆ°æ­¤æœåŠ¡å™¨ï¼Œè¯·è®¿é—®åŽŸé¡¹ç›®çš„设置页é¢ï¼Œç”Ÿæˆå¯¼å‡ºæ–‡ä»¶ï¼Œç„¶åŽåœ¨æ­¤å¤„上载。"
@@ -9924,13 +10338,13 @@ msgid "To only use CI/CD features for an external repository, choose <strong>CI/
msgstr "è¦ä»…为外部仓库使用CI / CD功能时,请选择</strong>使用外部仓库è¿è¡ŒCI/CD<strong>。"
msgid "To open Jaeger and easily view tracing from GitLab, link the %{link} page to your server"
-msgstr ""
+msgstr "请将%{link} 页é¢è¿žæŽ¥åˆ°æ‚¨çš„ Jaeger æœåŠ¡å™¨ï¼Œä»¥ä¾¿åœ¨ GitLab 打开并轻æ¾æŸ¥çœ‹è·Ÿè¸ª"
msgid "To preserve performance only <strong>%{display_size} of %{real_size}</strong> files are displayed."
-msgstr ""
+msgstr "为了ä¿æŒæ€§èƒ½ï¼Œä»…显示文件中的 <strong>%{display_size}/%{real_size}</strong>。"
msgid "To receive alerts from manually configured Prometheus services, add the following URL and Authorization key to your Prometheus webhook config file. Learn more about %{linkStart}configuring Prometheus%{linkEnd} to send alerts to GitLab."
-msgstr ""
+msgstr "è¦ä»Žæ‰‹åŠ¨é…置的PrometheusæœåŠ¡æŽ¥æ”¶è­¦æŠ¥ï¼Œè¯·å°†ä»¥ä¸‹URL和授æƒå¯†é’¥æ·»åŠ åˆ°Prometheus webhooké…置文件中。了解更多关于 %{linkStart}é…ç½®Prometheus%{linkEnd} 将警报å‘é€åˆ°GitLab。"
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身份验è¯ï¼š"
@@ -9939,7 +10353,7 @@ msgid "To start serving your jobs you can add Runners to your group"
msgstr "è¦å¼€å§‹æ‰§è¡Œä»»åŠ¡ï¼Œè¯·æŠŠRunner加到群组中"
msgid "To start serving your jobs you can either add specific Runners to your project or use shared Runners"
-msgstr ""
+msgstr "è¦å¼€å§‹ä½¿ç”¨ä½œä¸šï¼Œæ‚¨å¯ä»¥å‘项目添加特定的è¿è¡Œå™¨æˆ–使用共享的è¿è¡Œå™¨"
msgid "To this GitLab instance"
msgstr "转至此GitLab实例"
@@ -9948,10 +10362,10 @@ msgid "To validate your GitLab CI configurations, go to 'CI/CD → Pipelines' in
msgstr "如需验è¯GitLab CI设置,请访问当å‰é¡¹ç›®çš„'CI/CD → æµæ°´çº¿',然åŽç‚¹å‡»'CI Lint'按钮。"
msgid "To view the roadmap, add a start or due 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."
-msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ªå²è¯—故事。在月视图中,åªæ˜¾ç¤ºä¸Šä¸ªæœˆï¼Œæœ¬æœˆä»¥åŠæŽ¥ä¸‹æ¥5个月的å²è¯—故事."
+msgstr "如需查看路线图,请将计划的开始或结æŸæ—¥æœŸæ·»åŠ åˆ°å½“å‰ç¾¤ç»„或其å­ç»„中的æŸä¸ª epic。在月视图中,åªæ˜¾ç¤ºä¸Šä¸ªæœˆï¼Œæœ¬æœˆä»¥åŠæŽ¥ä¸‹æ¥5个月的 epic."
msgid "To widen your search, change or remove filters above"
-msgstr ""
+msgstr "è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–删除上é¢çš„过滤器"
msgid "To widen your search, change or remove filters."
msgstr "需è¦æ‰©å¤§æœç´¢èŒƒå›´ï¼Œè¯·æ›´æ”¹æˆ–移除过滤æ¡ä»¶ã€‚"
@@ -9969,13 +10383,13 @@ msgid "Toggle Sidebar"
msgstr "切æ¢ä¾§è¾¹æ "
msgid "Toggle comments for this file"
-msgstr ""
+msgstr "切æ¢æ­¤æ–‡ä»¶çš„评论"
msgid "Toggle commit description"
msgstr "切æ¢æ交æè¿°"
msgid "Toggle commit list"
-msgstr ""
+msgstr "切æ¢æ交列表"
msgid "Toggle discussion"
msgstr "开关讨论"
@@ -10041,10 +10455,10 @@ msgid "Trigger this manual action"
msgstr "触å‘此手动æ“作"
msgid "Trigger token:"
-msgstr ""
+msgstr "触å‘令牌:"
msgid "Trigger variables:"
-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 调用使特定的分支或标记被é‡æ–°æž„建,这些 token å¯ä»£è¡¨ä¸Žå…¶å…³è”的用户(包括该用户对项目的访问æƒé™ï¼‰"
@@ -10056,13 +10470,13 @@ msgid "Try again"
msgstr "请é‡è¯•"
msgid "Try again?"
-msgstr ""
+msgstr "é‡è¯•ï¼Ÿ"
msgid "Try all GitLab has to offer for 30 days."
msgstr "30天内体验GitLab的所有功能。"
msgid "Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now."
-msgstr ""
+msgstr "å°è¯•ä¸Žæ‚¨çš„设备通信。请将其æ’入并立å³æŒ‰ä¸‹ä¸Šçš„按钮。"
msgid "Turn on Service Desk"
msgstr "å¯ç”¨æœåŠ¡å°"
@@ -10077,7 +10491,7 @@ msgid "Type"
msgstr "类型"
msgid "URL"
-msgstr ""
+msgstr "URL"
msgid "Unable to load the diff. %{button_try_again}"
msgstr "无法加载差异。 %{button_try_again}"
@@ -10086,16 +10500,16 @@ msgid "Unable to sign you in to the group with SAML due to \"%{reason}\""
msgstr "由于\"%{reason}\"的原因,您暂时ä¸èƒ½è¿›å…¥é…置了SAML 的群组"
msgid "Unable to update this epic at this time."
-msgstr "当å‰æ— æ³•æ›´æ–°æ­¤å²è¯—故事。"
+msgstr "当å‰æ— æ³•æ›´æ–°æ­¤ epic。"
msgid "Unblock"
-msgstr ""
+msgstr "解除é”定"
msgid "Undo"
msgstr "撤消"
msgid "Unfortunately, your email message to GitLab could not be processed."
-msgstr ""
+msgstr "很é—憾,您å‘é€ç»™GitLab的电å­é‚®ä»¶æ— æ³•å¤„ç†ã€‚"
msgid "Unknown"
msgstr "未知的"
@@ -10146,7 +10560,7 @@ msgid "Unsubscribe at project level"
msgstr "在项目级别å–消订阅"
msgid "Unsubscribe from %{type}"
-msgstr ""
+msgstr "å–消订阅 %{type}"
msgid "Unverified"
msgstr "未验è¯"
@@ -10160,20 +10574,26 @@ msgstr "å³å°†å¼€å§‹"
msgid "Update"
msgstr "æ›´æ–°"
-msgid "Update failed"
+msgid "Update approvers"
msgstr ""
+msgid "Update failed"
+msgstr "更新失败"
+
msgid "Update now"
msgstr "ç«‹å³æ›´æ–°"
msgid "Update your group name, description, avatar, and visibility."
msgstr "更新您的群组å称ã€è¯´æ˜Žã€å¤´åƒä»¥åŠå¯è§æ€§ã€‚"
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "更新中"
msgid "Upgrade plan to unlock Canary Deployments feature"
-msgstr ""
+msgstr "å‡çº§åˆ°å¯ä½¿ç”¨é‡‘ä¸é›€éƒ¨ç½²åŠŸèƒ½çš„方案"
msgid "Upgrade your plan to activate Advanced Global Search."
msgstr "å‡çº§æ‚¨çš„订阅计划以å¯ç”¨é«˜çº§å…¨å±€æœç´¢ã€‚"
@@ -10194,28 +10614,28 @@ msgid "Upload <code>GoogleCodeProjectHosting.json</code> here:"
msgstr "在这里上传 <code>GoogleCodeProjectHosting.json</code>:"
msgid "Upload CSV file"
-msgstr ""
+msgstr "上传CSV文件"
msgid "Upload New File"
msgstr "上传新文件"
msgid "Upload a certificate for your domain with all intermediates"
-msgstr ""
+msgstr "通过存储介质上传您的域åè¯ä¹¦"
msgid "Upload a private key for your certificate"
-msgstr ""
+msgstr "为您的è¯ä¹¦ä¸Šä¼ ä¸€ä¸ªç§é’¥"
msgid "Upload file"
msgstr "上传文件"
msgid "Upload object map"
-msgstr ""
+msgstr "上传对象图"
msgid "UploadLink|click to upload"
msgstr "点击上传"
msgid "Upstream"
-msgstr ""
+msgstr "上游"
msgid "Upvotes"
msgstr "顶"
@@ -10248,13 +10668,13 @@ msgid "Use your global notification setting"
msgstr "使用全局通知设置"
msgid "Use your smart card to authenticate with the LDAP server."
-msgstr ""
+msgstr "使用智能å¡å¯¹LDAPæœåŠ¡å™¨è¿›è¡Œèº«ä»½éªŒè¯ã€‚"
msgid "Used by members to sign in to your group in GitLab"
msgstr "ä¾›æˆå‘˜ç™»å½•æ‚¨çš„GitLab群组"
msgid "Used to help configure your identity provider"
-msgstr ""
+msgstr "用于帮助é…置您的身份æ供者"
msgid "User Cohorts are only shown when the %{usage_ping_link_start}usage ping%{usage_ping_link_end} is enabled."
msgstr "用户世代表仅在å¯ç”¨ %{usage_ping_link_start}使用情况检测(usage ping)%{usage_ping_link_end} 时显示。"
@@ -10281,22 +10701,22 @@ msgid "UserProfile|Edit profile"
msgstr "编辑个人资料"
msgid "UserProfile|Explore public groups to find projects to contribute to."
-msgstr ""
+msgstr "æµè§ˆå…¬å…±ç¾¤ç»„以查找è¦è´¡çŒ®çš„项目。"
msgid "UserProfile|Groups"
msgstr "群组"
msgid "UserProfile|Groups are the best way to manage projects and members."
-msgstr ""
+msgstr "群组是管ç†é¡¹ç›®å’Œæˆå‘˜çš„最佳方å¼ã€‚"
msgid "UserProfile|Join or create a group to start contributing by commenting on issues or submitting merge requests!"
-msgstr ""
+msgstr "加入或创建一个群组,通过评论议题或æ交åˆå¹¶è¯·æ±‚æ¥å¼€å§‹è´¡çŒ®ï¼"
msgid "UserProfile|Most Recent Activity"
msgstr "最新活动"
msgid "UserProfile|No snippets found."
-msgstr ""
+msgstr "未找到代ç ç‰‡æ®µ"
msgid "UserProfile|Overview"
msgstr "概览"
@@ -10311,19 +10731,19 @@ msgid "UserProfile|Snippets"
msgstr "代ç ç‰‡æ®µ"
msgid "UserProfile|Snippets in GitLab can either be private, internal, or public."
-msgstr ""
+msgstr "GitLab 中的代ç ç‰‡æ®µå¯ä»¥æ˜¯ç§æœ‰çš„ã€å†…部或公开。"
msgid "UserProfile|Subscribe"
msgstr "关注"
msgid "UserProfile|This user doesn't have any personal projects"
-msgstr ""
+msgstr "用户没有任何个人项目"
msgid "UserProfile|This user has a private profile"
msgstr "此用户具有éžå…¬å¼€ä¸ªäººèµ„料设置"
msgid "UserProfile|This user hasn't contributed to any projects"
-msgstr ""
+msgstr "此用户未对任何项目åšå‡ºè´¡çŒ®"
msgid "UserProfile|View all"
msgstr "查看全部"
@@ -10332,31 +10752,31 @@ msgid "UserProfile|View user in admin area"
msgstr "在管ç†åŒºåŸŸä¸­æŸ¥çœ‹ç”¨æˆ·"
msgid "UserProfile|You can create a group for several dependent projects."
-msgstr ""
+msgstr "您å¯ä»¥ä¸ºå¤šä¸ªä¾èµ–项目创建一个群组。"
msgid "UserProfile|You haven't created any personal projects."
-msgstr ""
+msgstr "您还没有创建任何个人项目"
msgid "UserProfile|You haven't created any snippets."
-msgstr ""
+msgstr "您还没有创建任何代ç ç‰‡æ®µ"
msgid "UserProfile|Your projects can be available publicly, internally, or privately, at your choice."
-msgstr ""
+msgstr "您的项目å¯ä»¥æ ¹æ®æ‚¨çš„选择公开ã€å†…部或ç§ä¸‹æ供。"
msgid "Users"
msgstr "用户"
msgid "Users requesting access to"
-msgstr ""
+msgstr "请求访问的用户"
msgid "Validate"
-msgstr ""
+msgstr "验è¯"
msgid "Validate your GitLab CI configuration file"
-msgstr ""
+msgstr "验è¯æ‚¨çš„GitLab CIé…置文件"
msgid "Value"
-msgstr ""
+msgstr "值"
msgid "Various container registry settings."
msgstr "容器注册表相关设置。"
@@ -10365,7 +10785,7 @@ msgid "Various email settings."
msgstr "电å­é‚®ä»¶ç›¸å…³è®¾ç½®"
msgid "Various localization settings."
-msgstr ""
+msgstr "å„ç§æœ¬åœ°åŒ–设置。"
msgid "Various settings that affect GitLab performance."
msgstr "å½±å“GitLab性能相关设置。"
@@ -10374,7 +10794,7 @@ msgid "Verification information"
msgstr "验è¯ä¿¡æ¯"
msgid "Verification status"
-msgstr ""
+msgstr "验è¯çŠ¶æ€"
msgid "Verified"
msgstr "已验è¯"
@@ -10389,16 +10809,19 @@ msgid "View app"
msgstr "查看应用程åº"
msgid "View deployment"
-msgstr ""
+msgstr "查看部署"
msgid "View details: %{details_url}"
-msgstr ""
+msgstr "查看详细信æ¯: %{details_url}"
msgid "View documentation"
msgstr "查看文档"
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
-msgstr "查看å²è¯—故事列表"
+msgstr "查看 epic 列表"
msgid "View file @ "
msgstr "æµè§ˆæ–‡ä»¶ @ "
@@ -10406,6 +10829,9 @@ msgstr "æµè§ˆæ–‡ä»¶ @ "
msgid "View group labels"
msgstr "查看群组标记"
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "查看议题"
@@ -10434,7 +10860,7 @@ msgid "View the documentation"
msgstr "查看文档"
msgid "Viewing commit"
-msgstr ""
+msgstr "查看æ交"
msgid "Visibility and access controls"
msgstr "å¯è§æ€§ä¸Žè®¿é—®æŽ§åˆ¶"
@@ -10461,10 +10887,10 @@ msgid "VisibilityLevel|Unknown"
msgstr "未知"
msgid "Vulnerability Chart"
-msgstr ""
+msgstr "æ¼æ´žå›¾è¡¨"
msgid "Vulnerability List"
-msgstr ""
+msgstr "æ¼æ´žæ¸…å•"
msgid "Vulnerability|Class"
msgstr "类型"
@@ -10491,7 +10917,7 @@ msgid "Vulnerability|Project"
msgstr "项目"
msgid "Vulnerability|Report Type"
-msgstr ""
+msgstr "报告类型"
msgid "Vulnerability|Severity"
msgstr "严é‡æ€§"
@@ -10500,19 +10926,19 @@ msgid "Want to see the data? Please ask an administrator for access."
msgstr "æƒé™ä¸è¶³ã€‚如需查看相关数æ®ï¼Œè¯·å‘管ç†å‘˜ç”³è¯·æƒé™ã€‚"
msgid "We can't find an epic that matches what you are looking for."
-msgstr ""
+msgstr "我们找ä¸åˆ°ç¬¦åˆæ‚¨è¦æ±‚çš„epic。"
msgid "We can't find an issue that matches what you are looking for."
-msgstr ""
+msgstr "我们找ä¸åˆ°ä¸Žæ‚¨è¦æŸ¥æ‰¾çš„内容相匹é…的议题。"
msgid "We could not determine the path to remove the epic"
-msgstr ""
+msgstr "我们无法确定删除epic的路径"
msgid "We could not determine the path to remove the issue"
-msgstr ""
+msgstr "我们无法确定删除议题的路径"
msgid "We couldn't find any results matching"
-msgstr ""
+msgstr "我们找ä¸åˆ°ä»»ä½•åŒ¹é…的结果"
msgid "We detected potential spam in the %{humanized_resource_name}. Please solve the reCAPTCHA to proceed."
msgstr "我们在%{humanized_resource_name}检测到潜在滥用行为。请输入此reCAPTCHA验è¯ç å¹¶ç»§ç»­ã€‚"
@@ -10521,16 +10947,16 @@ msgid "We don't have enough data to show this stage."
msgstr "该阶段的数æ®ä¸è¶³ï¼Œæ— æ³•æ˜¾ç¤ºã€‚"
msgid "We heard back from your U2F device. You have been authenticated."
-msgstr ""
+msgstr "我们从您的U2F设备收到了回å¤ã€‚您已通过身份验è¯ã€‚"
msgid "We want to be sure it is you, please confirm you are not a robot."
-msgstr "我们è¦ç¡®å®šä½ æ˜¯ä¸æ˜¯æœºå™¨äººã€‚"
+msgstr "我们è¦ç¡®å®šæ‚¨æ˜¯ä¸æ˜¯æœºå™¨äººã€‚"
msgid "Web IDE"
msgstr "Web IDE"
msgid "Web Terminal"
-msgstr ""
+msgstr "Web终端"
msgid "Web terminal"
msgstr "Web终端"
@@ -10557,7 +10983,7 @@ msgid "When leaving the URL blank, classification labels can still be specified
msgstr "å°†URLä¿ç•™ä¸ºç©ºç™½æ—¶ï¼Œä»å¯æŒ‡å®šåˆ†ç±»æ ‡ç­¾ï¼Œè€Œæ— éœ€ç¦ç”¨è·¨é¡¹ç›®åŠŸèƒ½æˆ–执行外部授æƒæ£€æŸ¥ã€‚"
msgid "When:"
-msgstr ""
+msgstr "当:"
msgid "Who can see this group?"
msgstr "哪些人å¯ä»¥çœ‹åˆ°è¿™ä¸ªç¾¤ç»„?"
@@ -10701,7 +11127,7 @@ msgid "Wiki|Wiki Pages"
msgstr "Wiki 页é¢"
msgid "Will deploy to"
-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 "通过贡献分æžï¼Œæ‚¨å¯ä»¥ä»Žæ€»ä½“上了解您的组织åŠå…¶æˆå‘˜çš„议题〠åˆå¹¶è¯·æ±‚和推é€æ´»åŠ¨çš„情况。"
@@ -10709,11 +11135,14 @@ msgstr "通过贡献分æžï¼Œæ‚¨å¯ä»¥ä»Žæ€»ä½“上了解您的组织åŠå…¶æˆå‘˜
msgid "Withdraw Access Request"
msgstr "å–消æƒé™ç”³è¯·"
-msgid "Write a comment or drag your files here…"
+msgid "Write"
msgstr ""
+msgid "Write a comment or drag your files here…"
+msgstr "在此写评论或拖动您的文件到这里…"
+
msgid "Write milestone description..."
-msgstr ""
+msgstr "写入里程碑æè¿°..."
msgid "Yes"
msgstr "是"
@@ -10728,7 +11157,7 @@ msgid "Yesterday"
msgstr "昨天"
msgid "You"
-msgstr ""
+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 "您是一å管ç†å‘˜ï¼Œè¿™æ„味ç€æŽˆäºˆå¯¹ <strong>%{client_name}</strong> 访问æƒé™å°†å…许他们作为管ç†å‘˜ä¸ŽGitLab进行交互。请谨慎æ“作。"
@@ -10749,7 +11178,7 @@ msgid "You are on a read-only GitLab instance."
msgstr "当å‰æ­£åœ¨è®¿é—®åªè¯» GitLab 实例。"
msgid "You are receiving this message because you are a GitLab administrator for %{url}."
-msgstr ""
+msgstr "您收到此消æ¯æ˜¯å› ä¸ºæ‚¨æ˜¯ %{url} çš„GitLab管ç†å‘˜ã€‚"
msgid "You can %{linkStart}view the blob%{linkEnd} instead."
msgstr "您å¯ä»¥ %{linkStart}查看BLOB%{linkEnd} 代替。"
@@ -10776,7 +11205,7 @@ msgid "You can only edit files when you are on a branch"
msgstr "åªèƒ½åœ¨åˆ†æ”¯ä¸Šç¼–辑文件"
msgid "You can only merge once the items above are resolved"
-msgstr ""
+msgstr "åªæœ‰è§£å†³äº†ä¸Šè¿°é¡¹ç›®åŽæ‰èƒ½åˆå¹¶"
msgid "You can 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}"
@@ -10797,28 +11226,31 @@ msgid "You do not have any subscriptions yet"
msgstr "您当å‰å°šæœªè®¢é˜…任何计划"
msgid "You do not have permission to run the Web Terminal. Please contact a project administrator."
-msgstr ""
+msgstr "您无æƒè¿è¡ŒWeb终端。请è”系项目管ç†å‘˜ã€‚"
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 "你没有任何应用程åº"
+msgstr "您没有任何应用程åº"
msgid "You don't have any authorized applications"
msgstr "您没有任何授æƒçš„应用"
msgid "You don't have any deployments right now."
-msgstr ""
+msgstr "您现在没有任何部署。"
msgid "You have no permissions"
msgstr "没有æƒé™"
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "您已达到项目数é‡é™åˆ¶"
msgid "You may also add variables that are made available to the running application by prepending the variable key with <code>K8S_SECRET_</code>."
-msgstr ""
+msgstr "您还å¯ä»¥é€šè¿‡åœ¨å˜é‡é”®å‰é¢åŠ ä¸Š<code>K8S_SECRET_</code>æ¥æ·»åŠ å¯ç”¨äºŽæ­£åœ¨è¿è¡Œçš„应用程åºçš„å˜é‡ã€‚"
msgid "You must accept our Terms of Service and privacy policy in order to register an account"
msgstr "您必须接å—我们的æœåŠ¡æ¡æ¬¾å’Œéšç§æ”¿ç­–æ‰èƒ½æ³¨å†Œå¸æˆ·"
@@ -10830,7 +11262,7 @@ msgid "You need a different license to enable FileLocks feature"
msgstr "需è¦ä½¿ç”¨ä¸Žå½“å‰ä¸åŒçš„许å¯(license) æ‰èƒ½å¯ç”¨FileLocks功能"
msgid "You need a different license to enable Geo replication."
-msgstr ""
+msgstr "您需è¦ä¸åŒçš„许å¯è¯æ‰èƒ½å¯ç”¨ Geo å¤åˆ¶."
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"
@@ -10839,13 +11271,13 @@ msgid "You need permission."
msgstr "需è¦ç›¸å…³çš„æƒé™ã€‚"
msgid "You need to register a two-factor authentication app before you can set up a U2F device."
-msgstr ""
+msgstr "在设置U2F设备之å‰ï¼Œæ‚¨éœ€è¦æ³¨å†ŒåŒå› ç´ èº«ä»½éªŒè¯åº”用程åºã€‚"
msgid "You will lose all changes you've made to this file. This action cannot be undone."
-msgstr ""
+msgstr "您将丢失对此文件所åšçš„所有更改。此æ“作无法撤消。"
msgid "You will lose all the unstaged changes you've made in this project. This action cannot be undone."
-msgstr ""
+msgstr "您将丢失在此项目中所åšçš„所有未暂存的修改,此æ“作无法撤消。"
msgid "You will not get any notifications via email"
msgstr "ä¸ä¼šæ”¶åˆ°ä»»ä½•é€šçŸ¥é‚®ä»¶"
@@ -10874,6 +11306,9 @@ msgstr "在您的个人资料中添加SSH密钥之å‰ï¼Œæ‚¨ä¸èƒ½é€šè¿‡SSHæ¥æ‹‰
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "需è¦ä½¿ç”¨ä¸åŒçš„分支æ‰èƒ½è¿›è¡Œæœ‰æ•ˆçš„比较。"
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr "您收到此电å­é‚®ä»¶ï¼Œæ˜¯å› ä¸º%{reason}。"
@@ -10887,7 +11322,7 @@ msgid "YouTube"
msgstr "YouTube"
msgid "Your Conversational Development Index gives an overview of how you are using GitLab from a feature perspective. View how you compare with other organizations, discover features you are not using, and learn best practices through blog posts and white papers."
-msgstr ""
+msgstr "您的会è¯å¼€å‘指数概述了从功能角度使用GitLabçš„æ–¹å¼ã€‚查看您与其他组织的比较,å‘现您未使用的功能,并通过åšå®¢æ–‡ç« å’Œç™½çš®ä¹¦äº†è§£æœ€ä½³å®žè·µã€‚"
msgid "Your Groups"
msgstr "您的群组"
@@ -10899,22 +11334,22 @@ msgid "Your Projects (default)"
msgstr "您的项目 (默认值)"
msgid "Your Projects' Activity"
-msgstr "您的项目活动"
+msgstr "您的项目动æ€"
msgid "Your Todos"
msgstr "您的待办事项"
msgid "Your U2F device needs to be set up. Plug it in (if not already) and click the button on the left."
-msgstr ""
+msgstr "您的 U2F 设备需è¦è®¾ç½®ã€‚请将将其æ’入,并点击左边的按钮。"
msgid "Your applications (%{size})"
-msgstr "你的应用程åº(%{size})"
+msgstr "您的应用程åº(%{size})"
msgid "Your authorized applications"
msgstr "您已授æƒçš„应用"
msgid "Your browser doesn't support U2F. Please use Google Chrome desktop (version 41 or newer)."
-msgstr ""
+msgstr "您的æµè§ˆå™¨ä¸æ”¯æŒU2F。请使用Google Chromeæ¡Œé¢ç‰ˆï¼ˆ41或更高版本)。"
msgid "Your changes can be committed to %{branch_name} because a merge request is open."
msgstr "åˆå¹¶è¯·æ±‚已开å¯ï¼Œå¯ä»¥æ交å˜æ›´åˆ°%{branch_name}。"
@@ -10929,22 +11364,22 @@ msgid "Your comment will not be visible to the public."
msgstr "您的评论将ä¸ä¼šå…¬å¼€æ˜¾ç¤ºã€‚"
msgid "Your device was successfully set up! Give it a name and register it with the GitLab server."
-msgstr ""
+msgstr "您的设备已æˆåŠŸè®¾ç½®ï¼è¯·ç»™å®ƒå‘½å并将其注册到GitLabæœåŠ¡å™¨ã€‚"
msgid "Your groups"
msgstr "您的群组"
msgid "Your issues are being imported. Once finished, you'll get a confirmation email."
-msgstr ""
+msgstr "您的议题正在导入。完æˆåŽï¼Œæ‚¨å°†æ”¶åˆ°ä¸€å°ç¡®è®¤ç”µå­é‚®ä»¶ã€‚"
msgid "Your issues will be imported in the background. Once finished, you'll get a confirmation email."
-msgstr ""
+msgstr "您的议题将在åŽå°å¯¼å…¥ã€‚完æˆåŽï¼Œæ‚¨å°†æ”¶åˆ°ä¸€å°ç¡®è®¤ç”µå­é‚®ä»¶ã€‚"
msgid "Your name"
msgstr "您的åå­—"
msgid "Your project limit is %{limit} projects! Please contact your administrator to increase it"
-msgstr ""
+msgstr "您的被é™åˆ¶ä¸ºæœ€å¤§ %{limit} 个项目ï¼è¯·ä¸Žæ‚¨çš„管ç†å‘˜è”系以增加它"
msgid "Your projects"
msgstr "您的项目"
@@ -10956,7 +11391,7 @@ msgid "ago"
msgstr "å‰"
msgid "allowed to fail"
-msgstr ""
+msgstr "å…许失败"
msgid "among other things"
msgstr "åŠå…¶ä»–功能"
@@ -10965,6 +11400,9 @@ msgid "assign yourself"
msgstr "分é…给自己"
msgid "attach a new file"
+msgstr "附加一个文件"
+
+msgid "authored"
msgstr ""
msgid "branch name"
@@ -11046,6 +11484,9 @@ msgstr "代ç è´¨é‡"
msgid "ciReport|Confidence"
msgstr "置信水平"
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr "容器安全扫æ"
@@ -11056,7 +11497,7 @@ msgid "ciReport|DAST"
msgstr "DAST"
msgid "ciReport|Dependency Scanning"
-msgstr ""
+msgstr "ä¾èµ–扫æ"
msgid "ciReport|Dependency Scanning detects known vulnerabilities in your source code's dependencies."
msgstr "ä¾èµ–项扫æå¯ä»¥æ£€æµ‹æºä»£ç ä¾èµ–项中已知的æ¼æ´žã€‚"
@@ -11074,10 +11515,10 @@ msgid "ciReport|Dismissed by"
msgstr "忽略æ“作æ¥è‡ª"
msgid "ciReport|Download and apply the patch to fix this vulnerability."
-msgstr ""
+msgstr "下载并应用此修补程åºä»¥ä¿®å¤æ­¤æ¼æ´žã€‚"
msgid "ciReport|Download patch"
-msgstr ""
+msgstr "下载补ä¸"
msgid "ciReport|Dynamic Application Security Testing (DAST) detects known vulnerabilities in your web application."
msgstr "动æ€åº”用程åºå®‰å…¨æµ‹è¯•ï¼ˆDAST)å¯æ£€æµ‹Web应用程åºä¸­çš„已知æ¼æ´žã€‚"
@@ -11138,9 +11579,6 @@ msgstr "性能指标无å˜åŒ–"
msgid "ciReport|Performance metrics"
msgstr "性能指标"
-msgid "ciReport|Revert dismissal"
-msgstr "å–消忽略"
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11180,6 +11618,9 @@ msgstr "加载ä¾èµ–项扫æ报告时出错"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "å–消忽略æ“作时å‘生错误。请å†è¯•ä¸€æ¬¡ã€‚"
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "å°† %{name} 从 %{version} å‡çº§åˆ° %{fixed}。"
@@ -11197,7 +11638,7 @@ msgid "command line instructions"
msgstr "命令行指å—"
msgid "commented on %{link_to_project}"
-msgstr ""
+msgstr "评论 %{link_to_project}"
msgid "confidentiality|You are going to turn off the confidentiality. This means <strong>everyone</strong> will be able to see and leave a comment on this issue."
msgstr "å³å°†å…³é—­ç§å¯†æ€§ã€‚这将使得 <strong>所有用户</strong>都å¯ä»¥æŸ¥çœ‹å¹¶ä¸”评论当å‰è®®é¢˜ã€‚"
@@ -11219,7 +11660,7 @@ msgid_plural "days"
msgstr[0] "天"
msgid "deleted"
-msgstr ""
+msgstr "已删除"
msgid "deploy token"
msgstr "部署令牌"
@@ -11229,7 +11670,7 @@ msgstr "å·²ç¦ç”¨"
msgid "discussion resolved"
msgid_plural "discussions resolved"
-msgstr[0] ""
+msgstr[0] "讨论已解决"
msgid "done"
msgstr "完æˆ"
@@ -11242,13 +11683,13 @@ msgid "enabled"
msgstr "å·²å¯ç”¨"
msgid "epic"
-msgstr ""
+msgstr "epic"
msgid "error"
-msgstr ""
+msgstr "错误"
msgid "error code:"
-msgstr ""
+msgstr "错误代ç ï¼š"
msgid "estimateCommand|%{slash_command} will update the estimated time with the latest command."
msgstr "最åŽä¸€æ¬¡%{slash_command} 命令将更新预计时间。"
@@ -11260,7 +11701,7 @@ msgid "from"
msgstr "æ¥è‡ª"
msgid "group"
-msgstr ""
+msgstr "群组"
msgid "help"
msgstr "帮助"
@@ -11268,14 +11709,11 @@ msgstr "帮助"
msgid "here"
msgstr "此处"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://your-bitbucket-server"
msgid "image diff"
-msgstr ""
+msgstr "图åƒå·®å¼‚"
msgid "import flow"
msgstr "导入æµç¨‹"
@@ -11284,13 +11722,13 @@ msgid "importing"
msgstr "导入中"
msgid "in group %{link_to_group}"
-msgstr ""
+msgstr "在 %{link_to_group} 群组"
msgid "in project %{link_to_project}"
-msgstr ""
+msgstr "在 %{link_to_project} 项目"
msgid "index"
-msgstr ""
+msgstr "索引"
msgid "instance completed"
msgid_plural "instances completed"
@@ -11306,25 +11744,25 @@ msgid "is not a valid X509 certificate."
msgstr "ä¸æ˜¯æœ‰æ•ˆçš„X509è¯ä¹¦ã€‚"
msgid "is out of the hierarchy of the Group owning the template"
-msgstr ""
+msgstr "ä¸å±žäºŽæ‹¥æœ‰æ¨¡æ¿çš„群组层次结构"
msgid "issue"
-msgstr ""
+msgstr "议题"
msgid "issue boards"
msgstr "议题看æ¿"
msgid "it is stored externally"
-msgstr ""
+msgstr "它是外部存储的"
msgid "it is stored in LFS"
-msgstr ""
+msgstr "它存储在LFS中"
msgid "it is too large"
-msgstr ""
+msgstr "它太大了"
msgid "latest"
-msgstr ""
+msgstr "最新"
msgid "latest deployment"
msgstr "最新部署"
@@ -11339,26 +11777,26 @@ msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr "被 %{path_lock_user_name} 在 %{created_at} é”定"
msgid "manual"
-msgstr ""
+msgstr "手动"
msgid "merge request"
msgid_plural "merge requests"
msgstr[0] "åˆå¹¶è¯·æ±‚"
msgid "missing"
-msgstr ""
+msgstr "丢失"
msgid "mrWidgetCommitsAdded|%{commitCount} and %{mergeCommitCount} will be added to %{targetBranch}."
-msgstr ""
+msgstr "%{commitCount} 和 %{mergeCommitCount} 将被添加到 %{targetBranch}。"
msgid "mrWidgetCommitsAdded|1 merge commit"
-msgstr ""
+msgstr "1个åˆå¹¶æ交"
msgid "mrWidget| Please restore it or use a different %{missingBranchName} branch"
msgstr "请æ¢å¤æ­¤åˆ†æ”¯æˆ–使用其他的 %{missingBranchName} 分支"
msgid "mrWidget|%{link_start}Learn more about resolving conflicts%{link_end}"
-msgstr ""
+msgstr "%{link_start}了解更多关于解决冲çª%{link_end}"
msgid "mrWidget|%{metricsLinkStart} Memory %{metricsLinkEnd} usage %{emphasisStart} decreased %{emphasisEnd} from %{memoryFrom}MB to %{memoryTo}MB"
msgstr "%{metricsLinkStart} 内存 %{metricsLinkEnd} å ç”¨ %{emphasisStart} ä¸‹é™ %{emphasisEnd},从 %{memoryFrom}MB 到 %{memoryTo}MB"
@@ -11376,10 +11814,10 @@ msgid "mrWidget|Allows commits from members who can merge to the target branch"
msgstr "å…许具有åˆå¹¶åˆ°ç›®æ ‡åˆ†æ”¯æƒé™çš„æˆå‘˜æ交"
msgid "mrWidget|An error occurred while removing your approval."
-msgstr ""
+msgstr "删除您的核准时å‘生错误。"
msgid "mrWidget|An error occurred while retrieving approval data for this merge request."
-msgstr ""
+msgstr "检索此åˆå¹¶è¯·æ±‚的核准数æ®æ—¶å‘生错误。"
msgid "mrWidget|An error occurred while submitting your approval."
msgstr "æ交批准时å‘生错误。"
@@ -11387,6 +11825,9 @@ msgstr "æ交批准时å‘生错误。"
msgid "mrWidget|Approve"
msgstr "批准"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "批准人:"
@@ -11418,7 +11859,7 @@ msgid "mrWidget|Create an issue to resolve them later"
msgstr "创建议题以便åŽç»­å¤„ç†"
msgid "mrWidget|Delete source branch"
-msgstr ""
+msgstr "删除æºåˆ†æ”¯"
msgid "mrWidget|Deployment statistics are not available currently"
msgstr "部署统计信æ¯å½“å‰ä¸å¯ç”¨"
@@ -11459,6 +11900,9 @@ msgstr "本地åˆå¹¶"
msgid "mrWidget|Merge request approved"
msgstr "åˆå¹¶è¯·æ±‚已批准"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr "åˆå¹¶è¯·æ±‚已被批准; ä»å¯ä»¥æ·»åŠ é¢å¤–批准"
@@ -11518,6 +11962,9 @@ msgstr "还原"
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr "通过新的åˆå¹¶è¯·æ±‚中还原此åˆå¹¶è¯·æ±‚"
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "设置:"
@@ -11537,19 +11984,19 @@ msgid "mrWidget|The source branch HEAD has recently changed. Please reload the p
msgstr "æºåˆ†æ”¯HEAD最近已更改。请在é‡æ–°åˆå¹¶ä¹‹å‰é‡æ–°åŠ è½½é¡µé¢å¹¶æŸ¥çœ‹æ›´æ”¹"
msgid "mrWidget|The source branch has been deleted"
-msgstr ""
+msgstr "æºåˆ†æ”¯å·²è¢«åˆ é™¤"
msgid "mrWidget|The source branch is %{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd} the target branch"
msgstr "æºåˆ†æ”¯æ˜¯æ¯”目标分支%{commitsBehindLinkStart}%{commitsBehind}%{commitsBehindLinkEnd}"
msgid "mrWidget|The source branch is being deleted"
-msgstr ""
+msgstr "æºåˆ†æ”¯æ­£åœ¨åˆ é™¤"
msgid "mrWidget|The source branch will be deleted"
-msgstr ""
+msgstr "æºåˆ†æ”¯å°†è¢«åˆ é™¤"
msgid "mrWidget|The source branch will not be deleted"
-msgstr ""
+msgstr "æºåˆ†æ”¯å°†ä¸ä¼šè¢«åˆ é™¤"
msgid "mrWidget|There are merge conflicts"
msgstr "存在åˆå¹¶å†²çª"
@@ -11558,7 +12005,7 @@ msgid "mrWidget|There are unresolved discussions. Please resolve these discussio
msgstr "存在尚未解决的讨论。请先解决这些讨论。"
msgid "mrWidget|This feature merges changes from the target branch to the source branch. You cannot use this feature since the source branch is protected."
-msgstr ""
+msgstr "此功能是从目标分支åˆå¹¶åˆ°æºåˆ†æ”¯çš„更改。您无法使用此功能,因为æºåˆ†æ”¯å—到ä¿æŠ¤ã€‚"
msgid "mrWidget|This merge request failed to be merged automatically"
msgstr "该åˆå¹¶è¯·æ±‚未能自动åˆå¹¶"
@@ -11573,7 +12020,7 @@ msgid "mrWidget|You are not allowed to edit this project directly. Please fork t
msgstr "ä¸å…许直接编辑此项目。请派生(fork)åŽè¿›è¡Œæ›´æ”¹ã€‚"
msgid "mrWidget|You can delete the source branch now"
-msgstr ""
+msgstr "您现在å¯ä»¥åˆ é™¤æºåˆ†æ”¯"
msgid "mrWidget|You can merge this merge request manually using the"
msgstr "æ­¤åˆå¹¶è¯·æ±‚å¯ä»¥æ‰‹åŠ¨åˆå¹¶ï¼Œè¯·ä½¿ç”¨ä»¥ä¸‹"
@@ -11597,19 +12044,19 @@ msgid "new merge request"
msgstr "新建åˆå¹¶è¯·æ±‚"
msgid "none"
-msgstr ""
+msgstr "æ— "
msgid "notification emails"
msgstr "通知邮件"
msgid "nounSeries|%{firstItem} and %{lastItem}"
-msgstr ""
+msgstr "%{firstItem} 和 %{lastItem}"
msgid "nounSeries|%{item}, %{nextItem}"
-msgstr ""
+msgstr "%{item}, %{nextItem}"
msgid "nounSeries|%{item}, and %{lastItem}"
-msgstr ""
+msgstr "%{item},和 %{lastItem}"
msgid "or"
msgstr "或"
@@ -11629,7 +12076,7 @@ msgid "personal access token"
msgstr "个人访问令牌"
msgid "private"
-msgstr ""
+msgstr "ç§æœ‰"
msgid "private key does not match certificate."
msgstr "ç§é’¥ä¸Žè¯ä¹¦ä¸åŒ¹é…。"
@@ -11639,10 +12086,10 @@ msgid_plural "projects"
msgstr[0] "项目"
msgid "quick actions"
-msgstr ""
+msgstr "å¿«æ·æ“作"
msgid "register"
-msgstr ""
+msgstr "注册"
msgid "remaining"
msgstr "剩余"
@@ -11657,29 +12104,29 @@ msgid "remove weight"
msgstr "移除æƒé‡"
msgid "rendered diff"
-msgstr ""
+msgstr "渲染差异"
msgid "reply"
msgid_plural "replies"
msgstr[0] "æ¡å›žå¤"
msgid "score"
-msgstr ""
+msgstr "分"
msgid "should be higher than %{access} inherited membership from group %{group_name}"
-msgstr ""
+msgstr "应该从高于%{group_name} 群组继承æˆå‘˜èº«ä»½%{access}"
msgid "show less"
-msgstr ""
+msgstr "显示较少"
msgid "sign in"
-msgstr ""
+msgstr "登录"
msgid "source"
msgstr "æº"
msgid "source diff"
-msgstr ""
+msgstr "æºå·®å¼‚"
msgid "spendCommand|%{slash_command} will update the sum of the time spent."
msgstr "%{slash_command} 将会更新消耗的总时长。"
@@ -11688,13 +12135,13 @@ msgid "started"
msgstr "已开始"
msgid "stuck"
-msgstr ""
+msgstr "å¡ä½"
msgid "syntax is correct"
-msgstr ""
+msgstr "语法是正确的"
msgid "syntax is incorrect"
-msgstr ""
+msgstr "语法ä¸æ­£ç¡®"
msgid "this document"
msgstr "此文档"
@@ -11703,10 +12150,10 @@ msgid "to help your contributors communicate effectively!"
msgstr "帮助您的贡献者进行有效沟通ï¼"
msgid "triggered"
-msgstr ""
+msgstr "已触å‘"
msgid "updated"
-msgstr ""
+msgstr "已更新"
msgid "username"
msgstr "用户å"
@@ -11715,13 +12162,13 @@ msgid "uses Kubernetes clusters to deploy your code!"
msgstr "使用 Kubernetes 集群æ¥éƒ¨ç½²ä»£ç ï¼"
msgid "verify ownership"
-msgstr ""
+msgstr "验è¯æ‰€æœ‰æƒ"
msgid "view it on GitLab"
msgstr "使用GitLab查看"
msgid "view the blob"
-msgstr ""
+msgstr "查看blob"
msgid "with %{additions} additions, %{deletions} deletions."
msgstr "å…± %{additions} æ¡æ–°å¢ž, %{deletions} æ¡åˆ é™¤."
@@ -11731,5 +12178,5 @@ msgid_plural "within %d minutes "
msgstr[0] "在 %d 分钟内 "
msgid "yaml invalid"
-msgstr ""
+msgstr "yaml无效"
diff --git a/locale/zh_HK/gitlab.po b/locale/zh_HK/gitlab.po
index a6dfd5f6139..7df18cc4d70 100644
--- a/locale/zh_HK/gitlab.po
+++ b/locale/zh_HK/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-HK\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 08:13\n"
+"PO-Revision-Date: 2019-03-06 15:52\n"
msgid " Status"
msgstr ""
@@ -41,6 +41,10 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr ""
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] " %d 次æ交"
@@ -111,12 +115,25 @@ msgstr ""
msgid "%{count} %{alerts}"
msgstr ""
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr ""
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] ""
@@ -131,15 +148,15 @@ msgstr ""
msgid "%{firstLabel} +%{labelCount} more"
msgstr ""
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{issuableType} will be removed! Are you sure?"
msgstr ""
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -207,6 +224,9 @@ msgstr ""
msgid "+ %{moreCount} more"
msgstr ""
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -227,6 +247,10 @@ msgid "1 %{type} modification"
msgid_plural "%{count} %{type} modifications"
msgstr[0] "%{count}%{type} 個變更"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] ""
@@ -320,6 +344,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr ""
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "相關æŒçºŒé›†æˆçš„圖åƒé›†åˆ"
@@ -335,6 +374,9 @@ msgstr ""
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr ""
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A project is where you house your files (repository), plan your work (issues), and publish your documentation (wiki), %{among_other_things_link}."
msgstr ""
@@ -419,24 +461,51 @@ msgstr ""
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr ""
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr ""
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr ""
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr ""
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr ""
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr ""
@@ -605,6 +674,9 @@ msgstr ""
msgid "Advanced settings"
msgstr ""
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] ""
@@ -615,6 +687,9 @@ msgstr ""
msgid "All"
msgstr "全部"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr ""
@@ -684,7 +759,7 @@ msgstr ""
msgid "An error has occurred"
msgstr ""
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -696,6 +771,15 @@ msgstr ""
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr ""
@@ -708,6 +792,9 @@ msgstr ""
msgid "An error occurred while adding approver"
msgstr ""
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr ""
@@ -744,6 +831,9 @@ msgstr ""
msgid "An error occurred while fetching the pipeline."
msgstr ""
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr ""
@@ -801,12 +891,18 @@ msgstr ""
msgid "An error occurred while saving assignees"
msgstr ""
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr ""
msgid "An error occurred while unsubscribing to notifications."
msgstr ""
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr ""
@@ -897,6 +993,40 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -939,9 +1069,15 @@ msgstr ""
msgid "Are you sure you want to remove %{group_name}?"
msgstr ""
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1026,6 +1162,9 @@ msgstr ""
msgid "Assignee(s)"
msgstr ""
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1041,9 +1180,6 @@ msgstr ""
msgid "August"
msgstr ""
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr ""
@@ -1602,6 +1738,9 @@ msgstr ""
msgid "Cannot modify managed Kubernetes cluster"
msgstr ""
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1638,6 +1777,9 @@ msgstr "還原"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr ""
@@ -1650,6 +1792,9 @@ msgstr "統計圖"
msgid "Chat"
msgstr ""
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr ""
@@ -1716,9 +1861,6 @@ 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 ""
@@ -1878,6 +2020,9 @@ msgstr ""
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1893,9 +2038,6 @@ msgstr ""
msgid "Closed"
msgstr ""
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr ""
@@ -1941,10 +2083,10 @@ msgstr ""
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
+msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr ""
-msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
msgstr ""
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
@@ -1983,6 +2125,9 @@ msgstr ""
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr ""
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2079,7 +2224,7 @@ 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."
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
msgstr ""
msgid "ClusterIntegration|Ingress"
@@ -2094,9 +2239,6 @@ msgstr ""
msgid "ClusterIntegration|Install"
msgstr ""
-msgid "ClusterIntegration|Install Prometheus"
-msgstr ""
-
msgid "ClusterIntegration|Installed"
msgstr ""
@@ -2142,9 +2284,6 @@ msgstr ""
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr ""
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr ""
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr ""
@@ -2382,15 +2521,27 @@ msgstr ""
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr ""
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr ""
msgid "Collapse"
msgstr ""
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr ""
@@ -2689,6 +2840,9 @@ msgstr ""
msgid "Copy ID to clipboard"
msgstr ""
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr ""
@@ -3176,6 +3330,9 @@ msgstr ""
msgid "Description:"
msgstr ""
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr ""
@@ -3254,6 +3411,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr ""
@@ -3425,6 +3585,9 @@ msgstr ""
msgid "Enable group Runners"
msgstr ""
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr ""
@@ -3461,6 +3624,9 @@ msgstr ""
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3479,9 +3645,6 @@ msgstr ""
msgid "Enter the merge request title"
msgstr ""
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3506,6 +3669,12 @@ msgstr ""
msgid "Environments|An error occurred while making the request."
msgstr ""
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr ""
@@ -3557,15 +3726,33 @@ msgstr ""
msgid "Environments|Pod logs from"
msgstr ""
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr ""
@@ -3578,6 +3765,18 @@ msgstr ""
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr ""
@@ -3629,6 +3828,9 @@ msgstr ""
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr ""
@@ -3698,6 +3900,33 @@ msgstr ""
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3776,6 +4005,9 @@ msgstr ""
msgid "Expand all"
msgstr ""
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr ""
@@ -3863,7 +4095,7 @@ msgstr ""
msgid "Failed to load emoji list."
msgstr ""
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3929,9 +4161,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr ""
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr ""
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr ""
@@ -3989,9 +4218,6 @@ msgstr ""
msgid "FeatureFlags|New Feature Flag"
msgstr ""
-msgid "FeatureFlags|Save changes"
-msgstr ""
-
msgid "FeatureFlags|Status"
msgstr ""
@@ -4077,9 +4303,6 @@ msgstr ""
msgid "Filter..."
msgstr ""
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "按路徑查找"
@@ -4194,7 +4417,7 @@ msgstr ""
msgid "Free Trial of GitLab.com Gold"
msgstr ""
-msgid "From %{provider_title}"
+msgid "From %{providerTitle}"
msgstr ""
msgid "From Bitbucket"
@@ -4224,9 +4447,15 @@ msgstr ""
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr ""
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr ""
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr ""
@@ -4659,15 +4888,24 @@ msgstr ""
msgid "Go Back"
msgstr ""
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr ""
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr ""
msgid "Go to %{link_to_google_takeout}."
msgstr ""
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr ""
@@ -4725,6 +4963,9 @@ msgstr ""
msgid "Group maintainers can register group runners in the %{link}"
msgstr ""
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr ""
@@ -5092,9 +5333,24 @@ msgstr "導入存儲庫"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr ""
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr ""
@@ -5146,6 +5402,12 @@ msgstr ""
msgid "Input your repository URL"
msgstr ""
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5198,6 +5460,9 @@ msgstr "週期分æžç°¡ä»‹"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5216,6 +5481,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr ""
@@ -5258,7 +5526,7 @@ msgstr ""
msgid "Issues closed"
msgstr ""
-msgid "Issues, merge requests, pushes and comments."
+msgid "Issues, merge requests, pushes, and comments."
msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
@@ -5447,6 +5715,9 @@ msgstr ""
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr ""
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr ""
@@ -5529,6 +5800,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr ""
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "了解更多"
@@ -5701,6 +5975,15 @@ msgstr ""
msgid "Logs"
msgstr ""
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 ""
@@ -5746,6 +6029,9 @@ msgstr ""
msgid "Manifest file import"
msgstr ""
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr ""
@@ -5773,36 +6059,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a link"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a table"
-msgstr ""
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr ""
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr ""
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr ""
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr ""
-
-msgid "MarkdownToolbar|Insert code"
-msgstr ""
-
msgid "Maven Metadata"
msgstr ""
@@ -5857,6 +6113,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr ""
@@ -6007,9 +6266,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr ""
-msgid "Metrics|System"
-msgstr ""
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr ""
@@ -6154,6 +6410,9 @@ msgstr ""
msgid "More information is available|here"
msgstr "幫助文檔"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr ""
@@ -6230,15 +6489,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "創建æµæ°´ç·šè¨ˆåŠƒ"
msgid "New Snippet"
msgstr ""
-msgid "New Snippets"
-msgstr ""
-
msgid "New branch"
msgstr "新增分支"
@@ -6299,9 +6558,15 @@ msgstr ""
msgid "No"
msgstr ""
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr ""
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6329,13 +6594,16 @@ msgstr ""
msgid "No credit card required."
msgstr ""
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr ""
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6431,6 +6699,9 @@ msgstr "數據ä¸è¶³"
msgid "Not now"
msgstr ""
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr ""
@@ -6633,6 +6904,9 @@ msgstr ""
msgid "Operations Dashboard"
msgstr ""
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr ""
@@ -6642,6 +6916,9 @@ msgstr ""
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr ""
@@ -6834,6 +7111,12 @@ msgstr "變é‡"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "自定義"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "æµæ°´ç·š"
@@ -6849,6 +7132,9 @@ msgstr ""
msgid "Pipelines for last year"
msgstr ""
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr ""
@@ -6969,9 +7255,21 @@ msgstr ""
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr ""
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr ""
@@ -6981,9 +7279,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr ""
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr ""
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr ""
@@ -7008,6 +7315,9 @@ msgstr ""
msgid "Preferences|Navigation theme"
msgstr ""
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr ""
@@ -7206,9 +7516,6 @@ msgstr ""
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr ""
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7308,6 +7615,9 @@ msgstr ""
msgid "Project"
msgstr ""
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr ""
@@ -7350,6 +7660,9 @@ msgstr "項目導出éˆæŽ¥å·²éŽæœŸã€‚請從項目設置中é‡æ–°ç”Ÿæˆé …目導
msgid "Project export started. A download link will be sent by email."
msgstr "項目導出已開始。下載éˆæŽ¥å°‡é€šéŽé›»å­éƒµä»¶ç™¼é€ã€‚"
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7449,6 +7762,9 @@ msgstr ""
msgid "Projects"
msgstr ""
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr ""
@@ -7807,6 +8123,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr ""
@@ -7942,6 +8264,17 @@ msgstr ""
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr ""
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+
msgid "Resend invite"
msgstr ""
@@ -8099,6 +8432,9 @@ msgstr ""
msgid "Running"
msgstr ""
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr ""
@@ -8129,6 +8465,9 @@ msgstr ""
msgid "SSL Verification"
msgstr ""
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr ""
@@ -8162,6 +8501,9 @@ msgstr ""
msgid "Schedules"
msgstr ""
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "æµæ°´ç·šè¨ˆåŠƒ"
@@ -8222,6 +8564,9 @@ msgstr ""
msgid "Search users"
msgstr ""
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr ""
@@ -8480,6 +8825,9 @@ msgstr ""
msgid "Set max session time for web terminal."
msgstr ""
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr ""
@@ -8504,6 +8852,9 @@ msgstr ""
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr ""
@@ -8561,9 +8912,15 @@ msgstr ""
msgid "Sherlock Transactions"
msgstr ""
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr ""
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr ""
@@ -8661,6 +9018,24 @@ msgstr ""
msgid "Snippets"
msgstr ""
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8931,6 +9306,12 @@ msgstr ""
msgid "Starred projects"
msgstr ""
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9141,6 +9522,9 @@ msgstr "切æ›åˆ†æ”¯/標籤"
msgid "Sync information"
msgstr ""
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr ""
@@ -9330,6 +9714,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr ""
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr ""
@@ -9453,6 +9840,9 @@ msgstr ""
msgid "There was an error loading users activity calendar."
msgstr ""
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr ""
@@ -9498,6 +9888,21 @@ msgstr ""
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr ""
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr ""
@@ -9519,6 +9924,9 @@ msgstr ""
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr ""
@@ -9603,6 +10011,12 @@ msgstr "在創建壹個空的存儲庫或導入ç¾æœ‰å­˜å„²åº«ä¹‹å‰ï¼Œæ‚¨å°‡ç„¡
msgid "This merge request is locked."
msgstr ""
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr ""
@@ -10160,6 +10574,9 @@ msgstr ""
msgid "Update"
msgstr ""
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10169,6 +10586,9 @@ msgstr ""
msgid "Update your group name, description, avatar, and visibility."
msgstr ""
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr ""
@@ -10397,6 +10817,9 @@ msgstr ""
msgid "View documentation"
msgstr ""
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr ""
@@ -10406,6 +10829,9 @@ msgstr ""
msgid "View group labels"
msgstr ""
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr ""
@@ -10709,6 +11135,9 @@ msgstr ""
msgid "Withdraw Access Request"
msgstr "å–消權é™ç”³è¯·"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10814,6 +11243,9 @@ msgstr ""
msgid "You have no permissions"
msgstr ""
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "您已é”到項目數é‡é™åˆ¶"
@@ -10874,6 +11306,9 @@ msgstr ""
msgid "You'll need to use different branch names to get a valid comparison."
msgstr ""
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr ""
@@ -10967,6 +11402,9 @@ msgstr ""
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr ""
@@ -11046,6 +11484,9 @@ msgstr ""
msgid "ciReport|Confidence"
msgstr ""
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr ""
@@ -11138,9 +11579,6 @@ msgstr ""
msgid "ciReport|Performance metrics"
msgstr ""
-msgid "ciReport|Revert dismissal"
-msgstr ""
-
msgid "ciReport|SAST"
msgstr ""
@@ -11180,6 +11618,9 @@ msgstr ""
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr ""
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr ""
@@ -11268,9 +11709,6 @@ msgstr ""
msgid "here"
msgstr ""
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr ""
@@ -11387,6 +11825,9 @@ msgstr ""
msgid "mrWidget|Approve"
msgstr ""
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr ""
@@ -11459,6 +11900,9 @@ msgstr ""
msgid "mrWidget|Merge request approved"
msgstr ""
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr ""
@@ -11518,6 +11962,9 @@ msgstr ""
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr ""
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr ""
diff --git a/locale/zh_TW/gitlab.po b/locale/zh_TW/gitlab.po
index c4a58650dae..f374616a5fb 100644
--- a/locale/zh_TW/gitlab.po
+++ b/locale/zh_TW/gitlab.po
@@ -13,7 +13,7 @@ msgstr ""
"X-Crowdin-Project: gitlab-ee\n"
"X-Crowdin-Language: zh-TW\n"
"X-Crowdin-File: /master/locale/gitlab.pot\n"
-"PO-Revision-Date: 2019-02-11 10:20\n"
+"PO-Revision-Date: 2019-03-06 15:53\n"
msgid " Status"
msgstr " 狀態"
@@ -41,6 +41,10 @@ msgstr ""
msgid "\"%{query}\" in projects"
msgstr "在專案中查詢「%{query}ã€"
+msgid "%d comment"
+msgid_plural "%d comments"
+msgstr[0] ""
+
msgid "%d commit"
msgid_plural "%d commits"
msgstr[0] "%d 則æ交"
@@ -111,12 +115,25 @@ msgstr "%{counter_storage}ï¼ˆå…§å« %{counter_repositories} 個版本庫ã€%{cou
msgid "%{count} %{alerts}"
msgstr "%{count} 個 %{alerts} æ醒"
+msgid "%{count} approval required from %{name}"
+msgid_plural "%{count} approvals required from %{name}"
+msgstr[0] ""
+
+msgid "%{count} approvals from %{name}"
+msgstr ""
+
msgid "%{count} more"
msgstr ""
msgid "%{count} more assignees"
msgstr "%{count} å以上的被指派者"
+msgid "%{count} of %{required} approvals from %{name}"
+msgstr ""
+
+msgid "%{count} of %{total}"
+msgstr ""
+
msgid "%{count} participant"
msgid_plural "%{count} participants"
msgstr[0] "%{count} ä½åƒèˆ‡è€…"
@@ -131,15 +148,15 @@ msgstr "已刪除 %{filePath} 檔案"
msgid "%{firstLabel} +%{labelCount} more"
msgstr "%{firstLabel}(內å«å‰©é¤˜çš„ %{labelCount} 個)"
-msgid "%{firstOption} +%{extraOptionCount} more"
-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 "%{group_docs_link_start}群組%{group_docs_link_end} 讓您能跨管ç†ä»¥åŠå”作多個專案。群組的æˆå“¡å¯ä»¥å­˜å–群組之下的所有專案。"
msgid "%{issuableType} will be removed! Are you sure?"
msgstr "將移除 %{issuableType}ï¼ç¢ºå®šï¼Ÿ"
+msgid "%{label_for_message} unavailable"
+msgstr ""
+
msgid "%{link_start}Read more%{link_end} about role permissions"
msgstr ""
@@ -207,6 +224,9 @@ msgstr "(還有 %{count} 個)"
msgid "+ %{moreCount} more"
msgstr "(還有 %{moreCount} 個)"
+msgid "+%{extraOptionCount} more"
+msgstr ""
+
msgid ", or "
msgstr ""
@@ -227,6 +247,10 @@ msgid "1 %{type} modification"
msgid_plural "%{count} %{type} modifications"
msgstr[0] "%{count} 個 %{type} 變更"
+msgid "1 Day"
+msgid_plural "%d Days"
+msgstr[0] ""
+
msgid "1 closed issue"
msgid_plural "%d closed issues"
msgstr[0] "%d 個關閉的議題"
@@ -320,6 +344,21 @@ msgstr ""
msgid "A 'Runner' is a process which runs a job. You can set up as many Runners as you need."
msgstr "「執行器ã€æ˜¯å€‹ç”¨ä¾†åŸ·è¡Œä½œæ¥­çš„程å¼ã€‚你能設定你所需的多個執行器。"
+msgid "A .NET Core console application template, customizable for any .NET Core project"
+msgstr ""
+
+msgid "A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
+msgid "A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A collection of graphs regarding Continuous Integration"
msgstr "ä¸é–“æ–·æ•´åˆçš„圖表集åˆ"
@@ -335,6 +374,9 @@ msgstr "GitLab 濫用檢閱團隊的æˆå“¡å°‡æœƒç›¡å¿«æª¢é–±ä½ çš„回報。"
msgid "A new branch will be created in your fork and a new merge request will be started."
msgstr "將會在您的 fork 中建立一個新分支,並開啟新的åˆä½µè«‹æ±‚。"
+msgid "A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features."
+msgstr ""
+
msgid "A 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}。"
@@ -419,24 +461,51 @@ msgstr "新增 Kubernetes å¢é›†"
msgid "Add README"
msgstr ""
+msgid "Add a bullet list"
+msgstr ""
+
msgid "Add a general comment to this %{noteable_name}."
msgstr ""
msgid "Add a homepage to your wiki that contains information about your project and GitLab will display it here instead of this message."
msgstr "在 Wiki 中新增包å«æ‚¨å°ˆæ¡ˆç›¸é—œè³‡è¨Šçš„首é ï¼Œä¹‹å¾Œ GitLab 將會顯示 Wiki 首é æ–¼æ­¤è™•è€Œéžé€™å‰‡è¨Šæ¯ã€‚"
+msgid "Add a link"
+msgstr ""
+
+msgid "Add a numbered list"
+msgstr ""
+
msgid "Add a table"
msgstr "新增表格"
+msgid "Add a task list"
+msgstr ""
+
msgid "Add additional text to appear in all email communications. %{character_limit} character limit"
msgstr "新增顯示於所有電å­éƒµä»¶å°è©±çš„附加文字。最多 %{character_limit} 個字元。"
+msgid "Add approver(s)"
+msgstr ""
+
+msgid "Add approvers"
+msgstr ""
+
+msgid "Add bold text"
+msgstr ""
+
msgid "Add comment now"
msgstr "ç«‹å³ç•™è¨€"
+msgid "Add header and footer to emails. Please note that color settings will only be applied within the application interface"
+msgstr ""
+
msgid "Add image comment"
msgstr "增加圖片留言"
+msgid "Add italic text"
+msgstr ""
+
msgid "Add license"
msgstr "新增授權資訊"
@@ -605,6 +674,9 @@ msgstr "進階權é™ï¼Œã€ã€Œå¤§åž‹æª”案儲存空間 (LFS)ã€å’Œã€Œå…©æ­¥é©Ÿé©—
msgid "Advanced settings"
msgstr "進階設定"
+msgid "After a successful password update you will be redirected to login screen."
+msgstr ""
+
msgid "Alert"
msgid_plural "Alerts"
msgstr[0] "通知"
@@ -615,6 +687,9 @@ msgstr ""
msgid "All"
msgstr "全部"
+msgid "All Members"
+msgstr ""
+
msgid "All changes are committed"
msgstr "å·²æ交所有變更"
@@ -684,7 +759,7 @@ msgstr "空的 GitLab 使用者欄ä½å°‡æœƒåœ¨æ‰€æœ‰è­°é¡Œèˆ‡èªªæ˜Žçš„說明欄å
msgid "An error has occurred"
msgstr "發生了一個錯誤"
-msgid "An error occured while fetching the releases. Please try again."
+msgid "An error occured while loading designs. Please try again."
msgstr ""
msgid "An error occurred adding a draft to the discussion."
@@ -696,6 +771,15 @@ msgstr "新增è‰ç¨¿æ™‚發生錯誤。"
msgid "An error occurred creating the new branch."
msgstr ""
+msgid "An error occurred fetching the approval rules."
+msgstr ""
+
+msgid "An error occurred fetching the approvers for the new rule."
+msgstr ""
+
+msgid "An error occurred fetching the dropdown data."
+msgstr ""
+
msgid "An error occurred previewing the blob"
msgstr "é è¦½ blob 檔案時發生錯誤"
@@ -708,6 +792,9 @@ msgstr "æ›´æ–°å•é¡Œæ¬Šé‡æ™‚發生錯誤"
msgid "An error occurred while adding approver"
msgstr "增加審核者時發生錯誤"
+msgid "An error occurred while deleting the approvers group"
+msgstr ""
+
msgid "An error occurred while deleting the comment"
msgstr "刪除留言時發生錯誤"
@@ -744,6 +831,9 @@ msgstr "抓å–工作時發生錯誤。"
msgid "An error occurred while fetching the pipeline."
msgstr "讀å–管線時發生錯誤"
+msgid "An error occurred while fetching the releases. Please try again."
+msgstr ""
+
msgid "An error occurred while getting projects"
msgstr "å–得專案時發生錯誤"
@@ -801,12 +891,18 @@ msgstr "儲存 LDAP 覆蓋狀態時發生錯誤,請é‡è©¦ã€‚"
msgid "An error occurred while saving assignees"
msgstr "儲存被指派人時發生錯誤"
+msgid "An error occurred while saving the approval settings"
+msgstr ""
+
msgid "An error occurred while subscribing to notifications."
msgstr "訂閱通知時發生錯誤。"
msgid "An error occurred while unsubscribing to notifications."
msgstr "當å–消訂閱通知時發生錯誤"
+msgid "An error occurred while updating approvers"
+msgstr ""
+
msgid "An error occurred while updating the comment"
msgstr "更新留言時發生錯誤"
@@ -897,6 +993,40 @@ msgstr ""
msgid "Apply suggestion"
msgstr ""
+msgid "ApprovalRuleRemove|%d member"
+msgid_plural "ApprovalRuleRemove|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|Approvals from this member are not revoked."
+msgid_plural "ApprovalRuleRemove|Approvals from these members are not revoked."
+msgstr[0] ""
+
+msgid "ApprovalRuleRemove|You are about to remove the %{name} approver group which has %{nMembers}."
+msgstr ""
+
+msgid "ApprovalRuleSummary|%d member"
+msgid_plural "ApprovalRuleSummary|%d members"
+msgstr[0] ""
+
+msgid "ApprovalRuleSummary|%{count} approval required from %{membersCount}"
+msgid_plural "ApprovalRuleSummary|%{count} approvals required from %{membersCount}"
+msgstr[0] ""
+
+msgid "ApprovalRule|All members with Developer role or higher and code owners (if any)"
+msgstr ""
+
+msgid "ApprovalRule|Members"
+msgstr ""
+
+msgid "ApprovalRule|Name"
+msgstr ""
+
+msgid "ApprovalRule|No. approvals required"
+msgstr ""
+
+msgid "ApprovalRule|e.g. QA, Security, etc."
+msgstr ""
+
msgid "Approvals"
msgstr ""
@@ -939,9 +1069,15 @@ msgstr "確èªé‡æ–°ç”¢ç”Ÿå…¬é‘°ï¼Ÿæ‚¨å°‡éœ€è¦åœ¨é‡æ–°é‹ä½œé¡åƒå‰ï¼Œæ›´æ–°
msgid "Are you sure you want to remove %{group_name}?"
msgstr "確定移除 %{group_name}?"
+msgid "Are you sure you want to remove approver %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove approver %{name}?"
msgstr ""
+msgid "Are you sure you want to remove group %{name}"
+msgstr ""
+
msgid "Are you sure you want to remove group %{name}?"
msgstr ""
@@ -1026,6 +1162,9 @@ msgstr "指派列表顯示了分é…給é¸å®šä½¿ç”¨è€…的所有議題"
msgid "Assignee(s)"
msgstr "執行者"
+msgid "At least one approval from a code owner is required to change files matching the respective CODEOWNER rules."
+msgstr ""
+
msgid "Attach a file"
msgstr ""
@@ -1041,9 +1180,6 @@ msgstr "八月"
msgid "August"
msgstr "八月"
-msgid "Auth Token"
-msgstr ""
-
msgid "Authentication Log"
msgstr "驗證紀錄"
@@ -1602,6 +1738,9 @@ msgstr "ä¸èƒ½è‡ªå‹•åˆä½µ"
msgid "Cannot modify managed Kubernetes cluster"
msgstr "無法變更被管ç†çš„ Kubernetes å¢é›†"
+msgid "Cannot render the image. Maximum character count (%{charLimit}) has been exceeded."
+msgstr ""
+
msgid "Certificate"
msgstr ""
@@ -1638,6 +1777,9 @@ msgstr "復原"
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr "這將會建立新的æ交記錄,以還原ç¾æœ‰çš„變更。"
+msgid "Changes"
+msgstr ""
+
msgid "Changes are shown as if the <b>source</b> revision was being merged into the <b>target</b> revision."
msgstr "è‹¥<b>來æº</b>修訂版正è¦åˆä½µåˆ°<b>目標</b>修訂版時顯示變更比較。"
@@ -1650,6 +1792,9 @@ msgstr "統計圖表"
msgid "Chat"
msgstr "å³æ™‚通訊"
+msgid "Check again"
+msgstr ""
+
msgid "Check the %{docs_link_start}documentation%{docs_link_end}."
msgstr "檢查 %{docs_link_start} 檔案 %{docs_link_end}"
@@ -1716,9 +1861,6 @@ msgstr "é¸æ“‡æ‚¨å¸Œæœ›èˆ‡é€™å€‹æ¬¡è¦ç¯€é»žåŒæ­¥çš„群組"
msgid "Choose which repositories you want to connect and run CI/CD pipelines."
msgstr "é¸æ“‡ä½ æƒ³è¦é€£ç·šä¸¦åŸ·è¡Œ CI / CD 管線的版本庫。"
-msgid "Choose which repositories you want to import."
-msgstr "é¸æ“‡ä½ æƒ³åŒ¯å…¥çš„版本庫。"
-
msgid "Choose which shards you wish to synchronize to this secondary node."
msgstr "é¸æ“‡ä½ æƒ³è¦ç”¨ä¾†èˆ‡é€™å€‹æ¬¡è¦ç¯€é»žåŒæ­¥çš„碎片 (shards)。"
@@ -1878,6 +2020,9 @@ msgstr "複製版本庫"
msgid "Clone with %{http_label}"
msgstr ""
+msgid "Clone with KRB5"
+msgstr ""
+
msgid "Clone with SSH"
msgstr ""
@@ -1893,9 +2038,6 @@ msgstr ""
msgid "Closed"
msgstr "已關閉"
-msgid "Closed (moved)"
-msgstr ""
-
msgid "Closed issues"
msgstr "已關閉議題"
@@ -1941,12 +2083,12 @@ msgstr "å®‰è£ Ingress 後,您需è¦åœ¨ç”Ÿæˆçš„外部 IP ä½ç½®ä¸ŠæŒ‡å‘ DNSï
msgid "ClusterIntegration|Alternatively"
msgstr ""
-msgid "ClusterIntegration|An error occured while trying to fetch project zones: %{error}"
-msgstr "當嘗試å–得專案å€åŸŸæ™‚錯誤發生:%{error}"
-
msgid "ClusterIntegration|An error occurred when trying to contact the Google Cloud API. Please try again later."
msgstr "嘗試連線到 Google Cloud API 時出錯,請ç¨å€™å†è©¦ã€‚"
+msgid "ClusterIntegration|An error occurred while trying to fetch project zones: %{error}"
+msgstr ""
+
msgid "ClusterIntegration|An error occurred while trying to fetch your projects: %{error}"
msgstr ""
@@ -1983,6 +2125,9 @@ msgstr "é¸æ“‡è¦å®‰è£åœ¨æ‚¨ Kubernetes å¢é›†ä¸Šçš„應用程å¼ã€‚Helm Tiller
msgid "ClusterIntegration|Choose which of your environments will use this cluster."
msgstr "é¸æ“‡è¦ä½¿ç”¨åœ¨æ­¤å¢é›†çš„環境。"
+msgid "ClusterIntegration|Cluster health"
+msgstr ""
+
msgid "ClusterIntegration|Clusters are utilized by selecting the nearest ancestor with a matching environment scope. For example, project clusters will override group clusters."
msgstr ""
@@ -2079,8 +2224,8 @@ 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 "為了è¦é¡¯ç¤ºå¢é›†çš„å¥åº·ç‹€æ³ï¼Œæˆ‘們需è¦ç‚ºæ‚¨çš„å¢é›†è¨­å®š Prometheus 以收集所需的資訊。"
+msgid "ClusterIntegration|In order to view the health of your cluster, you must first install Prometheus below."
+msgstr ""
msgid "ClusterIntegration|Ingress"
msgstr "輸入"
@@ -2094,9 +2239,6 @@ msgstr "Ingress 為您æ供了一種將請求伺æœå™¨æˆ–路徑路由到æœå‹™ç
msgid "ClusterIntegration|Install"
msgstr "安è£"
-msgid "ClusterIntegration|Install Prometheus"
-msgstr "å®‰è£ Prometheus"
-
msgid "ClusterIntegration|Installed"
msgstr "已安è£"
@@ -2142,9 +2284,6 @@ msgstr "Kubernetes å¢é›†"
msgid "ClusterIntegration|Kubernetes cluster details"
msgstr "Kubernetes å¢é›†è©³ç´°è³‡è¨Š"
-msgid "ClusterIntegration|Kubernetes cluster health"
-msgstr "Kubernetes å¢é›†å¥åº·ç¨‹åº¦"
-
msgid "ClusterIntegration|Kubernetes cluster is being created on Google Kubernetes Engine..."
msgstr "Kubernetes å¢é›†å·²ç¶“在 Google Kubernetes Engine 建立"
@@ -2382,15 +2521,27 @@ msgstr "註冊"
msgid "Code"
msgstr ""
+msgid "Code Owners"
+msgstr ""
+
+msgid "Code owner approval is required"
+msgstr ""
+
msgid "Code owners"
msgstr "程å¼ç¢¼æ‰€æœ‰è€…"
+msgid "CodeOwner|Pattern"
+msgstr ""
+
msgid "Cohorts"
msgstr "Cohorts"
msgid "Collapse"
msgstr "收縮"
+msgid "Collapse approvers"
+msgstr ""
+
msgid "Collapse sidebar"
msgstr "收起å´é‚Šæ¬„"
@@ -2689,6 +2840,9 @@ msgstr "複製 %{protocol} 的複製 (clone) URL"
msgid "Copy ID to clipboard"
msgstr "複製 ID 到剪貼簿"
+msgid "Copy KRB5 clone URL"
+msgstr ""
+
msgid "Copy SSH clone URL"
msgstr "複製 SSH 的複製 (clone) URL"
@@ -3176,6 +3330,9 @@ msgstr "《æ述模æ¿ã€‹å…許你為您專案的議題或åˆä½µè«‹æ±‚之æè¿°
msgid "Description:"
msgstr "說明:"
+msgid "Designs"
+msgstr ""
+
msgid "Destroy"
msgstr "銷毀"
@@ -3254,6 +3411,9 @@ msgstr ""
msgid "Discuss a specific suggestion or question that needs to be resolved"
msgstr ""
+msgid "Discussion"
+msgstr ""
+
msgid "Dismiss"
msgstr "忽略"
@@ -3425,6 +3585,9 @@ msgstr "為此專案啟用"
msgid "Enable group Runners"
msgstr "啟用群組執行器"
+msgid "Enable header and footer in emails"
+msgstr ""
+
msgid "Enable or disable the Pseudonymizer data collection."
msgstr "啟用或åœç”¨ Pseudonymizer 的資料收集。"
@@ -3461,6 +3624,9 @@ msgstr "æ–¼ (UTC) çµæŸ"
msgid "Enforce SSO-only authentication for this group"
msgstr ""
+msgid "Enforce users to have dedicated group managed accounts for this group"
+msgstr ""
+
msgid "Enforced SSO"
msgstr ""
@@ -3479,9 +3645,6 @@ msgstr "請輸入åˆä½µè«‹æ±‚說明"
msgid "Enter the merge request title"
msgstr "請請輸入åˆä½µè«‹æ±‚標題"
-msgid "Enter your Sentry API URL"
-msgstr ""
-
msgid "Environment variables"
msgstr ""
@@ -3506,6 +3669,12 @@ msgstr "抓å–環境時發生錯誤。"
msgid "Environments|An error occurred while making the request."
msgstr "建立請求時發生錯誤"
+msgid "Environments|An error occurred while re-deploying the environment, please try again"
+msgstr ""
+
+msgid "Environments|An error occurred while rolling back the environment, please try again"
+msgstr ""
+
msgid "Environments|An error occurred while stopping the environment, please try again"
msgstr "åœæ­¢ç’°å¢ƒæ™‚發生錯誤,請é‡è©¦"
@@ -3557,15 +3726,33 @@ msgstr "打開實æ³ç’°å¢ƒ"
msgid "Environments|Pod logs from"
msgstr "Pod 記錄由"
+msgid "Environments|Re-deploy"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Re-deploy environment %{name}?"
+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|Rollback environment %{environment_name}?"
+msgstr ""
+
+msgid "Environments|Rollback environment %{name}?"
+msgstr ""
+
msgid "Environments|Show all"
msgstr "顯示所有內容"
@@ -3578,6 +3765,18 @@ msgstr "åœæ­¢ç’°å¢ƒ"
msgid "Environments|Stopping"
msgstr ""
+msgid "Environments|This action will relaunch the job for commit %{commit_id}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will relaunch the job for commit %{linkStart}%{commitId}%{linkEnd}, putting the environment in a previous version. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by %{name} for commit %{linkStart}%{commitId}%{linkEnd} putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
+msgid "Environments|This action will run the job defined by staging for commit %{commit_id}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?"
+msgstr ""
+
msgid "Environments|Updated"
msgstr "已更新"
@@ -3629,6 +3828,9 @@ msgstr "錯誤報告與記錄"
msgid "Error Tracking"
msgstr ""
+msgid "Error creating a new path"
+msgstr ""
+
msgid "Error creating epic"
msgstr "建立 epic 時發生錯誤"
@@ -3698,6 +3900,33 @@ msgstr "載入åˆä½µè«‹æ±‚時出錯。請é‡è©¦ã€‚"
msgid "Error:"
msgstr ""
+msgid "ErrorTracking|Active"
+msgstr ""
+
+msgid "ErrorTracking|After adding your Auth Token, use the 'Connect' button to load projects"
+msgstr ""
+
+msgid "ErrorTracking|Auth Token"
+msgstr ""
+
+msgid "ErrorTracking|Click 'Connect' to re-establish the connection to Sentry and activate the dropdown."
+msgstr ""
+
+msgid "ErrorTracking|Connection has failed. Re-check Auth Token and try again."
+msgstr ""
+
+msgid "ErrorTracking|Find your hostname in your Sentry account settings page"
+msgstr ""
+
+msgid "ErrorTracking|No projects available"
+msgstr ""
+
+msgid "ErrorTracking|Select project"
+msgstr ""
+
+msgid "ErrorTracking|To enable project selection, enter a valid Auth Token"
+msgstr ""
+
msgid "Errors"
msgstr ""
@@ -3776,6 +4005,9 @@ msgstr "展開"
msgid "Expand all"
msgstr "展開全部"
+msgid "Expand approvers"
+msgstr ""
+
msgid "Expand sidebar"
msgstr "展開å´é‚Šæ¬„"
@@ -3863,7 +4095,7 @@ msgstr "無法部署到"
msgid "Failed to load emoji list."
msgstr "無法讀å–表情符號列表。"
-msgid "Failed to load errors from Sentry"
+msgid "Failed to load errors from Sentry. Error message: %{errorMessage}"
msgstr ""
msgid "Failed to remove issue from board, please try again."
@@ -3929,9 +4161,6 @@ msgstr ""
msgid "FeatureFlags|Description"
msgstr "說明"
-msgid "FeatureFlags|Edit %{feature_flag_name}"
-msgstr "編輯 %{feature_flag_name}"
-
msgid "FeatureFlags|Edit Feature Flag"
msgstr "編輯功能標籤"
@@ -3989,9 +4218,6 @@ msgstr "æ–°"
msgid "FeatureFlags|New Feature Flag"
msgstr "新增功能標籤"
-msgid "FeatureFlags|Save changes"
-msgstr "儲存"
-
msgid "FeatureFlags|Status"
msgstr "狀態"
@@ -4077,9 +4303,6 @@ msgstr ""
msgid "Filter..."
msgstr "篩é¸â€¦"
-msgid "Find and manage Auth Tokens in your Sentry account settings page."
-msgstr ""
-
msgid "Find by path"
msgstr "ä¾è·¯å¾‘æœå°‹"
@@ -4194,8 +4417,8 @@ msgstr "在您的 .gitlab-ci.yml 中找到錯誤:"
msgid "Free Trial of GitLab.com Gold"
msgstr "å…費試用 GitLab.com 黃金計畫"
-msgid "From %{provider_title}"
-msgstr "來自 %{provider_title}"
+msgid "From %{providerTitle}"
+msgstr ""
msgid "From Bitbucket"
msgstr "從 Bitbucket"
@@ -4224,9 +4447,15 @@ msgstr "來自里程碑:"
msgid "From the Kubernetes cluster details view, install Runner from the applications list"
msgstr "從 Kubernetes å¢é›†è©³ç´°è³‡æ–™é ï¼Œå¾žæ‡‰ç”¨ç¨‹å¼åˆ—表中安è£åŸ·è¡Œå™¨"
+msgid "GPG Key ID:"
+msgstr ""
+
msgid "GPG Keys"
msgstr "GPG 金鑰"
+msgid "GPG signature (loading...)"
+msgstr ""
+
msgid "General"
msgstr "一般"
@@ -4659,15 +4888,24 @@ msgstr ""
msgid "Go Back"
msgstr "返回"
+msgid "Go Micro is a framework for micro service development."
+msgstr ""
+
msgid "Go back"
msgstr "上一é "
+msgid "Go full screen"
+msgstr ""
+
msgid "Go to"
msgstr "å‰å¾€"
msgid "Go to %{link_to_google_takeout}."
msgstr "å‰å¾€ %{link_to_google_takeout}。"
+msgid "Go to project"
+msgstr ""
+
msgid "Google Code import"
msgstr "匯入 Google Code"
@@ -4725,6 +4963,9 @@ msgstr "群組資訊:"
msgid "Group maintainers can register group runners in the %{link}"
msgstr "群組維護者å¯ä»¥åœ¨ %{link} 註冊群組執行器"
+msgid "Group managed accounts"
+msgstr ""
+
msgid "Group name"
msgstr "群組å稱"
@@ -5092,9 +5333,24 @@ msgstr "匯入版本庫"
msgid "Import timed out. Import took longer than %{import_jobs_expiration} seconds"
msgstr ""
+msgid "Import/Export illustration"
+msgstr ""
+
msgid "ImportButtons|Connect repositories from"
msgstr "連線版本庫自"
+msgid "ImportProjects|Importing the project failed"
+msgstr ""
+
+msgid "ImportProjects|Requesting your %{provider} repositories failed"
+msgstr ""
+
+msgid "ImportProjects|Select the projects you want to import"
+msgstr ""
+
+msgid "ImportProjects|Updating the imported projects failed"
+msgstr ""
+
msgid "Improve Issue boards with GitLab Enterprise Edition."
msgstr "改善 GitLab ä¼æ¥­ç‰ˆæœ¬çš„議題看æ¿ã€‚"
@@ -5146,6 +5402,12 @@ msgstr "手動輸入主機金鑰"
msgid "Input your repository URL"
msgstr "輸入您的版本庫URL"
+msgid "Insert a quote"
+msgstr ""
+
+msgid "Insert code"
+msgstr ""
+
msgid "Insert suggestion"
msgstr ""
@@ -5198,6 +5460,9 @@ msgstr "週期分æžç°¡ä»‹"
msgid "Introducing Your Conversational Development Index"
msgstr ""
+msgid "Invalid input, please avoid emojis"
+msgstr ""
+
msgid "Invitation"
msgstr ""
@@ -5216,6 +5481,9 @@ msgstr ""
msgid "Invoke Time"
msgstr ""
+msgid "IssuableStatus|Closed (%{moved_link_start}moved%{moved_link_end})"
+msgstr ""
+
msgid "Issue"
msgstr "å•é¡Œ"
@@ -5258,8 +5526,8 @@ msgstr "è­°é¡Œå¯ä»¥è¨Žè«–臭蟲ã€å·¥ä½œä»¥åŠæƒ³æ³•ã€‚此外,å•é¡Œæ˜¯å¯æœ
msgid "Issues closed"
msgstr "議題已關閉"
-msgid "Issues, merge requests, pushes and comments."
-msgstr "å•é¡Œã€åˆä½µè«‹æ±‚ã€æŽ¨é€å’Œç•™è¨€"
+msgid "Issues, merge requests, pushes, and comments."
+msgstr ""
msgid "IssuesAnalytics|After you begin creating issues for your projects, we can start tracking and displaying metrics for them"
msgstr "在您為專案建立議題後,我們開始追蹤並顯示它們的指標"
@@ -5447,6 +5715,9 @@ msgstr "æå‡æ¨™ç±¤"
msgid "Labels|Promoting %{labelTitle} will make it available for all projects inside %{groupName}. Existing project labels with the same title will be merged. This action cannot be reversed."
msgstr "æå‡ %{labelTitle} å°‡å¯ä»¥è®“所有 %{groupName} 內的專案都使用此標籤。ç¾æœ‰ç›¸åŒå稱的標籤將被åˆä½µã€‚該æ“作ä¸å¯å¾©åŽŸã€‚"
+msgid "Language"
+msgstr ""
+
msgid "Large File Storage"
msgstr "大檔案儲存"
@@ -5529,6 +5800,9 @@ msgstr ""
msgid "Learn more about protected branches"
msgstr "進一步了解å—ä¿è­·çš„分支"
+msgid "Learn more about signing commits"
+msgstr ""
+
msgid "Learn more in the"
msgstr "進一步了解於"
@@ -5701,6 +5975,15 @@ msgstr "使用 smartcard 登入"
msgid "Logs"
msgstr "記錄檔"
+msgid "MRApprovals|Approved by"
+msgstr ""
+
+msgid "MRApprovals|Approvers"
+msgstr ""
+
+msgid "MRApprovals|Pending approvals"
+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 主機的唯讀é¡åƒï¼Œæ‰€ä»¥æ‚¨å¯ä»¥ç¸®çŸ­è¤‡è£½å’ŒæŠ“å–大型版本庫的時間。"
@@ -5746,6 +6029,9 @@ msgstr "Manifest"
msgid "Manifest file import"
msgstr "匯入 Manifest 檔案"
+msgid "Manual job"
+msgstr ""
+
msgid "Map a FogBugz account ID to a GitLab user"
msgstr "å°‡ FogBugz 的帳號å°æ‡‰åˆ° GitLab 使用者"
@@ -5773,36 +6059,6 @@ msgstr ""
msgid "Markdown enabled"
msgstr "已啟用 Markdown"
-msgid "MarkdownToolbar|Add a bullet list"
-msgstr "增加項目清單"
-
-msgid "MarkdownToolbar|Add a link"
-msgstr "增加連çµ"
-
-msgid "MarkdownToolbar|Add a numbered list"
-msgstr "增加åºåˆ—清單"
-
-msgid "MarkdownToolbar|Add a table"
-msgstr "增加表格"
-
-msgid "MarkdownToolbar|Add a task list"
-msgstr "增加作業清單"
-
-msgid "MarkdownToolbar|Add bold text"
-msgstr "增加粗體文字"
-
-msgid "MarkdownToolbar|Add italic text"
-msgstr "增加斜體文字"
-
-msgid "MarkdownToolbar|Go full screen"
-msgstr "全螢幕顯示"
-
-msgid "MarkdownToolbar|Insert a quote"
-msgstr "æ’入引用"
-
-msgid "MarkdownToolbar|Insert code"
-msgstr "æ’入程å¼ç¢¼"
-
msgid "Maven Metadata"
msgstr "Maven 詮釋資料"
@@ -5857,6 +6113,9 @@ msgstr ""
msgid "Merge in progress"
msgstr ""
+msgid "Merge pipelines will try to validate the post-merge result prior to merging"
+msgstr ""
+
msgid "Merge request"
msgstr "åˆä½µè«‹æ±‚"
@@ -6007,9 +6266,6 @@ msgstr ""
msgid "Metrics|Prometheus Query Documentation"
msgstr "Prometheus 查詢檔案"
-msgid "Metrics|System"
-msgstr "系統"
-
msgid "Metrics|There was an error fetching the environments data, please try again"
msgstr "å–得環境資訊時發生錯誤,請é‡è©¦ã€‚"
@@ -6154,6 +6410,9 @@ msgstr "更多資訊"
msgid "More information is available|here"
msgstr "此處"
+msgid "More than %{number_commits_distance} commits different with %{default_branch}"
+msgstr ""
+
msgid "Most stars"
msgstr "最多星數"
@@ -6230,15 +6489,15 @@ msgstr ""
msgid "New Pages Domain"
msgstr ""
+msgid "New Password"
+msgstr ""
+
msgid "New Pipeline Schedule"
msgstr "建立排程管線"
msgid "New Snippet"
msgstr "新增片段"
-msgid "New Snippets"
-msgstr "新增片段"
-
msgid "New branch"
msgstr "新分支"
@@ -6299,9 +6558,15 @@ msgstr "新增…"
msgid "No"
msgstr "å¦"
+msgid "No %{providerTitle} repositories available to import"
+msgstr ""
+
msgid "No Label"
msgstr "沒有標籤"
+msgid "No Tag"
+msgstr ""
+
msgid "No activities found"
msgstr ""
@@ -6329,13 +6594,16 @@ msgstr "找ä¸åˆ°è²¢ç»"
msgid "No credit card required."
msgstr "無需信用å¡ã€‚"
+msgid "No designs found."
+msgstr ""
+
msgid "No details available"
msgstr ""
msgid "No due date"
msgstr "沒有到期時間"
-msgid "No errors to display"
+msgid "No errors to display."
msgstr ""
msgid "No estimate or time spent"
@@ -6431,6 +6699,9 @@ msgstr "資料ä¸è¶³"
msgid "Not now"
msgstr "ä¸æ˜¯ç¾åœ¨"
+msgid "Not started"
+msgstr ""
+
msgid "Note that the master branch is automatically protected. %{link_to_protected_branches}"
msgstr "請注æ„,master 分支é è¨­ç‚ºå—ä¿è­·çš„。 %{link_to_protected_branches}"
@@ -6633,6 +6904,9 @@ msgstr "動作"
msgid "Operations Dashboard"
msgstr "維é‹å„€è¡¨æ¿"
+msgid "Operations Settings"
+msgstr ""
+
msgid "OperationsDashboard|Add a project to the dashboard"
msgstr "新增專案到儀表æ¿"
@@ -6642,6 +6916,9 @@ msgstr "維é‹å„€è¡¨æ¿æä¾›æ¯å€‹å°ˆæ¡ˆçš„執行狀æ³æ‘˜è¦ï¼ŒåŒ…括管線和
msgid "OperationsDashboard|Unable to add %{invalidProjects}. The Operations Dashboard is available for public projects, and private projects in groups with a Gold plan."
msgstr ""
+msgid "Optional"
+msgstr ""
+
msgid "Optionally, you can %{link_to_customize} how FogBugz email addresses and usernames are imported into GitLab."
msgstr "ä½ å¯ä»¥é¸æ“‡ %{link_to_customize} 如何將 FogBugz çš„é›»å­ä¿¡ç®±åŠä½¿ç”¨è€…å稱匯入 GitLab 。"
@@ -6834,6 +7111,12 @@ msgstr "變數"
msgid "PipelineSheduleIntervalPattern|Custom"
msgstr "自訂"
+msgid "PipelineStatusTooltip|Commit: %{ci_status}"
+msgstr ""
+
+msgid "PipelineStatusTooltip|Pipeline: %{ci_status}"
+msgstr ""
+
msgid "Pipelines"
msgstr "管線"
@@ -6849,6 +7132,9 @@ msgstr "上週的管線"
msgid "Pipelines for last year"
msgstr "去年的管線"
+msgid "Pipelines need to be configured to enable this feature."
+msgstr ""
+
msgid "Pipelines|Build with confidence"
msgstr "信任編譯"
@@ -6969,9 +7255,21 @@ msgstr "請將它們轉為 %{link_to_git} ,然後å†æ¬¡é€éŽ %{link_to_import
msgid "Please convert them to Git on Google Code, and go through the %{link_to_import_flow} again."
msgstr "請將它們在 Google Code 上轉為 Git,然後å†æ¬¡é€éŽ %{link_to_import_flow} 。"
+msgid "Please create a username with only alphanumeric characters."
+msgstr ""
+
msgid "Please enable and migrate to hashed storage to avoid security issues and ensure data integrity. %{migrate_link}"
msgstr ""
+msgid "Please enter a non-negative number"
+msgstr ""
+
+msgid "Please enter a number greater than %{number} (from the project settings)"
+msgstr ""
+
+msgid "Please enter a valid number"
+msgstr ""
+
msgid "Please fill in a descriptive name for your group."
msgstr "請為您的群組填寫æ述性å稱。"
@@ -6981,9 +7279,18 @@ msgstr ""
msgid "Please note that this application is not provided by GitLab and you should verify its authenticity before allowing access."
msgstr "請注æ„,這個應用程å¼ä¸æ˜¯ç”± GitLab 所æ供的,您應該在å…許訪å•ä¹‹å‰é©—證其真實性。"
+msgid "Please provide a name"
+msgstr ""
+
+msgid "Please select and add a member"
+msgstr ""
+
msgid "Please select at least one filter to see results"
msgstr "è«‹é¸æ“‡è‡³å°‘一個篩é¸å™¨ä»¥æª¢è¦–çµæžœ"
+msgid "Please set a new password before proceeding."
+msgstr ""
+
msgid "Please solve the reCAPTCHA"
msgstr "請填寫此驗證碼"
@@ -7008,6 +7315,9 @@ msgstr "å好設定"
msgid "Preferences|Navigation theme"
msgstr "導航主題"
+msgid "Preferences|This feature is experimental and translations are not complete yet"
+msgstr ""
+
msgid "Press Enter or click to search"
msgstr "按下 Enter 或點é¸ä»¥æœå°‹"
@@ -7206,9 +7516,6 @@ msgstr "這個電å­éƒµä»¶å°‡ç”¨æ–¼Web的任何æ“作,例如編輯和åˆä½µã€‚
msgid "Profiles|This emoji and message will appear on your profile and throughout the interface."
msgstr "這個表情符號和訊æ¯å°‡é¡¯ç¤ºæ–¼æ‚¨çš„個人資料和整個工作介é¢ä¸­ã€‚"
-msgid "Profiles|This feature is experimental and translations are not complete yet"
-msgstr ""
-
msgid "Profiles|This information will appear on your profile"
msgstr ""
@@ -7308,6 +7615,9 @@ msgstr "進度"
msgid "Project"
msgstr "專案"
+msgid "Project \"%{name}\" is no longer available. Select another project to continue."
+msgstr ""
+
msgid "Project '%{project_name}' is in the process of being deleted."
msgstr "正在刪除專案「%{project_name}ã€ã€‚"
@@ -7350,6 +7660,9 @@ msgstr "專案匯出連çµå·²å¤±æ•ˆï¼Œè«‹è‡³ã€Œå°ˆæ¡ˆè¨­å®šã€ç”¢ç”Ÿæ–°çš„匯出
msgid "Project export started. A download link will be sent by email."
msgstr "已開始專案匯出程åºã€‚完æˆå¾Œä¸‹è¼‰é€£çµå°‡é€åˆ°æ‚¨çš„信箱。"
+msgid "Project has too many %{label_for_message} to search"
+msgstr ""
+
msgid "Project members"
msgstr ""
@@ -7449,6 +7762,9 @@ msgstr "使用者åªèƒ½é€éŽä»–們自己通éŽé©—證的電å­ä¿¡ç®±ä¹‹ä¸€æ交
msgid "Projects"
msgstr "專案"
+msgid "Projects Successfully Retrieved"
+msgstr ""
+
msgid "Projects shared with %{group_name}"
msgstr "與 %{group_name} 分享的專案"
@@ -7807,6 +8123,12 @@ msgstr ""
msgid "Remove approver"
msgstr ""
+msgid "Remove approvers"
+msgstr ""
+
+msgid "Remove approvers?"
+msgstr ""
+
msgid "Remove avatar"
msgstr "移除大頭貼"
@@ -7942,6 +8264,17 @@ msgstr "è¦æ±‚此群組中的所有使用者啟用兩步驟驗證"
msgid "Require all users to accept Terms of Service and Privacy Policy when they access GitLab."
msgstr "è¦æ±‚所有使用者在訪å•GitLab時接å—æœå‹™æ¢æ¬¾å’Œéš±ç§æ”¿ç­–。"
+msgid "Require approval from code owners"
+msgstr ""
+
+msgid "Requires approval from %{names}."
+msgid_plural "Requires %{count} more approvals from %{names}."
+msgstr[0] ""
+
+msgid "Requires approval."
+msgid_plural "Requires %d more approvals."
+msgstr[0] ""
+
msgid "Resend invite"
msgstr ""
@@ -8099,6 +8432,9 @@ msgstr "您已經使用了所有分享的執行器多久的時間。"
msgid "Running"
msgstr "執行中"
+msgid "Running…"
+msgstr ""
+
msgid "SAML SSO"
msgstr "SAML SSO"
@@ -8129,6 +8465,9 @@ msgstr "SSH 公鑰"
msgid "SSL Verification"
msgstr "SSL é©—è­‰"
+msgid "Saturday"
+msgstr ""
+
msgid "Save"
msgstr "儲存"
@@ -8162,6 +8501,9 @@ msgstr "已排程"
msgid "Schedules"
msgstr "排程"
+msgid "Scheduling"
+msgstr ""
+
msgid "Scheduling Pipelines"
msgstr "排程管線"
@@ -8222,6 +8564,9 @@ msgstr "æœå°‹å°ˆæ¡ˆ"
msgid "Search users"
msgstr "æœå°‹ä½¿ç”¨è€…"
+msgid "Search users or groups"
+msgstr ""
+
msgid "Search your projects"
msgstr "æœå°‹ä½ çš„專案"
@@ -8480,6 +8825,9 @@ msgstr "設定實例範åœçš„模æ¿ç‰ˆæœ¬åº«"
msgid "Set max session time for web terminal."
msgstr "為網é çµ‚端器設定最長工作階段時間"
+msgid "Set new password"
+msgstr ""
+
msgid "Set notification email for abuse reports."
msgstr "為濫用回報設定通知電å­ä¿¡ç®±ã€‚"
@@ -8504,6 +8852,9 @@ msgstr "根據 %{docsLinkStart}這個檔案%{icon}%{docsLinkEnd} 設定斷言 /
msgid "Set up new U2F device"
msgstr ""
+msgid "Set up new password"
+msgstr ""
+
msgid "Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically."
msgstr "設定你的專案以自動推é€å’Œ/或從å¦ä¸€å€‹ç‰ˆæœ¬åº«æ‹‰å–變更。分支ã€æ¨™ç±¤åŠæ交將會自動åŒæ­¥ã€‚"
@@ -8561,9 +8912,15 @@ msgstr "é‡è¨­ç®¡ç·šå·²ç”¨æ™‚é–“"
msgid "Sherlock Transactions"
msgstr "Sherlock 交易"
+msgid "Show all activity"
+msgstr ""
+
msgid "Show command"
msgstr "顯示指令"
+msgid "Show comments only"
+msgstr ""
+
msgid "Show complete raw log"
msgstr "顯示完整的原始記錄"
@@ -8661,6 +9018,24 @@ msgstr ""
msgid "Snippets"
msgstr "程å¼ç¢¼ç‰‡æ®µ"
+msgid "SnippetsEmptyState|Explore public snippets"
+msgstr ""
+
+msgid "SnippetsEmptyState|New snippet"
+msgstr ""
+
+msgid "SnippetsEmptyState|No snippets found"
+msgstr ""
+
+msgid "SnippetsEmptyState|Snippets are small pieces of code or notes that you want to keep."
+msgstr ""
+
+msgid "SnippetsEmptyState|There are no snippets to show."
+msgstr ""
+
+msgid "SnippetsEmptyState|They can be either public or private."
+msgstr ""
+
msgid "Someone edited this %{issueType} at the same time you did. The description has been updated and you will need to make your changes again."
msgstr ""
@@ -8931,6 +9306,12 @@ msgstr "已收è—的專案活動"
msgid "Starred projects"
msgstr "收è—專案"
+msgid "StarredProjectsEmptyState|Visit a project page and press on a star icon. Then, you can find the project on this page."
+msgstr ""
+
+msgid "StarredProjectsEmptyState|You don't have starred projects yet."
+msgstr ""
+
msgid "Stars"
msgstr ""
@@ -9141,6 +9522,9 @@ msgstr "切æ›åˆ†æ”¯/標籤"
msgid "Sync information"
msgstr "åŒæ­¥è©³ç´°è³‡è¨Š"
+msgid "System"
+msgstr ""
+
msgid "System Hooks"
msgstr "系統觸發器"
@@ -9330,6 +9714,9 @@ msgstr ""
msgid "The maximum file size allowed is 200KB."
msgstr "最大檔案大å°ç‚º 200KB"
+msgid "The name %{entryName} is already taken in this directory."
+msgstr ""
+
msgid "The passphrase required to decrypt the private key. This is optional and the value is encrypted at rest."
msgstr "解密ç§é‘°æ‰€éœ€çš„密碼。這是é¸ç”¨ä¸”該值將會被加密儲存。"
@@ -9453,6 +9840,9 @@ msgstr "刪除待辦事項時出錯。"
msgid "There was an error loading users activity calendar."
msgstr "讀å–使用者行事曆活動時發生錯誤"
+msgid "There was an error saving your changes."
+msgstr ""
+
msgid "There was an error saving your notification settings."
msgstr "儲存您的通知設定時發生錯誤。"
@@ -9498,6 +9888,21 @@ msgstr "已縮å°æ­¤çœ‹æ¿ç¯„åœ"
msgid "This branch has changed since you started editing. Would you like to create a new branch?"
msgstr "自上次編輯以來此分支已經變更éŽã€‚是å¦å»ºç«‹æ–°åˆ†æ”¯ï¼Ÿ"
+msgid "This commit is part of merge request %{link_to_merge_request}. Comments created here will be created in the context of that merge request."
+msgstr ""
+
+msgid "This commit was signed with a <strong>verified</strong> signature and the committer email is verified to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with a different user's verified signature."
+msgstr ""
+
+msgid "This commit was signed with a verified signature, but the committer email is <strong>not verified</strong> to belong to the same user."
+msgstr ""
+
+msgid "This commit was signed with an <strong>unverified</strong> signature."
+msgstr ""
+
msgid "This container registry has been scheduled for deletion."
msgstr "此容器登入表已經排定刪除。"
@@ -9519,6 +9924,9 @@ msgstr "這個資料夾"
msgid "This domain is not verified. You will need to verify ownership before access is enabled."
msgstr ""
+msgid "This field is required."
+msgstr ""
+
msgid "This group"
msgstr "此群組"
@@ -9603,6 +10011,12 @@ msgstr "這代表在您建立一個空的版本庫或是匯入一個ç¾å­˜çš„版
msgid "This merge request is locked."
msgstr "這個åˆä½µè«‹æ±‚已被鎖定。"
+msgid "This merge request must be approved by members of these groups. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
+msgid "This merge request must be approved by these users. You can override the project settings by setting your own list of approvers."
+msgstr ""
+
msgid "This option is disabled as you don't have write permissions for the current branch"
msgstr "由於您沒有此分支的寫入權é™ï¼Œå› æ­¤ç¦ç”¨æ­¤é¸é …。"
@@ -10160,6 +10574,9 @@ msgstr "å³å°‡é–‹å§‹"
msgid "Update"
msgstr "æ›´æ–°"
+msgid "Update approvers"
+msgstr ""
+
msgid "Update failed"
msgstr ""
@@ -10169,6 +10586,9 @@ msgstr "ç«‹å³æ›´æ–°"
msgid "Update your group name, description, avatar, and visibility."
msgstr "更新你的群組å稱ã€èªªæ˜Žã€é ­åƒåŠå¯è¦‹æ€§ã€‚"
+msgid "Updated"
+msgstr ""
+
msgid "Updating"
msgstr "更新中"
@@ -10397,6 +10817,9 @@ msgstr ""
msgid "View documentation"
msgstr "檢視檔案"
+msgid "View eligible approvers"
+msgstr ""
+
msgid "View epics list"
msgstr "檢視 Epic 列表"
@@ -10406,6 +10829,9 @@ msgstr "ç€è¦½æª”案 @ "
msgid "View group labels"
msgstr "檢視群組標籤"
+msgid "View in Sentry"
+msgstr ""
+
msgid "View issue"
msgstr "檢視議題"
@@ -10709,6 +11135,9 @@ msgstr "é€éŽè²¢ç»åˆ†æžï¼Œæ‚¨å¯ä»¥å–得活動概覽,其包å«äº†æ‚¨çš„組
msgid "Withdraw Access Request"
msgstr "å–消權é™ç”³è«‹"
+msgid "Write"
+msgstr ""
+
msgid "Write a comment or drag your files here…"
msgstr ""
@@ -10814,6 +11243,9 @@ msgstr ""
msgid "You have no permissions"
msgstr "你沒有權é™"
+msgid "You have not added any approvers. Start by adding users or groups."
+msgstr ""
+
msgid "You have reached your project limit"
msgstr "您已é”到專案數é‡é™åˆ¶"
@@ -10874,6 +11306,9 @@ msgstr "在您的個人資料中新增 SSH 密鑰之å‰ï¼Œä½ ä¸èƒ½é€éŽ SSH ä¾
msgid "You'll need to use different branch names to get a valid comparison."
msgstr "你需è¦é¸æ“‡å…©å€‹ä¸åŒçš„分支,æ‰èƒ½é€²è¡Œæ¯”較。"
+msgid "You're only seeing %{startTag}other activity%{endTag} in the feed. To add a comment, switch to one of the following options."
+msgstr ""
+
msgid "You're receiving this email because %{reason}."
msgstr "您因為%{reason}收到此電å­éƒµä»¶ã€‚"
@@ -10967,6 +11402,9 @@ msgstr "指派給自己"
msgid "attach a new file"
msgstr ""
+msgid "authored"
+msgstr ""
+
msgid "branch name"
msgstr "分支å稱"
@@ -11046,6 +11484,9 @@ msgstr "程å¼ç¢¼å“質"
msgid "ciReport|Confidence"
msgstr "ä¿¡ä»»"
+msgid "ciReport|Container Scanning"
+msgstr ""
+
msgid "ciReport|Container scanning"
msgstr "正在掃æ容器"
@@ -11138,9 +11579,6 @@ msgstr "沒有效能指標的變動"
msgid "ciReport|Performance metrics"
msgstr "效能指標"
-msgid "ciReport|Revert dismissal"
-msgstr "撤回忽略"
-
msgid "ciReport|SAST"
msgstr "SAST"
@@ -11180,6 +11618,9 @@ msgstr "載入相ä¾æ€§æŽƒæ報告時發生錯誤。"
msgid "ciReport|There was an error reverting the dismissal. Please try again."
msgstr "撤回é§å›žæ™‚發生錯誤。請é‡è©¦ã€‚"
+msgid "ciReport|Undo dismiss"
+msgstr ""
+
msgid "ciReport|Upgrade %{name} from %{version} to %{fixed}."
msgstr "å°‡ %{name} 從 %{version} å‡ç´šåˆ° %{fixed}。"
@@ -11268,9 +11709,6 @@ msgstr "幫助"
msgid "here"
msgstr "這裡"
-msgid "http://<sentry-host>/api/0/projects/{organization_slug}/{project_slug}/"
-msgstr ""
-
msgid "https://your-bitbucket-server"
msgstr "https://your-bitbucket-server"
@@ -11387,6 +11825,9 @@ msgstr "æ交你的請批準時發生錯誤。"
msgid "mrWidget|Approve"
msgstr "批准"
+msgid "mrWidget|Approve additionally"
+msgstr ""
+
msgid "mrWidget|Approved by"
msgstr "批准由"
@@ -11459,6 +11900,9 @@ msgstr "本機åˆä½µ"
msgid "mrWidget|Merge request approved"
msgstr "åˆä½µè«‹æ±‚已經批准"
+msgid "mrWidget|Merge request approved."
+msgstr ""
+
msgid "mrWidget|Merge request approved; you can approve additionally"
msgstr "åˆä½µè«‹æ±‚已經被批准; ä½ å¯ä»¥å¦å¤–批准"
@@ -11518,6 +11962,9 @@ msgstr "還原"
msgid "mrWidget|Revert this merge request in a new merge request"
msgstr "é€éŽæ–°çš„åˆä½µè«‹æ±‚還原此åˆä½µè«‹æ±‚變更的項目"
+msgid "mrWidget|Revoke approval"
+msgstr ""
+
msgid "mrWidget|Set by"
msgstr "設定者"
diff --git a/package.json b/package.json
index 9df2a3ccff7..f0f819fdb74 100644
--- a/package.json
+++ b/package.json
@@ -16,7 +16,10 @@
"prettier-staged-save": "node ./scripts/frontend/prettier.js save",
"prettier-all": "node ./scripts/frontend/prettier.js check-all",
"prettier-all-save": "node ./scripts/frontend/prettier.js save-all",
- "stylelint": "node node_modules/stylelint/bin/stylelint.js app/assets/stylesheets/**/*.*",
+ "stylelint": "node node_modules/stylelint/bin/stylelint.js app/assets/stylesheets/**/*.* --custom-formatter node_modules/stylelint-error-string-formatter",
+ "stylelint-file": "node node_modules/stylelint/bin/stylelint.js",
+ "stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js",
+ "test": "yarn jest && yarn karma",
"webpack": "webpack --config config/webpack.config.js",
"webpack-prod": "NODE_ENV=production webpack --config config/webpack.config.js"
},
@@ -29,10 +32,11 @@
"@babel/plugin-syntax-import-meta": "^7.2.0",
"@babel/preset-env": "^7.3.1",
"@gitlab/csslab": "^1.8.0",
- "@gitlab/svgs": "^1.52.0",
- "@gitlab/ui": "^2.0.4",
- "apollo-boost": "^0.1.20",
- "apollo-client": "^2.4.5",
+ "@gitlab/svgs": "^1.54.0",
+ "@gitlab/ui": "^3.0.0",
+ "apollo-boost": "^0.3.1",
+ "apollo-client": "^2.5.1",
+ "at.js": "^1.5.4",
"autosize": "^4.0.0",
"axios": "^0.17.1",
"babel-loader": "^8.0.5",
@@ -72,8 +76,10 @@
"graphql": "^14.0.2",
"imports-loader": "^0.8.0",
"jed": "^1.1.1",
+ "jest-transform-graphql": "^2.1.0",
"jquery": "^3.2.1",
"jquery-ujs": "1.2.2",
+ "jquery.caret": "^0.3.1",
"jquery.waitforimages": "^2.2.0",
"js-cookie": "^2.1.3",
"jszip": "^3.1.3",
@@ -100,6 +106,7 @@
"sql.js": "^0.4.0",
"stickyfilljs": "^2.0.5",
"style-loader": "^0.23.1",
+ "stylelint-error-string-formatter": "^1.0.1",
"svg4everybody": "2.1.9",
"three": "^0.84.0",
"three-orbit-controls": "^82.1.0",
@@ -112,7 +119,7 @@
"url-loader": "^1.1.2",
"visibilityjs": "^1.2.4",
"vue": "^2.5.21",
- "vue-apollo": "^3.0.0-beta.25",
+ "vue-apollo": "^3.0.0-beta.28",
"vue-loader": "^15.4.2",
"vue-resource": "^1.5.1",
"vue-router": "^3.0.2",
@@ -127,17 +134,14 @@
"xterm": "^3.5.0"
},
"devDependencies": {
+ "@babel/plugin-transform-modules-commonjs": "^7.2.0",
"@gitlab/eslint-config": "^1.4.0",
"@vue/test-utils": "^1.0.0-beta.25",
"axios-mock-adapter": "^1.15.0",
- "babel-core": "^7.0.0-bridge",
- "babel-jest": "^23.6.0",
+ "babel-jest": "^24.1.0",
"babel-plugin-dynamic-import-node": "^2.2.0",
"babel-plugin-istanbul": "^5.1.0",
"babel-plugin-rewire": "^1.2.0",
- "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
- "babel-template": "^6.26.0",
- "babel-types": "^6.26.0",
"chalk": "^2.4.1",
"commander": "^2.18.0",
"docdash": "^1.0.2",
@@ -147,7 +151,7 @@
"eslint-plugin-html": "5.0.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jasmine": "^2.10.1",
- "eslint-plugin-jest": "^22.1.0",
+ "eslint-plugin-jest": "^22.3.0",
"gettext-extractor": "^3.3.2",
"gettext-extractor-vue": "^4.0.1",
"graphql-tag": "^2.10.0",
@@ -155,8 +159,10 @@
"jasmine-core": "^2.9.0",
"jasmine-diff": "^0.1.3",
"jasmine-jquery": "^2.1.1",
- "jest": "^23.6.0",
- "jest-junit": "^5.2.0",
+ "jest": "^24.1.0",
+ "jest-environment-jsdom": "^24.0.0",
+ "jest-junit": "^6.3.0",
+ "jest-util": "^24.0.0",
"jsdoc": "^3.5.5",
"jsdoc-vue": "^1.0.0",
"karma": "^3.0.0",
@@ -167,16 +173,21 @@
"karma-mocha-reporter": "^2.2.5",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^4.0.0-beta.0",
+ "md5": "^2.2.1",
+ "node-sass": "^4.11.0",
"nodemon": "^1.18.9",
"pixelmatch": "^4.0.2",
"postcss": "^7.0.14",
- "prettier": "1.16.1",
+ "prettier": "1.16.4",
"stylelint": "^9.10.1",
"stylelint-config-recommended": "^2.1.0",
- "stylelint-scss": "^3.5.3",
- "vue-jest": "^3.0.2",
+ "stylelint-scss": "^3.5.4",
+ "vue-jest": "^4.0.0-beta.2",
"webpack-dev-server": "^3.1.14",
- "yarn-deduplicate": "^1.1.0"
+ "yarn-deduplicate": "^1.1.1"
+ },
+ "resolutions": {
+ "vue-jest/ts-jest": "24.0.0"
},
"engines": {
"node": ">=8.10.0",
diff --git a/app/assets/images/emoji/100.png b/public/-/emojis/1/100.png
index 6903ff0304a..6903ff0304a 100644
--- a/app/assets/images/emoji/100.png
+++ b/public/-/emojis/1/100.png
Binary files differ
diff --git a/app/assets/images/emoji/1234.png b/public/-/emojis/1/1234.png
index 248dc7e55b6..248dc7e55b6 100644
--- a/app/assets/images/emoji/1234.png
+++ b/public/-/emojis/1/1234.png
Binary files differ
diff --git a/app/assets/images/emoji/1F627.png b/public/-/emojis/1/1F627.png
index f99026a3bc7..f99026a3bc7 100644
--- a/app/assets/images/emoji/1F627.png
+++ b/public/-/emojis/1/1F627.png
Binary files differ
diff --git a/app/assets/images/emoji/8ball.png b/public/-/emojis/1/8ball.png
index 38ca662eded..38ca662eded 100644
--- a/app/assets/images/emoji/8ball.png
+++ b/public/-/emojis/1/8ball.png
Binary files differ
diff --git a/app/assets/images/emoji/a.png b/public/-/emojis/1/a.png
index 8603ff05a17..8603ff05a17 100644
--- a/app/assets/images/emoji/a.png
+++ b/public/-/emojis/1/a.png
Binary files differ
diff --git a/app/assets/images/emoji/ab.png b/public/-/emojis/1/ab.png
index d9f2d17dea0..d9f2d17dea0 100644
--- a/app/assets/images/emoji/ab.png
+++ b/public/-/emojis/1/ab.png
Binary files differ
diff --git a/app/assets/images/emoji/abc.png b/public/-/emojis/1/abc.png
index 7688de692a9..7688de692a9 100644
--- a/app/assets/images/emoji/abc.png
+++ b/public/-/emojis/1/abc.png
Binary files differ
diff --git a/app/assets/images/emoji/abcd.png b/public/-/emojis/1/abcd.png
index 0996a870570..0996a870570 100644
--- a/app/assets/images/emoji/abcd.png
+++ b/public/-/emojis/1/abcd.png
Binary files differ
diff --git a/app/assets/images/emoji/accept.png b/public/-/emojis/1/accept.png
index 8afd7ce99cf..8afd7ce99cf 100644
--- a/app/assets/images/emoji/accept.png
+++ b/public/-/emojis/1/accept.png
Binary files differ
diff --git a/app/assets/images/emoji/aerial_tramway.png b/public/-/emojis/1/aerial_tramway.png
index 3eb4b61bf1d..3eb4b61bf1d 100644
--- a/app/assets/images/emoji/aerial_tramway.png
+++ b/public/-/emojis/1/aerial_tramway.png
Binary files differ
diff --git a/app/assets/images/emoji/airplane.png b/public/-/emojis/1/airplane.png
index 268d2ac3c8e..268d2ac3c8e 100644
--- a/app/assets/images/emoji/airplane.png
+++ b/public/-/emojis/1/airplane.png
Binary files differ
diff --git a/app/assets/images/emoji/airplane_arriving.png b/public/-/emojis/1/airplane_arriving.png
index d66841962f2..d66841962f2 100644
--- a/app/assets/images/emoji/airplane_arriving.png
+++ b/public/-/emojis/1/airplane_arriving.png
Binary files differ
diff --git a/app/assets/images/emoji/airplane_departure.png b/public/-/emojis/1/airplane_departure.png
index a5766f9f4ae..a5766f9f4ae 100644
--- a/app/assets/images/emoji/airplane_departure.png
+++ b/public/-/emojis/1/airplane_departure.png
Binary files differ
diff --git a/app/assets/images/emoji/airplane_small.png b/public/-/emojis/1/airplane_small.png
index b731b15e3a8..b731b15e3a8 100644
--- a/app/assets/images/emoji/airplane_small.png
+++ b/public/-/emojis/1/airplane_small.png
Binary files differ
diff --git a/app/assets/images/emoji/alarm_clock.png b/public/-/emojis/1/alarm_clock.png
index cdbc2fbb950..cdbc2fbb950 100644
--- a/app/assets/images/emoji/alarm_clock.png
+++ b/public/-/emojis/1/alarm_clock.png
Binary files differ
diff --git a/app/assets/images/emoji/alembic.png b/public/-/emojis/1/alembic.png
index 307a7324249..307a7324249 100644
--- a/app/assets/images/emoji/alembic.png
+++ b/public/-/emojis/1/alembic.png
Binary files differ
diff --git a/app/assets/images/emoji/alien.png b/public/-/emojis/1/alien.png
index 3b90e97433b..3b90e97433b 100644
--- a/app/assets/images/emoji/alien.png
+++ b/public/-/emojis/1/alien.png
Binary files differ
diff --git a/app/assets/images/emoji/ambulance.png b/public/-/emojis/1/ambulance.png
index 6fb8076d766..6fb8076d766 100644
--- a/app/assets/images/emoji/ambulance.png
+++ b/public/-/emojis/1/ambulance.png
Binary files differ
diff --git a/app/assets/images/emoji/amphora.png b/public/-/emojis/1/amphora.png
index 96de5056059..96de5056059 100644
--- a/app/assets/images/emoji/amphora.png
+++ b/public/-/emojis/1/amphora.png
Binary files differ
diff --git a/app/assets/images/emoji/anchor.png b/public/-/emojis/1/anchor.png
index b036f70a00b..b036f70a00b 100644
--- a/app/assets/images/emoji/anchor.png
+++ b/public/-/emojis/1/anchor.png
Binary files differ
diff --git a/app/assets/images/emoji/angel.png b/public/-/emojis/1/angel.png
index 66ea97a3b99..66ea97a3b99 100644
--- a/app/assets/images/emoji/angel.png
+++ b/public/-/emojis/1/angel.png
Binary files differ
diff --git a/app/assets/images/emoji/angel_tone1.png b/public/-/emojis/1/angel_tone1.png
index 391694dc07e..391694dc07e 100644
--- a/app/assets/images/emoji/angel_tone1.png
+++ b/public/-/emojis/1/angel_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/angel_tone2.png b/public/-/emojis/1/angel_tone2.png
index 700cbe6ed2c..700cbe6ed2c 100644
--- a/app/assets/images/emoji/angel_tone2.png
+++ b/public/-/emojis/1/angel_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/angel_tone3.png b/public/-/emojis/1/angel_tone3.png
index be597437d25..be597437d25 100644
--- a/app/assets/images/emoji/angel_tone3.png
+++ b/public/-/emojis/1/angel_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/angel_tone4.png b/public/-/emojis/1/angel_tone4.png
index b06d3c853ef..b06d3c853ef 100644
--- a/app/assets/images/emoji/angel_tone4.png
+++ b/public/-/emojis/1/angel_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/angel_tone5.png b/public/-/emojis/1/angel_tone5.png
index 17bd677e334..17bd677e334 100644
--- a/app/assets/images/emoji/angel_tone5.png
+++ b/public/-/emojis/1/angel_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/anger.png b/public/-/emojis/1/anger.png
index d63c2e000e4..d63c2e000e4 100644
--- a/app/assets/images/emoji/anger.png
+++ b/public/-/emojis/1/anger.png
Binary files differ
diff --git a/app/assets/images/emoji/anger_right.png b/public/-/emojis/1/anger_right.png
index f5c97c4d297..f5c97c4d297 100644
--- a/app/assets/images/emoji/anger_right.png
+++ b/public/-/emojis/1/anger_right.png
Binary files differ
diff --git a/app/assets/images/emoji/angry.png b/public/-/emojis/1/angry.png
index cfc4a6ecde5..cfc4a6ecde5 100644
--- a/app/assets/images/emoji/angry.png
+++ b/public/-/emojis/1/angry.png
Binary files differ
diff --git a/app/assets/images/emoji/ant.png b/public/-/emojis/1/ant.png
index 994127ed6b3..994127ed6b3 100644
--- a/app/assets/images/emoji/ant.png
+++ b/public/-/emojis/1/ant.png
Binary files differ
diff --git a/app/assets/images/emoji/apple.png b/public/-/emojis/1/apple.png
index da650c60f62..da650c60f62 100644
--- a/app/assets/images/emoji/apple.png
+++ b/public/-/emojis/1/apple.png
Binary files differ
diff --git a/app/assets/images/emoji/aquarius.png b/public/-/emojis/1/aquarius.png
index 641a4f68889..641a4f68889 100644
--- a/app/assets/images/emoji/aquarius.png
+++ b/public/-/emojis/1/aquarius.png
Binary files differ
diff --git a/app/assets/images/emoji/aries.png b/public/-/emojis/1/aries.png
index 21a189d0ede..21a189d0ede 100644
--- a/app/assets/images/emoji/aries.png
+++ b/public/-/emojis/1/aries.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_backward.png b/public/-/emojis/1/arrow_backward.png
index ee38e3b038e..ee38e3b038e 100644
--- a/app/assets/images/emoji/arrow_backward.png
+++ b/public/-/emojis/1/arrow_backward.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_double_down.png b/public/-/emojis/1/arrow_double_down.png
index 90193bfcb40..90193bfcb40 100644
--- a/app/assets/images/emoji/arrow_double_down.png
+++ b/public/-/emojis/1/arrow_double_down.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_double_up.png b/public/-/emojis/1/arrow_double_up.png
index 13543d5eef2..13543d5eef2 100644
--- a/app/assets/images/emoji/arrow_double_up.png
+++ b/public/-/emojis/1/arrow_double_up.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_down.png b/public/-/emojis/1/arrow_down.png
index b8eefd0b19f..b8eefd0b19f 100644
--- a/app/assets/images/emoji/arrow_down.png
+++ b/public/-/emojis/1/arrow_down.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_down_small.png b/public/-/emojis/1/arrow_down_small.png
index 5870b9a2241..5870b9a2241 100644
--- a/app/assets/images/emoji/arrow_down_small.png
+++ b/public/-/emojis/1/arrow_down_small.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_forward.png b/public/-/emojis/1/arrow_forward.png
index 4e2b682857c..4e2b682857c 100644
--- a/app/assets/images/emoji/arrow_forward.png
+++ b/public/-/emojis/1/arrow_forward.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_heading_down.png b/public/-/emojis/1/arrow_heading_down.png
index 2d9d24bca80..2d9d24bca80 100644
--- a/app/assets/images/emoji/arrow_heading_down.png
+++ b/public/-/emojis/1/arrow_heading_down.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_heading_up.png b/public/-/emojis/1/arrow_heading_up.png
index f29bfcfc0de..f29bfcfc0de 100644
--- a/app/assets/images/emoji/arrow_heading_up.png
+++ b/public/-/emojis/1/arrow_heading_up.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_left.png b/public/-/emojis/1/arrow_left.png
index 8c685e0a81b..8c685e0a81b 100644
--- a/app/assets/images/emoji/arrow_left.png
+++ b/public/-/emojis/1/arrow_left.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_lower_left.png b/public/-/emojis/1/arrow_lower_left.png
index 88b37716078..88b37716078 100644
--- a/app/assets/images/emoji/arrow_lower_left.png
+++ b/public/-/emojis/1/arrow_lower_left.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_lower_right.png b/public/-/emojis/1/arrow_lower_right.png
index 7e807da7392..7e807da7392 100644
--- a/app/assets/images/emoji/arrow_lower_right.png
+++ b/public/-/emojis/1/arrow_lower_right.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_right.png b/public/-/emojis/1/arrow_right.png
index 4755670b5cc..4755670b5cc 100644
--- a/app/assets/images/emoji/arrow_right.png
+++ b/public/-/emojis/1/arrow_right.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_right_hook.png b/public/-/emojis/1/arrow_right_hook.png
index e7258ad3268..e7258ad3268 100644
--- a/app/assets/images/emoji/arrow_right_hook.png
+++ b/public/-/emojis/1/arrow_right_hook.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_up.png b/public/-/emojis/1/arrow_up.png
index af8218a87f7..af8218a87f7 100644
--- a/app/assets/images/emoji/arrow_up.png
+++ b/public/-/emojis/1/arrow_up.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_up_down.png b/public/-/emojis/1/arrow_up_down.png
index dfa32b97186..dfa32b97186 100644
--- a/app/assets/images/emoji/arrow_up_down.png
+++ b/public/-/emojis/1/arrow_up_down.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_up_small.png b/public/-/emojis/1/arrow_up_small.png
index 20a13dcd5cd..20a13dcd5cd 100644
--- a/app/assets/images/emoji/arrow_up_small.png
+++ b/public/-/emojis/1/arrow_up_small.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_upper_left.png b/public/-/emojis/1/arrow_upper_left.png
index f38718fbe34..f38718fbe34 100644
--- a/app/assets/images/emoji/arrow_upper_left.png
+++ b/public/-/emojis/1/arrow_upper_left.png
Binary files differ
diff --git a/app/assets/images/emoji/arrow_upper_right.png b/public/-/emojis/1/arrow_upper_right.png
index c43e12d0f64..c43e12d0f64 100644
--- a/app/assets/images/emoji/arrow_upper_right.png
+++ b/public/-/emojis/1/arrow_upper_right.png
Binary files differ
diff --git a/app/assets/images/emoji/arrows_clockwise.png b/public/-/emojis/1/arrows_clockwise.png
index 26e49c38388..26e49c38388 100644
--- a/app/assets/images/emoji/arrows_clockwise.png
+++ b/public/-/emojis/1/arrows_clockwise.png
Binary files differ
diff --git a/app/assets/images/emoji/arrows_counterclockwise.png b/public/-/emojis/1/arrows_counterclockwise.png
index 8d06d8e0912..8d06d8e0912 100644
--- a/app/assets/images/emoji/arrows_counterclockwise.png
+++ b/public/-/emojis/1/arrows_counterclockwise.png
Binary files differ
diff --git a/app/assets/images/emoji/art.png b/public/-/emojis/1/art.png
index bd6afe9ff06..bd6afe9ff06 100644
--- a/app/assets/images/emoji/art.png
+++ b/public/-/emojis/1/art.png
Binary files differ
diff --git a/app/assets/images/emoji/articulated_lorry.png b/public/-/emojis/1/articulated_lorry.png
index c8217317132..c8217317132 100644
--- a/app/assets/images/emoji/articulated_lorry.png
+++ b/public/-/emojis/1/articulated_lorry.png
Binary files differ
diff --git a/app/assets/images/emoji/asterisk.png b/public/-/emojis/1/asterisk.png
index 2f8e5113803..2f8e5113803 100644
--- a/app/assets/images/emoji/asterisk.png
+++ b/public/-/emojis/1/asterisk.png
Binary files differ
diff --git a/app/assets/images/emoji/astonished.png b/public/-/emojis/1/astonished.png
index bd0ac55ec8e..bd0ac55ec8e 100644
--- a/app/assets/images/emoji/astonished.png
+++ b/public/-/emojis/1/astonished.png
Binary files differ
diff --git a/app/assets/images/emoji/athletic_shoe.png b/public/-/emojis/1/athletic_shoe.png
index 423fa07dd5d..423fa07dd5d 100644
--- a/app/assets/images/emoji/athletic_shoe.png
+++ b/public/-/emojis/1/athletic_shoe.png
Binary files differ
diff --git a/app/assets/images/emoji/atm.png b/public/-/emojis/1/atm.png
index 4d935307b94..4d935307b94 100644
--- a/app/assets/images/emoji/atm.png
+++ b/public/-/emojis/1/atm.png
Binary files differ
diff --git a/app/assets/images/emoji/atom.png b/public/-/emojis/1/atom.png
index 5f4567aa093..5f4567aa093 100644
--- a/app/assets/images/emoji/atom.png
+++ b/public/-/emojis/1/atom.png
Binary files differ
diff --git a/app/assets/images/emoji/avocado.png b/public/-/emojis/1/avocado.png
index 06f0d124aed..06f0d124aed 100644
--- a/app/assets/images/emoji/avocado.png
+++ b/public/-/emojis/1/avocado.png
Binary files differ
diff --git a/app/assets/images/emoji/b.png b/public/-/emojis/1/b.png
index 25875bc6a14..25875bc6a14 100644
--- a/app/assets/images/emoji/b.png
+++ b/public/-/emojis/1/b.png
Binary files differ
diff --git a/app/assets/images/emoji/baby.png b/public/-/emojis/1/baby.png
index a4af92c63c7..a4af92c63c7 100644
--- a/app/assets/images/emoji/baby.png
+++ b/public/-/emojis/1/baby.png
Binary files differ
diff --git a/app/assets/images/emoji/baby_bottle.png b/public/-/emojis/1/baby_bottle.png
index 2bd10524180..2bd10524180 100644
--- a/app/assets/images/emoji/baby_bottle.png
+++ b/public/-/emojis/1/baby_bottle.png
Binary files differ
diff --git a/app/assets/images/emoji/baby_chick.png b/public/-/emojis/1/baby_chick.png
index dccd96576ea..dccd96576ea 100644
--- a/app/assets/images/emoji/baby_chick.png
+++ b/public/-/emojis/1/baby_chick.png
Binary files differ
diff --git a/app/assets/images/emoji/baby_symbol.png b/public/-/emojis/1/baby_symbol.png
index 64a10b71710..64a10b71710 100644
--- a/app/assets/images/emoji/baby_symbol.png
+++ b/public/-/emojis/1/baby_symbol.png
Binary files differ
diff --git a/app/assets/images/emoji/baby_tone1.png b/public/-/emojis/1/baby_tone1.png
index d20911d40db..d20911d40db 100644
--- a/app/assets/images/emoji/baby_tone1.png
+++ b/public/-/emojis/1/baby_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/baby_tone2.png b/public/-/emojis/1/baby_tone2.png
index b0a9b30ed17..b0a9b30ed17 100644
--- a/app/assets/images/emoji/baby_tone2.png
+++ b/public/-/emojis/1/baby_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/baby_tone3.png b/public/-/emojis/1/baby_tone3.png
index 7de5286fac1..7de5286fac1 100644
--- a/app/assets/images/emoji/baby_tone3.png
+++ b/public/-/emojis/1/baby_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/baby_tone4.png b/public/-/emojis/1/baby_tone4.png
index 9b7a86ac615..9b7a86ac615 100644
--- a/app/assets/images/emoji/baby_tone4.png
+++ b/public/-/emojis/1/baby_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/baby_tone5.png b/public/-/emojis/1/baby_tone5.png
index fe1be34cb88..fe1be34cb88 100644
--- a/app/assets/images/emoji/baby_tone5.png
+++ b/public/-/emojis/1/baby_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/back.png b/public/-/emojis/1/back.png
index d32c5d4f17f..d32c5d4f17f 100644
--- a/app/assets/images/emoji/back.png
+++ b/public/-/emojis/1/back.png
Binary files differ
diff --git a/app/assets/images/emoji/bacon.png b/public/-/emojis/1/bacon.png
index f38a485fbe4..f38a485fbe4 100644
--- a/app/assets/images/emoji/bacon.png
+++ b/public/-/emojis/1/bacon.png
Binary files differ
diff --git a/app/assets/images/emoji/badminton.png b/public/-/emojis/1/badminton.png
index 7ba15708990..7ba15708990 100644
--- a/app/assets/images/emoji/badminton.png
+++ b/public/-/emojis/1/badminton.png
Binary files differ
diff --git a/app/assets/images/emoji/baggage_claim.png b/public/-/emojis/1/baggage_claim.png
index 409b593e78a..409b593e78a 100644
--- a/app/assets/images/emoji/baggage_claim.png
+++ b/public/-/emojis/1/baggage_claim.png
Binary files differ
diff --git a/app/assets/images/emoji/balloon.png b/public/-/emojis/1/balloon.png
index 07916fe6df1..07916fe6df1 100644
--- a/app/assets/images/emoji/balloon.png
+++ b/public/-/emojis/1/balloon.png
Binary files differ
diff --git a/app/assets/images/emoji/ballot_box.png b/public/-/emojis/1/ballot_box.png
index 9b6767aea9e..9b6767aea9e 100644
--- a/app/assets/images/emoji/ballot_box.png
+++ b/public/-/emojis/1/ballot_box.png
Binary files differ
diff --git a/app/assets/images/emoji/ballot_box_with_check.png b/public/-/emojis/1/ballot_box_with_check.png
index 284d9573847..284d9573847 100644
--- a/app/assets/images/emoji/ballot_box_with_check.png
+++ b/public/-/emojis/1/ballot_box_with_check.png
Binary files differ
diff --git a/app/assets/images/emoji/bamboo.png b/public/-/emojis/1/bamboo.png
index 5d5e0e728a0..5d5e0e728a0 100644
--- a/app/assets/images/emoji/bamboo.png
+++ b/public/-/emojis/1/bamboo.png
Binary files differ
diff --git a/app/assets/images/emoji/banana.png b/public/-/emojis/1/banana.png
index f4987279580..f4987279580 100644
--- a/app/assets/images/emoji/banana.png
+++ b/public/-/emojis/1/banana.png
Binary files differ
diff --git a/app/assets/images/emoji/bangbang.png b/public/-/emojis/1/bangbang.png
index 58a9c528fca..58a9c528fca 100644
--- a/app/assets/images/emoji/bangbang.png
+++ b/public/-/emojis/1/bangbang.png
Binary files differ
diff --git a/app/assets/images/emoji/bank.png b/public/-/emojis/1/bank.png
index dffdcef36a1..dffdcef36a1 100644
--- a/app/assets/images/emoji/bank.png
+++ b/public/-/emojis/1/bank.png
Binary files differ
diff --git a/app/assets/images/emoji/bar_chart.png b/public/-/emojis/1/bar_chart.png
index 53c89455008..53c89455008 100644
--- a/app/assets/images/emoji/bar_chart.png
+++ b/public/-/emojis/1/bar_chart.png
Binary files differ
diff --git a/app/assets/images/emoji/barber.png b/public/-/emojis/1/barber.png
index 896f4d716cf..896f4d716cf 100644
--- a/app/assets/images/emoji/barber.png
+++ b/public/-/emojis/1/barber.png
Binary files differ
diff --git a/app/assets/images/emoji/baseball.png b/public/-/emojis/1/baseball.png
index f8463f1538b..f8463f1538b 100644
--- a/app/assets/images/emoji/baseball.png
+++ b/public/-/emojis/1/baseball.png
Binary files differ
diff --git a/app/assets/images/emoji/basketball.png b/public/-/emojis/1/basketball.png
index 64c76b79c6d..64c76b79c6d 100644
--- a/app/assets/images/emoji/basketball.png
+++ b/public/-/emojis/1/basketball.png
Binary files differ
diff --git a/app/assets/images/emoji/basketball_player.png b/public/-/emojis/1/basketball_player.png
index 8ce90c5cad6..8ce90c5cad6 100644
--- a/app/assets/images/emoji/basketball_player.png
+++ b/public/-/emojis/1/basketball_player.png
Binary files differ
diff --git a/app/assets/images/emoji/basketball_player_tone1.png b/public/-/emojis/1/basketball_player_tone1.png
index cd12c7ab9bf..cd12c7ab9bf 100644
--- a/app/assets/images/emoji/basketball_player_tone1.png
+++ b/public/-/emojis/1/basketball_player_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/basketball_player_tone2.png b/public/-/emojis/1/basketball_player_tone2.png
index f892fd596da..f892fd596da 100644
--- a/app/assets/images/emoji/basketball_player_tone2.png
+++ b/public/-/emojis/1/basketball_player_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/basketball_player_tone3.png b/public/-/emojis/1/basketball_player_tone3.png
index e109997a91a..e109997a91a 100644
--- a/app/assets/images/emoji/basketball_player_tone3.png
+++ b/public/-/emojis/1/basketball_player_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/basketball_player_tone4.png b/public/-/emojis/1/basketball_player_tone4.png
index 3b90b946af4..3b90b946af4 100644
--- a/app/assets/images/emoji/basketball_player_tone4.png
+++ b/public/-/emojis/1/basketball_player_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/basketball_player_tone5.png b/public/-/emojis/1/basketball_player_tone5.png
index bafed7828a7..bafed7828a7 100644
--- a/app/assets/images/emoji/basketball_player_tone5.png
+++ b/public/-/emojis/1/basketball_player_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/bat.png b/public/-/emojis/1/bat.png
index 3152c047e00..3152c047e00 100644
--- a/app/assets/images/emoji/bat.png
+++ b/public/-/emojis/1/bat.png
Binary files differ
diff --git a/app/assets/images/emoji/bath.png b/public/-/emojis/1/bath.png
index 43fba5c8a28..43fba5c8a28 100644
--- a/app/assets/images/emoji/bath.png
+++ b/public/-/emojis/1/bath.png
Binary files differ
diff --git a/app/assets/images/emoji/bath_tone1.png b/public/-/emojis/1/bath_tone1.png
index 2152eabf2f5..2152eabf2f5 100644
--- a/app/assets/images/emoji/bath_tone1.png
+++ b/public/-/emojis/1/bath_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/bath_tone2.png b/public/-/emojis/1/bath_tone2.png
index 2102e6133e3..2102e6133e3 100644
--- a/app/assets/images/emoji/bath_tone2.png
+++ b/public/-/emojis/1/bath_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/bath_tone3.png b/public/-/emojis/1/bath_tone3.png
index fae66181e9f..fae66181e9f 100644
--- a/app/assets/images/emoji/bath_tone3.png
+++ b/public/-/emojis/1/bath_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/bath_tone4.png b/public/-/emojis/1/bath_tone4.png
index 1f8959d0d99..1f8959d0d99 100644
--- a/app/assets/images/emoji/bath_tone4.png
+++ b/public/-/emojis/1/bath_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/bath_tone5.png b/public/-/emojis/1/bath_tone5.png
index c8a08e84f25..c8a08e84f25 100644
--- a/app/assets/images/emoji/bath_tone5.png
+++ b/public/-/emojis/1/bath_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/bathtub.png b/public/-/emojis/1/bathtub.png
index 9a5f09361eb..9a5f09361eb 100644
--- a/app/assets/images/emoji/bathtub.png
+++ b/public/-/emojis/1/bathtub.png
Binary files differ
diff --git a/app/assets/images/emoji/battery.png b/public/-/emojis/1/battery.png
index f593e2bdb65..f593e2bdb65 100644
--- a/app/assets/images/emoji/battery.png
+++ b/public/-/emojis/1/battery.png
Binary files differ
diff --git a/app/assets/images/emoji/beach.png b/public/-/emojis/1/beach.png
index 69108c8ea10..69108c8ea10 100644
--- a/app/assets/images/emoji/beach.png
+++ b/public/-/emojis/1/beach.png
Binary files differ
diff --git a/app/assets/images/emoji/beach_umbrella.png b/public/-/emojis/1/beach_umbrella.png
index 220a74f8132..220a74f8132 100644
--- a/app/assets/images/emoji/beach_umbrella.png
+++ b/public/-/emojis/1/beach_umbrella.png
Binary files differ
diff --git a/app/assets/images/emoji/bear.png b/public/-/emojis/1/bear.png
index 272d56bbbcc..272d56bbbcc 100644
--- a/app/assets/images/emoji/bear.png
+++ b/public/-/emojis/1/bear.png
Binary files differ
diff --git a/app/assets/images/emoji/bed.png b/public/-/emojis/1/bed.png
index 86f964e245d..86f964e245d 100644
--- a/app/assets/images/emoji/bed.png
+++ b/public/-/emojis/1/bed.png
Binary files differ
diff --git a/app/assets/images/emoji/bee.png b/public/-/emojis/1/bee.png
index 46156060096..46156060096 100644
--- a/app/assets/images/emoji/bee.png
+++ b/public/-/emojis/1/bee.png
Binary files differ
diff --git a/app/assets/images/emoji/beer.png b/public/-/emojis/1/beer.png
index b6d73dc0b7a..b6d73dc0b7a 100644
--- a/app/assets/images/emoji/beer.png
+++ b/public/-/emojis/1/beer.png
Binary files differ
diff --git a/app/assets/images/emoji/beers.png b/public/-/emojis/1/beers.png
index b55deb66b41..b55deb66b41 100644
--- a/app/assets/images/emoji/beers.png
+++ b/public/-/emojis/1/beers.png
Binary files differ
diff --git a/app/assets/images/emoji/beetle.png b/public/-/emojis/1/beetle.png
index 3d93174d7fc..3d93174d7fc 100644
--- a/app/assets/images/emoji/beetle.png
+++ b/public/-/emojis/1/beetle.png
Binary files differ
diff --git a/app/assets/images/emoji/beginner.png b/public/-/emojis/1/beginner.png
index bc434fb7cb5..bc434fb7cb5 100644
--- a/app/assets/images/emoji/beginner.png
+++ b/public/-/emojis/1/beginner.png
Binary files differ
diff --git a/app/assets/images/emoji/bell.png b/public/-/emojis/1/bell.png
index 5b3b0461999..5b3b0461999 100644
--- a/app/assets/images/emoji/bell.png
+++ b/public/-/emojis/1/bell.png
Binary files differ
diff --git a/app/assets/images/emoji/bellhop.png b/public/-/emojis/1/bellhop.png
index 6b3297ceaf7..6b3297ceaf7 100644
--- a/app/assets/images/emoji/bellhop.png
+++ b/public/-/emojis/1/bellhop.png
Binary files differ
diff --git a/app/assets/images/emoji/bento.png b/public/-/emojis/1/bento.png
index 83d41ca7eb9..83d41ca7eb9 100644
--- a/app/assets/images/emoji/bento.png
+++ b/public/-/emojis/1/bento.png
Binary files differ
diff --git a/app/assets/images/emoji/bicyclist.png b/public/-/emojis/1/bicyclist.png
index 9274da11048..9274da11048 100644
--- a/app/assets/images/emoji/bicyclist.png
+++ b/public/-/emojis/1/bicyclist.png
Binary files differ
diff --git a/app/assets/images/emoji/bicyclist_tone1.png b/public/-/emojis/1/bicyclist_tone1.png
index decc2f728fe..decc2f728fe 100644
--- a/app/assets/images/emoji/bicyclist_tone1.png
+++ b/public/-/emojis/1/bicyclist_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/bicyclist_tone2.png b/public/-/emojis/1/bicyclist_tone2.png
index 0067717b80a..0067717b80a 100644
--- a/app/assets/images/emoji/bicyclist_tone2.png
+++ b/public/-/emojis/1/bicyclist_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/bicyclist_tone3.png b/public/-/emojis/1/bicyclist_tone3.png
index a4f7b5e2776..a4f7b5e2776 100644
--- a/app/assets/images/emoji/bicyclist_tone3.png
+++ b/public/-/emojis/1/bicyclist_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/bicyclist_tone4.png b/public/-/emojis/1/bicyclist_tone4.png
index a3c8a797db4..a3c8a797db4 100644
--- a/app/assets/images/emoji/bicyclist_tone4.png
+++ b/public/-/emojis/1/bicyclist_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/bicyclist_tone5.png b/public/-/emojis/1/bicyclist_tone5.png
index 1606a874051..1606a874051 100644
--- a/app/assets/images/emoji/bicyclist_tone5.png
+++ b/public/-/emojis/1/bicyclist_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/bike.png b/public/-/emojis/1/bike.png
index 556ed70f1a7..556ed70f1a7 100644
--- a/app/assets/images/emoji/bike.png
+++ b/public/-/emojis/1/bike.png
Binary files differ
diff --git a/app/assets/images/emoji/bikini.png b/public/-/emojis/1/bikini.png
index 77a8a0aae5b..77a8a0aae5b 100644
--- a/app/assets/images/emoji/bikini.png
+++ b/public/-/emojis/1/bikini.png
Binary files differ
diff --git a/app/assets/images/emoji/biohazard.png b/public/-/emojis/1/biohazard.png
index 007b4fc2d85..007b4fc2d85 100644
--- a/app/assets/images/emoji/biohazard.png
+++ b/public/-/emojis/1/biohazard.png
Binary files differ
diff --git a/app/assets/images/emoji/bird.png b/public/-/emojis/1/bird.png
index e201c22be33..e201c22be33 100644
--- a/app/assets/images/emoji/bird.png
+++ b/public/-/emojis/1/bird.png
Binary files differ
diff --git a/app/assets/images/emoji/birthday.png b/public/-/emojis/1/birthday.png
index 317e9a41949..317e9a41949 100644
--- a/app/assets/images/emoji/birthday.png
+++ b/public/-/emojis/1/birthday.png
Binary files differ
diff --git a/app/assets/images/emoji/black_circle.png b/public/-/emojis/1/black_circle.png
index b62b87170e8..b62b87170e8 100644
--- a/app/assets/images/emoji/black_circle.png
+++ b/public/-/emojis/1/black_circle.png
Binary files differ
diff --git a/app/assets/images/emoji/black_heart.png b/public/-/emojis/1/black_heart.png
index b4068c3e6e8..b4068c3e6e8 100644
--- a/app/assets/images/emoji/black_heart.png
+++ b/public/-/emojis/1/black_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/black_joker.png b/public/-/emojis/1/black_joker.png
index 3d0924b68aa..3d0924b68aa 100644
--- a/app/assets/images/emoji/black_joker.png
+++ b/public/-/emojis/1/black_joker.png
Binary files differ
diff --git a/app/assets/images/emoji/black_large_square.png b/public/-/emojis/1/black_large_square.png
index 162f2bb4290..162f2bb4290 100644
--- a/app/assets/images/emoji/black_large_square.png
+++ b/public/-/emojis/1/black_large_square.png
Binary files differ
diff --git a/app/assets/images/emoji/black_medium_small_square.png b/public/-/emojis/1/black_medium_small_square.png
index 39765bba610..39765bba610 100644
--- a/app/assets/images/emoji/black_medium_small_square.png
+++ b/public/-/emojis/1/black_medium_small_square.png
Binary files differ
diff --git a/app/assets/images/emoji/black_medium_square.png b/public/-/emojis/1/black_medium_square.png
index 05a30a6aa2d..05a30a6aa2d 100644
--- a/app/assets/images/emoji/black_medium_square.png
+++ b/public/-/emojis/1/black_medium_square.png
Binary files differ
diff --git a/app/assets/images/emoji/black_nib.png b/public/-/emojis/1/black_nib.png
index 872d0ae1598..872d0ae1598 100644
--- a/app/assets/images/emoji/black_nib.png
+++ b/public/-/emojis/1/black_nib.png
Binary files differ
diff --git a/app/assets/images/emoji/black_small_square.png b/public/-/emojis/1/black_small_square.png
index 48595d3e1a9..48595d3e1a9 100644
--- a/app/assets/images/emoji/black_small_square.png
+++ b/public/-/emojis/1/black_small_square.png
Binary files differ
diff --git a/app/assets/images/emoji/black_square_button.png b/public/-/emojis/1/black_square_button.png
index a78fc2f6b63..a78fc2f6b63 100644
--- a/app/assets/images/emoji/black_square_button.png
+++ b/public/-/emojis/1/black_square_button.png
Binary files differ
diff --git a/app/assets/images/emoji/blossom.png b/public/-/emojis/1/blossom.png
index 4083026c157..4083026c157 100644
--- a/app/assets/images/emoji/blossom.png
+++ b/public/-/emojis/1/blossom.png
Binary files differ
diff --git a/app/assets/images/emoji/blowfish.png b/public/-/emojis/1/blowfish.png
index a10f4f84e35..a10f4f84e35 100644
--- a/app/assets/images/emoji/blowfish.png
+++ b/public/-/emojis/1/blowfish.png
Binary files differ
diff --git a/app/assets/images/emoji/blue_book.png b/public/-/emojis/1/blue_book.png
index e1e455401cc..e1e455401cc 100644
--- a/app/assets/images/emoji/blue_book.png
+++ b/public/-/emojis/1/blue_book.png
Binary files differ
diff --git a/app/assets/images/emoji/blue_car.png b/public/-/emojis/1/blue_car.png
index e8ba817d393..e8ba817d393 100644
--- a/app/assets/images/emoji/blue_car.png
+++ b/public/-/emojis/1/blue_car.png
Binary files differ
diff --git a/app/assets/images/emoji/blue_heart.png b/public/-/emojis/1/blue_heart.png
index bdf1287e55e..bdf1287e55e 100644
--- a/app/assets/images/emoji/blue_heart.png
+++ b/public/-/emojis/1/blue_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/blush.png b/public/-/emojis/1/blush.png
index aac1a424ad4..aac1a424ad4 100644
--- a/app/assets/images/emoji/blush.png
+++ b/public/-/emojis/1/blush.png
Binary files differ
diff --git a/app/assets/images/emoji/boar.png b/public/-/emojis/1/boar.png
index fead972633c..fead972633c 100644
--- a/app/assets/images/emoji/boar.png
+++ b/public/-/emojis/1/boar.png
Binary files differ
diff --git a/app/assets/images/emoji/bomb.png b/public/-/emojis/1/bomb.png
index c7f8f81c939..c7f8f81c939 100644
--- a/app/assets/images/emoji/bomb.png
+++ b/public/-/emojis/1/bomb.png
Binary files differ
diff --git a/app/assets/images/emoji/book.png b/public/-/emojis/1/book.png
index 0f4447ed396..0f4447ed396 100644
--- a/app/assets/images/emoji/book.png
+++ b/public/-/emojis/1/book.png
Binary files differ
diff --git a/app/assets/images/emoji/bookmark.png b/public/-/emojis/1/bookmark.png
index bbb444611f0..bbb444611f0 100644
--- a/app/assets/images/emoji/bookmark.png
+++ b/public/-/emojis/1/bookmark.png
Binary files differ
diff --git a/app/assets/images/emoji/bookmark_tabs.png b/public/-/emojis/1/bookmark_tabs.png
index f8d9e01b428..f8d9e01b428 100644
--- a/app/assets/images/emoji/bookmark_tabs.png
+++ b/public/-/emojis/1/bookmark_tabs.png
Binary files differ
diff --git a/app/assets/images/emoji/books.png b/public/-/emojis/1/books.png
index 59a8bafeb0d..59a8bafeb0d 100644
--- a/app/assets/images/emoji/books.png
+++ b/public/-/emojis/1/books.png
Binary files differ
diff --git a/app/assets/images/emoji/boom.png b/public/-/emojis/1/boom.png
index 9b0f027b1a8..9b0f027b1a8 100644
--- a/app/assets/images/emoji/boom.png
+++ b/public/-/emojis/1/boom.png
Binary files differ
diff --git a/app/assets/images/emoji/boot.png b/public/-/emojis/1/boot.png
index 11f1065ed07..11f1065ed07 100644
--- a/app/assets/images/emoji/boot.png
+++ b/public/-/emojis/1/boot.png
Binary files differ
diff --git a/app/assets/images/emoji/bouquet.png b/public/-/emojis/1/bouquet.png
index 11455af6df4..11455af6df4 100644
--- a/app/assets/images/emoji/bouquet.png
+++ b/public/-/emojis/1/bouquet.png
Binary files differ
diff --git a/app/assets/images/emoji/bow.png b/public/-/emojis/1/bow.png
index d8f793088dc..d8f793088dc 100644
--- a/app/assets/images/emoji/bow.png
+++ b/public/-/emojis/1/bow.png
Binary files differ
diff --git a/app/assets/images/emoji/bow_and_arrow.png b/public/-/emojis/1/bow_and_arrow.png
index 6a538bf475f..6a538bf475f 100644
--- a/app/assets/images/emoji/bow_and_arrow.png
+++ b/public/-/emojis/1/bow_and_arrow.png
Binary files differ
diff --git a/app/assets/images/emoji/bow_tone1.png b/public/-/emojis/1/bow_tone1.png
index 87afb7b54cf..87afb7b54cf 100644
--- a/app/assets/images/emoji/bow_tone1.png
+++ b/public/-/emojis/1/bow_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/bow_tone2.png b/public/-/emojis/1/bow_tone2.png
index 3ccf7dc0850..3ccf7dc0850 100644
--- a/app/assets/images/emoji/bow_tone2.png
+++ b/public/-/emojis/1/bow_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/bow_tone3.png b/public/-/emojis/1/bow_tone3.png
index 8b9eb64f926..8b9eb64f926 100644
--- a/app/assets/images/emoji/bow_tone3.png
+++ b/public/-/emojis/1/bow_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/bow_tone4.png b/public/-/emojis/1/bow_tone4.png
index 683795ff40d..683795ff40d 100644
--- a/app/assets/images/emoji/bow_tone4.png
+++ b/public/-/emojis/1/bow_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/bow_tone5.png b/public/-/emojis/1/bow_tone5.png
index 7969d971752..7969d971752 100644
--- a/app/assets/images/emoji/bow_tone5.png
+++ b/public/-/emojis/1/bow_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/bowling.png b/public/-/emojis/1/bowling.png
index 63add89e53b..63add89e53b 100644
--- a/app/assets/images/emoji/bowling.png
+++ b/public/-/emojis/1/bowling.png
Binary files differ
diff --git a/app/assets/images/emoji/boxing_glove.png b/public/-/emojis/1/boxing_glove.png
index 9838f24e51a..9838f24e51a 100644
--- a/app/assets/images/emoji/boxing_glove.png
+++ b/public/-/emojis/1/boxing_glove.png
Binary files differ
diff --git a/app/assets/images/emoji/boy.png b/public/-/emojis/1/boy.png
index 8ecfb0a4e92..8ecfb0a4e92 100644
--- a/app/assets/images/emoji/boy.png
+++ b/public/-/emojis/1/boy.png
Binary files differ
diff --git a/app/assets/images/emoji/boy_tone1.png b/public/-/emojis/1/boy_tone1.png
index 2fc436ea512..2fc436ea512 100644
--- a/app/assets/images/emoji/boy_tone1.png
+++ b/public/-/emojis/1/boy_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/boy_tone2.png b/public/-/emojis/1/boy_tone2.png
index 09a5f18d360..09a5f18d360 100644
--- a/app/assets/images/emoji/boy_tone2.png
+++ b/public/-/emojis/1/boy_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/boy_tone3.png b/public/-/emojis/1/boy_tone3.png
index 3cfe675dd3a..3cfe675dd3a 100644
--- a/app/assets/images/emoji/boy_tone3.png
+++ b/public/-/emojis/1/boy_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/boy_tone4.png b/public/-/emojis/1/boy_tone4.png
index 780be0ace36..780be0ace36 100644
--- a/app/assets/images/emoji/boy_tone4.png
+++ b/public/-/emojis/1/boy_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/boy_tone5.png b/public/-/emojis/1/boy_tone5.png
index f32fe22e35c..f32fe22e35c 100644
--- a/app/assets/images/emoji/boy_tone5.png
+++ b/public/-/emojis/1/boy_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/bread.png b/public/-/emojis/1/bread.png
index 6676510aaa5..6676510aaa5 100644
--- a/app/assets/images/emoji/bread.png
+++ b/public/-/emojis/1/bread.png
Binary files differ
diff --git a/app/assets/images/emoji/bride_with_veil.png b/public/-/emojis/1/bride_with_veil.png
index eaf4bd97890..eaf4bd97890 100644
--- a/app/assets/images/emoji/bride_with_veil.png
+++ b/public/-/emojis/1/bride_with_veil.png
Binary files differ
diff --git a/app/assets/images/emoji/bride_with_veil_tone1.png b/public/-/emojis/1/bride_with_veil_tone1.png
index c4fb141ae8f..c4fb141ae8f 100644
--- a/app/assets/images/emoji/bride_with_veil_tone1.png
+++ b/public/-/emojis/1/bride_with_veil_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/bride_with_veil_tone2.png b/public/-/emojis/1/bride_with_veil_tone2.png
index c248769fc06..c248769fc06 100644
--- a/app/assets/images/emoji/bride_with_veil_tone2.png
+++ b/public/-/emojis/1/bride_with_veil_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/bride_with_veil_tone3.png b/public/-/emojis/1/bride_with_veil_tone3.png
index 962c0a6eedb..962c0a6eedb 100644
--- a/app/assets/images/emoji/bride_with_veil_tone3.png
+++ b/public/-/emojis/1/bride_with_veil_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/bride_with_veil_tone4.png b/public/-/emojis/1/bride_with_veil_tone4.png
index 740ca208cd4..740ca208cd4 100644
--- a/app/assets/images/emoji/bride_with_veil_tone4.png
+++ b/public/-/emojis/1/bride_with_veil_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/bride_with_veil_tone5.png b/public/-/emojis/1/bride_with_veil_tone5.png
index 5cc5598587d..5cc5598587d 100644
--- a/app/assets/images/emoji/bride_with_veil_tone5.png
+++ b/public/-/emojis/1/bride_with_veil_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/bridge_at_night.png b/public/-/emojis/1/bridge_at_night.png
index 1d444e0be65..1d444e0be65 100644
--- a/app/assets/images/emoji/bridge_at_night.png
+++ b/public/-/emojis/1/bridge_at_night.png
Binary files differ
diff --git a/app/assets/images/emoji/briefcase.png b/public/-/emojis/1/briefcase.png
index b9912ba2148..b9912ba2148 100644
--- a/app/assets/images/emoji/briefcase.png
+++ b/public/-/emojis/1/briefcase.png
Binary files differ
diff --git a/app/assets/images/emoji/broken_heart.png b/public/-/emojis/1/broken_heart.png
index 718e26ee122..718e26ee122 100644
--- a/app/assets/images/emoji/broken_heart.png
+++ b/public/-/emojis/1/broken_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/bug.png b/public/-/emojis/1/bug.png
index e64e72f259a..e64e72f259a 100644
--- a/app/assets/images/emoji/bug.png
+++ b/public/-/emojis/1/bug.png
Binary files differ
diff --git a/app/assets/images/emoji/bulb.png b/public/-/emojis/1/bulb.png
index 38e32e02d9f..38e32e02d9f 100644
--- a/app/assets/images/emoji/bulb.png
+++ b/public/-/emojis/1/bulb.png
Binary files differ
diff --git a/app/assets/images/emoji/bullettrain_front.png b/public/-/emojis/1/bullettrain_front.png
index 4f698e056fa..4f698e056fa 100644
--- a/app/assets/images/emoji/bullettrain_front.png
+++ b/public/-/emojis/1/bullettrain_front.png
Binary files differ
diff --git a/app/assets/images/emoji/bullettrain_side.png b/public/-/emojis/1/bullettrain_side.png
index ed61c67bf07..ed61c67bf07 100644
--- a/app/assets/images/emoji/bullettrain_side.png
+++ b/public/-/emojis/1/bullettrain_side.png
Binary files differ
diff --git a/app/assets/images/emoji/burrito.png b/public/-/emojis/1/burrito.png
index 02bd5601df7..02bd5601df7 100644
--- a/app/assets/images/emoji/burrito.png
+++ b/public/-/emojis/1/burrito.png
Binary files differ
diff --git a/app/assets/images/emoji/bus.png b/public/-/emojis/1/bus.png
index 641ddc56ca7..641ddc56ca7 100644
--- a/app/assets/images/emoji/bus.png
+++ b/public/-/emojis/1/bus.png
Binary files differ
diff --git a/app/assets/images/emoji/busstop.png b/public/-/emojis/1/busstop.png
index b2b62208bfd..b2b62208bfd 100644
--- a/app/assets/images/emoji/busstop.png
+++ b/public/-/emojis/1/busstop.png
Binary files differ
diff --git a/app/assets/images/emoji/bust_in_silhouette.png b/public/-/emojis/1/bust_in_silhouette.png
index 123b2cbe1fb..123b2cbe1fb 100644
--- a/app/assets/images/emoji/bust_in_silhouette.png
+++ b/public/-/emojis/1/bust_in_silhouette.png
Binary files differ
diff --git a/app/assets/images/emoji/busts_in_silhouette.png b/public/-/emojis/1/busts_in_silhouette.png
index d7656860a1c..d7656860a1c 100644
--- a/app/assets/images/emoji/busts_in_silhouette.png
+++ b/public/-/emojis/1/busts_in_silhouette.png
Binary files differ
diff --git a/app/assets/images/emoji/butterfly.png b/public/-/emojis/1/butterfly.png
index 5631fe99226..5631fe99226 100644
--- a/app/assets/images/emoji/butterfly.png
+++ b/public/-/emojis/1/butterfly.png
Binary files differ
diff --git a/app/assets/images/emoji/cactus.png b/public/-/emojis/1/cactus.png
index 9b48ccf3d0c..9b48ccf3d0c 100644
--- a/app/assets/images/emoji/cactus.png
+++ b/public/-/emojis/1/cactus.png
Binary files differ
diff --git a/app/assets/images/emoji/cake.png b/public/-/emojis/1/cake.png
index 4368177be9a..4368177be9a 100644
--- a/app/assets/images/emoji/cake.png
+++ b/public/-/emojis/1/cake.png
Binary files differ
diff --git a/app/assets/images/emoji/calendar.png b/public/-/emojis/1/calendar.png
index 47353b74447..47353b74447 100644
--- a/app/assets/images/emoji/calendar.png
+++ b/public/-/emojis/1/calendar.png
Binary files differ
diff --git a/app/assets/images/emoji/calendar_spiral.png b/public/-/emojis/1/calendar_spiral.png
index dec8d49bfa8..dec8d49bfa8 100644
--- a/app/assets/images/emoji/calendar_spiral.png
+++ b/public/-/emojis/1/calendar_spiral.png
Binary files differ
diff --git a/app/assets/images/emoji/call_me.png b/public/-/emojis/1/call_me.png
index a10c59ba711..a10c59ba711 100644
--- a/app/assets/images/emoji/call_me.png
+++ b/public/-/emojis/1/call_me.png
Binary files differ
diff --git a/app/assets/images/emoji/call_me_tone1.png b/public/-/emojis/1/call_me_tone1.png
index 2c93201181a..2c93201181a 100644
--- a/app/assets/images/emoji/call_me_tone1.png
+++ b/public/-/emojis/1/call_me_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/call_me_tone2.png b/public/-/emojis/1/call_me_tone2.png
index c39f45a41ed..c39f45a41ed 100644
--- a/app/assets/images/emoji/call_me_tone2.png
+++ b/public/-/emojis/1/call_me_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/call_me_tone3.png b/public/-/emojis/1/call_me_tone3.png
index 83a57f63c29..83a57f63c29 100644
--- a/app/assets/images/emoji/call_me_tone3.png
+++ b/public/-/emojis/1/call_me_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/call_me_tone4.png b/public/-/emojis/1/call_me_tone4.png
index 65b3468fe44..65b3468fe44 100644
--- a/app/assets/images/emoji/call_me_tone4.png
+++ b/public/-/emojis/1/call_me_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/call_me_tone5.png b/public/-/emojis/1/call_me_tone5.png
index 94ef68ff3b3..94ef68ff3b3 100644
--- a/app/assets/images/emoji/call_me_tone5.png
+++ b/public/-/emojis/1/call_me_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/calling.png b/public/-/emojis/1/calling.png
index e2f308f8e46..e2f308f8e46 100644
--- a/app/assets/images/emoji/calling.png
+++ b/public/-/emojis/1/calling.png
Binary files differ
diff --git a/app/assets/images/emoji/camel.png b/public/-/emojis/1/camel.png
index b421d07a805..b421d07a805 100644
--- a/app/assets/images/emoji/camel.png
+++ b/public/-/emojis/1/camel.png
Binary files differ
diff --git a/app/assets/images/emoji/camera.png b/public/-/emojis/1/camera.png
index 0a3429f72ef..0a3429f72ef 100644
--- a/app/assets/images/emoji/camera.png
+++ b/public/-/emojis/1/camera.png
Binary files differ
diff --git a/app/assets/images/emoji/camera_with_flash.png b/public/-/emojis/1/camera_with_flash.png
index 27471da2029..27471da2029 100644
--- a/app/assets/images/emoji/camera_with_flash.png
+++ b/public/-/emojis/1/camera_with_flash.png
Binary files differ
diff --git a/app/assets/images/emoji/camping.png b/public/-/emojis/1/camping.png
index d589cc1f44b..d589cc1f44b 100644
--- a/app/assets/images/emoji/camping.png
+++ b/public/-/emojis/1/camping.png
Binary files differ
diff --git a/app/assets/images/emoji/cancer.png b/public/-/emojis/1/cancer.png
index a64af07cb5f..a64af07cb5f 100644
--- a/app/assets/images/emoji/cancer.png
+++ b/public/-/emojis/1/cancer.png
Binary files differ
diff --git a/app/assets/images/emoji/candle.png b/public/-/emojis/1/candle.png
index 0b56444e355..0b56444e355 100644
--- a/app/assets/images/emoji/candle.png
+++ b/public/-/emojis/1/candle.png
Binary files differ
diff --git a/app/assets/images/emoji/candy.png b/public/-/emojis/1/candy.png
index 8c67ace3a35..8c67ace3a35 100644
--- a/app/assets/images/emoji/candy.png
+++ b/public/-/emojis/1/candy.png
Binary files differ
diff --git a/app/assets/images/emoji/canoe.png b/public/-/emojis/1/canoe.png
index e26cdb9da69..e26cdb9da69 100644
--- a/app/assets/images/emoji/canoe.png
+++ b/public/-/emojis/1/canoe.png
Binary files differ
diff --git a/app/assets/images/emoji/capital_abcd.png b/public/-/emojis/1/capital_abcd.png
index fe9482d2d8a..fe9482d2d8a 100644
--- a/app/assets/images/emoji/capital_abcd.png
+++ b/public/-/emojis/1/capital_abcd.png
Binary files differ
diff --git a/app/assets/images/emoji/capricorn.png b/public/-/emojis/1/capricorn.png
index 6293d31d4b1..6293d31d4b1 100644
--- a/app/assets/images/emoji/capricorn.png
+++ b/public/-/emojis/1/capricorn.png
Binary files differ
diff --git a/app/assets/images/emoji/card_box.png b/public/-/emojis/1/card_box.png
index f2e764ce59d..f2e764ce59d 100644
--- a/app/assets/images/emoji/card_box.png
+++ b/public/-/emojis/1/card_box.png
Binary files differ
diff --git a/app/assets/images/emoji/card_index.png b/public/-/emojis/1/card_index.png
index 151e11cb3b4..151e11cb3b4 100644
--- a/app/assets/images/emoji/card_index.png
+++ b/public/-/emojis/1/card_index.png
Binary files differ
diff --git a/app/assets/images/emoji/carousel_horse.png b/public/-/emojis/1/carousel_horse.png
index a17074edf05..a17074edf05 100644
--- a/app/assets/images/emoji/carousel_horse.png
+++ b/public/-/emojis/1/carousel_horse.png
Binary files differ
diff --git a/app/assets/images/emoji/carrot.png b/public/-/emojis/1/carrot.png
index c68829b58e7..c68829b58e7 100644
--- a/app/assets/images/emoji/carrot.png
+++ b/public/-/emojis/1/carrot.png
Binary files differ
diff --git a/app/assets/images/emoji/cartwheel.png b/public/-/emojis/1/cartwheel.png
index cbcaa578253..cbcaa578253 100644
--- a/app/assets/images/emoji/cartwheel.png
+++ b/public/-/emojis/1/cartwheel.png
Binary files differ
diff --git a/app/assets/images/emoji/cartwheel_tone1.png b/public/-/emojis/1/cartwheel_tone1.png
index db6d65895fb..db6d65895fb 100644
--- a/app/assets/images/emoji/cartwheel_tone1.png
+++ b/public/-/emojis/1/cartwheel_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/cartwheel_tone2.png b/public/-/emojis/1/cartwheel_tone2.png
index e00ffbc27a8..e00ffbc27a8 100644
--- a/app/assets/images/emoji/cartwheel_tone2.png
+++ b/public/-/emojis/1/cartwheel_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/cartwheel_tone3.png b/public/-/emojis/1/cartwheel_tone3.png
index 49321be391f..49321be391f 100644
--- a/app/assets/images/emoji/cartwheel_tone3.png
+++ b/public/-/emojis/1/cartwheel_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/cartwheel_tone4.png b/public/-/emojis/1/cartwheel_tone4.png
index d4562b5e3dd..d4562b5e3dd 100644
--- a/app/assets/images/emoji/cartwheel_tone4.png
+++ b/public/-/emojis/1/cartwheel_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/cartwheel_tone5.png b/public/-/emojis/1/cartwheel_tone5.png
index 6e09a870767..6e09a870767 100644
--- a/app/assets/images/emoji/cartwheel_tone5.png
+++ b/public/-/emojis/1/cartwheel_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/cat.png b/public/-/emojis/1/cat.png
index efd82c2abf3..efd82c2abf3 100644
--- a/app/assets/images/emoji/cat.png
+++ b/public/-/emojis/1/cat.png
Binary files differ
diff --git a/app/assets/images/emoji/cat2.png b/public/-/emojis/1/cat2.png
index 46abe8cbc14..46abe8cbc14 100644
--- a/app/assets/images/emoji/cat2.png
+++ b/public/-/emojis/1/cat2.png
Binary files differ
diff --git a/app/assets/images/emoji/cd.png b/public/-/emojis/1/cd.png
index e6b01449cd9..e6b01449cd9 100644
--- a/app/assets/images/emoji/cd.png
+++ b/public/-/emojis/1/cd.png
Binary files differ
diff --git a/app/assets/images/emoji/chains.png b/public/-/emojis/1/chains.png
index 57f46139a06..57f46139a06 100644
--- a/app/assets/images/emoji/chains.png
+++ b/public/-/emojis/1/chains.png
Binary files differ
diff --git a/app/assets/images/emoji/champagne.png b/public/-/emojis/1/champagne.png
index 285a79a93d0..285a79a93d0 100644
--- a/app/assets/images/emoji/champagne.png
+++ b/public/-/emojis/1/champagne.png
Binary files differ
diff --git a/app/assets/images/emoji/champagne_glass.png b/public/-/emojis/1/champagne_glass.png
index 31937ae9392..31937ae9392 100644
--- a/app/assets/images/emoji/champagne_glass.png
+++ b/public/-/emojis/1/champagne_glass.png
Binary files differ
diff --git a/app/assets/images/emoji/chart.png b/public/-/emojis/1/chart.png
index 9773f03be22..9773f03be22 100644
--- a/app/assets/images/emoji/chart.png
+++ b/public/-/emojis/1/chart.png
Binary files differ
diff --git a/app/assets/images/emoji/chart_with_downwards_trend.png b/public/-/emojis/1/chart_with_downwards_trend.png
index 5222ec72d85..5222ec72d85 100644
--- a/app/assets/images/emoji/chart_with_downwards_trend.png
+++ b/public/-/emojis/1/chart_with_downwards_trend.png
Binary files differ
diff --git a/app/assets/images/emoji/chart_with_upwards_trend.png b/public/-/emojis/1/chart_with_upwards_trend.png
index f13cfcf9956..f13cfcf9956 100644
--- a/app/assets/images/emoji/chart_with_upwards_trend.png
+++ b/public/-/emojis/1/chart_with_upwards_trend.png
Binary files differ
diff --git a/app/assets/images/emoji/checkered_flag.png b/public/-/emojis/1/checkered_flag.png
index 5a71eecb89b..5a71eecb89b 100644
--- a/app/assets/images/emoji/checkered_flag.png
+++ b/public/-/emojis/1/checkered_flag.png
Binary files differ
diff --git a/app/assets/images/emoji/cheese.png b/public/-/emojis/1/cheese.png
index 00e99762286..00e99762286 100644
--- a/app/assets/images/emoji/cheese.png
+++ b/public/-/emojis/1/cheese.png
Binary files differ
diff --git a/app/assets/images/emoji/cherries.png b/public/-/emojis/1/cherries.png
index 9b10cbaac5e..9b10cbaac5e 100644
--- a/app/assets/images/emoji/cherries.png
+++ b/public/-/emojis/1/cherries.png
Binary files differ
diff --git a/app/assets/images/emoji/cherry_blossom.png b/public/-/emojis/1/cherry_blossom.png
index 282f3e7bc81..282f3e7bc81 100644
--- a/app/assets/images/emoji/cherry_blossom.png
+++ b/public/-/emojis/1/cherry_blossom.png
Binary files differ
diff --git a/app/assets/images/emoji/chestnut.png b/public/-/emojis/1/chestnut.png
index e9fb40468ed..e9fb40468ed 100644
--- a/app/assets/images/emoji/chestnut.png
+++ b/public/-/emojis/1/chestnut.png
Binary files differ
diff --git a/app/assets/images/emoji/chicken.png b/public/-/emojis/1/chicken.png
index 9a6992e55ba..9a6992e55ba 100644
--- a/app/assets/images/emoji/chicken.png
+++ b/public/-/emojis/1/chicken.png
Binary files differ
diff --git a/app/assets/images/emoji/children_crossing.png b/public/-/emojis/1/children_crossing.png
index fa4c091c7c3..fa4c091c7c3 100644
--- a/app/assets/images/emoji/children_crossing.png
+++ b/public/-/emojis/1/children_crossing.png
Binary files differ
diff --git a/app/assets/images/emoji/chipmunk.png b/public/-/emojis/1/chipmunk.png
index 2aac560cb22..2aac560cb22 100644
--- a/app/assets/images/emoji/chipmunk.png
+++ b/public/-/emojis/1/chipmunk.png
Binary files differ
diff --git a/app/assets/images/emoji/chocolate_bar.png b/public/-/emojis/1/chocolate_bar.png
index 318bbd40ef9..318bbd40ef9 100644
--- a/app/assets/images/emoji/chocolate_bar.png
+++ b/public/-/emojis/1/chocolate_bar.png
Binary files differ
diff --git a/app/assets/images/emoji/christmas_tree.png b/public/-/emojis/1/christmas_tree.png
index 4197d37a52b..4197d37a52b 100644
--- a/app/assets/images/emoji/christmas_tree.png
+++ b/public/-/emojis/1/christmas_tree.png
Binary files differ
diff --git a/app/assets/images/emoji/church.png b/public/-/emojis/1/church.png
index 8242fd272b3..8242fd272b3 100644
--- a/app/assets/images/emoji/church.png
+++ b/public/-/emojis/1/church.png
Binary files differ
diff --git a/app/assets/images/emoji/cinema.png b/public/-/emojis/1/cinema.png
index 65f27b386f2..65f27b386f2 100644
--- a/app/assets/images/emoji/cinema.png
+++ b/public/-/emojis/1/cinema.png
Binary files differ
diff --git a/app/assets/images/emoji/circus_tent.png b/public/-/emojis/1/circus_tent.png
index b0379775b12..b0379775b12 100644
--- a/app/assets/images/emoji/circus_tent.png
+++ b/public/-/emojis/1/circus_tent.png
Binary files differ
diff --git a/app/assets/images/emoji/city_dusk.png b/public/-/emojis/1/city_dusk.png
index 80cdff7cf5d..80cdff7cf5d 100644
--- a/app/assets/images/emoji/city_dusk.png
+++ b/public/-/emojis/1/city_dusk.png
Binary files differ
diff --git a/app/assets/images/emoji/city_sunset.png b/public/-/emojis/1/city_sunset.png
index 7cded0ba55b..7cded0ba55b 100644
--- a/app/assets/images/emoji/city_sunset.png
+++ b/public/-/emojis/1/city_sunset.png
Binary files differ
diff --git a/app/assets/images/emoji/cityscape.png b/public/-/emojis/1/cityscape.png
index d7b9844a0b4..d7b9844a0b4 100644
--- a/app/assets/images/emoji/cityscape.png
+++ b/public/-/emojis/1/cityscape.png
Binary files differ
diff --git a/app/assets/images/emoji/cl.png b/public/-/emojis/1/cl.png
index 8b01b4343e2..8b01b4343e2 100644
--- a/app/assets/images/emoji/cl.png
+++ b/public/-/emojis/1/cl.png
Binary files differ
diff --git a/app/assets/images/emoji/clap.png b/public/-/emojis/1/clap.png
index b0ffe928920..b0ffe928920 100644
--- a/app/assets/images/emoji/clap.png
+++ b/public/-/emojis/1/clap.png
Binary files differ
diff --git a/app/assets/images/emoji/clap_tone1.png b/public/-/emojis/1/clap_tone1.png
index de4bc837b96..de4bc837b96 100644
--- a/app/assets/images/emoji/clap_tone1.png
+++ b/public/-/emojis/1/clap_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/clap_tone2.png b/public/-/emojis/1/clap_tone2.png
index 1323de775ba..1323de775ba 100644
--- a/app/assets/images/emoji/clap_tone2.png
+++ b/public/-/emojis/1/clap_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/clap_tone3.png b/public/-/emojis/1/clap_tone3.png
index d448ca19dde..d448ca19dde 100644
--- a/app/assets/images/emoji/clap_tone3.png
+++ b/public/-/emojis/1/clap_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/clap_tone4.png b/public/-/emojis/1/clap_tone4.png
index c49f44ee91d..c49f44ee91d 100644
--- a/app/assets/images/emoji/clap_tone4.png
+++ b/public/-/emojis/1/clap_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/clap_tone5.png b/public/-/emojis/1/clap_tone5.png
index 29ee9bdf37c..29ee9bdf37c 100644
--- a/app/assets/images/emoji/clap_tone5.png
+++ b/public/-/emojis/1/clap_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/clapper.png b/public/-/emojis/1/clapper.png
index 81390883111..81390883111 100644
--- a/app/assets/images/emoji/clapper.png
+++ b/public/-/emojis/1/clapper.png
Binary files differ
diff --git a/app/assets/images/emoji/classical_building.png b/public/-/emojis/1/classical_building.png
index de7b559daaf..de7b559daaf 100644
--- a/app/assets/images/emoji/classical_building.png
+++ b/public/-/emojis/1/classical_building.png
Binary files differ
diff --git a/app/assets/images/emoji/clipboard.png b/public/-/emojis/1/clipboard.png
index 7edcfc52509..7edcfc52509 100644
--- a/app/assets/images/emoji/clipboard.png
+++ b/public/-/emojis/1/clipboard.png
Binary files differ
diff --git a/app/assets/images/emoji/clock.png b/public/-/emojis/1/clock.png
index ffdb451e3a8..ffdb451e3a8 100644
--- a/app/assets/images/emoji/clock.png
+++ b/public/-/emojis/1/clock.png
Binary files differ
diff --git a/app/assets/images/emoji/clock1.png b/public/-/emojis/1/clock1.png
index d6e34941f23..d6e34941f23 100644
--- a/app/assets/images/emoji/clock1.png
+++ b/public/-/emojis/1/clock1.png
Binary files differ
diff --git a/app/assets/images/emoji/clock10.png b/public/-/emojis/1/clock10.png
index e62b245cdbe..e62b245cdbe 100644
--- a/app/assets/images/emoji/clock10.png
+++ b/public/-/emojis/1/clock10.png
Binary files differ
diff --git a/app/assets/images/emoji/clock1030.png b/public/-/emojis/1/clock1030.png
index 0802b3c65b9..0802b3c65b9 100644
--- a/app/assets/images/emoji/clock1030.png
+++ b/public/-/emojis/1/clock1030.png
Binary files differ
diff --git a/app/assets/images/emoji/clock11.png b/public/-/emojis/1/clock11.png
index 0983345273b..0983345273b 100644
--- a/app/assets/images/emoji/clock11.png
+++ b/public/-/emojis/1/clock11.png
Binary files differ
diff --git a/app/assets/images/emoji/clock1130.png b/public/-/emojis/1/clock1130.png
index d970d03b809..d970d03b809 100644
--- a/app/assets/images/emoji/clock1130.png
+++ b/public/-/emojis/1/clock1130.png
Binary files differ
diff --git a/app/assets/images/emoji/clock12.png b/public/-/emojis/1/clock12.png
index e61caa4b3e2..e61caa4b3e2 100644
--- a/app/assets/images/emoji/clock12.png
+++ b/public/-/emojis/1/clock12.png
Binary files differ
diff --git a/app/assets/images/emoji/clock1230.png b/public/-/emojis/1/clock1230.png
index f2b1d261721..f2b1d261721 100644
--- a/app/assets/images/emoji/clock1230.png
+++ b/public/-/emojis/1/clock1230.png
Binary files differ
diff --git a/app/assets/images/emoji/clock130.png b/public/-/emojis/1/clock130.png
index 86b7689b84e..86b7689b84e 100644
--- a/app/assets/images/emoji/clock130.png
+++ b/public/-/emojis/1/clock130.png
Binary files differ
diff --git a/app/assets/images/emoji/clock2.png b/public/-/emojis/1/clock2.png
index a54253d7d57..a54253d7d57 100644
--- a/app/assets/images/emoji/clock2.png
+++ b/public/-/emojis/1/clock2.png
Binary files differ
diff --git a/app/assets/images/emoji/clock230.png b/public/-/emojis/1/clock230.png
index 7a787e018e6..7a787e018e6 100644
--- a/app/assets/images/emoji/clock230.png
+++ b/public/-/emojis/1/clock230.png
Binary files differ
diff --git a/app/assets/images/emoji/clock3.png b/public/-/emojis/1/clock3.png
index 27ec4b1f514..27ec4b1f514 100644
--- a/app/assets/images/emoji/clock3.png
+++ b/public/-/emojis/1/clock3.png
Binary files differ
diff --git a/app/assets/images/emoji/clock330.png b/public/-/emojis/1/clock330.png
index c6860395cec..c6860395cec 100644
--- a/app/assets/images/emoji/clock330.png
+++ b/public/-/emojis/1/clock330.png
Binary files differ
diff --git a/app/assets/images/emoji/clock4.png b/public/-/emojis/1/clock4.png
index 60a1ef4cc13..60a1ef4cc13 100644
--- a/app/assets/images/emoji/clock4.png
+++ b/public/-/emojis/1/clock4.png
Binary files differ
diff --git a/app/assets/images/emoji/clock430.png b/public/-/emojis/1/clock430.png
index 3c05b362122..3c05b362122 100644
--- a/app/assets/images/emoji/clock430.png
+++ b/public/-/emojis/1/clock430.png
Binary files differ
diff --git a/app/assets/images/emoji/clock5.png b/public/-/emojis/1/clock5.png
index c9382d1e094..c9382d1e094 100644
--- a/app/assets/images/emoji/clock5.png
+++ b/public/-/emojis/1/clock5.png
Binary files differ
diff --git a/app/assets/images/emoji/clock530.png b/public/-/emojis/1/clock530.png
index c21fa926db2..c21fa926db2 100644
--- a/app/assets/images/emoji/clock530.png
+++ b/public/-/emojis/1/clock530.png
Binary files differ
diff --git a/app/assets/images/emoji/clock6.png b/public/-/emojis/1/clock6.png
index 8fd5d3f5bd7..8fd5d3f5bd7 100644
--- a/app/assets/images/emoji/clock6.png
+++ b/public/-/emojis/1/clock6.png
Binary files differ
diff --git a/app/assets/images/emoji/clock630.png b/public/-/emojis/1/clock630.png
index 2aec87fefcf..2aec87fefcf 100644
--- a/app/assets/images/emoji/clock630.png
+++ b/public/-/emojis/1/clock630.png
Binary files differ
diff --git a/app/assets/images/emoji/clock7.png b/public/-/emojis/1/clock7.png
index 8c7084036f2..8c7084036f2 100644
--- a/app/assets/images/emoji/clock7.png
+++ b/public/-/emojis/1/clock7.png
Binary files differ
diff --git a/app/assets/images/emoji/clock730.png b/public/-/emojis/1/clock730.png
index f7a1135e03f..f7a1135e03f 100644
--- a/app/assets/images/emoji/clock730.png
+++ b/public/-/emojis/1/clock730.png
Binary files differ
diff --git a/app/assets/images/emoji/clock8.png b/public/-/emojis/1/clock8.png
index fcddf722e95..fcddf722e95 100644
--- a/app/assets/images/emoji/clock8.png
+++ b/public/-/emojis/1/clock8.png
Binary files differ
diff --git a/app/assets/images/emoji/clock830.png b/public/-/emojis/1/clock830.png
index 799b4aebc08..799b4aebc08 100644
--- a/app/assets/images/emoji/clock830.png
+++ b/public/-/emojis/1/clock830.png
Binary files differ
diff --git a/app/assets/images/emoji/clock9.png b/public/-/emojis/1/clock9.png
index dfbe0117981..dfbe0117981 100644
--- a/app/assets/images/emoji/clock9.png
+++ b/public/-/emojis/1/clock9.png
Binary files differ
diff --git a/app/assets/images/emoji/clock930.png b/public/-/emojis/1/clock930.png
index 4a2092ee6f0..4a2092ee6f0 100644
--- a/app/assets/images/emoji/clock930.png
+++ b/public/-/emojis/1/clock930.png
Binary files differ
diff --git a/app/assets/images/emoji/closed_book.png b/public/-/emojis/1/closed_book.png
index 6395cf2151e..6395cf2151e 100644
--- a/app/assets/images/emoji/closed_book.png
+++ b/public/-/emojis/1/closed_book.png
Binary files differ
diff --git a/app/assets/images/emoji/closed_lock_with_key.png b/public/-/emojis/1/closed_lock_with_key.png
index 1c1cd5d0741..1c1cd5d0741 100644
--- a/app/assets/images/emoji/closed_lock_with_key.png
+++ b/public/-/emojis/1/closed_lock_with_key.png
Binary files differ
diff --git a/app/assets/images/emoji/closed_umbrella.png b/public/-/emojis/1/closed_umbrella.png
index ecefba9e446..ecefba9e446 100644
--- a/app/assets/images/emoji/closed_umbrella.png
+++ b/public/-/emojis/1/closed_umbrella.png
Binary files differ
diff --git a/app/assets/images/emoji/cloud.png b/public/-/emojis/1/cloud.png
index 5b4f57f77ba..5b4f57f77ba 100644
--- a/app/assets/images/emoji/cloud.png
+++ b/public/-/emojis/1/cloud.png
Binary files differ
diff --git a/app/assets/images/emoji/cloud_lightning.png b/public/-/emojis/1/cloud_lightning.png
index 0831e88aa31..0831e88aa31 100644
--- a/app/assets/images/emoji/cloud_lightning.png
+++ b/public/-/emojis/1/cloud_lightning.png
Binary files differ
diff --git a/app/assets/images/emoji/cloud_rain.png b/public/-/emojis/1/cloud_rain.png
index 385685e0512..385685e0512 100644
--- a/app/assets/images/emoji/cloud_rain.png
+++ b/public/-/emojis/1/cloud_rain.png
Binary files differ
diff --git a/app/assets/images/emoji/cloud_snow.png b/public/-/emojis/1/cloud_snow.png
index 9720384eb99..9720384eb99 100644
--- a/app/assets/images/emoji/cloud_snow.png
+++ b/public/-/emojis/1/cloud_snow.png
Binary files differ
diff --git a/app/assets/images/emoji/cloud_tornado.png b/public/-/emojis/1/cloud_tornado.png
index 4821c89da1e..4821c89da1e 100644
--- a/app/assets/images/emoji/cloud_tornado.png
+++ b/public/-/emojis/1/cloud_tornado.png
Binary files differ
diff --git a/app/assets/images/emoji/clown.png b/public/-/emojis/1/clown.png
index 02b7ff70049..02b7ff70049 100644
--- a/app/assets/images/emoji/clown.png
+++ b/public/-/emojis/1/clown.png
Binary files differ
diff --git a/app/assets/images/emoji/clubs.png b/public/-/emojis/1/clubs.png
index 4f2abf791ca..4f2abf791ca 100644
--- a/app/assets/images/emoji/clubs.png
+++ b/public/-/emojis/1/clubs.png
Binary files differ
diff --git a/app/assets/images/emoji/cocktail.png b/public/-/emojis/1/cocktail.png
index 2e50c57e98d..2e50c57e98d 100644
--- a/app/assets/images/emoji/cocktail.png
+++ b/public/-/emojis/1/cocktail.png
Binary files differ
diff --git a/app/assets/images/emoji/coffee.png b/public/-/emojis/1/coffee.png
index 553061471b1..553061471b1 100644
--- a/app/assets/images/emoji/coffee.png
+++ b/public/-/emojis/1/coffee.png
Binary files differ
diff --git a/app/assets/images/emoji/coffin.png b/public/-/emojis/1/coffin.png
index fb2932aa5f6..fb2932aa5f6 100644
--- a/app/assets/images/emoji/coffin.png
+++ b/public/-/emojis/1/coffin.png
Binary files differ
diff --git a/app/assets/images/emoji/cold_sweat.png b/public/-/emojis/1/cold_sweat.png
index 85b2231bbf6..85b2231bbf6 100644
--- a/app/assets/images/emoji/cold_sweat.png
+++ b/public/-/emojis/1/cold_sweat.png
Binary files differ
diff --git a/app/assets/images/emoji/comet.png b/public/-/emojis/1/comet.png
index a99751f79be..a99751f79be 100644
--- a/app/assets/images/emoji/comet.png
+++ b/public/-/emojis/1/comet.png
Binary files differ
diff --git a/app/assets/images/emoji/compression.png b/public/-/emojis/1/compression.png
index d7eda7f362a..d7eda7f362a 100644
--- a/app/assets/images/emoji/compression.png
+++ b/public/-/emojis/1/compression.png
Binary files differ
diff --git a/app/assets/images/emoji/computer.png b/public/-/emojis/1/computer.png
index c1fee27e3a9..c1fee27e3a9 100644
--- a/app/assets/images/emoji/computer.png
+++ b/public/-/emojis/1/computer.png
Binary files differ
diff --git a/app/assets/images/emoji/confetti_ball.png b/public/-/emojis/1/confetti_ball.png
index ba4fd9b12be..ba4fd9b12be 100644
--- a/app/assets/images/emoji/confetti_ball.png
+++ b/public/-/emojis/1/confetti_ball.png
Binary files differ
diff --git a/app/assets/images/emoji/confounded.png b/public/-/emojis/1/confounded.png
index aa4b29e9375..aa4b29e9375 100644
--- a/app/assets/images/emoji/confounded.png
+++ b/public/-/emojis/1/confounded.png
Binary files differ
diff --git a/app/assets/images/emoji/confused.png b/public/-/emojis/1/confused.png
index 502b6bf0e0b..502b6bf0e0b 100644
--- a/app/assets/images/emoji/confused.png
+++ b/public/-/emojis/1/confused.png
Binary files differ
diff --git a/app/assets/images/emoji/congratulations.png b/public/-/emojis/1/congratulations.png
index ba8c89d95ee..ba8c89d95ee 100644
--- a/app/assets/images/emoji/congratulations.png
+++ b/public/-/emojis/1/congratulations.png
Binary files differ
diff --git a/app/assets/images/emoji/construction.png b/public/-/emojis/1/construction.png
index ef8db5f471c..ef8db5f471c 100644
--- a/app/assets/images/emoji/construction.png
+++ b/public/-/emojis/1/construction.png
Binary files differ
diff --git a/app/assets/images/emoji/construction_site.png b/public/-/emojis/1/construction_site.png
index 8206a20f63f..8206a20f63f 100644
--- a/app/assets/images/emoji/construction_site.png
+++ b/public/-/emojis/1/construction_site.png
Binary files differ
diff --git a/app/assets/images/emoji/construction_worker.png b/public/-/emojis/1/construction_worker.png
index a9970a89005..a9970a89005 100644
--- a/app/assets/images/emoji/construction_worker.png
+++ b/public/-/emojis/1/construction_worker.png
Binary files differ
diff --git a/app/assets/images/emoji/construction_worker_tone1.png b/public/-/emojis/1/construction_worker_tone1.png
index 2f24a2bab24..2f24a2bab24 100644
--- a/app/assets/images/emoji/construction_worker_tone1.png
+++ b/public/-/emojis/1/construction_worker_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/construction_worker_tone2.png b/public/-/emojis/1/construction_worker_tone2.png
index 93c8fec5a75..93c8fec5a75 100644
--- a/app/assets/images/emoji/construction_worker_tone2.png
+++ b/public/-/emojis/1/construction_worker_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/construction_worker_tone3.png b/public/-/emojis/1/construction_worker_tone3.png
index abc1f2af2e0..abc1f2af2e0 100644
--- a/app/assets/images/emoji/construction_worker_tone3.png
+++ b/public/-/emojis/1/construction_worker_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/construction_worker_tone4.png b/public/-/emojis/1/construction_worker_tone4.png
index eed83289aeb..eed83289aeb 100644
--- a/app/assets/images/emoji/construction_worker_tone4.png
+++ b/public/-/emojis/1/construction_worker_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/construction_worker_tone5.png b/public/-/emojis/1/construction_worker_tone5.png
index acbb220b8bb..acbb220b8bb 100644
--- a/app/assets/images/emoji/construction_worker_tone5.png
+++ b/public/-/emojis/1/construction_worker_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/control_knobs.png b/public/-/emojis/1/control_knobs.png
index 6635ac93b50..6635ac93b50 100644
--- a/app/assets/images/emoji/control_knobs.png
+++ b/public/-/emojis/1/control_knobs.png
Binary files differ
diff --git a/app/assets/images/emoji/convenience_store.png b/public/-/emojis/1/convenience_store.png
index 26b53b5669e..26b53b5669e 100644
--- a/app/assets/images/emoji/convenience_store.png
+++ b/public/-/emojis/1/convenience_store.png
Binary files differ
diff --git a/app/assets/images/emoji/cookie.png b/public/-/emojis/1/cookie.png
index 1b6bcb1554f..1b6bcb1554f 100644
--- a/app/assets/images/emoji/cookie.png
+++ b/public/-/emojis/1/cookie.png
Binary files differ
diff --git a/app/assets/images/emoji/cooking.png b/public/-/emojis/1/cooking.png
index 918c980577a..918c980577a 100644
--- a/app/assets/images/emoji/cooking.png
+++ b/public/-/emojis/1/cooking.png
Binary files differ
diff --git a/app/assets/images/emoji/cool.png b/public/-/emojis/1/cool.png
index 74674978d00..74674978d00 100644
--- a/app/assets/images/emoji/cool.png
+++ b/public/-/emojis/1/cool.png
Binary files differ
diff --git a/app/assets/images/emoji/cop.png b/public/-/emojis/1/cop.png
index 0b16d7c17b7..0b16d7c17b7 100644
--- a/app/assets/images/emoji/cop.png
+++ b/public/-/emojis/1/cop.png
Binary files differ
diff --git a/app/assets/images/emoji/cop_tone1.png b/public/-/emojis/1/cop_tone1.png
index 6ccba3879dc..6ccba3879dc 100644
--- a/app/assets/images/emoji/cop_tone1.png
+++ b/public/-/emojis/1/cop_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/cop_tone2.png b/public/-/emojis/1/cop_tone2.png
index 7814ea9f52d..7814ea9f52d 100644
--- a/app/assets/images/emoji/cop_tone2.png
+++ b/public/-/emojis/1/cop_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/cop_tone3.png b/public/-/emojis/1/cop_tone3.png
index d78e88ec872..d78e88ec872 100644
--- a/app/assets/images/emoji/cop_tone3.png
+++ b/public/-/emojis/1/cop_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/cop_tone4.png b/public/-/emojis/1/cop_tone4.png
index 2e13c508315..2e13c508315 100644
--- a/app/assets/images/emoji/cop_tone4.png
+++ b/public/-/emojis/1/cop_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/cop_tone5.png b/public/-/emojis/1/cop_tone5.png
index 2980d61cc2e..2980d61cc2e 100644
--- a/app/assets/images/emoji/cop_tone5.png
+++ b/public/-/emojis/1/cop_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/copyright.png b/public/-/emojis/1/copyright.png
index 6b9a6adbfd2..6b9a6adbfd2 100644
--- a/app/assets/images/emoji/copyright.png
+++ b/public/-/emojis/1/copyright.png
Binary files differ
diff --git a/app/assets/images/emoji/corn.png b/public/-/emojis/1/corn.png
index 36e20127931..36e20127931 100644
--- a/app/assets/images/emoji/corn.png
+++ b/public/-/emojis/1/corn.png
Binary files differ
diff --git a/app/assets/images/emoji/couch.png b/public/-/emojis/1/couch.png
index 27b19b13bb0..27b19b13bb0 100644
--- a/app/assets/images/emoji/couch.png
+++ b/public/-/emojis/1/couch.png
Binary files differ
diff --git a/app/assets/images/emoji/couple.png b/public/-/emojis/1/couple.png
index 960323f3c16..960323f3c16 100644
--- a/app/assets/images/emoji/couple.png
+++ b/public/-/emojis/1/couple.png
Binary files differ
diff --git a/app/assets/images/emoji/couple_mm.png b/public/-/emojis/1/couple_mm.png
index 8759fa5db87..8759fa5db87 100644
--- a/app/assets/images/emoji/couple_mm.png
+++ b/public/-/emojis/1/couple_mm.png
Binary files differ
diff --git a/app/assets/images/emoji/couple_with_heart.png b/public/-/emojis/1/couple_with_heart.png
index 62111601b36..62111601b36 100644
--- a/app/assets/images/emoji/couple_with_heart.png
+++ b/public/-/emojis/1/couple_with_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/couple_ww.png b/public/-/emojis/1/couple_ww.png
index 08fdabcdc5c..08fdabcdc5c 100644
--- a/app/assets/images/emoji/couple_ww.png
+++ b/public/-/emojis/1/couple_ww.png
Binary files differ
diff --git a/app/assets/images/emoji/couplekiss.png b/public/-/emojis/1/couplekiss.png
index 9aa519da9e8..9aa519da9e8 100644
--- a/app/assets/images/emoji/couplekiss.png
+++ b/public/-/emojis/1/couplekiss.png
Binary files differ
diff --git a/app/assets/images/emoji/cow.png b/public/-/emojis/1/cow.png
index 718a3986d64..718a3986d64 100644
--- a/app/assets/images/emoji/cow.png
+++ b/public/-/emojis/1/cow.png
Binary files differ
diff --git a/app/assets/images/emoji/cow2.png b/public/-/emojis/1/cow2.png
index 4d0ca534ff1..4d0ca534ff1 100644
--- a/app/assets/images/emoji/cow2.png
+++ b/public/-/emojis/1/cow2.png
Binary files differ
diff --git a/app/assets/images/emoji/cowboy.png b/public/-/emojis/1/cowboy.png
index 70dd5d0d9d1..70dd5d0d9d1 100644
--- a/app/assets/images/emoji/cowboy.png
+++ b/public/-/emojis/1/cowboy.png
Binary files differ
diff --git a/app/assets/images/emoji/crab.png b/public/-/emojis/1/crab.png
index 19f3047ab61..19f3047ab61 100644
--- a/app/assets/images/emoji/crab.png
+++ b/public/-/emojis/1/crab.png
Binary files differ
diff --git a/app/assets/images/emoji/crayon.png b/public/-/emojis/1/crayon.png
index 8d7b427aaa3..8d7b427aaa3 100644
--- a/app/assets/images/emoji/crayon.png
+++ b/public/-/emojis/1/crayon.png
Binary files differ
diff --git a/app/assets/images/emoji/credit_card.png b/public/-/emojis/1/credit_card.png
index 372777d5c61..372777d5c61 100644
--- a/app/assets/images/emoji/credit_card.png
+++ b/public/-/emojis/1/credit_card.png
Binary files differ
diff --git a/app/assets/images/emoji/crescent_moon.png b/public/-/emojis/1/crescent_moon.png
index 765420ecec7..765420ecec7 100644
--- a/app/assets/images/emoji/crescent_moon.png
+++ b/public/-/emojis/1/crescent_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/cricket.png b/public/-/emojis/1/cricket.png
index d602294a2cd..d602294a2cd 100644
--- a/app/assets/images/emoji/cricket.png
+++ b/public/-/emojis/1/cricket.png
Binary files differ
diff --git a/app/assets/images/emoji/crocodile.png b/public/-/emojis/1/crocodile.png
index 3005c46f176..3005c46f176 100644
--- a/app/assets/images/emoji/crocodile.png
+++ b/public/-/emojis/1/crocodile.png
Binary files differ
diff --git a/app/assets/images/emoji/croissant.png b/public/-/emojis/1/croissant.png
index fb33feb1a38..fb33feb1a38 100644
--- a/app/assets/images/emoji/croissant.png
+++ b/public/-/emojis/1/croissant.png
Binary files differ
diff --git a/app/assets/images/emoji/cross.png b/public/-/emojis/1/cross.png
index 42b10e82257..42b10e82257 100644
--- a/app/assets/images/emoji/cross.png
+++ b/public/-/emojis/1/cross.png
Binary files differ
diff --git a/app/assets/images/emoji/crossed_flags.png b/public/-/emojis/1/crossed_flags.png
index 273bd0f0fe5..273bd0f0fe5 100644
--- a/app/assets/images/emoji/crossed_flags.png
+++ b/public/-/emojis/1/crossed_flags.png
Binary files differ
diff --git a/app/assets/images/emoji/crossed_swords.png b/public/-/emojis/1/crossed_swords.png
index 907e9607134..907e9607134 100644
--- a/app/assets/images/emoji/crossed_swords.png
+++ b/public/-/emojis/1/crossed_swords.png
Binary files differ
diff --git a/app/assets/images/emoji/crown.png b/public/-/emojis/1/crown.png
index 93b82d92f04..93b82d92f04 100644
--- a/app/assets/images/emoji/crown.png
+++ b/public/-/emojis/1/crown.png
Binary files differ
diff --git a/app/assets/images/emoji/cruise_ship.png b/public/-/emojis/1/cruise_ship.png
index 19d4acbe40c..19d4acbe40c 100644
--- a/app/assets/images/emoji/cruise_ship.png
+++ b/public/-/emojis/1/cruise_ship.png
Binary files differ
diff --git a/app/assets/images/emoji/cry.png b/public/-/emojis/1/cry.png
index b7877f8a173..b7877f8a173 100644
--- a/app/assets/images/emoji/cry.png
+++ b/public/-/emojis/1/cry.png
Binary files differ
diff --git a/app/assets/images/emoji/crying_cat_face.png b/public/-/emojis/1/crying_cat_face.png
index b4f49715e00..b4f49715e00 100644
--- a/app/assets/images/emoji/crying_cat_face.png
+++ b/public/-/emojis/1/crying_cat_face.png
Binary files differ
diff --git a/app/assets/images/emoji/crystal_ball.png b/public/-/emojis/1/crystal_ball.png
index 485d5c888f1..485d5c888f1 100644
--- a/app/assets/images/emoji/crystal_ball.png
+++ b/public/-/emojis/1/crystal_ball.png
Binary files differ
diff --git a/app/assets/images/emoji/cucumber.png b/public/-/emojis/1/cucumber.png
index 500807059d2..500807059d2 100644
--- a/app/assets/images/emoji/cucumber.png
+++ b/public/-/emojis/1/cucumber.png
Binary files differ
diff --git a/app/assets/images/emoji/cupid.png b/public/-/emojis/1/cupid.png
index 2df0078ddd1..2df0078ddd1 100644
--- a/app/assets/images/emoji/cupid.png
+++ b/public/-/emojis/1/cupid.png
Binary files differ
diff --git a/app/assets/images/emoji/curly_loop.png b/public/-/emojis/1/curly_loop.png
index 440aa56d50e..440aa56d50e 100644
--- a/app/assets/images/emoji/curly_loop.png
+++ b/public/-/emojis/1/curly_loop.png
Binary files differ
diff --git a/app/assets/images/emoji/currency_exchange.png b/public/-/emojis/1/currency_exchange.png
index 4d46c6050e7..4d46c6050e7 100644
--- a/app/assets/images/emoji/currency_exchange.png
+++ b/public/-/emojis/1/currency_exchange.png
Binary files differ
diff --git a/app/assets/images/emoji/curry.png b/public/-/emojis/1/curry.png
index 69657ca8103..69657ca8103 100644
--- a/app/assets/images/emoji/curry.png
+++ b/public/-/emojis/1/curry.png
Binary files differ
diff --git a/app/assets/images/emoji/custard.png b/public/-/emojis/1/custard.png
index fa3df67b8f6..fa3df67b8f6 100644
--- a/app/assets/images/emoji/custard.png
+++ b/public/-/emojis/1/custard.png
Binary files differ
diff --git a/app/assets/images/emoji/customs.png b/public/-/emojis/1/customs.png
index 21b7ce2c69e..21b7ce2c69e 100644
--- a/app/assets/images/emoji/customs.png
+++ b/public/-/emojis/1/customs.png
Binary files differ
diff --git a/app/assets/images/emoji/cyclone.png b/public/-/emojis/1/cyclone.png
index ff00b1afe70..ff00b1afe70 100644
--- a/app/assets/images/emoji/cyclone.png
+++ b/public/-/emojis/1/cyclone.png
Binary files differ
diff --git a/app/assets/images/emoji/dagger.png b/public/-/emojis/1/dagger.png
index 66e97b0aa25..66e97b0aa25 100644
--- a/app/assets/images/emoji/dagger.png
+++ b/public/-/emojis/1/dagger.png
Binary files differ
diff --git a/app/assets/images/emoji/dancer.png b/public/-/emojis/1/dancer.png
index 04b166991cb..04b166991cb 100644
--- a/app/assets/images/emoji/dancer.png
+++ b/public/-/emojis/1/dancer.png
Binary files differ
diff --git a/app/assets/images/emoji/dancer_tone1.png b/public/-/emojis/1/dancer_tone1.png
index 2c7b11c3a6e..2c7b11c3a6e 100644
--- a/app/assets/images/emoji/dancer_tone1.png
+++ b/public/-/emojis/1/dancer_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/dancer_tone2.png b/public/-/emojis/1/dancer_tone2.png
index cb04b1f907e..cb04b1f907e 100644
--- a/app/assets/images/emoji/dancer_tone2.png
+++ b/public/-/emojis/1/dancer_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/dancer_tone3.png b/public/-/emojis/1/dancer_tone3.png
index 98c5bca7b64..98c5bca7b64 100644
--- a/app/assets/images/emoji/dancer_tone3.png
+++ b/public/-/emojis/1/dancer_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/dancer_tone4.png b/public/-/emojis/1/dancer_tone4.png
index fdb1e00cbba..fdb1e00cbba 100644
--- a/app/assets/images/emoji/dancer_tone4.png
+++ b/public/-/emojis/1/dancer_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/dancer_tone5.png b/public/-/emojis/1/dancer_tone5.png
index 0e34e0e23f0..0e34e0e23f0 100644
--- a/app/assets/images/emoji/dancer_tone5.png
+++ b/public/-/emojis/1/dancer_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/dancers.png b/public/-/emojis/1/dancers.png
index 67e6ffacb76..67e6ffacb76 100644
--- a/app/assets/images/emoji/dancers.png
+++ b/public/-/emojis/1/dancers.png
Binary files differ
diff --git a/app/assets/images/emoji/dango.png b/public/-/emojis/1/dango.png
index f73f37b01c7..f73f37b01c7 100644
--- a/app/assets/images/emoji/dango.png
+++ b/public/-/emojis/1/dango.png
Binary files differ
diff --git a/app/assets/images/emoji/dark_sunglasses.png b/public/-/emojis/1/dark_sunglasses.png
index b1b6db0acff..b1b6db0acff 100644
--- a/app/assets/images/emoji/dark_sunglasses.png
+++ b/public/-/emojis/1/dark_sunglasses.png
Binary files differ
diff --git a/app/assets/images/emoji/dart.png b/public/-/emojis/1/dart.png
index f6704aeb8ba..f6704aeb8ba 100644
--- a/app/assets/images/emoji/dart.png
+++ b/public/-/emojis/1/dart.png
Binary files differ
diff --git a/app/assets/images/emoji/dash.png b/public/-/emojis/1/dash.png
index 064b8525c12..064b8525c12 100644
--- a/app/assets/images/emoji/dash.png
+++ b/public/-/emojis/1/dash.png
Binary files differ
diff --git a/app/assets/images/emoji/date.png b/public/-/emojis/1/date.png
index f05b3da97b8..f05b3da97b8 100644
--- a/app/assets/images/emoji/date.png
+++ b/public/-/emojis/1/date.png
Binary files differ
diff --git a/app/assets/images/emoji/deciduous_tree.png b/public/-/emojis/1/deciduous_tree.png
index 785fc1c30ea..785fc1c30ea 100644
--- a/app/assets/images/emoji/deciduous_tree.png
+++ b/public/-/emojis/1/deciduous_tree.png
Binary files differ
diff --git a/app/assets/images/emoji/deer.png b/public/-/emojis/1/deer.png
index d8698195ff0..d8698195ff0 100644
--- a/app/assets/images/emoji/deer.png
+++ b/public/-/emojis/1/deer.png
Binary files differ
diff --git a/app/assets/images/emoji/department_store.png b/public/-/emojis/1/department_store.png
index 58867c7a6e1..58867c7a6e1 100644
--- a/app/assets/images/emoji/department_store.png
+++ b/public/-/emojis/1/department_store.png
Binary files differ
diff --git a/app/assets/images/emoji/desert.png b/public/-/emojis/1/desert.png
index e9966ff8c65..e9966ff8c65 100644
--- a/app/assets/images/emoji/desert.png
+++ b/public/-/emojis/1/desert.png
Binary files differ
diff --git a/app/assets/images/emoji/desktop.png b/public/-/emojis/1/desktop.png
index 909bd42b5e1..909bd42b5e1 100644
--- a/app/assets/images/emoji/desktop.png
+++ b/public/-/emojis/1/desktop.png
Binary files differ
diff --git a/app/assets/images/emoji/diamond_shape_with_a_dot_inside.png b/public/-/emojis/1/diamond_shape_with_a_dot_inside.png
index 2a22a26d1e2..2a22a26d1e2 100644
--- a/app/assets/images/emoji/diamond_shape_with_a_dot_inside.png
+++ b/public/-/emojis/1/diamond_shape_with_a_dot_inside.png
Binary files differ
diff --git a/app/assets/images/emoji/diamonds.png b/public/-/emojis/1/diamonds.png
index 1f25f51f97a..1f25f51f97a 100644
--- a/app/assets/images/emoji/diamonds.png
+++ b/public/-/emojis/1/diamonds.png
Binary files differ
diff --git a/app/assets/images/emoji/disappointed.png b/public/-/emojis/1/disappointed.png
index efe4e67e23c..efe4e67e23c 100644
--- a/app/assets/images/emoji/disappointed.png
+++ b/public/-/emojis/1/disappointed.png
Binary files differ
diff --git a/app/assets/images/emoji/disappointed_relieved.png b/public/-/emojis/1/disappointed_relieved.png
index aef864d2b3d..aef864d2b3d 100644
--- a/app/assets/images/emoji/disappointed_relieved.png
+++ b/public/-/emojis/1/disappointed_relieved.png
Binary files differ
diff --git a/app/assets/images/emoji/dividers.png b/public/-/emojis/1/dividers.png
index 46a7e403f9d..46a7e403f9d 100644
--- a/app/assets/images/emoji/dividers.png
+++ b/public/-/emojis/1/dividers.png
Binary files differ
diff --git a/app/assets/images/emoji/dizzy.png b/public/-/emojis/1/dizzy.png
index 85f52efad24..85f52efad24 100644
--- a/app/assets/images/emoji/dizzy.png
+++ b/public/-/emojis/1/dizzy.png
Binary files differ
diff --git a/app/assets/images/emoji/dizzy_face.png b/public/-/emojis/1/dizzy_face.png
index 3120316ab5e..3120316ab5e 100644
--- a/app/assets/images/emoji/dizzy_face.png
+++ b/public/-/emojis/1/dizzy_face.png
Binary files differ
diff --git a/app/assets/images/emoji/do_not_litter.png b/public/-/emojis/1/do_not_litter.png
index 341d2575f4f..341d2575f4f 100644
--- a/app/assets/images/emoji/do_not_litter.png
+++ b/public/-/emojis/1/do_not_litter.png
Binary files differ
diff --git a/app/assets/images/emoji/dog.png b/public/-/emojis/1/dog.png
index 281b81d58bd..281b81d58bd 100644
--- a/app/assets/images/emoji/dog.png
+++ b/public/-/emojis/1/dog.png
Binary files differ
diff --git a/app/assets/images/emoji/dog2.png b/public/-/emojis/1/dog2.png
index 976143dbdbe..976143dbdbe 100644
--- a/app/assets/images/emoji/dog2.png
+++ b/public/-/emojis/1/dog2.png
Binary files differ
diff --git a/app/assets/images/emoji/dollar.png b/public/-/emojis/1/dollar.png
index a9904c28293..a9904c28293 100644
--- a/app/assets/images/emoji/dollar.png
+++ b/public/-/emojis/1/dollar.png
Binary files differ
diff --git a/app/assets/images/emoji/dolls.png b/public/-/emojis/1/dolls.png
index 10955615110..10955615110 100644
--- a/app/assets/images/emoji/dolls.png
+++ b/public/-/emojis/1/dolls.png
Binary files differ
diff --git a/app/assets/images/emoji/dolphin.png b/public/-/emojis/1/dolphin.png
index 81434809003..81434809003 100644
--- a/app/assets/images/emoji/dolphin.png
+++ b/public/-/emojis/1/dolphin.png
Binary files differ
diff --git a/app/assets/images/emoji/door.png b/public/-/emojis/1/door.png
index 36ae3e27494..36ae3e27494 100644
--- a/app/assets/images/emoji/door.png
+++ b/public/-/emojis/1/door.png
Binary files differ
diff --git a/app/assets/images/emoji/doughnut.png b/public/-/emojis/1/doughnut.png
index 0ca4cd0bde8..0ca4cd0bde8 100644
--- a/app/assets/images/emoji/doughnut.png
+++ b/public/-/emojis/1/doughnut.png
Binary files differ
diff --git a/app/assets/images/emoji/dove.png b/public/-/emojis/1/dove.png
index 9580c4917d7..9580c4917d7 100644
--- a/app/assets/images/emoji/dove.png
+++ b/public/-/emojis/1/dove.png
Binary files differ
diff --git a/app/assets/images/emoji/dragon.png b/public/-/emojis/1/dragon.png
index d6311cf5429..d6311cf5429 100644
--- a/app/assets/images/emoji/dragon.png
+++ b/public/-/emojis/1/dragon.png
Binary files differ
diff --git a/app/assets/images/emoji/dragon_face.png b/public/-/emojis/1/dragon_face.png
index 3c2720446c6..3c2720446c6 100644
--- a/app/assets/images/emoji/dragon_face.png
+++ b/public/-/emojis/1/dragon_face.png
Binary files differ
diff --git a/app/assets/images/emoji/dress.png b/public/-/emojis/1/dress.png
index a697ca5c57d..a697ca5c57d 100644
--- a/app/assets/images/emoji/dress.png
+++ b/public/-/emojis/1/dress.png
Binary files differ
diff --git a/app/assets/images/emoji/dromedary_camel.png b/public/-/emojis/1/dromedary_camel.png
index 5271637c7c4..5271637c7c4 100644
--- a/app/assets/images/emoji/dromedary_camel.png
+++ b/public/-/emojis/1/dromedary_camel.png
Binary files differ
diff --git a/app/assets/images/emoji/drooling_face.png b/public/-/emojis/1/drooling_face.png
index a5460532597..a5460532597 100644
--- a/app/assets/images/emoji/drooling_face.png
+++ b/public/-/emojis/1/drooling_face.png
Binary files differ
diff --git a/app/assets/images/emoji/droplet.png b/public/-/emojis/1/droplet.png
index 71241ec3061..71241ec3061 100644
--- a/app/assets/images/emoji/droplet.png
+++ b/public/-/emojis/1/droplet.png
Binary files differ
diff --git a/app/assets/images/emoji/drum.png b/public/-/emojis/1/drum.png
index b038727cc99..b038727cc99 100644
--- a/app/assets/images/emoji/drum.png
+++ b/public/-/emojis/1/drum.png
Binary files differ
diff --git a/app/assets/images/emoji/duck.png b/public/-/emojis/1/duck.png
index 74330b77ca3..74330b77ca3 100644
--- a/app/assets/images/emoji/duck.png
+++ b/public/-/emojis/1/duck.png
Binary files differ
diff --git a/app/assets/images/emoji/dvd.png b/public/-/emojis/1/dvd.png
index 045a6f7a08d..045a6f7a08d 100644
--- a/app/assets/images/emoji/dvd.png
+++ b/public/-/emojis/1/dvd.png
Binary files differ
diff --git a/app/assets/images/emoji/e-mail.png b/public/-/emojis/1/e-mail.png
index d22e654a20b..d22e654a20b 100644
--- a/app/assets/images/emoji/e-mail.png
+++ b/public/-/emojis/1/e-mail.png
Binary files differ
diff --git a/app/assets/images/emoji/eagle.png b/public/-/emojis/1/eagle.png
index 4f277debeef..4f277debeef 100644
--- a/app/assets/images/emoji/eagle.png
+++ b/public/-/emojis/1/eagle.png
Binary files differ
diff --git a/app/assets/images/emoji/ear.png b/public/-/emojis/1/ear.png
index f84f9ff154a..f84f9ff154a 100644
--- a/app/assets/images/emoji/ear.png
+++ b/public/-/emojis/1/ear.png
Binary files differ
diff --git a/app/assets/images/emoji/ear_of_rice.png b/public/-/emojis/1/ear_of_rice.png
index 3564d9d643a..3564d9d643a 100644
--- a/app/assets/images/emoji/ear_of_rice.png
+++ b/public/-/emojis/1/ear_of_rice.png
Binary files differ
diff --git a/app/assets/images/emoji/ear_tone1.png b/public/-/emojis/1/ear_tone1.png
index d09e1e41996..d09e1e41996 100644
--- a/app/assets/images/emoji/ear_tone1.png
+++ b/public/-/emojis/1/ear_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/ear_tone2.png b/public/-/emojis/1/ear_tone2.png
index 300d60a9948..300d60a9948 100644
--- a/app/assets/images/emoji/ear_tone2.png
+++ b/public/-/emojis/1/ear_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/ear_tone3.png b/public/-/emojis/1/ear_tone3.png
index 2a56eebe445..2a56eebe445 100644
--- a/app/assets/images/emoji/ear_tone3.png
+++ b/public/-/emojis/1/ear_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/ear_tone4.png b/public/-/emojis/1/ear_tone4.png
index bd270f7763e..bd270f7763e 100644
--- a/app/assets/images/emoji/ear_tone4.png
+++ b/public/-/emojis/1/ear_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/ear_tone5.png b/public/-/emojis/1/ear_tone5.png
index b96bb441dff..b96bb441dff 100644
--- a/app/assets/images/emoji/ear_tone5.png
+++ b/public/-/emojis/1/ear_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/earth_africa.png b/public/-/emojis/1/earth_africa.png
index 66c3348c23a..66c3348c23a 100644
--- a/app/assets/images/emoji/earth_africa.png
+++ b/public/-/emojis/1/earth_africa.png
Binary files differ
diff --git a/app/assets/images/emoji/earth_americas.png b/public/-/emojis/1/earth_americas.png
index 538c3cddd68..538c3cddd68 100644
--- a/app/assets/images/emoji/earth_americas.png
+++ b/public/-/emojis/1/earth_americas.png
Binary files differ
diff --git a/app/assets/images/emoji/earth_asia.png b/public/-/emojis/1/earth_asia.png
index d8df97fec3c..d8df97fec3c 100644
--- a/app/assets/images/emoji/earth_asia.png
+++ b/public/-/emojis/1/earth_asia.png
Binary files differ
diff --git a/app/assets/images/emoji/egg.png b/public/-/emojis/1/egg.png
index c171974d993..c171974d993 100644
--- a/app/assets/images/emoji/egg.png
+++ b/public/-/emojis/1/egg.png
Binary files differ
diff --git a/app/assets/images/emoji/eggplant.png b/public/-/emojis/1/eggplant.png
index fafd7c1a14c..fafd7c1a14c 100644
--- a/app/assets/images/emoji/eggplant.png
+++ b/public/-/emojis/1/eggplant.png
Binary files differ
diff --git a/app/assets/images/emoji/eight.png b/public/-/emojis/1/eight.png
index 8c95874d4c5..8c95874d4c5 100644
--- a/app/assets/images/emoji/eight.png
+++ b/public/-/emojis/1/eight.png
Binary files differ
diff --git a/app/assets/images/emoji/eight_pointed_black_star.png b/public/-/emojis/1/eight_pointed_black_star.png
index 820179bda50..820179bda50 100644
--- a/app/assets/images/emoji/eight_pointed_black_star.png
+++ b/public/-/emojis/1/eight_pointed_black_star.png
Binary files differ
diff --git a/app/assets/images/emoji/eight_spoked_asterisk.png b/public/-/emojis/1/eight_spoked_asterisk.png
index 3307ffa62ee..3307ffa62ee 100644
--- a/app/assets/images/emoji/eight_spoked_asterisk.png
+++ b/public/-/emojis/1/eight_spoked_asterisk.png
Binary files differ
diff --git a/app/assets/images/emoji/eject.png b/public/-/emojis/1/eject.png
index ec5cfc48973..ec5cfc48973 100644
--- a/app/assets/images/emoji/eject.png
+++ b/public/-/emojis/1/eject.png
Binary files differ
diff --git a/app/assets/images/emoji/electric_plug.png b/public/-/emojis/1/electric_plug.png
index 31d1eb215b4..31d1eb215b4 100644
--- a/app/assets/images/emoji/electric_plug.png
+++ b/public/-/emojis/1/electric_plug.png
Binary files differ
diff --git a/app/assets/images/emoji/elephant.png b/public/-/emojis/1/elephant.png
index b8a6d140595..b8a6d140595 100644
--- a/app/assets/images/emoji/elephant.png
+++ b/public/-/emojis/1/elephant.png
Binary files differ
diff --git a/public/-/emojis/1/emojis.json b/public/-/emojis/1/emojis.json
new file mode 100644
index 00000000000..20a564720d1
--- /dev/null
+++ b/public/-/emojis/1/emojis.json
@@ -0,0 +1,10760 @@
+{
+ "100": {
+ "c": "symbols",
+ "e": "💯",
+ "d": "hundred points symbol",
+ "u": "6.0"
+ },
+ "1234": {
+ "c": "symbols",
+ "e": "🔢",
+ "d": "input symbol for numbers",
+ "u": "6.0"
+ },
+ "8ball": {
+ "c": "activity",
+ "e": "🎱",
+ "d": "billiards",
+ "u": "6.0"
+ },
+ "a": {
+ "c": "symbols",
+ "e": "🅰",
+ "d": "negative squared latin capital letter a",
+ "u": "6.0"
+ },
+ "ab": {
+ "c": "symbols",
+ "e": "🆎",
+ "d": "negative squared ab",
+ "u": "6.0"
+ },
+ "abc": {
+ "c": "symbols",
+ "e": "🔤",
+ "d": "input symbol for latin letters",
+ "u": "6.0"
+ },
+ "abcd": {
+ "c": "symbols",
+ "e": "🔡",
+ "d": "input symbol for latin small letters",
+ "u": "6.0"
+ },
+ "accept": {
+ "c": "symbols",
+ "e": "🉑",
+ "d": "circled ideograph accept",
+ "u": "6.0"
+ },
+ "aerial_tramway": {
+ "c": "travel",
+ "e": "🚡",
+ "d": "aerial tramway",
+ "u": "6.0"
+ },
+ "airplane": {
+ "c": "travel",
+ "e": "✈",
+ "d": "airplane",
+ "u": "1.1"
+ },
+ "airplane_arriving": {
+ "c": "travel",
+ "e": "🛬",
+ "d": "airplane arriving",
+ "u": "7.0"
+ },
+ "airplane_departure": {
+ "c": "travel",
+ "e": "🛫",
+ "d": "airplane departure",
+ "u": "7.0"
+ },
+ "airplane_small": {
+ "c": "travel",
+ "e": "🛩",
+ "d": "small airplane",
+ "u": "7.0"
+ },
+ "alarm_clock": {
+ "c": "objects",
+ "e": "â°",
+ "d": "alarm clock",
+ "u": "6.0"
+ },
+ "alembic": {
+ "c": "objects",
+ "e": "âš—",
+ "d": "alembic",
+ "u": "4.1"
+ },
+ "alien": {
+ "c": "people",
+ "e": "👽",
+ "d": "extraterrestrial alien",
+ "u": "6.0"
+ },
+ "ambulance": {
+ "c": "travel",
+ "e": "🚑",
+ "d": "ambulance",
+ "u": "6.0"
+ },
+ "amphora": {
+ "c": "objects",
+ "e": "ðŸº",
+ "d": "amphora",
+ "u": "8.0"
+ },
+ "anchor": {
+ "c": "travel",
+ "e": "âš“",
+ "d": "anchor",
+ "u": "4.1"
+ },
+ "angel": {
+ "c": "people",
+ "e": "👼",
+ "d": "baby angel",
+ "u": "6.0"
+ },
+ "angel_tone1": {
+ "c": "people",
+ "e": "👼ðŸ»",
+ "d": "baby angel tone 1",
+ "u": "8.0"
+ },
+ "angel_tone2": {
+ "c": "people",
+ "e": "👼ðŸ¼",
+ "d": "baby angel tone 2",
+ "u": "8.0"
+ },
+ "angel_tone3": {
+ "c": "people",
+ "e": "👼ðŸ½",
+ "d": "baby angel tone 3",
+ "u": "8.0"
+ },
+ "angel_tone4": {
+ "c": "people",
+ "e": "👼ðŸ¾",
+ "d": "baby angel tone 4",
+ "u": "8.0"
+ },
+ "angel_tone5": {
+ "c": "people",
+ "e": "👼ðŸ¿",
+ "d": "baby angel tone 5",
+ "u": "8.0"
+ },
+ "anger": {
+ "c": "symbols",
+ "e": "💢",
+ "d": "anger symbol",
+ "u": "6.0"
+ },
+ "anger_right": {
+ "c": "symbols",
+ "e": "🗯",
+ "d": "right anger bubble",
+ "u": "7.0"
+ },
+ "angry": {
+ "c": "people",
+ "e": "😠",
+ "d": "angry face",
+ "u": "6.0"
+ },
+ "ant": {
+ "c": "nature",
+ "e": "ðŸœ",
+ "d": "ant",
+ "u": "6.0"
+ },
+ "apple": {
+ "c": "food",
+ "e": "ðŸŽ",
+ "d": "red apple",
+ "u": "6.0"
+ },
+ "aquarius": {
+ "c": "symbols",
+ "e": "â™’",
+ "d": "aquarius",
+ "u": "1.1"
+ },
+ "aries": {
+ "c": "symbols",
+ "e": "♈",
+ "d": "aries",
+ "u": "1.1"
+ },
+ "arrow_backward": {
+ "c": "symbols",
+ "e": "â—€",
+ "d": "black left-pointing triangle",
+ "u": "1.1"
+ },
+ "arrow_double_down": {
+ "c": "symbols",
+ "e": "â¬",
+ "d": "black down-pointing double triangle",
+ "u": "6.0"
+ },
+ "arrow_double_up": {
+ "c": "symbols",
+ "e": "â«",
+ "d": "black up-pointing double triangle",
+ "u": "6.0"
+ },
+ "arrow_down": {
+ "c": "symbols",
+ "e": "⬇",
+ "d": "downwards black arrow",
+ "u": "4.0"
+ },
+ "arrow_down_small": {
+ "c": "symbols",
+ "e": "🔽",
+ "d": "down-pointing small red triangle",
+ "u": "6.0"
+ },
+ "arrow_forward": {
+ "c": "symbols",
+ "e": "â–¶",
+ "d": "black right-pointing triangle",
+ "u": "1.1"
+ },
+ "arrow_heading_down": {
+ "c": "symbols",
+ "e": "⤵",
+ "d": "arrow pointing rightwards then curving downwards",
+ "u": "3.2"
+ },
+ "arrow_heading_up": {
+ "c": "symbols",
+ "e": "⤴",
+ "d": "arrow pointing rightwards then curving upwards",
+ "u": "3.2"
+ },
+ "arrow_left": {
+ "c": "symbols",
+ "e": "⬅",
+ "d": "leftwards black arrow",
+ "u": "4.0"
+ },
+ "arrow_lower_left": {
+ "c": "symbols",
+ "e": "↙",
+ "d": "south west arrow",
+ "u": "1.1"
+ },
+ "arrow_lower_right": {
+ "c": "symbols",
+ "e": "↘",
+ "d": "south east arrow",
+ "u": "1.1"
+ },
+ "arrow_right": {
+ "c": "symbols",
+ "e": "âž¡",
+ "d": "black rightwards arrow",
+ "u": "1.1"
+ },
+ "arrow_right_hook": {
+ "c": "symbols",
+ "e": "↪",
+ "d": "rightwards arrow with hook",
+ "u": "1.1"
+ },
+ "arrow_up": {
+ "c": "symbols",
+ "e": "⬆",
+ "d": "upwards black arrow",
+ "u": "4.0"
+ },
+ "arrow_up_down": {
+ "c": "symbols",
+ "e": "↕",
+ "d": "up down arrow",
+ "u": "1.1"
+ },
+ "arrow_up_small": {
+ "c": "symbols",
+ "e": "🔼",
+ "d": "up-pointing small red triangle",
+ "u": "6.0"
+ },
+ "arrow_upper_left": {
+ "c": "symbols",
+ "e": "↖",
+ "d": "north west arrow",
+ "u": "1.1"
+ },
+ "arrow_upper_right": {
+ "c": "symbols",
+ "e": "↗",
+ "d": "north east arrow",
+ "u": "1.1"
+ },
+ "arrows_clockwise": {
+ "c": "symbols",
+ "e": "🔃",
+ "d": "clockwise downwards and upwards open circle arrows",
+ "u": "6.0"
+ },
+ "arrows_counterclockwise": {
+ "c": "symbols",
+ "e": "🔄",
+ "d": "anticlockwise downwards and upwards open circle ar",
+ "u": "6.0"
+ },
+ "art": {
+ "c": "activity",
+ "e": "🎨",
+ "d": "artist palette",
+ "u": "6.0"
+ },
+ "articulated_lorry": {
+ "c": "travel",
+ "e": "🚛",
+ "d": "articulated lorry",
+ "u": "6.0"
+ },
+ "asterisk": {
+ "c": "symbols",
+ "e": "*⃣",
+ "d": "keycap asterisk",
+ "u": "3.0"
+ },
+ "astonished": {
+ "c": "people",
+ "e": "😲",
+ "d": "astonished face",
+ "u": "6.0"
+ },
+ "athletic_shoe": {
+ "c": "people",
+ "e": "👟",
+ "d": "athletic shoe",
+ "u": "6.0"
+ },
+ "atm": {
+ "c": "symbols",
+ "e": "ðŸ§",
+ "d": "automated teller machine",
+ "u": "6.0"
+ },
+ "atom": {
+ "c": "symbols",
+ "e": "âš›",
+ "d": "atom symbol",
+ "u": "4.1"
+ },
+ "avocado": {
+ "c": "food",
+ "e": "🥑",
+ "d": "avocado",
+ "u": "9.0"
+ },
+ "b": {
+ "c": "symbols",
+ "e": "🅱",
+ "d": "negative squared latin capital letter b",
+ "u": "6.0"
+ },
+ "baby": {
+ "c": "people",
+ "e": "👶",
+ "d": "baby",
+ "u": "6.0"
+ },
+ "baby_bottle": {
+ "c": "food",
+ "e": "ðŸ¼",
+ "d": "baby bottle",
+ "u": "6.0"
+ },
+ "baby_chick": {
+ "c": "nature",
+ "e": "ðŸ¤",
+ "d": "baby chick",
+ "u": "6.0"
+ },
+ "baby_symbol": {
+ "c": "symbols",
+ "e": "🚼",
+ "d": "baby symbol",
+ "u": "6.0"
+ },
+ "baby_tone1": {
+ "c": "people",
+ "e": "👶ðŸ»",
+ "d": "baby tone 1",
+ "u": "8.0"
+ },
+ "baby_tone2": {
+ "c": "people",
+ "e": "👶ðŸ¼",
+ "d": "baby tone 2",
+ "u": "8.0"
+ },
+ "baby_tone3": {
+ "c": "people",
+ "e": "👶ðŸ½",
+ "d": "baby tone 3",
+ "u": "8.0"
+ },
+ "baby_tone4": {
+ "c": "people",
+ "e": "👶ðŸ¾",
+ "d": "baby tone 4",
+ "u": "8.0"
+ },
+ "baby_tone5": {
+ "c": "people",
+ "e": "👶ðŸ¿",
+ "d": "baby tone 5",
+ "u": "8.0"
+ },
+ "back": {
+ "c": "symbols",
+ "e": "🔙",
+ "d": "back with leftwards arrow above",
+ "u": "6.0"
+ },
+ "bacon": {
+ "c": "food",
+ "e": "🥓",
+ "d": "bacon",
+ "u": "9.0"
+ },
+ "badminton": {
+ "c": "activity",
+ "e": "ðŸ¸",
+ "d": "badminton racquet",
+ "u": "8.0"
+ },
+ "baggage_claim": {
+ "c": "symbols",
+ "e": "🛄",
+ "d": "baggage claim",
+ "u": "6.0"
+ },
+ "balloon": {
+ "c": "objects",
+ "e": "🎈",
+ "d": "balloon",
+ "u": "6.0"
+ },
+ "ballot_box": {
+ "c": "objects",
+ "e": "🗳",
+ "d": "ballot box with ballot",
+ "u": "7.0"
+ },
+ "ballot_box_with_check": {
+ "c": "symbols",
+ "e": "☑",
+ "d": "ballot box with check",
+ "u": "1.1"
+ },
+ "bamboo": {
+ "c": "nature",
+ "e": "ðŸŽ",
+ "d": "pine decoration",
+ "u": "6.0"
+ },
+ "banana": {
+ "c": "food",
+ "e": "ðŸŒ",
+ "d": "banana",
+ "u": "6.0"
+ },
+ "bangbang": {
+ "c": "symbols",
+ "e": "‼",
+ "d": "double exclamation mark",
+ "u": "1.1"
+ },
+ "bank": {
+ "c": "travel",
+ "e": "ðŸ¦",
+ "d": "bank",
+ "u": "6.0"
+ },
+ "bar_chart": {
+ "c": "objects",
+ "e": "📊",
+ "d": "bar chart",
+ "u": "6.0"
+ },
+ "barber": {
+ "c": "objects",
+ "e": "💈",
+ "d": "barber pole",
+ "u": "6.0"
+ },
+ "baseball": {
+ "c": "activity",
+ "e": "âš¾",
+ "d": "baseball",
+ "u": "5.2"
+ },
+ "basketball": {
+ "c": "activity",
+ "e": "ðŸ€",
+ "d": "basketball and hoop",
+ "u": "6.0"
+ },
+ "basketball_player": {
+ "c": "activity",
+ "e": "⛹",
+ "d": "person with ball",
+ "u": "5.2"
+ },
+ "basketball_player_tone1": {
+ "c": "activity",
+ "e": "⛹ðŸ»",
+ "d": "person with ball tone 1",
+ "u": "8.0"
+ },
+ "basketball_player_tone2": {
+ "c": "activity",
+ "e": "⛹ðŸ¼",
+ "d": "person with ball tone 2",
+ "u": "8.0"
+ },
+ "basketball_player_tone3": {
+ "c": "activity",
+ "e": "⛹ðŸ½",
+ "d": "person with ball tone 3",
+ "u": "8.0"
+ },
+ "basketball_player_tone4": {
+ "c": "activity",
+ "e": "⛹ðŸ¾",
+ "d": "person with ball tone 4",
+ "u": "8.0"
+ },
+ "basketball_player_tone5": {
+ "c": "activity",
+ "e": "⛹ðŸ¿",
+ "d": "person with ball tone 5",
+ "u": "8.0"
+ },
+ "bat": {
+ "c": "nature",
+ "e": "🦇",
+ "d": "bat",
+ "u": "9.0"
+ },
+ "bath": {
+ "c": "activity",
+ "e": "🛀",
+ "d": "bath",
+ "u": "6.0"
+ },
+ "bath_tone1": {
+ "c": "activity",
+ "e": "🛀ðŸ»",
+ "d": "bath tone 1",
+ "u": "8.0"
+ },
+ "bath_tone2": {
+ "c": "activity",
+ "e": "🛀ðŸ¼",
+ "d": "bath tone 2",
+ "u": "8.0"
+ },
+ "bath_tone3": {
+ "c": "activity",
+ "e": "🛀ðŸ½",
+ "d": "bath tone 3",
+ "u": "8.0"
+ },
+ "bath_tone4": {
+ "c": "activity",
+ "e": "🛀ðŸ¾",
+ "d": "bath tone 4",
+ "u": "8.0"
+ },
+ "bath_tone5": {
+ "c": "activity",
+ "e": "🛀ðŸ¿",
+ "d": "bath tone 5",
+ "u": "8.0"
+ },
+ "bathtub": {
+ "c": "objects",
+ "e": "ðŸ›",
+ "d": "bathtub",
+ "u": "6.0"
+ },
+ "battery": {
+ "c": "objects",
+ "e": "🔋",
+ "d": "battery",
+ "u": "6.0"
+ },
+ "beach": {
+ "c": "travel",
+ "e": "ðŸ–",
+ "d": "beach with umbrella",
+ "u": "7.0"
+ },
+ "beach_umbrella": {
+ "c": "objects",
+ "e": "â›±",
+ "d": "umbrella on ground",
+ "u": "5.2"
+ },
+ "bear": {
+ "c": "nature",
+ "e": "ðŸ»",
+ "d": "bear face",
+ "u": "6.0"
+ },
+ "bed": {
+ "c": "objects",
+ "e": "ðŸ›",
+ "d": "bed",
+ "u": "7.0"
+ },
+ "bee": {
+ "c": "nature",
+ "e": "ðŸ",
+ "d": "honeybee",
+ "u": "6.0"
+ },
+ "beer": {
+ "c": "food",
+ "e": "ðŸº",
+ "d": "beer mug",
+ "u": "6.0"
+ },
+ "beers": {
+ "c": "food",
+ "e": "ðŸ»",
+ "d": "clinking beer mugs",
+ "u": "6.0"
+ },
+ "beetle": {
+ "c": "nature",
+ "e": "ðŸž",
+ "d": "lady beetle",
+ "u": "6.0"
+ },
+ "beginner": {
+ "c": "symbols",
+ "e": "🔰",
+ "d": "japanese symbol for beginner",
+ "u": "6.0"
+ },
+ "bell": {
+ "c": "symbols",
+ "e": "🔔",
+ "d": "bell",
+ "u": "6.0"
+ },
+ "bellhop": {
+ "c": "objects",
+ "e": "🛎",
+ "d": "bellhop bell",
+ "u": "7.0"
+ },
+ "bento": {
+ "c": "food",
+ "e": "ðŸ±",
+ "d": "bento box",
+ "u": "6.0"
+ },
+ "bicyclist": {
+ "c": "activity",
+ "e": "🚴",
+ "d": "bicyclist",
+ "u": "6.0"
+ },
+ "bicyclist_tone1": {
+ "c": "activity",
+ "e": "🚴ðŸ»",
+ "d": "bicyclist tone 1",
+ "u": "8.0"
+ },
+ "bicyclist_tone2": {
+ "c": "activity",
+ "e": "🚴ðŸ¼",
+ "d": "bicyclist tone 2",
+ "u": "8.0"
+ },
+ "bicyclist_tone3": {
+ "c": "activity",
+ "e": "🚴ðŸ½",
+ "d": "bicyclist tone 3",
+ "u": "8.0"
+ },
+ "bicyclist_tone4": {
+ "c": "activity",
+ "e": "🚴ðŸ¾",
+ "d": "bicyclist tone 4",
+ "u": "8.0"
+ },
+ "bicyclist_tone5": {
+ "c": "activity",
+ "e": "🚴ðŸ¿",
+ "d": "bicyclist tone 5",
+ "u": "8.0"
+ },
+ "bike": {
+ "c": "travel",
+ "e": "🚲",
+ "d": "bicycle",
+ "u": "6.0"
+ },
+ "bikini": {
+ "c": "people",
+ "e": "👙",
+ "d": "bikini",
+ "u": "6.0"
+ },
+ "biohazard": {
+ "c": "symbols",
+ "e": "☣",
+ "d": "biohazard sign",
+ "u": "1.1"
+ },
+ "bird": {
+ "c": "nature",
+ "e": "ðŸ¦",
+ "d": "bird",
+ "u": "6.0"
+ },
+ "birthday": {
+ "c": "food",
+ "e": "🎂",
+ "d": "birthday cake",
+ "u": "6.0"
+ },
+ "black_circle": {
+ "c": "symbols",
+ "e": "âš«",
+ "d": "medium black circle",
+ "u": "4.1"
+ },
+ "black_heart": {
+ "c": "symbols",
+ "e": "🖤",
+ "d": "black heart",
+ "u": "9.0"
+ },
+ "black_joker": {
+ "c": "symbols",
+ "e": "ðŸƒ",
+ "d": "playing card black joker",
+ "u": "6.0"
+ },
+ "black_large_square": {
+ "c": "symbols",
+ "e": "⬛",
+ "d": "black large square",
+ "u": "5.1"
+ },
+ "black_medium_small_square": {
+ "c": "symbols",
+ "e": "â—¾",
+ "d": "black medium small square",
+ "u": "3.2"
+ },
+ "black_medium_square": {
+ "c": "symbols",
+ "e": "â—¼",
+ "d": "black medium square",
+ "u": "3.2"
+ },
+ "black_nib": {
+ "c": "objects",
+ "e": "✒",
+ "d": "black nib",
+ "u": "1.1"
+ },
+ "black_small_square": {
+ "c": "symbols",
+ "e": "â–ª",
+ "d": "black small square",
+ "u": "1.1"
+ },
+ "black_square_button": {
+ "c": "symbols",
+ "e": "🔲",
+ "d": "black square button",
+ "u": "6.0"
+ },
+ "blossom": {
+ "c": "nature",
+ "e": "🌼",
+ "d": "blossom",
+ "u": "6.0"
+ },
+ "blowfish": {
+ "c": "nature",
+ "e": "ðŸ¡",
+ "d": "blowfish",
+ "u": "6.0"
+ },
+ "blue_book": {
+ "c": "objects",
+ "e": "📘",
+ "d": "blue book",
+ "u": "6.0"
+ },
+ "blue_car": {
+ "c": "travel",
+ "e": "🚙",
+ "d": "recreational vehicle",
+ "u": "6.0"
+ },
+ "blue_heart": {
+ "c": "symbols",
+ "e": "💙",
+ "d": "blue heart",
+ "u": "6.0"
+ },
+ "blush": {
+ "c": "people",
+ "e": "😊",
+ "d": "smiling face with smiling eyes",
+ "u": "6.0"
+ },
+ "boar": {
+ "c": "nature",
+ "e": "ðŸ—",
+ "d": "boar",
+ "u": "6.0"
+ },
+ "bomb": {
+ "c": "objects",
+ "e": "💣",
+ "d": "bomb",
+ "u": "6.0"
+ },
+ "book": {
+ "c": "objects",
+ "e": "📖",
+ "d": "open book",
+ "u": "6.0"
+ },
+ "bookmark": {
+ "c": "objects",
+ "e": "🔖",
+ "d": "bookmark",
+ "u": "6.0"
+ },
+ "bookmark_tabs": {
+ "c": "objects",
+ "e": "📑",
+ "d": "bookmark tabs",
+ "u": "6.0"
+ },
+ "books": {
+ "c": "objects",
+ "e": "📚",
+ "d": "books",
+ "u": "6.0"
+ },
+ "boom": {
+ "c": "nature",
+ "e": "💥",
+ "d": "collision symbol",
+ "u": "6.0"
+ },
+ "boot": {
+ "c": "people",
+ "e": "👢",
+ "d": "womans boots",
+ "u": "6.0"
+ },
+ "bouquet": {
+ "c": "nature",
+ "e": "ðŸ’",
+ "d": "bouquet",
+ "u": "6.0"
+ },
+ "bow": {
+ "c": "people",
+ "e": "🙇",
+ "d": "person bowing deeply",
+ "u": "6.0"
+ },
+ "bow_and_arrow": {
+ "c": "activity",
+ "e": "ðŸ¹",
+ "d": "bow and arrow",
+ "u": "8.0"
+ },
+ "bow_tone1": {
+ "c": "people",
+ "e": "🙇ðŸ»",
+ "d": "person bowing deeply tone 1",
+ "u": "8.0"
+ },
+ "bow_tone2": {
+ "c": "people",
+ "e": "🙇ðŸ¼",
+ "d": "person bowing deeply tone 2",
+ "u": "8.0"
+ },
+ "bow_tone3": {
+ "c": "people",
+ "e": "🙇ðŸ½",
+ "d": "person bowing deeply tone 3",
+ "u": "8.0"
+ },
+ "bow_tone4": {
+ "c": "people",
+ "e": "🙇ðŸ¾",
+ "d": "person bowing deeply tone 4",
+ "u": "8.0"
+ },
+ "bow_tone5": {
+ "c": "people",
+ "e": "🙇ðŸ¿",
+ "d": "person bowing deeply tone 5",
+ "u": "8.0"
+ },
+ "bowling": {
+ "c": "activity",
+ "e": "🎳",
+ "d": "bowling",
+ "u": "6.0"
+ },
+ "boxing_glove": {
+ "c": "activity",
+ "e": "🥊",
+ "d": "boxing glove",
+ "u": "9.0"
+ },
+ "boy": {
+ "c": "people",
+ "e": "👦",
+ "d": "boy",
+ "u": "6.0"
+ },
+ "boy_tone1": {
+ "c": "people",
+ "e": "👦ðŸ»",
+ "d": "boy tone 1",
+ "u": "8.0"
+ },
+ "boy_tone2": {
+ "c": "people",
+ "e": "👦ðŸ¼",
+ "d": "boy tone 2",
+ "u": "8.0"
+ },
+ "boy_tone3": {
+ "c": "people",
+ "e": "👦ðŸ½",
+ "d": "boy tone 3",
+ "u": "8.0"
+ },
+ "boy_tone4": {
+ "c": "people",
+ "e": "👦ðŸ¾",
+ "d": "boy tone 4",
+ "u": "8.0"
+ },
+ "boy_tone5": {
+ "c": "people",
+ "e": "👦ðŸ¿",
+ "d": "boy tone 5",
+ "u": "8.0"
+ },
+ "bread": {
+ "c": "food",
+ "e": "ðŸž",
+ "d": "bread",
+ "u": "6.0"
+ },
+ "bride_with_veil": {
+ "c": "people",
+ "e": "👰",
+ "d": "bride with veil",
+ "u": "6.0"
+ },
+ "bride_with_veil_tone1": {
+ "c": "people",
+ "e": "👰ðŸ»",
+ "d": "bride with veil tone 1",
+ "u": "8.0"
+ },
+ "bride_with_veil_tone2": {
+ "c": "people",
+ "e": "👰ðŸ¼",
+ "d": "bride with veil tone 2",
+ "u": "8.0"
+ },
+ "bride_with_veil_tone3": {
+ "c": "people",
+ "e": "👰ðŸ½",
+ "d": "bride with veil tone 3",
+ "u": "8.0"
+ },
+ "bride_with_veil_tone4": {
+ "c": "people",
+ "e": "👰ðŸ¾",
+ "d": "bride with veil tone 4",
+ "u": "8.0"
+ },
+ "bride_with_veil_tone5": {
+ "c": "people",
+ "e": "👰ðŸ¿",
+ "d": "bride with veil tone 5",
+ "u": "8.0"
+ },
+ "bridge_at_night": {
+ "c": "travel",
+ "e": "🌉",
+ "d": "bridge at night",
+ "u": "6.0"
+ },
+ "briefcase": {
+ "c": "people",
+ "e": "💼",
+ "d": "briefcase",
+ "u": "6.0"
+ },
+ "broken_heart": {
+ "c": "symbols",
+ "e": "💔",
+ "d": "broken heart",
+ "u": "6.0"
+ },
+ "bug": {
+ "c": "nature",
+ "e": "ðŸ›",
+ "d": "bug",
+ "u": "6.0"
+ },
+ "bulb": {
+ "c": "objects",
+ "e": "💡",
+ "d": "electric light bulb",
+ "u": "6.0"
+ },
+ "bullettrain_front": {
+ "c": "travel",
+ "e": "🚅",
+ "d": "high-speed train with bullet nose",
+ "u": "6.0"
+ },
+ "bullettrain_side": {
+ "c": "travel",
+ "e": "🚄",
+ "d": "high-speed train",
+ "u": "6.0"
+ },
+ "burrito": {
+ "c": "food",
+ "e": "🌯",
+ "d": "burrito",
+ "u": "8.0"
+ },
+ "bus": {
+ "c": "travel",
+ "e": "🚌",
+ "d": "bus",
+ "u": "6.0"
+ },
+ "busstop": {
+ "c": "travel",
+ "e": "ðŸš",
+ "d": "bus stop",
+ "u": "6.0"
+ },
+ "bust_in_silhouette": {
+ "c": "people",
+ "e": "👤",
+ "d": "bust in silhouette",
+ "u": "6.0"
+ },
+ "busts_in_silhouette": {
+ "c": "people",
+ "e": "👥",
+ "d": "busts in silhouette",
+ "u": "6.0"
+ },
+ "butterfly": {
+ "c": "nature",
+ "e": "🦋",
+ "d": "butterfly",
+ "u": "9.0"
+ },
+ "cactus": {
+ "c": "nature",
+ "e": "🌵",
+ "d": "cactus",
+ "u": "6.0"
+ },
+ "cake": {
+ "c": "food",
+ "e": "ðŸ°",
+ "d": "shortcake",
+ "u": "6.0"
+ },
+ "calendar": {
+ "c": "objects",
+ "e": "📆",
+ "d": "tear-off calendar",
+ "u": "6.0"
+ },
+ "calendar_spiral": {
+ "c": "objects",
+ "e": "🗓",
+ "d": "spiral calendar pad",
+ "u": "7.0"
+ },
+ "call_me": {
+ "c": "people",
+ "e": "🤙",
+ "d": "call me hand",
+ "u": "9.0"
+ },
+ "call_me_tone1": {
+ "c": "people",
+ "e": "🤙ðŸ»",
+ "d": "call me hand tone 1",
+ "u": "9.0"
+ },
+ "call_me_tone2": {
+ "c": "people",
+ "e": "🤙ðŸ¼",
+ "d": "call me hand tone 2",
+ "u": "9.0"
+ },
+ "call_me_tone3": {
+ "c": "people",
+ "e": "🤙ðŸ½",
+ "d": "call me hand tone 3",
+ "u": "9.0"
+ },
+ "call_me_tone4": {
+ "c": "people",
+ "e": "🤙ðŸ¾",
+ "d": "call me hand tone 4",
+ "u": "9.0"
+ },
+ "call_me_tone5": {
+ "c": "people",
+ "e": "🤙ðŸ¿",
+ "d": "call me hand tone 5",
+ "u": "9.0"
+ },
+ "calling": {
+ "c": "objects",
+ "e": "📲",
+ "d": "mobile phone with rightwards arrow at left",
+ "u": "6.0"
+ },
+ "camel": {
+ "c": "nature",
+ "e": "ðŸ«",
+ "d": "bactrian camel",
+ "u": "6.0"
+ },
+ "camera": {
+ "c": "objects",
+ "e": "📷",
+ "d": "camera",
+ "u": "6.0"
+ },
+ "camera_with_flash": {
+ "c": "objects",
+ "e": "📸",
+ "d": "camera with flash",
+ "u": "7.0"
+ },
+ "camping": {
+ "c": "travel",
+ "e": "ðŸ•",
+ "d": "camping",
+ "u": "7.0"
+ },
+ "cancer": {
+ "c": "symbols",
+ "e": "♋",
+ "d": "cancer",
+ "u": "1.1"
+ },
+ "candle": {
+ "c": "objects",
+ "e": "🕯",
+ "d": "candle",
+ "u": "7.0"
+ },
+ "candy": {
+ "c": "food",
+ "e": "ðŸ¬",
+ "d": "candy",
+ "u": "6.0"
+ },
+ "canoe": {
+ "c": "travel",
+ "e": "🛶",
+ "d": "canoe",
+ "u": "9.0"
+ },
+ "capital_abcd": {
+ "c": "symbols",
+ "e": "🔠",
+ "d": "input symbol for latin capital letters",
+ "u": "6.0"
+ },
+ "capricorn": {
+ "c": "symbols",
+ "e": "♑",
+ "d": "capricorn",
+ "u": "1.1"
+ },
+ "card_box": {
+ "c": "objects",
+ "e": "🗃",
+ "d": "card file box",
+ "u": "7.0"
+ },
+ "card_index": {
+ "c": "objects",
+ "e": "📇",
+ "d": "card index",
+ "u": "6.0"
+ },
+ "carousel_horse": {
+ "c": "travel",
+ "e": "🎠",
+ "d": "carousel horse",
+ "u": "6.0"
+ },
+ "carrot": {
+ "c": "food",
+ "e": "🥕",
+ "d": "carrot",
+ "u": "9.0"
+ },
+ "cartwheel": {
+ "c": "activity",
+ "e": "🤸",
+ "d": "person doing cartwheel",
+ "u": "9.0"
+ },
+ "cartwheel_tone1": {
+ "c": "activity",
+ "e": "🤸ðŸ»",
+ "d": "person doing cartwheel tone 1",
+ "u": "9.0"
+ },
+ "cartwheel_tone2": {
+ "c": "activity",
+ "e": "🤸ðŸ¼",
+ "d": "person doing cartwheel tone 2",
+ "u": "9.0"
+ },
+ "cartwheel_tone3": {
+ "c": "activity",
+ "e": "🤸ðŸ½",
+ "d": "person doing cartwheel tone 3",
+ "u": "9.0"
+ },
+ "cartwheel_tone4": {
+ "c": "activity",
+ "e": "🤸ðŸ¾",
+ "d": "person doing cartwheel tone 4",
+ "u": "9.0"
+ },
+ "cartwheel_tone5": {
+ "c": "activity",
+ "e": "🤸ðŸ¿",
+ "d": "person doing cartwheel tone 5",
+ "u": "9.0"
+ },
+ "cat": {
+ "c": "nature",
+ "e": "ðŸ±",
+ "d": "cat face",
+ "u": "6.0"
+ },
+ "cat2": {
+ "c": "nature",
+ "e": "ðŸˆ",
+ "d": "cat",
+ "u": "6.0"
+ },
+ "cd": {
+ "c": "objects",
+ "e": "💿",
+ "d": "optical disc",
+ "u": "6.0"
+ },
+ "chains": {
+ "c": "objects",
+ "e": "⛓",
+ "d": "chains",
+ "u": "5.2"
+ },
+ "champagne": {
+ "c": "food",
+ "e": "ðŸ¾",
+ "d": "bottle with popping cork",
+ "u": "8.0"
+ },
+ "champagne_glass": {
+ "c": "food",
+ "e": "🥂",
+ "d": "clinking glasses",
+ "u": "9.0"
+ },
+ "chart": {
+ "c": "symbols",
+ "e": "💹",
+ "d": "chart with upwards trend and yen sign",
+ "u": "6.0"
+ },
+ "chart_with_downwards_trend": {
+ "c": "objects",
+ "e": "📉",
+ "d": "chart with downwards trend",
+ "u": "6.0"
+ },
+ "chart_with_upwards_trend": {
+ "c": "objects",
+ "e": "📈",
+ "d": "chart with upwards trend",
+ "u": "6.0"
+ },
+ "checkered_flag": {
+ "c": "travel",
+ "e": "ðŸ",
+ "d": "chequered flag",
+ "u": "6.0"
+ },
+ "cheese": {
+ "c": "food",
+ "e": "🧀",
+ "d": "cheese wedge",
+ "u": "8.0"
+ },
+ "cherries": {
+ "c": "food",
+ "e": "ðŸ’",
+ "d": "cherries",
+ "u": "6.0"
+ },
+ "cherry_blossom": {
+ "c": "nature",
+ "e": "🌸",
+ "d": "cherry blossom",
+ "u": "6.0"
+ },
+ "chestnut": {
+ "c": "nature",
+ "e": "🌰",
+ "d": "chestnut",
+ "u": "6.0"
+ },
+ "chicken": {
+ "c": "nature",
+ "e": "ðŸ”",
+ "d": "chicken",
+ "u": "6.0"
+ },
+ "children_crossing": {
+ "c": "symbols",
+ "e": "🚸",
+ "d": "children crossing",
+ "u": "6.0"
+ },
+ "chipmunk": {
+ "c": "nature",
+ "e": "ðŸ¿",
+ "d": "chipmunk",
+ "u": "7.0"
+ },
+ "chocolate_bar": {
+ "c": "food",
+ "e": "ðŸ«",
+ "d": "chocolate bar",
+ "u": "6.0"
+ },
+ "christmas_tree": {
+ "c": "nature",
+ "e": "🎄",
+ "d": "christmas tree",
+ "u": "6.0"
+ },
+ "church": {
+ "c": "travel",
+ "e": "⛪",
+ "d": "church",
+ "u": "5.2"
+ },
+ "cinema": {
+ "c": "symbols",
+ "e": "🎦",
+ "d": "cinema",
+ "u": "6.0"
+ },
+ "circus_tent": {
+ "c": "activity",
+ "e": "🎪",
+ "d": "circus tent",
+ "u": "6.0"
+ },
+ "city_dusk": {
+ "c": "travel",
+ "e": "🌆",
+ "d": "cityscape at dusk",
+ "u": "6.0"
+ },
+ "city_sunset": {
+ "c": "travel",
+ "e": "🌇",
+ "d": "sunset over buildings",
+ "u": "6.0"
+ },
+ "cityscape": {
+ "c": "travel",
+ "e": "ðŸ™",
+ "d": "cityscape",
+ "u": "7.0"
+ },
+ "cl": {
+ "c": "symbols",
+ "e": "🆑",
+ "d": "squared cl",
+ "u": "6.0"
+ },
+ "clap": {
+ "c": "people",
+ "e": "ðŸ‘",
+ "d": "clapping hands sign",
+ "u": "6.0"
+ },
+ "clap_tone1": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ»",
+ "d": "clapping hands sign tone 1",
+ "u": "8.0"
+ },
+ "clap_tone2": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¼",
+ "d": "clapping hands sign tone 2",
+ "u": "8.0"
+ },
+ "clap_tone3": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ½",
+ "d": "clapping hands sign tone 3",
+ "u": "8.0"
+ },
+ "clap_tone4": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¾",
+ "d": "clapping hands sign tone 4",
+ "u": "8.0"
+ },
+ "clap_tone5": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¿",
+ "d": "clapping hands sign tone 5",
+ "u": "8.0"
+ },
+ "clapper": {
+ "c": "activity",
+ "e": "🎬",
+ "d": "clapper board",
+ "u": "6.0"
+ },
+ "classical_building": {
+ "c": "travel",
+ "e": "ðŸ›",
+ "d": "classical building",
+ "u": "7.0"
+ },
+ "clipboard": {
+ "c": "objects",
+ "e": "📋",
+ "d": "clipboard",
+ "u": "6.0"
+ },
+ "clock": {
+ "c": "objects",
+ "e": "🕰",
+ "d": "mantlepiece clock",
+ "u": "7.0"
+ },
+ "clock1": {
+ "c": "symbols",
+ "e": "ðŸ•",
+ "d": "clock face one oclock",
+ "u": "6.0"
+ },
+ "clock10": {
+ "c": "symbols",
+ "e": "🕙",
+ "d": "clock face ten oclock",
+ "u": "6.0"
+ },
+ "clock1030": {
+ "c": "symbols",
+ "e": "🕥",
+ "d": "clock face ten-thirty",
+ "u": "6.0"
+ },
+ "clock11": {
+ "c": "symbols",
+ "e": "🕚",
+ "d": "clock face eleven oclock",
+ "u": "6.0"
+ },
+ "clock1130": {
+ "c": "symbols",
+ "e": "🕦",
+ "d": "clock face eleven-thirty",
+ "u": "6.0"
+ },
+ "clock12": {
+ "c": "symbols",
+ "e": "🕛",
+ "d": "clock face twelve oclock",
+ "u": "6.0"
+ },
+ "clock1230": {
+ "c": "symbols",
+ "e": "🕧",
+ "d": "clock face twelve-thirty",
+ "u": "6.0"
+ },
+ "clock130": {
+ "c": "symbols",
+ "e": "🕜",
+ "d": "clock face one-thirty",
+ "u": "6.0"
+ },
+ "clock2": {
+ "c": "symbols",
+ "e": "🕑",
+ "d": "clock face two oclock",
+ "u": "6.0"
+ },
+ "clock230": {
+ "c": "symbols",
+ "e": "ðŸ•",
+ "d": "clock face two-thirty",
+ "u": "6.0"
+ },
+ "clock3": {
+ "c": "symbols",
+ "e": "🕒",
+ "d": "clock face three oclock",
+ "u": "6.0"
+ },
+ "clock330": {
+ "c": "symbols",
+ "e": "🕞",
+ "d": "clock face three-thirty",
+ "u": "6.0"
+ },
+ "clock4": {
+ "c": "symbols",
+ "e": "🕓",
+ "d": "clock face four oclock",
+ "u": "6.0"
+ },
+ "clock430": {
+ "c": "symbols",
+ "e": "🕟",
+ "d": "clock face four-thirty",
+ "u": "6.0"
+ },
+ "clock5": {
+ "c": "symbols",
+ "e": "🕔",
+ "d": "clock face five oclock",
+ "u": "6.0"
+ },
+ "clock530": {
+ "c": "symbols",
+ "e": "🕠",
+ "d": "clock face five-thirty",
+ "u": "6.0"
+ },
+ "clock6": {
+ "c": "symbols",
+ "e": "🕕",
+ "d": "clock face six oclock",
+ "u": "6.0"
+ },
+ "clock630": {
+ "c": "symbols",
+ "e": "🕡",
+ "d": "clock face six-thirty",
+ "u": "6.0"
+ },
+ "clock7": {
+ "c": "symbols",
+ "e": "🕖",
+ "d": "clock face seven oclock",
+ "u": "6.0"
+ },
+ "clock730": {
+ "c": "symbols",
+ "e": "🕢",
+ "d": "clock face seven-thirty",
+ "u": "6.0"
+ },
+ "clock8": {
+ "c": "symbols",
+ "e": "🕗",
+ "d": "clock face eight oclock",
+ "u": "6.0"
+ },
+ "clock830": {
+ "c": "symbols",
+ "e": "🕣",
+ "d": "clock face eight-thirty",
+ "u": "6.0"
+ },
+ "clock9": {
+ "c": "symbols",
+ "e": "🕘",
+ "d": "clock face nine oclock",
+ "u": "6.0"
+ },
+ "clock930": {
+ "c": "symbols",
+ "e": "🕤",
+ "d": "clock face nine-thirty",
+ "u": "6.0"
+ },
+ "closed_book": {
+ "c": "objects",
+ "e": "📕",
+ "d": "closed book",
+ "u": "6.0"
+ },
+ "closed_lock_with_key": {
+ "c": "objects",
+ "e": "ðŸ”",
+ "d": "closed lock with key",
+ "u": "6.0"
+ },
+ "closed_umbrella": {
+ "c": "people",
+ "e": "🌂",
+ "d": "closed umbrella",
+ "u": "6.0"
+ },
+ "cloud": {
+ "c": "nature",
+ "e": "â˜",
+ "d": "cloud",
+ "u": "1.1"
+ },
+ "cloud_lightning": {
+ "c": "nature",
+ "e": "🌩",
+ "d": "cloud with lightning",
+ "u": "7.0"
+ },
+ "cloud_rain": {
+ "c": "nature",
+ "e": "🌧",
+ "d": "cloud with rain",
+ "u": "7.0"
+ },
+ "cloud_snow": {
+ "c": "nature",
+ "e": "🌨",
+ "d": "cloud with snow",
+ "u": "7.0"
+ },
+ "cloud_tornado": {
+ "c": "nature",
+ "e": "🌪",
+ "d": "cloud with tornado",
+ "u": "7.0"
+ },
+ "clown": {
+ "c": "people",
+ "e": "🤡",
+ "d": "clown face",
+ "u": "9.0"
+ },
+ "clubs": {
+ "c": "symbols",
+ "e": "♣",
+ "d": "black club suit",
+ "u": "1.1"
+ },
+ "cocktail": {
+ "c": "food",
+ "e": "ðŸ¸",
+ "d": "cocktail glass",
+ "u": "6.0"
+ },
+ "coffee": {
+ "c": "food",
+ "e": "☕",
+ "d": "hot beverage",
+ "u": "4.0"
+ },
+ "coffin": {
+ "c": "objects",
+ "e": "âš°",
+ "d": "coffin",
+ "u": "4.1"
+ },
+ "cold_sweat": {
+ "c": "people",
+ "e": "😰",
+ "d": "face with open mouth and cold sweat",
+ "u": "6.0"
+ },
+ "comet": {
+ "c": "nature",
+ "e": "☄",
+ "d": "comet",
+ "u": "1.1"
+ },
+ "compression": {
+ "c": "objects",
+ "e": "🗜",
+ "d": "compression",
+ "u": "7.0"
+ },
+ "computer": {
+ "c": "objects",
+ "e": "💻",
+ "d": "personal computer",
+ "u": "6.0"
+ },
+ "confetti_ball": {
+ "c": "objects",
+ "e": "🎊",
+ "d": "confetti ball",
+ "u": "6.0"
+ },
+ "confounded": {
+ "c": "people",
+ "e": "😖",
+ "d": "confounded face",
+ "u": "6.0"
+ },
+ "confused": {
+ "c": "people",
+ "e": "😕",
+ "d": "confused face",
+ "u": "6.1"
+ },
+ "congratulations": {
+ "c": "symbols",
+ "e": "㊗",
+ "d": "circled ideograph congratulation",
+ "u": "1.1"
+ },
+ "construction": {
+ "c": "travel",
+ "e": "🚧",
+ "d": "construction sign",
+ "u": "6.0"
+ },
+ "construction_site": {
+ "c": "travel",
+ "e": "ðŸ—",
+ "d": "building construction",
+ "u": "7.0"
+ },
+ "construction_worker": {
+ "c": "people",
+ "e": "👷",
+ "d": "construction worker",
+ "u": "6.0"
+ },
+ "construction_worker_tone1": {
+ "c": "people",
+ "e": "👷ðŸ»",
+ "d": "construction worker tone 1",
+ "u": "8.0"
+ },
+ "construction_worker_tone2": {
+ "c": "people",
+ "e": "👷ðŸ¼",
+ "d": "construction worker tone 2",
+ "u": "8.0"
+ },
+ "construction_worker_tone3": {
+ "c": "people",
+ "e": "👷ðŸ½",
+ "d": "construction worker tone 3",
+ "u": "8.0"
+ },
+ "construction_worker_tone4": {
+ "c": "people",
+ "e": "👷ðŸ¾",
+ "d": "construction worker tone 4",
+ "u": "8.0"
+ },
+ "construction_worker_tone5": {
+ "c": "people",
+ "e": "👷ðŸ¿",
+ "d": "construction worker tone 5",
+ "u": "8.0"
+ },
+ "control_knobs": {
+ "c": "objects",
+ "e": "🎛",
+ "d": "control knobs",
+ "u": "7.0"
+ },
+ "convenience_store": {
+ "c": "travel",
+ "e": "ðŸª",
+ "d": "convenience store",
+ "u": "6.0"
+ },
+ "cookie": {
+ "c": "food",
+ "e": "ðŸª",
+ "d": "cookie",
+ "u": "6.0"
+ },
+ "cooking": {
+ "c": "food",
+ "e": "ðŸ³",
+ "d": "cooking",
+ "u": "6.0"
+ },
+ "cool": {
+ "c": "symbols",
+ "e": "🆒",
+ "d": "squared cool",
+ "u": "6.0"
+ },
+ "cop": {
+ "c": "people",
+ "e": "👮",
+ "d": "police officer",
+ "u": "6.0"
+ },
+ "cop_tone1": {
+ "c": "people",
+ "e": "👮ðŸ»",
+ "d": "police officer tone 1",
+ "u": "8.0"
+ },
+ "cop_tone2": {
+ "c": "people",
+ "e": "👮ðŸ¼",
+ "d": "police officer tone 2",
+ "u": "8.0"
+ },
+ "cop_tone3": {
+ "c": "people",
+ "e": "👮ðŸ½",
+ "d": "police officer tone 3",
+ "u": "8.0"
+ },
+ "cop_tone4": {
+ "c": "people",
+ "e": "👮ðŸ¾",
+ "d": "police officer tone 4",
+ "u": "8.0"
+ },
+ "cop_tone5": {
+ "c": "people",
+ "e": "👮ðŸ¿",
+ "d": "police officer tone 5",
+ "u": "8.0"
+ },
+ "copyright": {
+ "c": "symbols",
+ "e": "©",
+ "d": "copyright sign",
+ "u": "1.1"
+ },
+ "corn": {
+ "c": "food",
+ "e": "🌽",
+ "d": "ear of maize",
+ "u": "6.0"
+ },
+ "couch": {
+ "c": "objects",
+ "e": "🛋",
+ "d": "couch and lamp",
+ "u": "7.0"
+ },
+ "couple": {
+ "c": "people",
+ "e": "👫",
+ "d": "man and woman holding hands",
+ "u": "6.0"
+ },
+ "couple_mm": {
+ "c": "people",
+ "e": "👨â€â¤ï¸â€ðŸ‘¨",
+ "d": "couple (man,man)",
+ "u": "6.0"
+ },
+ "couple_with_heart": {
+ "c": "people",
+ "e": "💑",
+ "d": "couple with heart",
+ "u": "6.0"
+ },
+ "couple_ww": {
+ "c": "people",
+ "e": "👩â€â¤ï¸â€ðŸ‘©",
+ "d": "couple (woman,woman)",
+ "u": "6.0"
+ },
+ "couplekiss": {
+ "c": "people",
+ "e": "ðŸ’",
+ "d": "kiss",
+ "u": "6.0"
+ },
+ "cow": {
+ "c": "nature",
+ "e": "ðŸ®",
+ "d": "cow face",
+ "u": "6.0"
+ },
+ "cow2": {
+ "c": "nature",
+ "e": "ðŸ„",
+ "d": "cow",
+ "u": "6.0"
+ },
+ "cowboy": {
+ "c": "people",
+ "e": "🤠",
+ "d": "face with cowboy hat",
+ "u": "9.0"
+ },
+ "crab": {
+ "c": "nature",
+ "e": "🦀",
+ "d": "crab",
+ "u": "8.0"
+ },
+ "crayon": {
+ "c": "objects",
+ "e": "ðŸ–",
+ "d": "lower left crayon",
+ "u": "7.0"
+ },
+ "credit_card": {
+ "c": "objects",
+ "e": "💳",
+ "d": "credit card",
+ "u": "6.0"
+ },
+ "crescent_moon": {
+ "c": "nature",
+ "e": "🌙",
+ "d": "crescent moon",
+ "u": "6.0"
+ },
+ "cricket": {
+ "c": "activity",
+ "e": "ðŸ",
+ "d": "cricket bat and ball",
+ "u": "8.0"
+ },
+ "crocodile": {
+ "c": "nature",
+ "e": "ðŸŠ",
+ "d": "crocodile",
+ "u": "6.0"
+ },
+ "croissant": {
+ "c": "food",
+ "e": "ðŸ¥",
+ "d": "croissant",
+ "u": "9.0"
+ },
+ "cross": {
+ "c": "symbols",
+ "e": "âœ",
+ "d": "latin cross",
+ "u": "1.1"
+ },
+ "crossed_flags": {
+ "c": "objects",
+ "e": "🎌",
+ "d": "crossed flags",
+ "u": "6.0"
+ },
+ "crossed_swords": {
+ "c": "objects",
+ "e": "âš”",
+ "d": "crossed swords",
+ "u": "4.1"
+ },
+ "crown": {
+ "c": "people",
+ "e": "👑",
+ "d": "crown",
+ "u": "6.0"
+ },
+ "cruise_ship": {
+ "c": "travel",
+ "e": "🛳",
+ "d": "passenger ship",
+ "u": "7.0"
+ },
+ "cry": {
+ "c": "people",
+ "e": "😢",
+ "d": "crying face",
+ "u": "6.0"
+ },
+ "crying_cat_face": {
+ "c": "people",
+ "e": "😿",
+ "d": "crying cat face",
+ "u": "6.0"
+ },
+ "crystal_ball": {
+ "c": "objects",
+ "e": "🔮",
+ "d": "crystal ball",
+ "u": "6.0"
+ },
+ "cucumber": {
+ "c": "food",
+ "e": "🥒",
+ "d": "cucumber",
+ "u": "9.0"
+ },
+ "cupid": {
+ "c": "symbols",
+ "e": "💘",
+ "d": "heart with arrow",
+ "u": "6.0"
+ },
+ "curly_loop": {
+ "c": "symbols",
+ "e": "âž°",
+ "d": "curly loop",
+ "u": "6.0"
+ },
+ "currency_exchange": {
+ "c": "symbols",
+ "e": "💱",
+ "d": "currency exchange",
+ "u": "6.0"
+ },
+ "curry": {
+ "c": "food",
+ "e": "ðŸ›",
+ "d": "curry and rice",
+ "u": "6.0"
+ },
+ "custard": {
+ "c": "food",
+ "e": "ðŸ®",
+ "d": "custard",
+ "u": "6.0"
+ },
+ "customs": {
+ "c": "symbols",
+ "e": "🛃",
+ "d": "customs",
+ "u": "6.0"
+ },
+ "cyclone": {
+ "c": "symbols",
+ "e": "🌀",
+ "d": "cyclone",
+ "u": "6.0"
+ },
+ "dagger": {
+ "c": "objects",
+ "e": "🗡",
+ "d": "dagger knife",
+ "u": "7.0"
+ },
+ "dancer": {
+ "c": "people",
+ "e": "💃",
+ "d": "dancer",
+ "u": "6.0"
+ },
+ "dancer_tone1": {
+ "c": "people",
+ "e": "💃ðŸ»",
+ "d": "dancer tone 1",
+ "u": "8.0"
+ },
+ "dancer_tone2": {
+ "c": "people",
+ "e": "💃ðŸ¼",
+ "d": "dancer tone 2",
+ "u": "8.0"
+ },
+ "dancer_tone3": {
+ "c": "people",
+ "e": "💃ðŸ½",
+ "d": "dancer tone 3",
+ "u": "8.0"
+ },
+ "dancer_tone4": {
+ "c": "people",
+ "e": "💃ðŸ¾",
+ "d": "dancer tone 4",
+ "u": "8.0"
+ },
+ "dancer_tone5": {
+ "c": "people",
+ "e": "💃ðŸ¿",
+ "d": "dancer tone 5",
+ "u": "8.0"
+ },
+ "dancers": {
+ "c": "people",
+ "e": "👯",
+ "d": "woman with bunny ears",
+ "u": "6.0"
+ },
+ "dango": {
+ "c": "food",
+ "e": "ðŸ¡",
+ "d": "dango",
+ "u": "6.0"
+ },
+ "dark_sunglasses": {
+ "c": "people",
+ "e": "🕶",
+ "d": "dark sunglasses",
+ "u": "7.0"
+ },
+ "dart": {
+ "c": "activity",
+ "e": "🎯",
+ "d": "direct hit",
+ "u": "6.0"
+ },
+ "dash": {
+ "c": "nature",
+ "e": "💨",
+ "d": "dash symbol",
+ "u": "6.0"
+ },
+ "date": {
+ "c": "objects",
+ "e": "📅",
+ "d": "calendar",
+ "u": "6.0"
+ },
+ "deciduous_tree": {
+ "c": "nature",
+ "e": "🌳",
+ "d": "deciduous tree",
+ "u": "6.0"
+ },
+ "deer": {
+ "c": "nature",
+ "e": "🦌",
+ "d": "deer",
+ "u": "9.0"
+ },
+ "department_store": {
+ "c": "travel",
+ "e": "ðŸ¬",
+ "d": "department store",
+ "u": "6.0"
+ },
+ "desert": {
+ "c": "travel",
+ "e": "ðŸœ",
+ "d": "desert",
+ "u": "7.0"
+ },
+ "desktop": {
+ "c": "objects",
+ "e": "🖥",
+ "d": "desktop computer",
+ "u": "7.0"
+ },
+ "diamond_shape_with_a_dot_inside": {
+ "c": "symbols",
+ "e": "💠",
+ "d": "diamond shape with a dot inside",
+ "u": "6.0"
+ },
+ "diamonds": {
+ "c": "symbols",
+ "e": "♦",
+ "d": "black diamond suit",
+ "u": "1.1"
+ },
+ "disappointed": {
+ "c": "people",
+ "e": "😞",
+ "d": "disappointed face",
+ "u": "6.0"
+ },
+ "disappointed_relieved": {
+ "c": "people",
+ "e": "😥",
+ "d": "disappointed but relieved face",
+ "u": "6.0"
+ },
+ "dividers": {
+ "c": "objects",
+ "e": "🗂",
+ "d": "card index dividers",
+ "u": "7.0"
+ },
+ "dizzy": {
+ "c": "nature",
+ "e": "💫",
+ "d": "dizzy symbol",
+ "u": "6.0"
+ },
+ "dizzy_face": {
+ "c": "people",
+ "e": "😵",
+ "d": "dizzy face",
+ "u": "6.0"
+ },
+ "do_not_litter": {
+ "c": "symbols",
+ "e": "🚯",
+ "d": "do not litter symbol",
+ "u": "6.0"
+ },
+ "dog": {
+ "c": "nature",
+ "e": "ðŸ¶",
+ "d": "dog face",
+ "u": "6.0"
+ },
+ "dog2": {
+ "c": "nature",
+ "e": "ðŸ•",
+ "d": "dog",
+ "u": "6.0"
+ },
+ "dollar": {
+ "c": "objects",
+ "e": "💵",
+ "d": "banknote with dollar sign",
+ "u": "6.0"
+ },
+ "dolls": {
+ "c": "objects",
+ "e": "🎎",
+ "d": "japanese dolls",
+ "u": "6.0"
+ },
+ "dolphin": {
+ "c": "nature",
+ "e": "ðŸ¬",
+ "d": "dolphin",
+ "u": "6.0"
+ },
+ "door": {
+ "c": "objects",
+ "e": "🚪",
+ "d": "door",
+ "u": "6.0"
+ },
+ "doughnut": {
+ "c": "food",
+ "e": "ðŸ©",
+ "d": "doughnut",
+ "u": "6.0"
+ },
+ "dove": {
+ "c": "nature",
+ "e": "🕊",
+ "d": "dove of peace",
+ "u": "7.0"
+ },
+ "dragon": {
+ "c": "nature",
+ "e": "ðŸ‰",
+ "d": "dragon",
+ "u": "6.0"
+ },
+ "dragon_face": {
+ "c": "nature",
+ "e": "ðŸ²",
+ "d": "dragon face",
+ "u": "6.0"
+ },
+ "dress": {
+ "c": "people",
+ "e": "👗",
+ "d": "dress",
+ "u": "6.0"
+ },
+ "dromedary_camel": {
+ "c": "nature",
+ "e": "ðŸª",
+ "d": "dromedary camel",
+ "u": "6.0"
+ },
+ "drooling_face": {
+ "c": "people",
+ "e": "🤤",
+ "d": "drooling face",
+ "u": "9.0"
+ },
+ "droplet": {
+ "c": "nature",
+ "e": "💧",
+ "d": "droplet",
+ "u": "6.0"
+ },
+ "drum": {
+ "c": "activity",
+ "e": "ðŸ¥",
+ "d": "drum with drumsticks",
+ "u": "9.0"
+ },
+ "duck": {
+ "c": "nature",
+ "e": "🦆",
+ "d": "duck",
+ "u": "9.0"
+ },
+ "dvd": {
+ "c": "objects",
+ "e": "📀",
+ "d": "dvd",
+ "u": "6.0"
+ },
+ "e-mail": {
+ "c": "objects",
+ "e": "📧",
+ "d": "e-mail symbol",
+ "u": "6.0"
+ },
+ "eagle": {
+ "c": "nature",
+ "e": "🦅",
+ "d": "eagle",
+ "u": "9.0"
+ },
+ "ear": {
+ "c": "people",
+ "e": "👂",
+ "d": "ear",
+ "u": "6.0"
+ },
+ "ear_of_rice": {
+ "c": "nature",
+ "e": "🌾",
+ "d": "ear of rice",
+ "u": "6.0"
+ },
+ "ear_tone1": {
+ "c": "people",
+ "e": "👂ðŸ»",
+ "d": "ear tone 1",
+ "u": "8.0"
+ },
+ "ear_tone2": {
+ "c": "people",
+ "e": "👂ðŸ¼",
+ "d": "ear tone 2",
+ "u": "8.0"
+ },
+ "ear_tone3": {
+ "c": "people",
+ "e": "👂ðŸ½",
+ "d": "ear tone 3",
+ "u": "8.0"
+ },
+ "ear_tone4": {
+ "c": "people",
+ "e": "👂ðŸ¾",
+ "d": "ear tone 4",
+ "u": "8.0"
+ },
+ "ear_tone5": {
+ "c": "people",
+ "e": "👂ðŸ¿",
+ "d": "ear tone 5",
+ "u": "8.0"
+ },
+ "earth_africa": {
+ "c": "nature",
+ "e": "ðŸŒ",
+ "d": "earth globe europe-africa",
+ "u": "6.0"
+ },
+ "earth_americas": {
+ "c": "nature",
+ "e": "🌎",
+ "d": "earth globe americas",
+ "u": "6.0"
+ },
+ "earth_asia": {
+ "c": "nature",
+ "e": "ðŸŒ",
+ "d": "earth globe asia-australia",
+ "u": "6.0"
+ },
+ "egg": {
+ "c": "food",
+ "e": "🥚",
+ "d": "egg",
+ "u": "9.0"
+ },
+ "eggplant": {
+ "c": "food",
+ "e": "ðŸ†",
+ "d": "aubergine",
+ "u": "6.0"
+ },
+ "eight": {
+ "c": "symbols",
+ "e": "8ï¸âƒ£",
+ "d": "keycap digit eight",
+ "u": "3.0"
+ },
+ "eight_pointed_black_star": {
+ "c": "symbols",
+ "e": "✴",
+ "d": "eight pointed black star",
+ "u": "1.1"
+ },
+ "eight_spoked_asterisk": {
+ "c": "symbols",
+ "e": "✳",
+ "d": "eight spoked asterisk",
+ "u": "1.1"
+ },
+ "eject": {
+ "c": "symbols",
+ "e": "â",
+ "d": "eject symbol",
+ "u": "4.0"
+ },
+ "electric_plug": {
+ "c": "objects",
+ "e": "🔌",
+ "d": "electric plug",
+ "u": "6.0"
+ },
+ "elephant": {
+ "c": "nature",
+ "e": "ðŸ˜",
+ "d": "elephant",
+ "u": "6.0"
+ },
+ "end": {
+ "c": "symbols",
+ "e": "🔚",
+ "d": "end with leftwards arrow above",
+ "u": "6.0"
+ },
+ "envelope": {
+ "c": "objects",
+ "e": "✉",
+ "d": "envelope",
+ "u": "1.1"
+ },
+ "envelope_with_arrow": {
+ "c": "objects",
+ "e": "📩",
+ "d": "envelope with downwards arrow above",
+ "u": "6.0"
+ },
+ "euro": {
+ "c": "objects",
+ "e": "💶",
+ "d": "banknote with euro sign",
+ "u": "6.0"
+ },
+ "european_castle": {
+ "c": "travel",
+ "e": "ðŸ°",
+ "d": "european castle",
+ "u": "6.0"
+ },
+ "european_post_office": {
+ "c": "travel",
+ "e": "ðŸ¤",
+ "d": "european post office",
+ "u": "6.0"
+ },
+ "evergreen_tree": {
+ "c": "nature",
+ "e": "🌲",
+ "d": "evergreen tree",
+ "u": "6.0"
+ },
+ "exclamation": {
+ "c": "symbols",
+ "e": "â—",
+ "d": "heavy exclamation mark symbol",
+ "u": "5.2"
+ },
+ "expressionless": {
+ "c": "people",
+ "e": "😑",
+ "d": "expressionless face",
+ "u": "6.1"
+ },
+ "eye": {
+ "c": "people",
+ "e": "ðŸ‘",
+ "d": "eye",
+ "u": "7.0"
+ },
+ "eye_in_speech_bubble": {
+ "c": "symbols",
+ "e": "ðŸ‘â€ðŸ—¨",
+ "d": "eye in speech bubble",
+ "u": "7.0"
+ },
+ "eyeglasses": {
+ "c": "people",
+ "e": "👓",
+ "d": "eyeglasses",
+ "u": "6.0"
+ },
+ "eyes": {
+ "c": "people",
+ "e": "👀",
+ "d": "eyes",
+ "u": "6.0"
+ },
+ "face_palm": {
+ "c": "people",
+ "e": "🤦",
+ "d": "face palm",
+ "u": "9.0"
+ },
+ "face_palm_tone1": {
+ "c": "people",
+ "e": "🤦ðŸ»",
+ "d": "face palm tone 1",
+ "u": "9.0"
+ },
+ "face_palm_tone2": {
+ "c": "people",
+ "e": "🤦ðŸ¼",
+ "d": "face palm tone 2",
+ "u": "9.0"
+ },
+ "face_palm_tone3": {
+ "c": "people",
+ "e": "🤦ðŸ½",
+ "d": "face palm tone 3",
+ "u": "9.0"
+ },
+ "face_palm_tone4": {
+ "c": "people",
+ "e": "🤦ðŸ¾",
+ "d": "face palm tone 4",
+ "u": "9.0"
+ },
+ "face_palm_tone5": {
+ "c": "people",
+ "e": "🤦ðŸ¿",
+ "d": "face palm tone 5",
+ "u": "9.0"
+ },
+ "factory": {
+ "c": "travel",
+ "e": "ðŸ­",
+ "d": "factory",
+ "u": "6.0"
+ },
+ "fallen_leaf": {
+ "c": "nature",
+ "e": "ðŸ‚",
+ "d": "fallen leaf",
+ "u": "6.0"
+ },
+ "family": {
+ "c": "people",
+ "e": "👪",
+ "d": "family",
+ "u": "6.0"
+ },
+ "family_mmb": {
+ "c": "people",
+ "e": "👨â€ðŸ‘¨â€ðŸ‘¦",
+ "d": "family (man,man,boy)",
+ "u": "6.0"
+ },
+ "family_mmbb": {
+ "c": "people",
+ "e": "👨â€ðŸ‘¨â€ðŸ‘¦â€ðŸ‘¦",
+ "d": "family (man,man,boy,boy)",
+ "u": "6.0"
+ },
+ "family_mmg": {
+ "c": "people",
+ "e": "👨â€ðŸ‘¨â€ðŸ‘§",
+ "d": "family (man,man,girl)",
+ "u": "6.0"
+ },
+ "family_mmgb": {
+ "c": "people",
+ "e": "👨â€ðŸ‘¨â€ðŸ‘§â€ðŸ‘¦",
+ "d": "family (man,man,girl,boy)",
+ "u": "6.0"
+ },
+ "family_mmgg": {
+ "c": "people",
+ "e": "👨â€ðŸ‘¨â€ðŸ‘§â€ðŸ‘§",
+ "d": "family (man,man,girl,girl)",
+ "u": "6.0"
+ },
+ "family_mwbb": {
+ "c": "people",
+ "e": "👨â€ðŸ‘©â€ðŸ‘¦â€ðŸ‘¦",
+ "d": "family (man,woman,boy,boy)",
+ "u": "6.0"
+ },
+ "family_mwg": {
+ "c": "people",
+ "e": "👨â€ðŸ‘©â€ðŸ‘§",
+ "d": "family (man,woman,girl)",
+ "u": "6.0"
+ },
+ "family_mwgb": {
+ "c": "people",
+ "e": "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦",
+ "d": "family (man,woman,girl,boy)",
+ "u": "6.0"
+ },
+ "family_mwgg": {
+ "c": "people",
+ "e": "👨â€ðŸ‘©â€ðŸ‘§â€ðŸ‘§",
+ "d": "family (man,woman,girl,girl)",
+ "u": "6.0"
+ },
+ "family_wwb": {
+ "c": "people",
+ "e": "👩â€ðŸ‘©â€ðŸ‘¦",
+ "d": "family (woman,woman,boy)",
+ "u": "6.0"
+ },
+ "family_wwbb": {
+ "c": "people",
+ "e": "👩â€ðŸ‘©â€ðŸ‘¦â€ðŸ‘¦",
+ "d": "family (woman,woman,boy,boy)",
+ "u": "6.0"
+ },
+ "family_wwg": {
+ "c": "people",
+ "e": "👩â€ðŸ‘©â€ðŸ‘§",
+ "d": "family (woman,woman,girl)",
+ "u": "6.0"
+ },
+ "family_wwgb": {
+ "c": "people",
+ "e": "👩â€ðŸ‘©â€ðŸ‘§â€ðŸ‘¦",
+ "d": "family (woman,woman,girl,boy)",
+ "u": "6.0"
+ },
+ "family_wwgg": {
+ "c": "people",
+ "e": "👩â€ðŸ‘©â€ðŸ‘§â€ðŸ‘§",
+ "d": "family (woman,woman,girl,girl)",
+ "u": "6.0"
+ },
+ "fast_forward": {
+ "c": "symbols",
+ "e": "â©",
+ "d": "black right-pointing double triangle",
+ "u": "6.0"
+ },
+ "fax": {
+ "c": "objects",
+ "e": "📠",
+ "d": "fax machine",
+ "u": "6.0"
+ },
+ "fearful": {
+ "c": "people",
+ "e": "😨",
+ "d": "fearful face",
+ "u": "6.0"
+ },
+ "feet": {
+ "c": "nature",
+ "e": "ðŸ¾",
+ "d": "paw prints",
+ "u": "6.0"
+ },
+ "fencer": {
+ "c": "activity",
+ "e": "🤺",
+ "d": "fencer",
+ "u": "9.0"
+ },
+ "ferris_wheel": {
+ "c": "travel",
+ "e": "🎡",
+ "d": "ferris wheel",
+ "u": "6.0"
+ },
+ "ferry": {
+ "c": "travel",
+ "e": "â›´",
+ "d": "ferry",
+ "u": "5.2"
+ },
+ "field_hockey": {
+ "c": "activity",
+ "e": "ðŸ‘",
+ "d": "field hockey stick and ball",
+ "u": "8.0"
+ },
+ "file_cabinet": {
+ "c": "objects",
+ "e": "🗄",
+ "d": "file cabinet",
+ "u": "7.0"
+ },
+ "file_folder": {
+ "c": "objects",
+ "e": "ðŸ“",
+ "d": "file folder",
+ "u": "6.0"
+ },
+ "film_frames": {
+ "c": "objects",
+ "e": "🎞",
+ "d": "film frames",
+ "u": "7.0"
+ },
+ "fingers_crossed": {
+ "c": "people",
+ "e": "🤞",
+ "d": "hand with first and index finger crossed",
+ "u": "9.0"
+ },
+ "fingers_crossed_tone1": {
+ "c": "people",
+ "e": "🤞ðŸ»",
+ "d": "hand with index and middle fingers crossed tone 1",
+ "u": "9.0"
+ },
+ "fingers_crossed_tone2": {
+ "c": "people",
+ "e": "🤞ðŸ¼",
+ "d": "hand with index and middle fingers crossed tone 2",
+ "u": "9.0"
+ },
+ "fingers_crossed_tone3": {
+ "c": "people",
+ "e": "🤞ðŸ½",
+ "d": "hand with index and middle fingers crossed tone 3",
+ "u": "9.0"
+ },
+ "fingers_crossed_tone4": {
+ "c": "people",
+ "e": "🤞ðŸ¾",
+ "d": "hand with index and middle fingers crossed tone 4",
+ "u": "9.0"
+ },
+ "fingers_crossed_tone5": {
+ "c": "people",
+ "e": "🤞ðŸ¿",
+ "d": "hand with index and middle fingers crossed tone 5",
+ "u": "9.0"
+ },
+ "fire": {
+ "c": "nature",
+ "e": "🔥",
+ "d": "fire",
+ "u": "6.0"
+ },
+ "fire_engine": {
+ "c": "travel",
+ "e": "🚒",
+ "d": "fire engine",
+ "u": "6.0"
+ },
+ "fireworks": {
+ "c": "travel",
+ "e": "🎆",
+ "d": "fireworks",
+ "u": "6.0"
+ },
+ "first_place": {
+ "c": "activity",
+ "e": "🥇",
+ "d": "first place medal",
+ "u": "9.0"
+ },
+ "first_quarter_moon": {
+ "c": "nature",
+ "e": "🌓",
+ "d": "first quarter moon symbol",
+ "u": "6.0"
+ },
+ "first_quarter_moon_with_face": {
+ "c": "nature",
+ "e": "🌛",
+ "d": "first quarter moon with face",
+ "u": "6.0"
+ },
+ "fish": {
+ "c": "nature",
+ "e": "ðŸŸ",
+ "d": "fish",
+ "u": "6.0"
+ },
+ "fish_cake": {
+ "c": "food",
+ "e": "ðŸ¥",
+ "d": "fish cake with swirl design",
+ "u": "6.0"
+ },
+ "fishing_pole_and_fish": {
+ "c": "activity",
+ "e": "🎣",
+ "d": "fishing pole and fish",
+ "u": "6.0"
+ },
+ "fist": {
+ "c": "people",
+ "e": "✊",
+ "d": "raised fist",
+ "u": "6.0"
+ },
+ "fist_tone1": {
+ "c": "people",
+ "e": "✊ðŸ»",
+ "d": "raised fist tone 1",
+ "u": "8.0"
+ },
+ "fist_tone2": {
+ "c": "people",
+ "e": "✊ðŸ¼",
+ "d": "raised fist tone 2",
+ "u": "8.0"
+ },
+ "fist_tone3": {
+ "c": "people",
+ "e": "✊ðŸ½",
+ "d": "raised fist tone 3",
+ "u": "8.0"
+ },
+ "fist_tone4": {
+ "c": "people",
+ "e": "✊ðŸ¾",
+ "d": "raised fist tone 4",
+ "u": "8.0"
+ },
+ "fist_tone5": {
+ "c": "people",
+ "e": "✊ðŸ¿",
+ "d": "raised fist tone 5",
+ "u": "8.0"
+ },
+ "five": {
+ "c": "symbols",
+ "e": "5ï¸âƒ£",
+ "d": "keycap digit five",
+ "u": "3.0"
+ },
+ "flag_ac": {
+ "c": "flags",
+ "e": "🇦🇨",
+ "d": "ascension",
+ "u": "6.0"
+ },
+ "flag_ad": {
+ "c": "flags",
+ "e": "🇦🇩",
+ "d": "andorra",
+ "u": "6.0"
+ },
+ "flag_ae": {
+ "c": "flags",
+ "e": "🇦🇪",
+ "d": "the united arab emirates",
+ "u": "6.0"
+ },
+ "flag_af": {
+ "c": "flags",
+ "e": "🇦🇫",
+ "d": "afghanistan",
+ "u": "6.0"
+ },
+ "flag_ag": {
+ "c": "flags",
+ "e": "🇦🇬",
+ "d": "antigua and barbuda",
+ "u": "6.0"
+ },
+ "flag_ai": {
+ "c": "flags",
+ "e": "🇦🇮",
+ "d": "anguilla",
+ "u": "6.0"
+ },
+ "flag_al": {
+ "c": "flags",
+ "e": "🇦🇱",
+ "d": "albania",
+ "u": "6.0"
+ },
+ "flag_am": {
+ "c": "flags",
+ "e": "🇦🇲",
+ "d": "armenia",
+ "u": "6.0"
+ },
+ "flag_ao": {
+ "c": "flags",
+ "e": "🇦🇴",
+ "d": "angola",
+ "u": "6.0"
+ },
+ "flag_aq": {
+ "c": "flags",
+ "e": "🇦🇶",
+ "d": "antarctica",
+ "u": "6.0"
+ },
+ "flag_ar": {
+ "c": "flags",
+ "e": "🇦🇷",
+ "d": "argentina",
+ "u": "6.0"
+ },
+ "flag_as": {
+ "c": "flags",
+ "e": "🇦🇸",
+ "d": "american samoa",
+ "u": "6.0"
+ },
+ "flag_at": {
+ "c": "flags",
+ "e": "🇦🇹",
+ "d": "austria",
+ "u": "6.0"
+ },
+ "flag_au": {
+ "c": "flags",
+ "e": "🇦🇺",
+ "d": "australia",
+ "u": "6.0"
+ },
+ "flag_aw": {
+ "c": "flags",
+ "e": "🇦🇼",
+ "d": "aruba",
+ "u": "6.0"
+ },
+ "flag_ax": {
+ "c": "flags",
+ "e": "🇦🇽",
+ "d": "Ã¥land islands",
+ "u": "6.0"
+ },
+ "flag_az": {
+ "c": "flags",
+ "e": "🇦🇿",
+ "d": "azerbaijan",
+ "u": "6.0"
+ },
+ "flag_ba": {
+ "c": "flags",
+ "e": "🇧🇦",
+ "d": "bosnia and herzegovina",
+ "u": "6.0"
+ },
+ "flag_bb": {
+ "c": "flags",
+ "e": "🇧🇧",
+ "d": "barbados",
+ "u": "6.0"
+ },
+ "flag_bd": {
+ "c": "flags",
+ "e": "🇧🇩",
+ "d": "bangladesh",
+ "u": "6.0"
+ },
+ "flag_be": {
+ "c": "flags",
+ "e": "🇧🇪",
+ "d": "belgium",
+ "u": "6.0"
+ },
+ "flag_bf": {
+ "c": "flags",
+ "e": "🇧🇫",
+ "d": "burkina faso",
+ "u": "6.0"
+ },
+ "flag_bg": {
+ "c": "flags",
+ "e": "🇧🇬",
+ "d": "bulgaria",
+ "u": "6.0"
+ },
+ "flag_bh": {
+ "c": "flags",
+ "e": "🇧🇭",
+ "d": "bahrain",
+ "u": "6.0"
+ },
+ "flag_bi": {
+ "c": "flags",
+ "e": "🇧🇮",
+ "d": "burundi",
+ "u": "6.0"
+ },
+ "flag_bj": {
+ "c": "flags",
+ "e": "🇧🇯",
+ "d": "benin",
+ "u": "6.0"
+ },
+ "flag_bl": {
+ "c": "flags",
+ "e": "🇧🇱",
+ "d": "saint barthélemy",
+ "u": "6.0"
+ },
+ "flag_black": {
+ "c": "objects",
+ "e": "ðŸ´",
+ "d": "waving black flag",
+ "u": "6.0"
+ },
+ "flag_bm": {
+ "c": "flags",
+ "e": "🇧🇲",
+ "d": "bermuda",
+ "u": "6.0"
+ },
+ "flag_bn": {
+ "c": "flags",
+ "e": "🇧🇳",
+ "d": "brunei",
+ "u": "6.0"
+ },
+ "flag_bo": {
+ "c": "flags",
+ "e": "🇧🇴",
+ "d": "bolivia",
+ "u": "6.0"
+ },
+ "flag_bq": {
+ "c": "flags",
+ "e": "🇧🇶",
+ "d": "caribbean netherlands",
+ "u": "6.0"
+ },
+ "flag_br": {
+ "c": "flags",
+ "e": "🇧🇷",
+ "d": "brazil",
+ "u": "6.0"
+ },
+ "flag_bs": {
+ "c": "flags",
+ "e": "🇧🇸",
+ "d": "the bahamas",
+ "u": "6.0"
+ },
+ "flag_bt": {
+ "c": "flags",
+ "e": "🇧🇹",
+ "d": "bhutan",
+ "u": "6.0"
+ },
+ "flag_bv": {
+ "c": "flags",
+ "e": "🇧🇻",
+ "d": "bouvet island",
+ "u": "6.0"
+ },
+ "flag_bw": {
+ "c": "flags",
+ "e": "🇧🇼",
+ "d": "botswana",
+ "u": "6.0"
+ },
+ "flag_by": {
+ "c": "flags",
+ "e": "🇧🇾",
+ "d": "belarus",
+ "u": "6.0"
+ },
+ "flag_bz": {
+ "c": "flags",
+ "e": "🇧🇿",
+ "d": "belize",
+ "u": "6.0"
+ },
+ "flag_ca": {
+ "c": "flags",
+ "e": "🇨🇦",
+ "d": "canada",
+ "u": "6.0"
+ },
+ "flag_cc": {
+ "c": "flags",
+ "e": "🇨🇨",
+ "d": "cocos (keeling) islands",
+ "u": "6.0"
+ },
+ "flag_cd": {
+ "c": "flags",
+ "e": "🇨🇩",
+ "d": "the democratic republic of the congo",
+ "u": "6.0"
+ },
+ "flag_cf": {
+ "c": "flags",
+ "e": "🇨🇫",
+ "d": "central african republic",
+ "u": "6.0"
+ },
+ "flag_cg": {
+ "c": "flags",
+ "e": "🇨🇬",
+ "d": "the republic of the congo",
+ "u": "6.0"
+ },
+ "flag_ch": {
+ "c": "flags",
+ "e": "🇨🇭",
+ "d": "switzerland",
+ "u": "6.0"
+ },
+ "flag_ci": {
+ "c": "flags",
+ "e": "🇨🇮",
+ "d": "cote d'ivoire",
+ "u": "6.0"
+ },
+ "flag_ck": {
+ "c": "flags",
+ "e": "🇨🇰",
+ "d": "cook islands",
+ "u": "6.0"
+ },
+ "flag_cl": {
+ "c": "flags",
+ "e": "🇨🇱",
+ "d": "chile",
+ "u": "6.0"
+ },
+ "flag_cm": {
+ "c": "flags",
+ "e": "🇨🇲",
+ "d": "cameroon",
+ "u": "6.0"
+ },
+ "flag_cn": {
+ "c": "flags",
+ "e": "🇨🇳",
+ "d": "china",
+ "u": "6.0"
+ },
+ "flag_co": {
+ "c": "flags",
+ "e": "🇨🇴",
+ "d": "colombia",
+ "u": "6.0"
+ },
+ "flag_cp": {
+ "c": "flags",
+ "e": "🇨🇵",
+ "d": "clipperton island",
+ "u": "6.0"
+ },
+ "flag_cr": {
+ "c": "flags",
+ "e": "🇨🇷",
+ "d": "costa rica",
+ "u": "6.0"
+ },
+ "flag_cu": {
+ "c": "flags",
+ "e": "🇨🇺",
+ "d": "cuba",
+ "u": "6.0"
+ },
+ "flag_cv": {
+ "c": "flags",
+ "e": "🇨🇻",
+ "d": "cape verde",
+ "u": "6.0"
+ },
+ "flag_cw": {
+ "c": "flags",
+ "e": "🇨🇼",
+ "d": "curaçao",
+ "u": "6.0"
+ },
+ "flag_cx": {
+ "c": "flags",
+ "e": "🇨🇽",
+ "d": "christmas island",
+ "u": "6.0"
+ },
+ "flag_cy": {
+ "c": "flags",
+ "e": "🇨🇾",
+ "d": "cyprus",
+ "u": "6.0"
+ },
+ "flag_cz": {
+ "c": "flags",
+ "e": "🇨🇿",
+ "d": "the czech republic",
+ "u": "6.0"
+ },
+ "flag_de": {
+ "c": "flags",
+ "e": "🇩🇪",
+ "d": "germany",
+ "u": "6.0"
+ },
+ "flag_dg": {
+ "c": "flags",
+ "e": "🇩🇬",
+ "d": "diego garcia",
+ "u": "6.0"
+ },
+ "flag_dj": {
+ "c": "flags",
+ "e": "🇩🇯",
+ "d": "djibouti",
+ "u": "6.0"
+ },
+ "flag_dk": {
+ "c": "flags",
+ "e": "🇩🇰",
+ "d": "denmark",
+ "u": "6.0"
+ },
+ "flag_dm": {
+ "c": "flags",
+ "e": "🇩🇲",
+ "d": "dominica",
+ "u": "6.0"
+ },
+ "flag_do": {
+ "c": "flags",
+ "e": "🇩🇴",
+ "d": "the dominican republic",
+ "u": "6.0"
+ },
+ "flag_dz": {
+ "c": "flags",
+ "e": "🇩🇿",
+ "d": "algeria",
+ "u": "6.0"
+ },
+ "flag_ea": {
+ "c": "flags",
+ "e": "🇪🇦",
+ "d": "ceuta, melilla",
+ "u": "6.0"
+ },
+ "flag_ec": {
+ "c": "flags",
+ "e": "🇪🇨",
+ "d": "ecuador",
+ "u": "6.0"
+ },
+ "flag_ee": {
+ "c": "flags",
+ "e": "🇪🇪",
+ "d": "estonia",
+ "u": "6.0"
+ },
+ "flag_eg": {
+ "c": "flags",
+ "e": "🇪🇬",
+ "d": "egypt",
+ "u": "6.0"
+ },
+ "flag_eh": {
+ "c": "flags",
+ "e": "🇪🇭",
+ "d": "western sahara",
+ "u": "6.0"
+ },
+ "flag_er": {
+ "c": "flags",
+ "e": "🇪🇷",
+ "d": "eritrea",
+ "u": "6.0"
+ },
+ "flag_es": {
+ "c": "flags",
+ "e": "🇪🇸",
+ "d": "spain",
+ "u": "6.0"
+ },
+ "flag_et": {
+ "c": "flags",
+ "e": "🇪🇹",
+ "d": "ethiopia",
+ "u": "6.0"
+ },
+ "flag_eu": {
+ "c": "flags",
+ "e": "🇪🇺",
+ "d": "european union",
+ "u": "6.0"
+ },
+ "flag_fi": {
+ "c": "flags",
+ "e": "🇫🇮",
+ "d": "finland",
+ "u": "6.0"
+ },
+ "flag_fj": {
+ "c": "flags",
+ "e": "🇫🇯",
+ "d": "fiji",
+ "u": "6.0"
+ },
+ "flag_fk": {
+ "c": "flags",
+ "e": "🇫🇰",
+ "d": "falkland islands",
+ "u": "6.0"
+ },
+ "flag_fm": {
+ "c": "flags",
+ "e": "🇫🇲",
+ "d": "micronesia",
+ "u": "6.0"
+ },
+ "flag_fo": {
+ "c": "flags",
+ "e": "🇫🇴",
+ "d": "faroe islands",
+ "u": "6.0"
+ },
+ "flag_fr": {
+ "c": "flags",
+ "e": "🇫🇷",
+ "d": "france",
+ "u": "6.0"
+ },
+ "flag_ga": {
+ "c": "flags",
+ "e": "🇬🇦",
+ "d": "gabon",
+ "u": "6.0"
+ },
+ "flag_gb": {
+ "c": "flags",
+ "e": "🇬🇧",
+ "d": "great britain",
+ "u": "6.0"
+ },
+ "flag_gd": {
+ "c": "flags",
+ "e": "🇬🇩",
+ "d": "grenada",
+ "u": "6.0"
+ },
+ "flag_ge": {
+ "c": "flags",
+ "e": "🇬🇪",
+ "d": "georgia",
+ "u": "6.0"
+ },
+ "flag_gf": {
+ "c": "flags",
+ "e": "🇬🇫",
+ "d": "french guiana",
+ "u": "6.0"
+ },
+ "flag_gg": {
+ "c": "flags",
+ "e": "🇬🇬",
+ "d": "guernsey",
+ "u": "6.0"
+ },
+ "flag_gh": {
+ "c": "flags",
+ "e": "🇬🇭",
+ "d": "ghana",
+ "u": "6.0"
+ },
+ "flag_gi": {
+ "c": "flags",
+ "e": "🇬🇮",
+ "d": "gibraltar",
+ "u": "6.0"
+ },
+ "flag_gl": {
+ "c": "flags",
+ "e": "🇬🇱",
+ "d": "greenland",
+ "u": "6.0"
+ },
+ "flag_gm": {
+ "c": "flags",
+ "e": "🇬🇲",
+ "d": "the gambia",
+ "u": "6.0"
+ },
+ "flag_gn": {
+ "c": "flags",
+ "e": "🇬🇳",
+ "d": "guinea",
+ "u": "6.0"
+ },
+ "flag_gp": {
+ "c": "flags",
+ "e": "🇬🇵",
+ "d": "guadeloupe",
+ "u": "6.0"
+ },
+ "flag_gq": {
+ "c": "flags",
+ "e": "🇬🇶",
+ "d": "equatorial guinea",
+ "u": "6.0"
+ },
+ "flag_gr": {
+ "c": "flags",
+ "e": "🇬🇷",
+ "d": "greece",
+ "u": "6.0"
+ },
+ "flag_gs": {
+ "c": "flags",
+ "e": "🇬🇸",
+ "d": "south georgia",
+ "u": "6.0"
+ },
+ "flag_gt": {
+ "c": "flags",
+ "e": "🇬🇹",
+ "d": "guatemala",
+ "u": "6.0"
+ },
+ "flag_gu": {
+ "c": "flags",
+ "e": "🇬🇺",
+ "d": "guam",
+ "u": "6.0"
+ },
+ "flag_gw": {
+ "c": "flags",
+ "e": "🇬🇼",
+ "d": "guinea-bissau",
+ "u": "6.0"
+ },
+ "flag_gy": {
+ "c": "flags",
+ "e": "🇬🇾",
+ "d": "guyana",
+ "u": "6.0"
+ },
+ "flag_hk": {
+ "c": "flags",
+ "e": "🇭🇰",
+ "d": "hong kong",
+ "u": "6.0"
+ },
+ "flag_hm": {
+ "c": "flags",
+ "e": "🇭🇲",
+ "d": "heard island and mcdonald islands",
+ "u": "6.0"
+ },
+ "flag_hn": {
+ "c": "flags",
+ "e": "🇭🇳",
+ "d": "honduras",
+ "u": "6.0"
+ },
+ "flag_hr": {
+ "c": "flags",
+ "e": "🇭🇷",
+ "d": "croatia",
+ "u": "6.0"
+ },
+ "flag_ht": {
+ "c": "flags",
+ "e": "🇭🇹",
+ "d": "haiti",
+ "u": "6.0"
+ },
+ "flag_hu": {
+ "c": "flags",
+ "e": "🇭🇺",
+ "d": "hungary",
+ "u": "6.0"
+ },
+ "flag_ic": {
+ "c": "flags",
+ "e": "🇮🇨",
+ "d": "canary islands",
+ "u": "6.0"
+ },
+ "flag_id": {
+ "c": "flags",
+ "e": "🇮🇩",
+ "d": "indonesia",
+ "u": "6.0"
+ },
+ "flag_ie": {
+ "c": "flags",
+ "e": "🇮🇪",
+ "d": "ireland",
+ "u": "6.0"
+ },
+ "flag_il": {
+ "c": "flags",
+ "e": "🇮🇱",
+ "d": "israel",
+ "u": "6.0"
+ },
+ "flag_im": {
+ "c": "flags",
+ "e": "🇮🇲",
+ "d": "isle of man",
+ "u": "6.0"
+ },
+ "flag_in": {
+ "c": "flags",
+ "e": "🇮🇳",
+ "d": "india",
+ "u": "6.0"
+ },
+ "flag_io": {
+ "c": "flags",
+ "e": "🇮🇴",
+ "d": "british indian ocean territory",
+ "u": "6.0"
+ },
+ "flag_iq": {
+ "c": "flags",
+ "e": "🇮🇶",
+ "d": "iraq",
+ "u": "6.0"
+ },
+ "flag_ir": {
+ "c": "flags",
+ "e": "🇮🇷",
+ "d": "iran",
+ "u": "6.0"
+ },
+ "flag_is": {
+ "c": "flags",
+ "e": "🇮🇸",
+ "d": "iceland",
+ "u": "6.0"
+ },
+ "flag_it": {
+ "c": "flags",
+ "e": "🇮🇹",
+ "d": "italy",
+ "u": "6.0"
+ },
+ "flag_je": {
+ "c": "flags",
+ "e": "🇯🇪",
+ "d": "jersey",
+ "u": "6.0"
+ },
+ "flag_jm": {
+ "c": "flags",
+ "e": "🇯🇲",
+ "d": "jamaica",
+ "u": "6.0"
+ },
+ "flag_jo": {
+ "c": "flags",
+ "e": "🇯🇴",
+ "d": "jordan",
+ "u": "6.0"
+ },
+ "flag_jp": {
+ "c": "flags",
+ "e": "🇯🇵",
+ "d": "japan",
+ "u": "6.0"
+ },
+ "flag_ke": {
+ "c": "flags",
+ "e": "🇰🇪",
+ "d": "kenya",
+ "u": "6.0"
+ },
+ "flag_kg": {
+ "c": "flags",
+ "e": "🇰🇬",
+ "d": "kyrgyzstan",
+ "u": "6.0"
+ },
+ "flag_kh": {
+ "c": "flags",
+ "e": "🇰🇭",
+ "d": "cambodia",
+ "u": "6.0"
+ },
+ "flag_ki": {
+ "c": "flags",
+ "e": "🇰🇮",
+ "d": "kiribati",
+ "u": "6.0"
+ },
+ "flag_km": {
+ "c": "flags",
+ "e": "🇰🇲",
+ "d": "the comoros",
+ "u": "6.0"
+ },
+ "flag_kn": {
+ "c": "flags",
+ "e": "🇰🇳",
+ "d": "saint kitts and nevis",
+ "u": "6.0"
+ },
+ "flag_kp": {
+ "c": "flags",
+ "e": "🇰🇵",
+ "d": "north korea",
+ "u": "6.0"
+ },
+ "flag_kr": {
+ "c": "flags",
+ "e": "🇰🇷",
+ "d": "korea",
+ "u": "6.0"
+ },
+ "flag_kw": {
+ "c": "flags",
+ "e": "🇰🇼",
+ "d": "kuwait",
+ "u": "6.0"
+ },
+ "flag_ky": {
+ "c": "flags",
+ "e": "🇰🇾",
+ "d": "cayman islands",
+ "u": "6.0"
+ },
+ "flag_kz": {
+ "c": "flags",
+ "e": "🇰🇿",
+ "d": "kazakhstan",
+ "u": "6.0"
+ },
+ "flag_la": {
+ "c": "flags",
+ "e": "🇱🇦",
+ "d": "laos",
+ "u": "6.0"
+ },
+ "flag_lb": {
+ "c": "flags",
+ "e": "🇱🇧",
+ "d": "lebanon",
+ "u": "6.0"
+ },
+ "flag_lc": {
+ "c": "flags",
+ "e": "🇱🇨",
+ "d": "saint lucia",
+ "u": "6.0"
+ },
+ "flag_li": {
+ "c": "flags",
+ "e": "🇱🇮",
+ "d": "liechtenstein",
+ "u": "6.0"
+ },
+ "flag_lk": {
+ "c": "flags",
+ "e": "🇱🇰",
+ "d": "sri lanka",
+ "u": "6.0"
+ },
+ "flag_lr": {
+ "c": "flags",
+ "e": "🇱🇷",
+ "d": "liberia",
+ "u": "6.0"
+ },
+ "flag_ls": {
+ "c": "flags",
+ "e": "🇱🇸",
+ "d": "lesotho",
+ "u": "6.0"
+ },
+ "flag_lt": {
+ "c": "flags",
+ "e": "🇱🇹",
+ "d": "lithuania",
+ "u": "6.0"
+ },
+ "flag_lu": {
+ "c": "flags",
+ "e": "🇱🇺",
+ "d": "luxembourg",
+ "u": "6.0"
+ },
+ "flag_lv": {
+ "c": "flags",
+ "e": "🇱🇻",
+ "d": "latvia",
+ "u": "6.0"
+ },
+ "flag_ly": {
+ "c": "flags",
+ "e": "🇱🇾",
+ "d": "libya",
+ "u": "6.0"
+ },
+ "flag_ma": {
+ "c": "flags",
+ "e": "🇲🇦",
+ "d": "morocco",
+ "u": "6.0"
+ },
+ "flag_mc": {
+ "c": "flags",
+ "e": "🇲🇨",
+ "d": "monaco",
+ "u": "6.0"
+ },
+ "flag_md": {
+ "c": "flags",
+ "e": "🇲🇩",
+ "d": "moldova",
+ "u": "6.0"
+ },
+ "flag_me": {
+ "c": "flags",
+ "e": "🇲🇪",
+ "d": "montenegro",
+ "u": "6.0"
+ },
+ "flag_mf": {
+ "c": "flags",
+ "e": "🇲🇫",
+ "d": "saint martin",
+ "u": "6.0"
+ },
+ "flag_mg": {
+ "c": "flags",
+ "e": "🇲🇬",
+ "d": "madagascar",
+ "u": "6.0"
+ },
+ "flag_mh": {
+ "c": "flags",
+ "e": "🇲🇭",
+ "d": "the marshall islands",
+ "u": "6.0"
+ },
+ "flag_mk": {
+ "c": "flags",
+ "e": "🇲🇰",
+ "d": "macedonia",
+ "u": "6.0"
+ },
+ "flag_ml": {
+ "c": "flags",
+ "e": "🇲🇱",
+ "d": "mali",
+ "u": "6.0"
+ },
+ "flag_mm": {
+ "c": "flags",
+ "e": "🇲🇲",
+ "d": "myanmar",
+ "u": "6.0"
+ },
+ "flag_mn": {
+ "c": "flags",
+ "e": "🇲🇳",
+ "d": "mongolia",
+ "u": "6.0"
+ },
+ "flag_mo": {
+ "c": "flags",
+ "e": "🇲🇴",
+ "d": "macau",
+ "u": "6.0"
+ },
+ "flag_mp": {
+ "c": "flags",
+ "e": "🇲🇵",
+ "d": "northern mariana islands",
+ "u": "6.0"
+ },
+ "flag_mq": {
+ "c": "flags",
+ "e": "🇲🇶",
+ "d": "martinique",
+ "u": "6.0"
+ },
+ "flag_mr": {
+ "c": "flags",
+ "e": "🇲🇷",
+ "d": "mauritania",
+ "u": "6.0"
+ },
+ "flag_ms": {
+ "c": "flags",
+ "e": "🇲🇸",
+ "d": "montserrat",
+ "u": "6.0"
+ },
+ "flag_mt": {
+ "c": "flags",
+ "e": "🇲🇹",
+ "d": "malta",
+ "u": "6.0"
+ },
+ "flag_mu": {
+ "c": "flags",
+ "e": "🇲🇺",
+ "d": "mauritius",
+ "u": "6.0"
+ },
+ "flag_mv": {
+ "c": "flags",
+ "e": "🇲🇻",
+ "d": "maldives",
+ "u": "6.0"
+ },
+ "flag_mw": {
+ "c": "flags",
+ "e": "🇲🇼",
+ "d": "malawi",
+ "u": "6.0"
+ },
+ "flag_mx": {
+ "c": "flags",
+ "e": "🇲🇽",
+ "d": "mexico",
+ "u": "6.0"
+ },
+ "flag_my": {
+ "c": "flags",
+ "e": "🇲🇾",
+ "d": "malaysia",
+ "u": "6.0"
+ },
+ "flag_mz": {
+ "c": "flags",
+ "e": "🇲🇿",
+ "d": "mozambique",
+ "u": "6.0"
+ },
+ "flag_na": {
+ "c": "flags",
+ "e": "🇳🇦",
+ "d": "namibia",
+ "u": "6.0"
+ },
+ "flag_nc": {
+ "c": "flags",
+ "e": "🇳🇨",
+ "d": "new caledonia",
+ "u": "6.0"
+ },
+ "flag_ne": {
+ "c": "flags",
+ "e": "🇳🇪",
+ "d": "niger",
+ "u": "6.0"
+ },
+ "flag_nf": {
+ "c": "flags",
+ "e": "🇳🇫",
+ "d": "norfolk island",
+ "u": "6.0"
+ },
+ "flag_ng": {
+ "c": "flags",
+ "e": "🇳🇬",
+ "d": "nigeria",
+ "u": "6.0"
+ },
+ "flag_ni": {
+ "c": "flags",
+ "e": "🇳🇮",
+ "d": "nicaragua",
+ "u": "6.0"
+ },
+ "flag_nl": {
+ "c": "flags",
+ "e": "🇳🇱",
+ "d": "the netherlands",
+ "u": "6.0"
+ },
+ "flag_no": {
+ "c": "flags",
+ "e": "🇳🇴",
+ "d": "norway",
+ "u": "6.0"
+ },
+ "flag_np": {
+ "c": "flags",
+ "e": "🇳🇵",
+ "d": "nepal",
+ "u": "6.0"
+ },
+ "flag_nr": {
+ "c": "flags",
+ "e": "🇳🇷",
+ "d": "nauru",
+ "u": "6.0"
+ },
+ "flag_nu": {
+ "c": "flags",
+ "e": "🇳🇺",
+ "d": "niue",
+ "u": "6.0"
+ },
+ "flag_nz": {
+ "c": "flags",
+ "e": "🇳🇿",
+ "d": "new zealand",
+ "u": "6.0"
+ },
+ "flag_om": {
+ "c": "flags",
+ "e": "🇴🇲",
+ "d": "oman",
+ "u": "6.0"
+ },
+ "flag_pa": {
+ "c": "flags",
+ "e": "🇵🇦",
+ "d": "panama",
+ "u": "6.0"
+ },
+ "flag_pe": {
+ "c": "flags",
+ "e": "🇵🇪",
+ "d": "peru",
+ "u": "6.0"
+ },
+ "flag_pf": {
+ "c": "flags",
+ "e": "🇵🇫",
+ "d": "french polynesia",
+ "u": "6.0"
+ },
+ "flag_pg": {
+ "c": "flags",
+ "e": "🇵🇬",
+ "d": "papua new guinea",
+ "u": "6.0"
+ },
+ "flag_ph": {
+ "c": "flags",
+ "e": "🇵🇭",
+ "d": "the philippines",
+ "u": "6.0"
+ },
+ "flag_pk": {
+ "c": "flags",
+ "e": "🇵🇰",
+ "d": "pakistan",
+ "u": "6.0"
+ },
+ "flag_pl": {
+ "c": "flags",
+ "e": "🇵🇱",
+ "d": "poland",
+ "u": "6.0"
+ },
+ "flag_pm": {
+ "c": "flags",
+ "e": "🇵🇲",
+ "d": "saint pierre and miquelon",
+ "u": "6.0"
+ },
+ "flag_pn": {
+ "c": "flags",
+ "e": "🇵🇳",
+ "d": "pitcairn",
+ "u": "6.0"
+ },
+ "flag_pr": {
+ "c": "flags",
+ "e": "🇵🇷",
+ "d": "puerto rico",
+ "u": "6.0"
+ },
+ "flag_ps": {
+ "c": "flags",
+ "e": "🇵🇸",
+ "d": "palestinian authority",
+ "u": "6.0"
+ },
+ "flag_pt": {
+ "c": "flags",
+ "e": "🇵🇹",
+ "d": "portugal",
+ "u": "6.0"
+ },
+ "flag_pw": {
+ "c": "flags",
+ "e": "🇵🇼",
+ "d": "palau",
+ "u": "6.0"
+ },
+ "flag_py": {
+ "c": "flags",
+ "e": "🇵🇾",
+ "d": "paraguay",
+ "u": "6.0"
+ },
+ "flag_qa": {
+ "c": "flags",
+ "e": "🇶🇦",
+ "d": "qatar",
+ "u": "6.0"
+ },
+ "flag_re": {
+ "c": "flags",
+ "e": "🇷🇪",
+ "d": "réunion",
+ "u": "6.0"
+ },
+ "flag_ro": {
+ "c": "flags",
+ "e": "🇷🇴",
+ "d": "romania",
+ "u": "6.0"
+ },
+ "flag_rs": {
+ "c": "flags",
+ "e": "🇷🇸",
+ "d": "serbia",
+ "u": "6.0"
+ },
+ "flag_ru": {
+ "c": "flags",
+ "e": "🇷🇺",
+ "d": "russia",
+ "u": "6.0"
+ },
+ "flag_rw": {
+ "c": "flags",
+ "e": "🇷🇼",
+ "d": "rwanda",
+ "u": "6.0"
+ },
+ "flag_sa": {
+ "c": "flags",
+ "e": "🇸🇦",
+ "d": "saudi arabia",
+ "u": "6.0"
+ },
+ "flag_sb": {
+ "c": "flags",
+ "e": "🇸🇧",
+ "d": "the solomon islands",
+ "u": "6.0"
+ },
+ "flag_sc": {
+ "c": "flags",
+ "e": "🇸🇨",
+ "d": "the seychelles",
+ "u": "6.0"
+ },
+ "flag_sd": {
+ "c": "flags",
+ "e": "🇸🇩",
+ "d": "sudan",
+ "u": "6.0"
+ },
+ "flag_se": {
+ "c": "flags",
+ "e": "🇸🇪",
+ "d": "sweden",
+ "u": "6.0"
+ },
+ "flag_sg": {
+ "c": "flags",
+ "e": "🇸🇬",
+ "d": "singapore",
+ "u": "6.0"
+ },
+ "flag_sh": {
+ "c": "flags",
+ "e": "🇸🇭",
+ "d": "saint helena",
+ "u": "6.0"
+ },
+ "flag_si": {
+ "c": "flags",
+ "e": "🇸🇮",
+ "d": "slovenia",
+ "u": "6.0"
+ },
+ "flag_sj": {
+ "c": "flags",
+ "e": "🇸🇯",
+ "d": "svalbard and jan mayen",
+ "u": "6.0"
+ },
+ "flag_sk": {
+ "c": "flags",
+ "e": "🇸🇰",
+ "d": "slovakia",
+ "u": "6.0"
+ },
+ "flag_sl": {
+ "c": "flags",
+ "e": "🇸🇱",
+ "d": "sierra leone",
+ "u": "6.0"
+ },
+ "flag_sm": {
+ "c": "flags",
+ "e": "🇸🇲",
+ "d": "san marino",
+ "u": "6.0"
+ },
+ "flag_sn": {
+ "c": "flags",
+ "e": "🇸🇳",
+ "d": "senegal",
+ "u": "6.0"
+ },
+ "flag_so": {
+ "c": "flags",
+ "e": "🇸🇴",
+ "d": "somalia",
+ "u": "6.0"
+ },
+ "flag_sr": {
+ "c": "flags",
+ "e": "🇸🇷",
+ "d": "suriname",
+ "u": "6.0"
+ },
+ "flag_ss": {
+ "c": "flags",
+ "e": "🇸🇸",
+ "d": "south sudan",
+ "u": "6.0"
+ },
+ "flag_st": {
+ "c": "flags",
+ "e": "🇸🇹",
+ "d": "sao tome and principe",
+ "u": "6.0"
+ },
+ "flag_sv": {
+ "c": "flags",
+ "e": "🇸🇻",
+ "d": "el salvador",
+ "u": "6.0"
+ },
+ "flag_sx": {
+ "c": "flags",
+ "e": "🇸🇽",
+ "d": "sint maarten",
+ "u": "6.0"
+ },
+ "flag_sy": {
+ "c": "flags",
+ "e": "🇸🇾",
+ "d": "syria",
+ "u": "6.0"
+ },
+ "flag_sz": {
+ "c": "flags",
+ "e": "🇸🇿",
+ "d": "swaziland",
+ "u": "6.0"
+ },
+ "flag_ta": {
+ "c": "flags",
+ "e": "🇹🇦",
+ "d": "tristan da cunha",
+ "u": "6.0"
+ },
+ "flag_tc": {
+ "c": "flags",
+ "e": "🇹🇨",
+ "d": "turks and caicos islands",
+ "u": "6.0"
+ },
+ "flag_td": {
+ "c": "flags",
+ "e": "🇹🇩",
+ "d": "chad",
+ "u": "6.0"
+ },
+ "flag_tf": {
+ "c": "flags",
+ "e": "🇹🇫",
+ "d": "french southern territories",
+ "u": "6.0"
+ },
+ "flag_tg": {
+ "c": "flags",
+ "e": "🇹🇬",
+ "d": "togo",
+ "u": "6.0"
+ },
+ "flag_th": {
+ "c": "flags",
+ "e": "🇹🇭",
+ "d": "thailand",
+ "u": "6.0"
+ },
+ "flag_tj": {
+ "c": "flags",
+ "e": "🇹🇯",
+ "d": "tajikistan",
+ "u": "6.0"
+ },
+ "flag_tk": {
+ "c": "flags",
+ "e": "🇹🇰",
+ "d": "tokelau",
+ "u": "6.0"
+ },
+ "flag_tl": {
+ "c": "flags",
+ "e": "🇹🇱",
+ "d": "east timor",
+ "u": "6.0"
+ },
+ "flag_tm": {
+ "c": "flags",
+ "e": "🇹🇲",
+ "d": "turkmenistan",
+ "u": "6.0"
+ },
+ "flag_tn": {
+ "c": "flags",
+ "e": "🇹🇳",
+ "d": "tunisia",
+ "u": "6.0"
+ },
+ "flag_to": {
+ "c": "flags",
+ "e": "🇹🇴",
+ "d": "tonga",
+ "u": "6.0"
+ },
+ "flag_tr": {
+ "c": "flags",
+ "e": "🇹🇷",
+ "d": "turkey",
+ "u": "6.0"
+ },
+ "flag_tt": {
+ "c": "flags",
+ "e": "🇹🇹",
+ "d": "trinidad and tobago",
+ "u": "6.0"
+ },
+ "flag_tv": {
+ "c": "flags",
+ "e": "🇹🇻",
+ "d": "tuvalu",
+ "u": "6.0"
+ },
+ "flag_tw": {
+ "c": "flags",
+ "e": "🇹🇼",
+ "d": "the republic of china",
+ "u": "6.0"
+ },
+ "flag_tz": {
+ "c": "flags",
+ "e": "🇹🇿",
+ "d": "tanzania",
+ "u": "6.0"
+ },
+ "flag_ua": {
+ "c": "flags",
+ "e": "🇺🇦",
+ "d": "ukraine",
+ "u": "6.0"
+ },
+ "flag_ug": {
+ "c": "flags",
+ "e": "🇺🇬",
+ "d": "uganda",
+ "u": "6.0"
+ },
+ "flag_um": {
+ "c": "flags",
+ "e": "🇺🇲",
+ "d": "united states minor outlying islands",
+ "u": "6.0"
+ },
+ "flag_us": {
+ "c": "flags",
+ "e": "🇺🇸",
+ "d": "united states",
+ "u": "6.0"
+ },
+ "flag_uy": {
+ "c": "flags",
+ "e": "🇺🇾",
+ "d": "uruguay",
+ "u": "6.0"
+ },
+ "flag_uz": {
+ "c": "flags",
+ "e": "🇺🇿",
+ "d": "uzbekistan",
+ "u": "6.0"
+ },
+ "flag_va": {
+ "c": "flags",
+ "e": "🇻🇦",
+ "d": "the vatican city",
+ "u": "6.0"
+ },
+ "flag_vc": {
+ "c": "flags",
+ "e": "🇻🇨",
+ "d": "saint vincent and the grenadines",
+ "u": "6.0"
+ },
+ "flag_ve": {
+ "c": "flags",
+ "e": "🇻🇪",
+ "d": "venezuela",
+ "u": "6.0"
+ },
+ "flag_vg": {
+ "c": "flags",
+ "e": "🇻🇬",
+ "d": "british virgin islands",
+ "u": "6.0"
+ },
+ "flag_vi": {
+ "c": "flags",
+ "e": "🇻🇮",
+ "d": "u.s. virgin islands",
+ "u": "6.0"
+ },
+ "flag_vn": {
+ "c": "flags",
+ "e": "🇻🇳",
+ "d": "vietnam",
+ "u": "6.0"
+ },
+ "flag_vu": {
+ "c": "flags",
+ "e": "🇻🇺",
+ "d": "vanuatu",
+ "u": "6.0"
+ },
+ "flag_wf": {
+ "c": "flags",
+ "e": "🇼🇫",
+ "d": "wallis and futuna",
+ "u": "6.0"
+ },
+ "flag_white": {
+ "c": "objects",
+ "e": "ðŸ³",
+ "d": "waving white flag",
+ "u": "6.0"
+ },
+ "flag_ws": {
+ "c": "flags",
+ "e": "🇼🇸",
+ "d": "samoa",
+ "u": "6.0"
+ },
+ "flag_xk": {
+ "c": "flags",
+ "e": "🇽🇰",
+ "d": "kosovo",
+ "u": "6.0"
+ },
+ "flag_ye": {
+ "c": "flags",
+ "e": "🇾🇪",
+ "d": "yemen",
+ "u": "6.0"
+ },
+ "flag_yt": {
+ "c": "flags",
+ "e": "🇾🇹",
+ "d": "mayotte",
+ "u": "6.0"
+ },
+ "flag_za": {
+ "c": "flags",
+ "e": "🇿🇦",
+ "d": "south africa",
+ "u": "6.0"
+ },
+ "flag_zm": {
+ "c": "flags",
+ "e": "🇿🇲",
+ "d": "zambia",
+ "u": "6.0"
+ },
+ "flag_zw": {
+ "c": "flags",
+ "e": "🇿🇼",
+ "d": "zimbabwe",
+ "u": "6.0"
+ },
+ "flags": {
+ "c": "objects",
+ "e": "ðŸŽ",
+ "d": "carp streamer",
+ "u": "6.0"
+ },
+ "flashlight": {
+ "c": "objects",
+ "e": "🔦",
+ "d": "electric torch",
+ "u": "6.0"
+ },
+ "fleur-de-lis": {
+ "c": "symbols",
+ "e": "⚜",
+ "d": "fleur-de-lis",
+ "u": "4.1"
+ },
+ "floppy_disk": {
+ "c": "objects",
+ "e": "💾",
+ "d": "floppy disk",
+ "u": "6.0"
+ },
+ "flower_playing_cards": {
+ "c": "symbols",
+ "e": "🎴",
+ "d": "flower playing cards",
+ "u": "6.0"
+ },
+ "flushed": {
+ "c": "people",
+ "e": "😳",
+ "d": "flushed face",
+ "u": "6.0"
+ },
+ "fog": {
+ "c": "nature",
+ "e": "🌫",
+ "d": "fog",
+ "u": "7.0"
+ },
+ "foggy": {
+ "c": "travel",
+ "e": "ðŸŒ",
+ "d": "foggy",
+ "u": "6.0"
+ },
+ "football": {
+ "c": "activity",
+ "e": "ðŸˆ",
+ "d": "american football",
+ "u": "6.0"
+ },
+ "footprints": {
+ "c": "people",
+ "e": "👣",
+ "d": "footprints",
+ "u": "6.0"
+ },
+ "fork_and_knife": {
+ "c": "food",
+ "e": "ðŸ´",
+ "d": "fork and knife",
+ "u": "6.0"
+ },
+ "fork_knife_plate": {
+ "c": "food",
+ "e": "ðŸ½",
+ "d": "fork and knife with plate",
+ "u": "7.0"
+ },
+ "fountain": {
+ "c": "travel",
+ "e": "⛲",
+ "d": "fountain",
+ "u": "5.2"
+ },
+ "four": {
+ "c": "symbols",
+ "e": "4ï¸âƒ£",
+ "d": "keycap digit four",
+ "u": "3.0"
+ },
+ "four_leaf_clover": {
+ "c": "nature",
+ "e": "ðŸ€",
+ "d": "four leaf clover",
+ "u": "6.0"
+ },
+ "fox": {
+ "c": "nature",
+ "e": "🦊",
+ "d": "fox face",
+ "u": "9.0"
+ },
+ "frame_photo": {
+ "c": "objects",
+ "e": "🖼",
+ "d": "frame with picture",
+ "u": "7.0"
+ },
+ "free": {
+ "c": "symbols",
+ "e": "🆓",
+ "d": "squared free",
+ "u": "6.0"
+ },
+ "french_bread": {
+ "c": "food",
+ "e": "🥖",
+ "d": "baguette bread",
+ "u": "9.0"
+ },
+ "fried_shrimp": {
+ "c": "food",
+ "e": "ðŸ¤",
+ "d": "fried shrimp",
+ "u": "6.0"
+ },
+ "fries": {
+ "c": "food",
+ "e": "ðŸŸ",
+ "d": "french fries",
+ "u": "6.0"
+ },
+ "frog": {
+ "c": "nature",
+ "e": "ðŸ¸",
+ "d": "frog face",
+ "u": "6.0"
+ },
+ "frowning": {
+ "c": "people",
+ "e": "😦",
+ "d": "frowning face with open mouth",
+ "u": "6.1"
+ },
+ "frowning2": {
+ "c": "people",
+ "e": "☹",
+ "d": "white frowning face",
+ "u": "1.1"
+ },
+ "fuelpump": {
+ "c": "travel",
+ "e": "⛽",
+ "d": "fuel pump",
+ "u": "5.2"
+ },
+ "full_moon": {
+ "c": "nature",
+ "e": "🌕",
+ "d": "full moon symbol",
+ "u": "6.0"
+ },
+ "full_moon_with_face": {
+ "c": "nature",
+ "e": "ðŸŒ",
+ "d": "full moon with face",
+ "u": "6.0"
+ },
+ "game_die": {
+ "c": "activity",
+ "e": "🎲",
+ "d": "game die",
+ "u": "6.0"
+ },
+ "gear": {
+ "c": "objects",
+ "e": "âš™",
+ "d": "gear",
+ "u": "4.1"
+ },
+ "gem": {
+ "c": "objects",
+ "e": "💎",
+ "d": "gem stone",
+ "u": "6.0"
+ },
+ "gay_pride_flag": {
+ "c": "flags",
+ "e": "ðŸ³ðŸŒˆ",
+ "d": "gay_pride_flag",
+ "u": "6.0"
+ },
+ "gemini": {
+ "c": "symbols",
+ "e": "♊",
+ "d": "gemini",
+ "u": "1.1"
+ },
+ "ghost": {
+ "c": "people",
+ "e": "👻",
+ "d": "ghost",
+ "u": "6.0"
+ },
+ "gift": {
+ "c": "objects",
+ "e": "ðŸŽ",
+ "d": "wrapped present",
+ "u": "6.0"
+ },
+ "gift_heart": {
+ "c": "symbols",
+ "e": "ðŸ’",
+ "d": "heart with ribbon",
+ "u": "6.0"
+ },
+ "girl": {
+ "c": "people",
+ "e": "👧",
+ "d": "girl",
+ "u": "6.0"
+ },
+ "girl_tone1": {
+ "c": "people",
+ "e": "👧ðŸ»",
+ "d": "girl tone 1",
+ "u": "8.0"
+ },
+ "girl_tone2": {
+ "c": "people",
+ "e": "👧ðŸ¼",
+ "d": "girl tone 2",
+ "u": "8.0"
+ },
+ "girl_tone3": {
+ "c": "people",
+ "e": "👧ðŸ½",
+ "d": "girl tone 3",
+ "u": "8.0"
+ },
+ "girl_tone4": {
+ "c": "people",
+ "e": "👧ðŸ¾",
+ "d": "girl tone 4",
+ "u": "8.0"
+ },
+ "girl_tone5": {
+ "c": "people",
+ "e": "👧ðŸ¿",
+ "d": "girl tone 5",
+ "u": "8.0"
+ },
+ "globe_with_meridians": {
+ "c": "symbols",
+ "e": "ðŸŒ",
+ "d": "globe with meridians",
+ "u": "6.0"
+ },
+ "goal": {
+ "c": "activity",
+ "e": "🥅",
+ "d": "goal net",
+ "u": "9.0"
+ },
+ "goat": {
+ "c": "nature",
+ "e": "ðŸ",
+ "d": "goat",
+ "u": "6.0"
+ },
+ "golf": {
+ "c": "activity",
+ "e": "⛳",
+ "d": "flag in hole",
+ "u": "5.2"
+ },
+ "golfer": {
+ "c": "activity",
+ "e": "ðŸŒ",
+ "d": "golfer",
+ "u": "7.0"
+ },
+ "gorilla": {
+ "c": "nature",
+ "e": "ðŸ¦",
+ "d": "gorilla",
+ "u": "9.0"
+ },
+ "grapes": {
+ "c": "food",
+ "e": "ðŸ‡",
+ "d": "grapes",
+ "u": "6.0"
+ },
+ "green_apple": {
+ "c": "food",
+ "e": "ðŸ",
+ "d": "green apple",
+ "u": "6.0"
+ },
+ "green_book": {
+ "c": "objects",
+ "e": "📗",
+ "d": "green book",
+ "u": "6.0"
+ },
+ "green_heart": {
+ "c": "symbols",
+ "e": "💚",
+ "d": "green heart",
+ "u": "6.0"
+ },
+ "grey_exclamation": {
+ "c": "symbols",
+ "e": "â•",
+ "d": "white exclamation mark ornament",
+ "u": "6.0"
+ },
+ "grey_question": {
+ "c": "symbols",
+ "e": "â”",
+ "d": "white question mark ornament",
+ "u": "6.0"
+ },
+ "grimacing": {
+ "c": "people",
+ "e": "😬",
+ "d": "grimacing face",
+ "u": "6.1"
+ },
+ "grin": {
+ "c": "people",
+ "e": "ðŸ˜",
+ "d": "grinning face with smiling eyes",
+ "u": "6.0"
+ },
+ "grinning": {
+ "c": "people",
+ "e": "😀",
+ "d": "grinning face",
+ "u": "6.1"
+ },
+ "guardsman": {
+ "c": "people",
+ "e": "💂",
+ "d": "guardsman",
+ "u": "6.0"
+ },
+ "guardsman_tone1": {
+ "c": "people",
+ "e": "💂ðŸ»",
+ "d": "guardsman tone 1",
+ "u": "8.0"
+ },
+ "guardsman_tone2": {
+ "c": "people",
+ "e": "💂ðŸ¼",
+ "d": "guardsman tone 2",
+ "u": "8.0"
+ },
+ "guardsman_tone3": {
+ "c": "people",
+ "e": "💂ðŸ½",
+ "d": "guardsman tone 3",
+ "u": "8.0"
+ },
+ "guardsman_tone4": {
+ "c": "people",
+ "e": "💂ðŸ¾",
+ "d": "guardsman tone 4",
+ "u": "8.0"
+ },
+ "guardsman_tone5": {
+ "c": "people",
+ "e": "💂ðŸ¿",
+ "d": "guardsman tone 5",
+ "u": "8.0"
+ },
+ "guitar": {
+ "c": "activity",
+ "e": "🎸",
+ "d": "guitar",
+ "u": "6.0"
+ },
+ "gun": {
+ "c": "objects",
+ "e": "🔫",
+ "d": "pistol",
+ "u": "6.0"
+ },
+ "haircut": {
+ "c": "people",
+ "e": "💇",
+ "d": "haircut",
+ "u": "6.0"
+ },
+ "haircut_tone1": {
+ "c": "people",
+ "e": "💇ðŸ»",
+ "d": "haircut tone 1",
+ "u": "8.0"
+ },
+ "haircut_tone2": {
+ "c": "people",
+ "e": "💇ðŸ¼",
+ "d": "haircut tone 2",
+ "u": "8.0"
+ },
+ "haircut_tone3": {
+ "c": "people",
+ "e": "💇ðŸ½",
+ "d": "haircut tone 3",
+ "u": "8.0"
+ },
+ "haircut_tone4": {
+ "c": "people",
+ "e": "💇ðŸ¾",
+ "d": "haircut tone 4",
+ "u": "8.0"
+ },
+ "haircut_tone5": {
+ "c": "people",
+ "e": "💇ðŸ¿",
+ "d": "haircut tone 5",
+ "u": "8.0"
+ },
+ "hamburger": {
+ "c": "food",
+ "e": "ðŸ”",
+ "d": "hamburger",
+ "u": "6.0"
+ },
+ "hammer": {
+ "c": "objects",
+ "e": "🔨",
+ "d": "hammer",
+ "u": "6.0"
+ },
+ "hammer_pick": {
+ "c": "objects",
+ "e": "âš’",
+ "d": "hammer and pick",
+ "u": "4.1"
+ },
+ "hamster": {
+ "c": "nature",
+ "e": "ðŸ¹",
+ "d": "hamster face",
+ "u": "6.0"
+ },
+ "hand_splayed": {
+ "c": "people",
+ "e": "ðŸ–",
+ "d": "raised hand with fingers splayed",
+ "u": "7.0"
+ },
+ "hand_splayed_tone1": {
+ "c": "people",
+ "e": "ðŸ–ðŸ»",
+ "d": "raised hand with fingers splayed tone 1",
+ "u": "8.0"
+ },
+ "hand_splayed_tone2": {
+ "c": "people",
+ "e": "ðŸ–ðŸ¼",
+ "d": "raised hand with fingers splayed tone 2",
+ "u": "8.0"
+ },
+ "hand_splayed_tone3": {
+ "c": "people",
+ "e": "ðŸ–ðŸ½",
+ "d": "raised hand with fingers splayed tone 3",
+ "u": "8.0"
+ },
+ "hand_splayed_tone4": {
+ "c": "people",
+ "e": "ðŸ–ðŸ¾",
+ "d": "raised hand with fingers splayed tone 4",
+ "u": "8.0"
+ },
+ "hand_splayed_tone5": {
+ "c": "people",
+ "e": "ðŸ–ðŸ¿",
+ "d": "raised hand with fingers splayed tone 5",
+ "u": "8.0"
+ },
+ "handbag": {
+ "c": "people",
+ "e": "👜",
+ "d": "handbag",
+ "u": "6.0"
+ },
+ "handball": {
+ "c": "activity",
+ "e": "🤾",
+ "d": "handball",
+ "u": "9.0"
+ },
+ "handball_tone1": {
+ "c": "activity",
+ "e": "🤾ðŸ»",
+ "d": "handball tone 1",
+ "u": "9.0"
+ },
+ "handball_tone2": {
+ "c": "activity",
+ "e": "🤾ðŸ¼",
+ "d": "handball tone 2",
+ "u": "9.0"
+ },
+ "handball_tone3": {
+ "c": "activity",
+ "e": "🤾ðŸ½",
+ "d": "handball tone 3",
+ "u": "9.0"
+ },
+ "handball_tone4": {
+ "c": "activity",
+ "e": "🤾ðŸ¾",
+ "d": "handball tone 4",
+ "u": "9.0"
+ },
+ "handball_tone5": {
+ "c": "activity",
+ "e": "🤾ðŸ¿",
+ "d": "handball tone 5",
+ "u": "9.0"
+ },
+ "handshake": {
+ "c": "people",
+ "e": "ðŸ¤",
+ "d": "handshake",
+ "u": "9.0"
+ },
+ "handshake_tone1": {
+ "c": "people",
+ "e": "ðŸ¤ðŸ»",
+ "d": "handshake tone 1",
+ "u": "9.0"
+ },
+ "handshake_tone2": {
+ "c": "people",
+ "e": "ðŸ¤ðŸ¼",
+ "d": "handshake tone 2",
+ "u": "9.0"
+ },
+ "handshake_tone3": {
+ "c": "people",
+ "e": "ðŸ¤ðŸ½",
+ "d": "handshake tone 3",
+ "u": "9.0"
+ },
+ "handshake_tone4": {
+ "c": "people",
+ "e": "ðŸ¤ðŸ¾",
+ "d": "handshake tone 4",
+ "u": "9.0"
+ },
+ "handshake_tone5": {
+ "c": "people",
+ "e": "ðŸ¤ðŸ¿",
+ "d": "handshake tone 5",
+ "u": "9.0"
+ },
+ "hash": {
+ "c": "symbols",
+ "e": "#⃣",
+ "d": "number sign",
+ "u": "3.0"
+ },
+ "hatched_chick": {
+ "c": "nature",
+ "e": "ðŸ¥",
+ "d": "front-facing baby chick",
+ "u": "6.0"
+ },
+ "hatching_chick": {
+ "c": "nature",
+ "e": "ðŸ£",
+ "d": "hatching chick",
+ "u": "6.0"
+ },
+ "head_bandage": {
+ "c": "people",
+ "e": "🤕",
+ "d": "face with head-bandage",
+ "u": "8.0"
+ },
+ "headphones": {
+ "c": "activity",
+ "e": "🎧",
+ "d": "headphone",
+ "u": "6.0"
+ },
+ "hear_no_evil": {
+ "c": "nature",
+ "e": "🙉",
+ "d": "hear-no-evil monkey",
+ "u": "6.0"
+ },
+ "heart": {
+ "c": "symbols",
+ "e": "â¤",
+ "d": "heavy black heart",
+ "u": "1.1"
+ },
+ "heart_decoration": {
+ "c": "symbols",
+ "e": "💟",
+ "d": "heart decoration",
+ "u": "6.0"
+ },
+ "heart_exclamation": {
+ "c": "symbols",
+ "e": "â£",
+ "d": "heavy heart exclamation mark ornament",
+ "u": "1.1"
+ },
+ "heart_eyes": {
+ "c": "people",
+ "e": "ðŸ˜",
+ "d": "smiling face with heart-shaped eyes",
+ "u": "6.0"
+ },
+ "heart_eyes_cat": {
+ "c": "people",
+ "e": "😻",
+ "d": "smiling cat face with heart-shaped eyes",
+ "u": "6.0"
+ },
+ "heartbeat": {
+ "c": "symbols",
+ "e": "💓",
+ "d": "beating heart",
+ "u": "6.0"
+ },
+ "heartpulse": {
+ "c": "symbols",
+ "e": "💗",
+ "d": "growing heart",
+ "u": "6.0"
+ },
+ "hearts": {
+ "c": "symbols",
+ "e": "♥",
+ "d": "black heart suit",
+ "u": "1.1"
+ },
+ "heavy_check_mark": {
+ "c": "symbols",
+ "e": "✔",
+ "d": "heavy check mark",
+ "u": "1.1"
+ },
+ "heavy_division_sign": {
+ "c": "symbols",
+ "e": "âž—",
+ "d": "heavy division sign",
+ "u": "6.0"
+ },
+ "heavy_dollar_sign": {
+ "c": "symbols",
+ "e": "💲",
+ "d": "heavy dollar sign",
+ "u": "6.0"
+ },
+ "heavy_minus_sign": {
+ "c": "symbols",
+ "e": "âž–",
+ "d": "heavy minus sign",
+ "u": "6.0"
+ },
+ "heavy_multiplication_x": {
+ "c": "symbols",
+ "e": "✖",
+ "d": "heavy multiplication x",
+ "u": "1.1"
+ },
+ "heavy_plus_sign": {
+ "c": "symbols",
+ "e": "âž•",
+ "d": "heavy plus sign",
+ "u": "6.0"
+ },
+ "helicopter": {
+ "c": "travel",
+ "e": "ðŸš",
+ "d": "helicopter",
+ "u": "6.0"
+ },
+ "helmet_with_cross": {
+ "c": "people",
+ "e": "⛑",
+ "d": "helmet with white cross",
+ "u": "5.2"
+ },
+ "herb": {
+ "c": "nature",
+ "e": "🌿",
+ "d": "herb",
+ "u": "6.0"
+ },
+ "hibiscus": {
+ "c": "nature",
+ "e": "🌺",
+ "d": "hibiscus",
+ "u": "6.0"
+ },
+ "high_brightness": {
+ "c": "symbols",
+ "e": "🔆",
+ "d": "high brightness symbol",
+ "u": "6.0"
+ },
+ "high_heel": {
+ "c": "people",
+ "e": "👠",
+ "d": "high-heeled shoe",
+ "u": "6.0"
+ },
+ "hockey": {
+ "c": "activity",
+ "e": "ðŸ’",
+ "d": "ice hockey stick and puck",
+ "u": "8.0"
+ },
+ "hole": {
+ "c": "objects",
+ "e": "🕳",
+ "d": "hole",
+ "u": "7.0"
+ },
+ "homes": {
+ "c": "travel",
+ "e": "ðŸ˜",
+ "d": "house buildings",
+ "u": "7.0"
+ },
+ "honey_pot": {
+ "c": "food",
+ "e": "ðŸ¯",
+ "d": "honey pot",
+ "u": "6.0"
+ },
+ "horse": {
+ "c": "nature",
+ "e": "ðŸ´",
+ "d": "horse face",
+ "u": "6.0"
+ },
+ "horse_racing": {
+ "c": "activity",
+ "e": "ðŸ‡",
+ "d": "horse racing",
+ "u": "6.0"
+ },
+ "horse_racing_tone1": {
+ "c": "activity",
+ "e": "ðŸ‡ðŸ»",
+ "d": "horse racing tone 1",
+ "u": "8.0"
+ },
+ "horse_racing_tone2": {
+ "c": "activity",
+ "e": "ðŸ‡ðŸ¼",
+ "d": "horse racing tone 2",
+ "u": "8.0"
+ },
+ "horse_racing_tone3": {
+ "c": "activity",
+ "e": "ðŸ‡ðŸ½",
+ "d": "horse racing tone 3",
+ "u": "8.0"
+ },
+ "horse_racing_tone4": {
+ "c": "activity",
+ "e": "ðŸ‡ðŸ¾",
+ "d": "horse racing tone 4",
+ "u": "8.0"
+ },
+ "horse_racing_tone5": {
+ "c": "activity",
+ "e": "ðŸ‡ðŸ¿",
+ "d": "horse racing tone 5",
+ "u": "8.0"
+ },
+ "hospital": {
+ "c": "travel",
+ "e": "ðŸ¥",
+ "d": "hospital",
+ "u": "6.0"
+ },
+ "hot_pepper": {
+ "c": "food",
+ "e": "🌶",
+ "d": "hot pepper",
+ "u": "7.0"
+ },
+ "hotdog": {
+ "c": "food",
+ "e": "🌭",
+ "d": "hot dog",
+ "u": "8.0"
+ },
+ "hotel": {
+ "c": "travel",
+ "e": "ðŸ¨",
+ "d": "hotel",
+ "u": "6.0"
+ },
+ "hotsprings": {
+ "c": "symbols",
+ "e": "♨",
+ "d": "hot springs",
+ "u": "1.1"
+ },
+ "hourglass": {
+ "c": "objects",
+ "e": "⌛",
+ "d": "hourglass",
+ "u": "1.1"
+ },
+ "hourglass_flowing_sand": {
+ "c": "objects",
+ "e": "â³",
+ "d": "hourglass with flowing sand",
+ "u": "6.0"
+ },
+ "house": {
+ "c": "travel",
+ "e": "ðŸ ",
+ "d": "house building",
+ "u": "6.0"
+ },
+ "house_abandoned": {
+ "c": "travel",
+ "e": "ðŸš",
+ "d": "derelict house building",
+ "u": "7.0"
+ },
+ "house_with_garden": {
+ "c": "travel",
+ "e": "ðŸ¡",
+ "d": "house with garden",
+ "u": "6.0"
+ },
+ "hugging": {
+ "c": "people",
+ "e": "🤗",
+ "d": "hugging face",
+ "u": "8.0"
+ },
+ "hushed": {
+ "c": "people",
+ "e": "😯",
+ "d": "hushed face",
+ "u": "6.1"
+ },
+ "ice_cream": {
+ "c": "food",
+ "e": "ðŸ¨",
+ "d": "ice cream",
+ "u": "6.0"
+ },
+ "ice_skate": {
+ "c": "activity",
+ "e": "⛸",
+ "d": "ice skate",
+ "u": "5.2"
+ },
+ "icecream": {
+ "c": "food",
+ "e": "ðŸ¦",
+ "d": "soft ice cream",
+ "u": "6.0"
+ },
+ "id": {
+ "c": "symbols",
+ "e": "🆔",
+ "d": "squared id",
+ "u": "6.0"
+ },
+ "ideograph_advantage": {
+ "c": "symbols",
+ "e": "ðŸ‰",
+ "d": "circled ideograph advantage",
+ "u": "6.0"
+ },
+ "imp": {
+ "c": "people",
+ "e": "👿",
+ "d": "imp",
+ "u": "6.0"
+ },
+ "inbox_tray": {
+ "c": "objects",
+ "e": "📥",
+ "d": "inbox tray",
+ "u": "6.0"
+ },
+ "incoming_envelope": {
+ "c": "objects",
+ "e": "📨",
+ "d": "incoming envelope",
+ "u": "6.0"
+ },
+ "information_desk_person": {
+ "c": "people",
+ "e": "ðŸ’",
+ "d": "information desk person",
+ "u": "6.0"
+ },
+ "information_desk_person_tone1": {
+ "c": "people",
+ "e": "ðŸ’ðŸ»",
+ "d": "information desk person tone 1",
+ "u": "8.0"
+ },
+ "information_desk_person_tone2": {
+ "c": "people",
+ "e": "ðŸ’ðŸ¼",
+ "d": "information desk person tone 2",
+ "u": "8.0"
+ },
+ "information_desk_person_tone3": {
+ "c": "people",
+ "e": "ðŸ’ðŸ½",
+ "d": "information desk person tone 3",
+ "u": "8.0"
+ },
+ "information_desk_person_tone4": {
+ "c": "people",
+ "e": "ðŸ’ðŸ¾",
+ "d": "information desk person tone 4",
+ "u": "8.0"
+ },
+ "information_desk_person_tone5": {
+ "c": "people",
+ "e": "ðŸ’ðŸ¿",
+ "d": "information desk person tone 5",
+ "u": "8.0"
+ },
+ "information_source": {
+ "c": "symbols",
+ "e": "ℹ",
+ "d": "information source",
+ "u": "3.0"
+ },
+ "innocent": {
+ "c": "people",
+ "e": "😇",
+ "d": "smiling face with halo",
+ "u": "6.0"
+ },
+ "interrobang": {
+ "c": "symbols",
+ "e": "â‰",
+ "d": "exclamation question mark",
+ "u": "3.0"
+ },
+ "iphone": {
+ "c": "objects",
+ "e": "📱",
+ "d": "mobile phone",
+ "u": "6.0"
+ },
+ "island": {
+ "c": "travel",
+ "e": "ðŸ",
+ "d": "desert island",
+ "u": "7.0"
+ },
+ "izakaya_lantern": {
+ "c": "objects",
+ "e": "ðŸ®",
+ "d": "izakaya lantern",
+ "u": "6.0"
+ },
+ "jack_o_lantern": {
+ "c": "nature",
+ "e": "🎃",
+ "d": "jack-o-lantern",
+ "u": "6.0"
+ },
+ "japan": {
+ "c": "travel",
+ "e": "🗾",
+ "d": "silhouette of japan",
+ "u": "6.0"
+ },
+ "japanese_castle": {
+ "c": "travel",
+ "e": "ðŸ¯",
+ "d": "japanese castle",
+ "u": "6.0"
+ },
+ "japanese_goblin": {
+ "c": "people",
+ "e": "👺",
+ "d": "japanese goblin",
+ "u": "6.0"
+ },
+ "japanese_ogre": {
+ "c": "people",
+ "e": "👹",
+ "d": "japanese ogre",
+ "u": "6.0"
+ },
+ "jeans": {
+ "c": "people",
+ "e": "👖",
+ "d": "jeans",
+ "u": "6.0"
+ },
+ "joy": {
+ "c": "people",
+ "e": "😂",
+ "d": "face with tears of joy",
+ "u": "6.0"
+ },
+ "joy_cat": {
+ "c": "people",
+ "e": "😹",
+ "d": "cat face with tears of joy",
+ "u": "6.0"
+ },
+ "joystick": {
+ "c": "objects",
+ "e": "🕹",
+ "d": "joystick",
+ "u": "7.0"
+ },
+ "juggling": {
+ "c": "activity",
+ "e": "🤹",
+ "d": "juggling",
+ "u": "9.0"
+ },
+ "juggling_tone1": {
+ "c": "activity",
+ "e": "🤹ðŸ»",
+ "d": "juggling tone 1",
+ "u": "9.0"
+ },
+ "juggling_tone2": {
+ "c": "activity",
+ "e": "🤹ðŸ¼",
+ "d": "juggling tone 2",
+ "u": "9.0"
+ },
+ "juggling_tone3": {
+ "c": "activity",
+ "e": "🤹ðŸ½",
+ "d": "juggling tone 3",
+ "u": "9.0"
+ },
+ "juggling_tone4": {
+ "c": "activity",
+ "e": "🤹ðŸ¾",
+ "d": "juggling tone 4",
+ "u": "9.0"
+ },
+ "juggling_tone5": {
+ "c": "activity",
+ "e": "🤹ðŸ¿",
+ "d": "juggling tone 5",
+ "u": "9.0"
+ },
+ "kaaba": {
+ "c": "travel",
+ "e": "🕋",
+ "d": "kaaba",
+ "u": "8.0"
+ },
+ "key": {
+ "c": "objects",
+ "e": "🔑",
+ "d": "key",
+ "u": "6.0"
+ },
+ "key2": {
+ "c": "objects",
+ "e": "ðŸ—",
+ "d": "old key",
+ "u": "7.0"
+ },
+ "keyboard": {
+ "c": "objects",
+ "e": "⌨",
+ "d": "keyboard",
+ "u": "1.1"
+ },
+ "kimono": {
+ "c": "people",
+ "e": "👘",
+ "d": "kimono",
+ "u": "6.0"
+ },
+ "kiss": {
+ "c": "people",
+ "e": "💋",
+ "d": "kiss mark",
+ "u": "6.0"
+ },
+ "kiss_mm": {
+ "c": "people",
+ "e": "👨â€â¤ï¸â€ðŸ’‹â€ðŸ‘¨",
+ "d": "kiss (man,man)",
+ "u": "6.0"
+ },
+ "kiss_ww": {
+ "c": "people",
+ "e": "👩â€â¤ï¸â€ðŸ’‹â€ðŸ‘©",
+ "d": "kiss (woman,woman)",
+ "u": "6.0"
+ },
+ "kissing": {
+ "c": "people",
+ "e": "😗",
+ "d": "kissing face",
+ "u": "6.1"
+ },
+ "kissing_cat": {
+ "c": "people",
+ "e": "😽",
+ "d": "kissing cat face with closed eyes",
+ "u": "6.0"
+ },
+ "kissing_closed_eyes": {
+ "c": "people",
+ "e": "😚",
+ "d": "kissing face with closed eyes",
+ "u": "6.0"
+ },
+ "kissing_heart": {
+ "c": "people",
+ "e": "😘",
+ "d": "face throwing a kiss",
+ "u": "6.0"
+ },
+ "kissing_smiling_eyes": {
+ "c": "people",
+ "e": "😙",
+ "d": "kissing face with smiling eyes",
+ "u": "6.1"
+ },
+ "kiwi": {
+ "c": "food",
+ "e": "ðŸ¥",
+ "d": "kiwifruit",
+ "u": "9.0"
+ },
+ "knife": {
+ "c": "objects",
+ "e": "🔪",
+ "d": "hocho",
+ "u": "6.0"
+ },
+ "koala": {
+ "c": "nature",
+ "e": "ðŸ¨",
+ "d": "koala",
+ "u": "6.0"
+ },
+ "koko": {
+ "c": "symbols",
+ "e": "ðŸˆ",
+ "d": "squared katakana koko",
+ "u": "6.0"
+ },
+ "label": {
+ "c": "objects",
+ "e": "ðŸ·",
+ "d": "label",
+ "u": "7.0"
+ },
+ "large_blue_circle": {
+ "c": "symbols",
+ "e": "🔵",
+ "d": "large blue circle",
+ "u": "6.0"
+ },
+ "large_blue_diamond": {
+ "c": "symbols",
+ "e": "🔷",
+ "d": "large blue diamond",
+ "u": "6.0"
+ },
+ "large_orange_diamond": {
+ "c": "symbols",
+ "e": "🔶",
+ "d": "large orange diamond",
+ "u": "6.0"
+ },
+ "last_quarter_moon": {
+ "c": "nature",
+ "e": "🌗",
+ "d": "last quarter moon symbol",
+ "u": "6.0"
+ },
+ "last_quarter_moon_with_face": {
+ "c": "nature",
+ "e": "🌜",
+ "d": "last quarter moon with face",
+ "u": "6.0"
+ },
+ "laughing": {
+ "c": "people",
+ "e": "😆",
+ "d": "smiling face with open mouth and tightly-closed ey",
+ "u": "6.0"
+ },
+ "leaves": {
+ "c": "nature",
+ "e": "ðŸƒ",
+ "d": "leaf fluttering in wind",
+ "u": "6.0"
+ },
+ "ledger": {
+ "c": "objects",
+ "e": "📒",
+ "d": "ledger",
+ "u": "6.0"
+ },
+ "left_facing_fist": {
+ "c": "people",
+ "e": "🤛",
+ "d": "left-facing fist",
+ "u": "9.0"
+ },
+ "left_facing_fist_tone1": {
+ "c": "people",
+ "e": "🤛ðŸ»",
+ "d": "left facing fist tone 1",
+ "u": "9.0"
+ },
+ "left_facing_fist_tone2": {
+ "c": "people",
+ "e": "🤛ðŸ¼",
+ "d": "left facing fist tone 2",
+ "u": "9.0"
+ },
+ "left_facing_fist_tone3": {
+ "c": "people",
+ "e": "🤛ðŸ½",
+ "d": "left facing fist tone 3",
+ "u": "9.0"
+ },
+ "left_facing_fist_tone4": {
+ "c": "people",
+ "e": "🤛ðŸ¾",
+ "d": "left facing fist tone 4",
+ "u": "9.0"
+ },
+ "left_facing_fist_tone5": {
+ "c": "people",
+ "e": "🤛ðŸ¿",
+ "d": "left facing fist tone 5",
+ "u": "9.0"
+ },
+ "left_luggage": {
+ "c": "symbols",
+ "e": "🛅",
+ "d": "left luggage",
+ "u": "6.0"
+ },
+ "left_right_arrow": {
+ "c": "symbols",
+ "e": "↔",
+ "d": "left right arrow",
+ "u": "1.1"
+ },
+ "leftwards_arrow_with_hook": {
+ "c": "symbols",
+ "e": "↩",
+ "d": "leftwards arrow with hook",
+ "u": "1.1"
+ },
+ "lemon": {
+ "c": "food",
+ "e": "ðŸ‹",
+ "d": "lemon",
+ "u": "6.0"
+ },
+ "leo": {
+ "c": "symbols",
+ "e": "♌",
+ "d": "leo",
+ "u": "1.1"
+ },
+ "leopard": {
+ "c": "nature",
+ "e": "ðŸ†",
+ "d": "leopard",
+ "u": "6.0"
+ },
+ "level_slider": {
+ "c": "objects",
+ "e": "🎚",
+ "d": "level slider",
+ "u": "7.0"
+ },
+ "levitate": {
+ "c": "activity",
+ "e": "🕴",
+ "d": "man in business suit levitating",
+ "u": "7.0"
+ },
+ "libra": {
+ "c": "symbols",
+ "e": "♎",
+ "d": "libra",
+ "u": "1.1"
+ },
+ "lifter": {
+ "c": "activity",
+ "e": "ðŸ‹",
+ "d": "weight lifter",
+ "u": "7.0"
+ },
+ "lifter_tone1": {
+ "c": "activity",
+ "e": "ðŸ‹ðŸ»",
+ "d": "weight lifter tone 1",
+ "u": "8.0"
+ },
+ "lifter_tone2": {
+ "c": "activity",
+ "e": "ðŸ‹ðŸ¼",
+ "d": "weight lifter tone 2",
+ "u": "8.0"
+ },
+ "lifter_tone3": {
+ "c": "activity",
+ "e": "ðŸ‹ðŸ½",
+ "d": "weight lifter tone 3",
+ "u": "8.0"
+ },
+ "lifter_tone4": {
+ "c": "activity",
+ "e": "ðŸ‹ðŸ¾",
+ "d": "weight lifter tone 4",
+ "u": "8.0"
+ },
+ "lifter_tone5": {
+ "c": "activity",
+ "e": "ðŸ‹ðŸ¿",
+ "d": "weight lifter tone 5",
+ "u": "8.0"
+ },
+ "light_rail": {
+ "c": "travel",
+ "e": "🚈",
+ "d": "light rail",
+ "u": "6.0"
+ },
+ "link": {
+ "c": "objects",
+ "e": "🔗",
+ "d": "link symbol",
+ "u": "6.0"
+ },
+ "lion_face": {
+ "c": "nature",
+ "e": "ðŸ¦",
+ "d": "lion face",
+ "u": "8.0"
+ },
+ "lips": {
+ "c": "people",
+ "e": "👄",
+ "d": "mouth",
+ "u": "6.0"
+ },
+ "lipstick": {
+ "c": "people",
+ "e": "💄",
+ "d": "lipstick",
+ "u": "6.0"
+ },
+ "lizard": {
+ "c": "nature",
+ "e": "🦎",
+ "d": "lizard",
+ "u": "9.0"
+ },
+ "lock": {
+ "c": "objects",
+ "e": "🔒",
+ "d": "lock",
+ "u": "6.0"
+ },
+ "lock_with_ink_pen": {
+ "c": "objects",
+ "e": "ðŸ”",
+ "d": "lock with ink pen",
+ "u": "6.0"
+ },
+ "lollipop": {
+ "c": "food",
+ "e": "ðŸ­",
+ "d": "lollipop",
+ "u": "6.0"
+ },
+ "loop": {
+ "c": "symbols",
+ "e": "âž¿",
+ "d": "double curly loop",
+ "u": "6.0"
+ },
+ "loud_sound": {
+ "c": "symbols",
+ "e": "🔊",
+ "d": "speaker with three sound waves",
+ "u": "6.0"
+ },
+ "loudspeaker": {
+ "c": "symbols",
+ "e": "📢",
+ "d": "public address loudspeaker",
+ "u": "6.0"
+ },
+ "love_hotel": {
+ "c": "travel",
+ "e": "ðŸ©",
+ "d": "love hotel",
+ "u": "6.0"
+ },
+ "love_letter": {
+ "c": "objects",
+ "e": "💌",
+ "d": "love letter",
+ "u": "6.0"
+ },
+ "low_brightness": {
+ "c": "symbols",
+ "e": "🔅",
+ "d": "low brightness symbol",
+ "u": "6.0"
+ },
+ "lying_face": {
+ "c": "people",
+ "e": "🤥",
+ "d": "lying face",
+ "u": "9.0"
+ },
+ "m": {
+ "c": "symbols",
+ "e": "â“‚",
+ "d": "circled latin capital letter m",
+ "u": "1.1"
+ },
+ "mag": {
+ "c": "objects",
+ "e": "ðŸ”",
+ "d": "left-pointing magnifying glass",
+ "u": "6.0"
+ },
+ "mag_right": {
+ "c": "objects",
+ "e": "🔎",
+ "d": "right-pointing magnifying glass",
+ "u": "6.0"
+ },
+ "mahjong": {
+ "c": "symbols",
+ "e": "🀄",
+ "d": "mahjong tile red dragon",
+ "u": "5.1"
+ },
+ "mailbox": {
+ "c": "objects",
+ "e": "📫",
+ "d": "closed mailbox with raised flag",
+ "u": "6.0"
+ },
+ "mailbox_closed": {
+ "c": "objects",
+ "e": "📪",
+ "d": "closed mailbox with lowered flag",
+ "u": "6.0"
+ },
+ "mailbox_with_mail": {
+ "c": "objects",
+ "e": "📬",
+ "d": "open mailbox with raised flag",
+ "u": "6.0"
+ },
+ "mailbox_with_no_mail": {
+ "c": "objects",
+ "e": "📭",
+ "d": "open mailbox with lowered flag",
+ "u": "6.0"
+ },
+ "man": {
+ "c": "people",
+ "e": "👨",
+ "d": "man",
+ "u": "6.0"
+ },
+ "man_dancing": {
+ "c": "people",
+ "e": "🕺",
+ "d": "man dancing",
+ "u": "9.0"
+ },
+ "man_dancing_tone1": {
+ "c": "activity",
+ "e": "🕺ðŸ»",
+ "d": "man dancing tone 1",
+ "u": "9.0"
+ },
+ "man_dancing_tone2": {
+ "c": "activity",
+ "e": "🕺ðŸ¼",
+ "d": "man dancing tone 2",
+ "u": "9.0"
+ },
+ "man_dancing_tone3": {
+ "c": "activity",
+ "e": "🕺ðŸ½",
+ "d": "man dancing tone 3",
+ "u": "9.0"
+ },
+ "man_dancing_tone4": {
+ "c": "activity",
+ "e": "🕺ðŸ¾",
+ "d": "man dancing tone 4",
+ "u": "9.0"
+ },
+ "man_dancing_tone5": {
+ "c": "activity",
+ "e": "🕺ðŸ¿",
+ "d": "man dancing tone 5",
+ "u": "9.0"
+ },
+ "man_in_tuxedo": {
+ "c": "people",
+ "e": "🤵",
+ "d": "man in tuxedo",
+ "u": "9.0"
+ },
+ "man_in_tuxedo_tone1": {
+ "c": "people",
+ "e": "🤵ðŸ»",
+ "d": "man in tuxedo tone 1",
+ "u": "9.0"
+ },
+ "man_in_tuxedo_tone2": {
+ "c": "people",
+ "e": "🤵ðŸ¼",
+ "d": "man in tuxedo tone 2",
+ "u": "9.0"
+ },
+ "man_in_tuxedo_tone3": {
+ "c": "people",
+ "e": "🤵ðŸ½",
+ "d": "man in tuxedo tone 3",
+ "u": "9.0"
+ },
+ "man_in_tuxedo_tone4": {
+ "c": "people",
+ "e": "🤵ðŸ¾",
+ "d": "man in tuxedo tone 4",
+ "u": "9.0"
+ },
+ "man_in_tuxedo_tone5": {
+ "c": "people",
+ "e": "🤵ðŸ¿",
+ "d": "man in tuxedo tone 5",
+ "u": "9.0"
+ },
+ "man_tone1": {
+ "c": "people",
+ "e": "👨ðŸ»",
+ "d": "man tone 1",
+ "u": "8.0"
+ },
+ "man_tone2": {
+ "c": "people",
+ "e": "👨ðŸ¼",
+ "d": "man tone 2",
+ "u": "8.0"
+ },
+ "man_tone3": {
+ "c": "people",
+ "e": "👨ðŸ½",
+ "d": "man tone 3",
+ "u": "8.0"
+ },
+ "man_tone4": {
+ "c": "people",
+ "e": "👨ðŸ¾",
+ "d": "man tone 4",
+ "u": "8.0"
+ },
+ "man_tone5": {
+ "c": "people",
+ "e": "👨ðŸ¿",
+ "d": "man tone 5",
+ "u": "8.0"
+ },
+ "man_with_gua_pi_mao": {
+ "c": "people",
+ "e": "👲",
+ "d": "man with gua pi mao",
+ "u": "6.0"
+ },
+ "man_with_gua_pi_mao_tone1": {
+ "c": "people",
+ "e": "👲ðŸ»",
+ "d": "man with gua pi mao tone 1",
+ "u": "8.0"
+ },
+ "man_with_gua_pi_mao_tone2": {
+ "c": "people",
+ "e": "👲ðŸ¼",
+ "d": "man with gua pi mao tone 2",
+ "u": "8.0"
+ },
+ "man_with_gua_pi_mao_tone3": {
+ "c": "people",
+ "e": "👲ðŸ½",
+ "d": "man with gua pi mao tone 3",
+ "u": "8.0"
+ },
+ "man_with_gua_pi_mao_tone4": {
+ "c": "people",
+ "e": "👲ðŸ¾",
+ "d": "man with gua pi mao tone 4",
+ "u": "8.0"
+ },
+ "man_with_gua_pi_mao_tone5": {
+ "c": "people",
+ "e": "👲ðŸ¿",
+ "d": "man with gua pi mao tone 5",
+ "u": "8.0"
+ },
+ "man_with_turban": {
+ "c": "people",
+ "e": "👳",
+ "d": "man with turban",
+ "u": "6.0"
+ },
+ "man_with_turban_tone1": {
+ "c": "people",
+ "e": "👳ðŸ»",
+ "d": "man with turban tone 1",
+ "u": "8.0"
+ },
+ "man_with_turban_tone2": {
+ "c": "people",
+ "e": "👳ðŸ¼",
+ "d": "man with turban tone 2",
+ "u": "8.0"
+ },
+ "man_with_turban_tone3": {
+ "c": "people",
+ "e": "👳ðŸ½",
+ "d": "man with turban tone 3",
+ "u": "8.0"
+ },
+ "man_with_turban_tone4": {
+ "c": "people",
+ "e": "👳ðŸ¾",
+ "d": "man with turban tone 4",
+ "u": "8.0"
+ },
+ "man_with_turban_tone5": {
+ "c": "people",
+ "e": "👳ðŸ¿",
+ "d": "man with turban tone 5",
+ "u": "8.0"
+ },
+ "mans_shoe": {
+ "c": "people",
+ "e": "👞",
+ "d": "mans shoe",
+ "u": "6.0"
+ },
+ "map": {
+ "c": "objects",
+ "e": "🗺",
+ "d": "world map",
+ "u": "7.0"
+ },
+ "maple_leaf": {
+ "c": "nature",
+ "e": "ðŸ",
+ "d": "maple leaf",
+ "u": "6.0"
+ },
+ "martial_arts_uniform": {
+ "c": "activity",
+ "e": "🥋",
+ "d": "martial arts uniform",
+ "u": "9.0"
+ },
+ "mask": {
+ "c": "people",
+ "e": "😷",
+ "d": "face with medical mask",
+ "u": "6.0"
+ },
+ "massage": {
+ "c": "people",
+ "e": "💆",
+ "d": "face massage",
+ "u": "6.0"
+ },
+ "massage_tone1": {
+ "c": "people",
+ "e": "💆ðŸ»",
+ "d": "face massage tone 1",
+ "u": "8.0"
+ },
+ "massage_tone2": {
+ "c": "people",
+ "e": "💆ðŸ¼",
+ "d": "face massage tone 2",
+ "u": "8.0"
+ },
+ "massage_tone3": {
+ "c": "people",
+ "e": "💆ðŸ½",
+ "d": "face massage tone 3",
+ "u": "8.0"
+ },
+ "massage_tone4": {
+ "c": "people",
+ "e": "💆ðŸ¾",
+ "d": "face massage tone 4",
+ "u": "8.0"
+ },
+ "massage_tone5": {
+ "c": "people",
+ "e": "💆ðŸ¿",
+ "d": "face massage tone 5",
+ "u": "8.0"
+ },
+ "meat_on_bone": {
+ "c": "food",
+ "e": "ðŸ–",
+ "d": "meat on bone",
+ "u": "6.0"
+ },
+ "medal": {
+ "c": "activity",
+ "e": "ðŸ…",
+ "d": "sports medal",
+ "u": "7.0"
+ },
+ "mega": {
+ "c": "symbols",
+ "e": "📣",
+ "d": "cheering megaphone",
+ "u": "6.0"
+ },
+ "melon": {
+ "c": "food",
+ "e": "ðŸˆ",
+ "d": "melon",
+ "u": "6.0"
+ },
+ "menorah": {
+ "c": "symbols",
+ "e": "🕎",
+ "d": "menorah with nine branches",
+ "u": "8.0"
+ },
+ "mens": {
+ "c": "symbols",
+ "e": "🚹",
+ "d": "mens symbol",
+ "u": "6.0"
+ },
+ "metal": {
+ "c": "people",
+ "e": "🤘",
+ "d": "sign of the horns",
+ "u": "8.0"
+ },
+ "metal_tone1": {
+ "c": "people",
+ "e": "🤘ðŸ»",
+ "d": "sign of the horns tone 1",
+ "u": "8.0"
+ },
+ "metal_tone2": {
+ "c": "people",
+ "e": "🤘ðŸ¼",
+ "d": "sign of the horns tone 2",
+ "u": "8.0"
+ },
+ "metal_tone3": {
+ "c": "people",
+ "e": "🤘ðŸ½",
+ "d": "sign of the horns tone 3",
+ "u": "8.0"
+ },
+ "metal_tone4": {
+ "c": "people",
+ "e": "🤘ðŸ¾",
+ "d": "sign of the horns tone 4",
+ "u": "8.0"
+ },
+ "metal_tone5": {
+ "c": "people",
+ "e": "🤘ðŸ¿",
+ "d": "sign of the horns tone 5",
+ "u": "8.0"
+ },
+ "metro": {
+ "c": "travel",
+ "e": "🚇",
+ "d": "metro",
+ "u": "6.0"
+ },
+ "microphone": {
+ "c": "activity",
+ "e": "🎤",
+ "d": "microphone",
+ "u": "6.0"
+ },
+ "microphone2": {
+ "c": "objects",
+ "e": "🎙",
+ "d": "studio microphone",
+ "u": "7.0"
+ },
+ "microscope": {
+ "c": "objects",
+ "e": "🔬",
+ "d": "microscope",
+ "u": "6.0"
+ },
+ "middle_finger": {
+ "c": "people",
+ "e": "🖕",
+ "d": "reversed hand with middle finger extended",
+ "u": "7.0"
+ },
+ "middle_finger_tone1": {
+ "c": "people",
+ "e": "🖕ðŸ»",
+ "d": "reversed hand with middle finger extended tone 1",
+ "u": "8.0"
+ },
+ "middle_finger_tone2": {
+ "c": "people",
+ "e": "🖕ðŸ¼",
+ "d": "reversed hand with middle finger extended tone 2",
+ "u": "8.0"
+ },
+ "middle_finger_tone3": {
+ "c": "people",
+ "e": "🖕ðŸ½",
+ "d": "reversed hand with middle finger extended tone 3",
+ "u": "8.0"
+ },
+ "middle_finger_tone4": {
+ "c": "people",
+ "e": "🖕ðŸ¾",
+ "d": "reversed hand with middle finger extended tone 4",
+ "u": "8.0"
+ },
+ "middle_finger_tone5": {
+ "c": "people",
+ "e": "🖕ðŸ¿",
+ "d": "reversed hand with middle finger extended tone 5",
+ "u": "8.0"
+ },
+ "military_medal": {
+ "c": "activity",
+ "e": "🎖",
+ "d": "military medal",
+ "u": "7.0"
+ },
+ "milk": {
+ "c": "food",
+ "e": "🥛",
+ "d": "glass of milk",
+ "u": "9.0"
+ },
+ "milky_way": {
+ "c": "travel",
+ "e": "🌌",
+ "d": "milky way",
+ "u": "6.0"
+ },
+ "minibus": {
+ "c": "travel",
+ "e": "ðŸš",
+ "d": "minibus",
+ "u": "6.0"
+ },
+ "minidisc": {
+ "c": "objects",
+ "e": "💽",
+ "d": "minidisc",
+ "u": "6.0"
+ },
+ "mobile_phone_off": {
+ "c": "symbols",
+ "e": "📴",
+ "d": "mobile phone off",
+ "u": "6.0"
+ },
+ "money_mouth": {
+ "c": "people",
+ "e": "🤑",
+ "d": "money-mouth face",
+ "u": "8.0"
+ },
+ "money_with_wings": {
+ "c": "objects",
+ "e": "💸",
+ "d": "money with wings",
+ "u": "6.0"
+ },
+ "moneybag": {
+ "c": "objects",
+ "e": "💰",
+ "d": "money bag",
+ "u": "6.0"
+ },
+ "monkey": {
+ "c": "nature",
+ "e": "ðŸ’",
+ "d": "monkey",
+ "u": "6.0"
+ },
+ "monkey_face": {
+ "c": "nature",
+ "e": "ðŸµ",
+ "d": "monkey face",
+ "u": "6.0"
+ },
+ "monorail": {
+ "c": "travel",
+ "e": "ðŸš",
+ "d": "monorail",
+ "u": "6.0"
+ },
+ "mortar_board": {
+ "c": "people",
+ "e": "🎓",
+ "d": "graduation cap",
+ "u": "6.0"
+ },
+ "mosque": {
+ "c": "travel",
+ "e": "🕌",
+ "d": "mosque",
+ "u": "8.0"
+ },
+ "motor_scooter": {
+ "c": "travel",
+ "e": "🛵",
+ "d": "motor scooter",
+ "u": "9.0"
+ },
+ "motorboat": {
+ "c": "travel",
+ "e": "🛥",
+ "d": "motorboat",
+ "u": "7.0"
+ },
+ "motorcycle": {
+ "c": "travel",
+ "e": "ðŸ",
+ "d": "racing motorcycle",
+ "u": "7.0"
+ },
+ "motorway": {
+ "c": "travel",
+ "e": "🛣",
+ "d": "motorway",
+ "u": "7.0"
+ },
+ "mount_fuji": {
+ "c": "travel",
+ "e": "🗻",
+ "d": "mount fuji",
+ "u": "6.0"
+ },
+ "mountain": {
+ "c": "travel",
+ "e": "â›°",
+ "d": "mountain",
+ "u": "5.2"
+ },
+ "mountain_bicyclist": {
+ "c": "activity",
+ "e": "🚵",
+ "d": "mountain bicyclist",
+ "u": "6.0"
+ },
+ "mountain_bicyclist_tone1": {
+ "c": "activity",
+ "e": "🚵ðŸ»",
+ "d": "mountain bicyclist tone 1",
+ "u": "8.0"
+ },
+ "mountain_bicyclist_tone2": {
+ "c": "activity",
+ "e": "🚵ðŸ¼",
+ "d": "mountain bicyclist tone 2",
+ "u": "8.0"
+ },
+ "mountain_bicyclist_tone3": {
+ "c": "activity",
+ "e": "🚵ðŸ½",
+ "d": "mountain bicyclist tone 3",
+ "u": "8.0"
+ },
+ "mountain_bicyclist_tone4": {
+ "c": "activity",
+ "e": "🚵ðŸ¾",
+ "d": "mountain bicyclist tone 4",
+ "u": "8.0"
+ },
+ "mountain_bicyclist_tone5": {
+ "c": "activity",
+ "e": "🚵ðŸ¿",
+ "d": "mountain bicyclist tone 5",
+ "u": "8.0"
+ },
+ "mountain_cableway": {
+ "c": "travel",
+ "e": "🚠",
+ "d": "mountain cableway",
+ "u": "6.0"
+ },
+ "mountain_railway": {
+ "c": "travel",
+ "e": "🚞",
+ "d": "mountain railway",
+ "u": "6.0"
+ },
+ "mountain_snow": {
+ "c": "travel",
+ "e": "ðŸ”",
+ "d": "snow capped mountain",
+ "u": "7.0"
+ },
+ "mouse": {
+ "c": "nature",
+ "e": "ðŸ­",
+ "d": "mouse face",
+ "u": "6.0"
+ },
+ "mouse2": {
+ "c": "nature",
+ "e": "ðŸ",
+ "d": "mouse",
+ "u": "6.0"
+ },
+ "mouse_three_button": {
+ "c": "objects",
+ "e": "🖱",
+ "d": "three button mouse",
+ "u": "7.0"
+ },
+ "movie_camera": {
+ "c": "objects",
+ "e": "🎥",
+ "d": "movie camera",
+ "u": "6.0"
+ },
+ "moyai": {
+ "c": "objects",
+ "e": "🗿",
+ "d": "moyai",
+ "u": "6.0"
+ },
+ "mrs_claus": {
+ "c": "people",
+ "e": "🤶",
+ "d": "mother christmas",
+ "u": "9.0"
+ },
+ "mrs_claus_tone1": {
+ "c": "people",
+ "e": "🤶ðŸ»",
+ "d": "mother christmas tone 1",
+ "u": "9.0"
+ },
+ "mrs_claus_tone2": {
+ "c": "people",
+ "e": "🤶ðŸ¼",
+ "d": "mother christmas tone 2",
+ "u": "9.0"
+ },
+ "mrs_claus_tone3": {
+ "c": "people",
+ "e": "🤶ðŸ½",
+ "d": "mother christmas tone 3",
+ "u": "9.0"
+ },
+ "mrs_claus_tone4": {
+ "c": "people",
+ "e": "🤶ðŸ¾",
+ "d": "mother christmas tone 4",
+ "u": "9.0"
+ },
+ "mrs_claus_tone5": {
+ "c": "people",
+ "e": "🤶ðŸ¿",
+ "d": "mother christmas tone 5",
+ "u": "9.0"
+ },
+ "muscle": {
+ "c": "people",
+ "e": "💪",
+ "d": "flexed biceps",
+ "u": "6.0"
+ },
+ "muscle_tone1": {
+ "c": "people",
+ "e": "💪ðŸ»",
+ "d": "flexed biceps tone 1",
+ "u": "8.0"
+ },
+ "muscle_tone2": {
+ "c": "people",
+ "e": "💪ðŸ¼",
+ "d": "flexed biceps tone 2",
+ "u": "8.0"
+ },
+ "muscle_tone3": {
+ "c": "people",
+ "e": "💪ðŸ½",
+ "d": "flexed biceps tone 3",
+ "u": "8.0"
+ },
+ "muscle_tone4": {
+ "c": "people",
+ "e": "💪ðŸ¾",
+ "d": "flexed biceps tone 4",
+ "u": "8.0"
+ },
+ "muscle_tone5": {
+ "c": "people",
+ "e": "💪ðŸ¿",
+ "d": "flexed biceps tone 5",
+ "u": "8.0"
+ },
+ "mushroom": {
+ "c": "nature",
+ "e": "ðŸ„",
+ "d": "mushroom",
+ "u": "6.0"
+ },
+ "musical_keyboard": {
+ "c": "activity",
+ "e": "🎹",
+ "d": "musical keyboard",
+ "u": "6.0"
+ },
+ "musical_note": {
+ "c": "symbols",
+ "e": "🎵",
+ "d": "musical note",
+ "u": "6.0"
+ },
+ "musical_score": {
+ "c": "activity",
+ "e": "🎼",
+ "d": "musical score",
+ "u": "6.0"
+ },
+ "mute": {
+ "c": "symbols",
+ "e": "🔇",
+ "d": "speaker with cancellation stroke",
+ "u": "6.0"
+ },
+ "nail_care": {
+ "c": "people",
+ "e": "💅",
+ "d": "nail polish",
+ "u": "6.0"
+ },
+ "nail_care_tone1": {
+ "c": "people",
+ "e": "💅ðŸ»",
+ "d": "nail polish tone 1",
+ "u": "8.0"
+ },
+ "nail_care_tone2": {
+ "c": "people",
+ "e": "💅ðŸ¼",
+ "d": "nail polish tone 2",
+ "u": "8.0"
+ },
+ "nail_care_tone3": {
+ "c": "people",
+ "e": "💅ðŸ½",
+ "d": "nail polish tone 3",
+ "u": "8.0"
+ },
+ "nail_care_tone4": {
+ "c": "people",
+ "e": "💅ðŸ¾",
+ "d": "nail polish tone 4",
+ "u": "8.0"
+ },
+ "nail_care_tone5": {
+ "c": "people",
+ "e": "💅ðŸ¿",
+ "d": "nail polish tone 5",
+ "u": "8.0"
+ },
+ "name_badge": {
+ "c": "symbols",
+ "e": "📛",
+ "d": "name badge",
+ "u": "6.0"
+ },
+ "nauseated_face": {
+ "c": "people",
+ "e": "🤢",
+ "d": "nauseated face",
+ "u": "9.0"
+ },
+ "necktie": {
+ "c": "people",
+ "e": "👔",
+ "d": "necktie",
+ "u": "6.0"
+ },
+ "negative_squared_cross_mark": {
+ "c": "symbols",
+ "e": "âŽ",
+ "d": "negative squared cross mark",
+ "u": "6.0"
+ },
+ "nerd": {
+ "c": "people",
+ "e": "🤓",
+ "d": "nerd face",
+ "u": "8.0"
+ },
+ "neutral_face": {
+ "c": "people",
+ "e": "ðŸ˜",
+ "d": "neutral face",
+ "u": "6.0"
+ },
+ "new": {
+ "c": "symbols",
+ "e": "🆕",
+ "d": "squared new",
+ "u": "6.0"
+ },
+ "new_moon": {
+ "c": "nature",
+ "e": "🌑",
+ "d": "new moon symbol",
+ "u": "6.0"
+ },
+ "new_moon_with_face": {
+ "c": "nature",
+ "e": "🌚",
+ "d": "new moon with face",
+ "u": "6.0"
+ },
+ "newspaper": {
+ "c": "objects",
+ "e": "📰",
+ "d": "newspaper",
+ "u": "6.0"
+ },
+ "newspaper2": {
+ "c": "objects",
+ "e": "🗞",
+ "d": "rolled-up newspaper",
+ "u": "7.0"
+ },
+ "ng": {
+ "c": "symbols",
+ "e": "🆖",
+ "d": "squared ng",
+ "u": "6.0"
+ },
+ "night_with_stars": {
+ "c": "travel",
+ "e": "🌃",
+ "d": "night with stars",
+ "u": "6.0"
+ },
+ "nine": {
+ "c": "symbols",
+ "e": "9ï¸âƒ£",
+ "d": "keycap digit nine",
+ "u": "3.0"
+ },
+ "no_bell": {
+ "c": "symbols",
+ "e": "🔕",
+ "d": "bell with cancellation stroke",
+ "u": "6.0"
+ },
+ "no_bicycles": {
+ "c": "symbols",
+ "e": "🚳",
+ "d": "no bicycles",
+ "u": "6.0"
+ },
+ "no_entry": {
+ "c": "symbols",
+ "e": "â›”",
+ "d": "no entry",
+ "u": "5.2"
+ },
+ "no_entry_sign": {
+ "c": "symbols",
+ "e": "🚫",
+ "d": "no entry sign",
+ "u": "6.0"
+ },
+ "no_good": {
+ "c": "people",
+ "e": "🙅",
+ "d": "face with no good gesture",
+ "u": "6.0"
+ },
+ "no_good_tone1": {
+ "c": "people",
+ "e": "🙅ðŸ»",
+ "d": "face with no good gesture tone 1",
+ "u": "8.0"
+ },
+ "no_good_tone2": {
+ "c": "people",
+ "e": "🙅ðŸ¼",
+ "d": "face with no good gesture tone 2",
+ "u": "8.0"
+ },
+ "no_good_tone3": {
+ "c": "people",
+ "e": "🙅ðŸ½",
+ "d": "face with no good gesture tone 3",
+ "u": "8.0"
+ },
+ "no_good_tone4": {
+ "c": "people",
+ "e": "🙅ðŸ¾",
+ "d": "face with no good gesture tone 4",
+ "u": "8.0"
+ },
+ "no_good_tone5": {
+ "c": "people",
+ "e": "🙅ðŸ¿",
+ "d": "face with no good gesture tone 5",
+ "u": "8.0"
+ },
+ "no_mobile_phones": {
+ "c": "symbols",
+ "e": "📵",
+ "d": "no mobile phones",
+ "u": "6.0"
+ },
+ "no_mouth": {
+ "c": "people",
+ "e": "😶",
+ "d": "face without mouth",
+ "u": "6.0"
+ },
+ "no_pedestrians": {
+ "c": "symbols",
+ "e": "🚷",
+ "d": "no pedestrians",
+ "u": "6.0"
+ },
+ "no_smoking": {
+ "c": "symbols",
+ "e": "🚭",
+ "d": "no smoking symbol",
+ "u": "6.0"
+ },
+ "non-potable_water": {
+ "c": "symbols",
+ "e": "🚱",
+ "d": "non-potable water symbol",
+ "u": "6.0"
+ },
+ "nose": {
+ "c": "people",
+ "e": "👃",
+ "d": "nose",
+ "u": "6.0"
+ },
+ "nose_tone1": {
+ "c": "people",
+ "e": "👃ðŸ»",
+ "d": "nose tone 1",
+ "u": "8.0"
+ },
+ "nose_tone2": {
+ "c": "people",
+ "e": "👃ðŸ¼",
+ "d": "nose tone 2",
+ "u": "8.0"
+ },
+ "nose_tone3": {
+ "c": "people",
+ "e": "👃ðŸ½",
+ "d": "nose tone 3",
+ "u": "8.0"
+ },
+ "nose_tone4": {
+ "c": "people",
+ "e": "👃ðŸ¾",
+ "d": "nose tone 4",
+ "u": "8.0"
+ },
+ "nose_tone5": {
+ "c": "people",
+ "e": "👃ðŸ¿",
+ "d": "nose tone 5",
+ "u": "8.0"
+ },
+ "notebook": {
+ "c": "objects",
+ "e": "📓",
+ "d": "notebook",
+ "u": "6.0"
+ },
+ "notebook_with_decorative_cover": {
+ "c": "objects",
+ "e": "📔",
+ "d": "notebook with decorative cover",
+ "u": "6.0"
+ },
+ "notepad_spiral": {
+ "c": "objects",
+ "e": "🗒",
+ "d": "spiral note pad",
+ "u": "7.0"
+ },
+ "notes": {
+ "c": "symbols",
+ "e": "🎶",
+ "d": "multiple musical notes",
+ "u": "6.0"
+ },
+ "nut_and_bolt": {
+ "c": "objects",
+ "e": "🔩",
+ "d": "nut and bolt",
+ "u": "6.0"
+ },
+ "o": {
+ "c": "symbols",
+ "e": "â­•",
+ "d": "heavy large circle",
+ "u": "5.2"
+ },
+ "o2": {
+ "c": "symbols",
+ "e": "🅾",
+ "d": "negative squared latin capital letter o",
+ "u": "6.0"
+ },
+ "ocean": {
+ "c": "nature",
+ "e": "🌊",
+ "d": "water wave",
+ "u": "6.0"
+ },
+ "octagonal_sign": {
+ "c": "symbols",
+ "e": "🛑",
+ "d": "octagonal sign",
+ "u": "9.0"
+ },
+ "octopus": {
+ "c": "nature",
+ "e": "ðŸ™",
+ "d": "octopus",
+ "u": "6.0"
+ },
+ "oden": {
+ "c": "food",
+ "e": "ðŸ¢",
+ "d": "oden",
+ "u": "6.0"
+ },
+ "office": {
+ "c": "travel",
+ "e": "ðŸ¢",
+ "d": "office building",
+ "u": "6.0"
+ },
+ "oil": {
+ "c": "objects",
+ "e": "🛢",
+ "d": "oil drum",
+ "u": "7.0"
+ },
+ "ok": {
+ "c": "symbols",
+ "e": "🆗",
+ "d": "squared ok",
+ "u": "6.0"
+ },
+ "ok_hand": {
+ "c": "people",
+ "e": "👌",
+ "d": "ok hand sign",
+ "u": "6.0"
+ },
+ "ok_hand_tone1": {
+ "c": "people",
+ "e": "👌ðŸ»",
+ "d": "ok hand sign tone 1",
+ "u": "8.0"
+ },
+ "ok_hand_tone2": {
+ "c": "people",
+ "e": "👌ðŸ¼",
+ "d": "ok hand sign tone 2",
+ "u": "8.0"
+ },
+ "ok_hand_tone3": {
+ "c": "people",
+ "e": "👌ðŸ½",
+ "d": "ok hand sign tone 3",
+ "u": "8.0"
+ },
+ "ok_hand_tone4": {
+ "c": "people",
+ "e": "👌ðŸ¾",
+ "d": "ok hand sign tone 4",
+ "u": "8.0"
+ },
+ "ok_hand_tone5": {
+ "c": "people",
+ "e": "👌ðŸ¿",
+ "d": "ok hand sign tone 5",
+ "u": "8.0"
+ },
+ "ok_woman": {
+ "c": "people",
+ "e": "🙆",
+ "d": "face with ok gesture",
+ "u": "6.0"
+ },
+ "ok_woman_tone1": {
+ "c": "people",
+ "e": "🙆ðŸ»",
+ "d": "face with ok gesture tone1",
+ "u": "8.0"
+ },
+ "ok_woman_tone2": {
+ "c": "people",
+ "e": "🙆ðŸ¼",
+ "d": "face with ok gesture tone2",
+ "u": "8.0"
+ },
+ "ok_woman_tone3": {
+ "c": "people",
+ "e": "🙆ðŸ½",
+ "d": "face with ok gesture tone3",
+ "u": "8.0"
+ },
+ "ok_woman_tone4": {
+ "c": "people",
+ "e": "🙆ðŸ¾",
+ "d": "face with ok gesture tone4",
+ "u": "8.0"
+ },
+ "ok_woman_tone5": {
+ "c": "people",
+ "e": "🙆ðŸ¿",
+ "d": "face with ok gesture tone5",
+ "u": "8.0"
+ },
+ "older_man": {
+ "c": "people",
+ "e": "👴",
+ "d": "older man",
+ "u": "6.0"
+ },
+ "older_man_tone1": {
+ "c": "people",
+ "e": "👴ðŸ»",
+ "d": "older man tone 1",
+ "u": "8.0"
+ },
+ "older_man_tone2": {
+ "c": "people",
+ "e": "👴ðŸ¼",
+ "d": "older man tone 2",
+ "u": "8.0"
+ },
+ "older_man_tone3": {
+ "c": "people",
+ "e": "👴ðŸ½",
+ "d": "older man tone 3",
+ "u": "8.0"
+ },
+ "older_man_tone4": {
+ "c": "people",
+ "e": "👴ðŸ¾",
+ "d": "older man tone 4",
+ "u": "8.0"
+ },
+ "older_man_tone5": {
+ "c": "people",
+ "e": "👴ðŸ¿",
+ "d": "older man tone 5",
+ "u": "8.0"
+ },
+ "older_woman": {
+ "c": "people",
+ "e": "👵",
+ "d": "older woman",
+ "u": "6.0"
+ },
+ "older_woman_tone1": {
+ "c": "people",
+ "e": "👵ðŸ»",
+ "d": "older woman tone 1",
+ "u": "8.0"
+ },
+ "older_woman_tone2": {
+ "c": "people",
+ "e": "👵ðŸ¼",
+ "d": "older woman tone 2",
+ "u": "8.0"
+ },
+ "older_woman_tone3": {
+ "c": "people",
+ "e": "👵ðŸ½",
+ "d": "older woman tone 3",
+ "u": "8.0"
+ },
+ "older_woman_tone4": {
+ "c": "people",
+ "e": "👵ðŸ¾",
+ "d": "older woman tone 4",
+ "u": "8.0"
+ },
+ "older_woman_tone5": {
+ "c": "people",
+ "e": "👵ðŸ¿",
+ "d": "older woman tone 5",
+ "u": "8.0"
+ },
+ "om_symbol": {
+ "c": "symbols",
+ "e": "🕉",
+ "d": "om symbol",
+ "u": "7.0"
+ },
+ "on": {
+ "c": "symbols",
+ "e": "🔛",
+ "d": "on with exclamation mark with left right arrow abo",
+ "u": "6.0"
+ },
+ "oncoming_automobile": {
+ "c": "travel",
+ "e": "🚘",
+ "d": "oncoming automobile",
+ "u": "6.0"
+ },
+ "oncoming_bus": {
+ "c": "travel",
+ "e": "ðŸš",
+ "d": "oncoming bus",
+ "u": "6.0"
+ },
+ "oncoming_police_car": {
+ "c": "travel",
+ "e": "🚔",
+ "d": "oncoming police car",
+ "u": "6.0"
+ },
+ "oncoming_taxi": {
+ "c": "travel",
+ "e": "🚖",
+ "d": "oncoming taxi",
+ "u": "6.0"
+ },
+ "one": {
+ "c": "symbols",
+ "e": "1ï¸âƒ£",
+ "d": "keycap digit one",
+ "u": "3.0"
+ },
+ "open_file_folder": {
+ "c": "objects",
+ "e": "📂",
+ "d": "open file folder",
+ "u": "6.0"
+ },
+ "open_hands": {
+ "c": "people",
+ "e": "ðŸ‘",
+ "d": "open hands sign",
+ "u": "6.0"
+ },
+ "open_hands_tone1": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ»",
+ "d": "open hands sign tone 1",
+ "u": "8.0"
+ },
+ "open_hands_tone2": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¼",
+ "d": "open hands sign tone 2",
+ "u": "8.0"
+ },
+ "open_hands_tone3": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ½",
+ "d": "open hands sign tone 3",
+ "u": "8.0"
+ },
+ "open_hands_tone4": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¾",
+ "d": "open hands sign tone 4",
+ "u": "8.0"
+ },
+ "open_hands_tone5": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¿",
+ "d": "open hands sign tone 5",
+ "u": "8.0"
+ },
+ "open_mouth": {
+ "c": "people",
+ "e": "😮",
+ "d": "face with open mouth",
+ "u": "6.1"
+ },
+ "ophiuchus": {
+ "c": "symbols",
+ "e": "⛎",
+ "d": "ophiuchus",
+ "u": "6.0"
+ },
+ "orange_book": {
+ "c": "objects",
+ "e": "📙",
+ "d": "orange book",
+ "u": "6.0"
+ },
+ "orthodox_cross": {
+ "c": "symbols",
+ "e": "☦",
+ "d": "orthodox cross",
+ "u": "1.1"
+ },
+ "outbox_tray": {
+ "c": "objects",
+ "e": "📤",
+ "d": "outbox tray",
+ "u": "6.0"
+ },
+ "owl": {
+ "c": "nature",
+ "e": "🦉",
+ "d": "owl",
+ "u": "9.0"
+ },
+ "ox": {
+ "c": "nature",
+ "e": "ðŸ‚",
+ "d": "ox",
+ "u": "6.0"
+ },
+ "package": {
+ "c": "objects",
+ "e": "📦",
+ "d": "package",
+ "u": "6.0"
+ },
+ "page_facing_up": {
+ "c": "objects",
+ "e": "📄",
+ "d": "page facing up",
+ "u": "6.0"
+ },
+ "page_with_curl": {
+ "c": "objects",
+ "e": "📃",
+ "d": "page with curl",
+ "u": "6.0"
+ },
+ "pager": {
+ "c": "objects",
+ "e": "📟",
+ "d": "pager",
+ "u": "6.0"
+ },
+ "paintbrush": {
+ "c": "objects",
+ "e": "🖌",
+ "d": "lower left paintbrush",
+ "u": "7.0"
+ },
+ "palm_tree": {
+ "c": "nature",
+ "e": "🌴",
+ "d": "palm tree",
+ "u": "6.0"
+ },
+ "pancakes": {
+ "c": "food",
+ "e": "🥞",
+ "d": "pancakes",
+ "u": "9.0"
+ },
+ "panda_face": {
+ "c": "nature",
+ "e": "ðŸ¼",
+ "d": "panda face",
+ "u": "6.0"
+ },
+ "paperclip": {
+ "c": "objects",
+ "e": "📎",
+ "d": "paperclip",
+ "u": "6.0"
+ },
+ "paperclips": {
+ "c": "objects",
+ "e": "🖇",
+ "d": "linked paperclips",
+ "u": "7.0"
+ },
+ "park": {
+ "c": "travel",
+ "e": "ðŸž",
+ "d": "national park",
+ "u": "7.0"
+ },
+ "parking": {
+ "c": "symbols",
+ "e": "🅿",
+ "d": "negative squared latin capital letter p",
+ "u": "5.2"
+ },
+ "part_alternation_mark": {
+ "c": "symbols",
+ "e": "〽",
+ "d": "part alternation mark",
+ "u": "3.2"
+ },
+ "partly_sunny": {
+ "c": "nature",
+ "e": "â›…",
+ "d": "sun behind cloud",
+ "u": "5.2"
+ },
+ "passport_control": {
+ "c": "symbols",
+ "e": "🛂",
+ "d": "passport control",
+ "u": "6.0"
+ },
+ "pause_button": {
+ "c": "symbols",
+ "e": "â¸",
+ "d": "double vertical bar",
+ "u": "7.0"
+ },
+ "peace": {
+ "c": "symbols",
+ "e": "☮",
+ "d": "peace symbol",
+ "u": "1.1"
+ },
+ "peach": {
+ "c": "food",
+ "e": "ðŸ‘",
+ "d": "peach",
+ "u": "6.0"
+ },
+ "peanuts": {
+ "c": "food",
+ "e": "🥜",
+ "d": "peanuts",
+ "u": "9.0"
+ },
+ "pear": {
+ "c": "food",
+ "e": "ðŸ",
+ "d": "pear",
+ "u": "6.0"
+ },
+ "pen_ballpoint": {
+ "c": "objects",
+ "e": "🖊",
+ "d": "lower left ballpoint pen",
+ "u": "7.0"
+ },
+ "pen_fountain": {
+ "c": "objects",
+ "e": "🖋",
+ "d": "lower left fountain pen",
+ "u": "7.0"
+ },
+ "pencil": {
+ "c": "objects",
+ "e": "ðŸ“",
+ "d": "memo",
+ "u": "6.0"
+ },
+ "pencil2": {
+ "c": "objects",
+ "e": "âœ",
+ "d": "pencil",
+ "u": "1.1"
+ },
+ "penguin": {
+ "c": "nature",
+ "e": "ðŸ§",
+ "d": "penguin",
+ "u": "6.0"
+ },
+ "pensive": {
+ "c": "people",
+ "e": "😔",
+ "d": "pensive face",
+ "u": "6.0"
+ },
+ "performing_arts": {
+ "c": "activity",
+ "e": "🎭",
+ "d": "performing arts",
+ "u": "6.0"
+ },
+ "persevere": {
+ "c": "people",
+ "e": "😣",
+ "d": "persevering face",
+ "u": "6.0"
+ },
+ "person_frowning": {
+ "c": "people",
+ "e": "ðŸ™",
+ "d": "person frowning",
+ "u": "6.0"
+ },
+ "person_frowning_tone1": {
+ "c": "people",
+ "e": "ðŸ™ðŸ»",
+ "d": "person frowning tone 1",
+ "u": "8.0"
+ },
+ "person_frowning_tone2": {
+ "c": "people",
+ "e": "ðŸ™ðŸ¼",
+ "d": "person frowning tone 2",
+ "u": "8.0"
+ },
+ "person_frowning_tone3": {
+ "c": "people",
+ "e": "ðŸ™ðŸ½",
+ "d": "person frowning tone 3",
+ "u": "8.0"
+ },
+ "person_frowning_tone4": {
+ "c": "people",
+ "e": "ðŸ™ðŸ¾",
+ "d": "person frowning tone 4",
+ "u": "8.0"
+ },
+ "person_frowning_tone5": {
+ "c": "people",
+ "e": "ðŸ™ðŸ¿",
+ "d": "person frowning tone 5",
+ "u": "8.0"
+ },
+ "person_with_blond_hair": {
+ "c": "people",
+ "e": "👱",
+ "d": "person with blond hair",
+ "u": "6.0"
+ },
+ "person_with_blond_hair_tone1": {
+ "c": "people",
+ "e": "👱ðŸ»",
+ "d": "person with blond hair tone 1",
+ "u": "8.0"
+ },
+ "person_with_blond_hair_tone2": {
+ "c": "people",
+ "e": "👱ðŸ¼",
+ "d": "person with blond hair tone 2",
+ "u": "8.0"
+ },
+ "person_with_blond_hair_tone3": {
+ "c": "people",
+ "e": "👱ðŸ½",
+ "d": "person with blond hair tone 3",
+ "u": "8.0"
+ },
+ "person_with_blond_hair_tone4": {
+ "c": "people",
+ "e": "👱ðŸ¾",
+ "d": "person with blond hair tone 4",
+ "u": "8.0"
+ },
+ "person_with_blond_hair_tone5": {
+ "c": "people",
+ "e": "👱ðŸ¿",
+ "d": "person with blond hair tone 5",
+ "u": "8.0"
+ },
+ "person_with_pouting_face": {
+ "c": "people",
+ "e": "🙎",
+ "d": "person with pouting face",
+ "u": "6.0"
+ },
+ "person_with_pouting_face_tone1": {
+ "c": "people",
+ "e": "🙎ðŸ»",
+ "d": "person with pouting face tone1",
+ "u": "8.0"
+ },
+ "person_with_pouting_face_tone2": {
+ "c": "people",
+ "e": "🙎ðŸ¼",
+ "d": "person with pouting face tone2",
+ "u": "8.0"
+ },
+ "person_with_pouting_face_tone3": {
+ "c": "people",
+ "e": "🙎ðŸ½",
+ "d": "person with pouting face tone3",
+ "u": "8.0"
+ },
+ "person_with_pouting_face_tone4": {
+ "c": "people",
+ "e": "🙎ðŸ¾",
+ "d": "person with pouting face tone4",
+ "u": "8.0"
+ },
+ "person_with_pouting_face_tone5": {
+ "c": "people",
+ "e": "🙎ðŸ¿",
+ "d": "person with pouting face tone5",
+ "u": "8.0"
+ },
+ "pick": {
+ "c": "objects",
+ "e": "â›",
+ "d": "pick",
+ "u": "5.2"
+ },
+ "pig": {
+ "c": "nature",
+ "e": "ðŸ·",
+ "d": "pig face",
+ "u": "6.0"
+ },
+ "pig2": {
+ "c": "nature",
+ "e": "ðŸ–",
+ "d": "pig",
+ "u": "6.0"
+ },
+ "pig_nose": {
+ "c": "nature",
+ "e": "ðŸ½",
+ "d": "pig nose",
+ "u": "6.0"
+ },
+ "pill": {
+ "c": "objects",
+ "e": "💊",
+ "d": "pill",
+ "u": "6.0"
+ },
+ "pineapple": {
+ "c": "food",
+ "e": "ðŸ",
+ "d": "pineapple",
+ "u": "6.0"
+ },
+ "ping_pong": {
+ "c": "activity",
+ "e": "ðŸ“",
+ "d": "table tennis paddle and ball",
+ "u": "8.0"
+ },
+ "pisces": {
+ "c": "symbols",
+ "e": "♓",
+ "d": "pisces",
+ "u": "1.1"
+ },
+ "pizza": {
+ "c": "food",
+ "e": "ðŸ•",
+ "d": "slice of pizza",
+ "u": "6.0"
+ },
+ "place_of_worship": {
+ "c": "symbols",
+ "e": "ðŸ›",
+ "d": "place of worship",
+ "u": "8.0"
+ },
+ "play_pause": {
+ "c": "symbols",
+ "e": "â¯",
+ "d": "black right-pointing double triangle with double vertical bar",
+ "u": "6.0"
+ },
+ "point_down": {
+ "c": "people",
+ "e": "👇",
+ "d": "white down pointing backhand index",
+ "u": "6.0"
+ },
+ "point_down_tone1": {
+ "c": "people",
+ "e": "👇ðŸ»",
+ "d": "white down pointing backhand index tone 1",
+ "u": "8.0"
+ },
+ "point_down_tone2": {
+ "c": "people",
+ "e": "👇ðŸ¼",
+ "d": "white down pointing backhand index tone 2",
+ "u": "8.0"
+ },
+ "point_down_tone3": {
+ "c": "people",
+ "e": "👇ðŸ½",
+ "d": "white down pointing backhand index tone 3",
+ "u": "8.0"
+ },
+ "point_down_tone4": {
+ "c": "people",
+ "e": "👇ðŸ¾",
+ "d": "white down pointing backhand index tone 4",
+ "u": "8.0"
+ },
+ "point_down_tone5": {
+ "c": "people",
+ "e": "👇ðŸ¿",
+ "d": "white down pointing backhand index tone 5",
+ "u": "8.0"
+ },
+ "point_left": {
+ "c": "people",
+ "e": "👈",
+ "d": "white left pointing backhand index",
+ "u": "6.0"
+ },
+ "point_left_tone1": {
+ "c": "people",
+ "e": "👈ðŸ»",
+ "d": "white left pointing backhand index tone 1",
+ "u": "8.0"
+ },
+ "point_left_tone2": {
+ "c": "people",
+ "e": "👈ðŸ¼",
+ "d": "white left pointing backhand index tone 2",
+ "u": "8.0"
+ },
+ "point_left_tone3": {
+ "c": "people",
+ "e": "👈ðŸ½",
+ "d": "white left pointing backhand index tone 3",
+ "u": "8.0"
+ },
+ "point_left_tone4": {
+ "c": "people",
+ "e": "👈ðŸ¾",
+ "d": "white left pointing backhand index tone 4",
+ "u": "8.0"
+ },
+ "point_left_tone5": {
+ "c": "people",
+ "e": "👈ðŸ¿",
+ "d": "white left pointing backhand index tone 5",
+ "u": "8.0"
+ },
+ "point_right": {
+ "c": "people",
+ "e": "👉",
+ "d": "white right pointing backhand index",
+ "u": "6.0"
+ },
+ "point_right_tone1": {
+ "c": "people",
+ "e": "👉ðŸ»",
+ "d": "white right pointing backhand index tone 1",
+ "u": "8.0"
+ },
+ "point_right_tone2": {
+ "c": "people",
+ "e": "👉ðŸ¼",
+ "d": "white right pointing backhand index tone 2",
+ "u": "8.0"
+ },
+ "point_right_tone3": {
+ "c": "people",
+ "e": "👉ðŸ½",
+ "d": "white right pointing backhand index tone 3",
+ "u": "8.0"
+ },
+ "point_right_tone4": {
+ "c": "people",
+ "e": "👉ðŸ¾",
+ "d": "white right pointing backhand index tone 4",
+ "u": "8.0"
+ },
+ "point_right_tone5": {
+ "c": "people",
+ "e": "👉ðŸ¿",
+ "d": "white right pointing backhand index tone 5",
+ "u": "8.0"
+ },
+ "point_up": {
+ "c": "people",
+ "e": "â˜",
+ "d": "white up pointing index",
+ "u": "1.1"
+ },
+ "point_up_2": {
+ "c": "people",
+ "e": "👆",
+ "d": "white up pointing backhand index",
+ "u": "6.0"
+ },
+ "point_up_2_tone1": {
+ "c": "people",
+ "e": "👆ðŸ»",
+ "d": "white up pointing backhand index tone 1",
+ "u": "8.0"
+ },
+ "point_up_2_tone2": {
+ "c": "people",
+ "e": "👆ðŸ¼",
+ "d": "white up pointing backhand index tone 2",
+ "u": "8.0"
+ },
+ "point_up_2_tone3": {
+ "c": "people",
+ "e": "👆ðŸ½",
+ "d": "white up pointing backhand index tone 3",
+ "u": "8.0"
+ },
+ "point_up_2_tone4": {
+ "c": "people",
+ "e": "👆ðŸ¾",
+ "d": "white up pointing backhand index tone 4",
+ "u": "8.0"
+ },
+ "point_up_2_tone5": {
+ "c": "people",
+ "e": "👆ðŸ¿",
+ "d": "white up pointing backhand index tone 5",
+ "u": "8.0"
+ },
+ "point_up_tone1": {
+ "c": "people",
+ "e": "â˜ðŸ»",
+ "d": "white up pointing index tone 1",
+ "u": "8.0"
+ },
+ "point_up_tone2": {
+ "c": "people",
+ "e": "â˜ðŸ¼",
+ "d": "white up pointing index tone 2",
+ "u": "8.0"
+ },
+ "point_up_tone3": {
+ "c": "people",
+ "e": "â˜ðŸ½",
+ "d": "white up pointing index tone 3",
+ "u": "8.0"
+ },
+ "point_up_tone4": {
+ "c": "people",
+ "e": "â˜ðŸ¾",
+ "d": "white up pointing index tone 4",
+ "u": "8.0"
+ },
+ "point_up_tone5": {
+ "c": "people",
+ "e": "â˜ðŸ¿",
+ "d": "white up pointing index tone 5",
+ "u": "8.0"
+ },
+ "police_car": {
+ "c": "travel",
+ "e": "🚓",
+ "d": "police car",
+ "u": "6.0"
+ },
+ "poodle": {
+ "c": "nature",
+ "e": "ðŸ©",
+ "d": "poodle",
+ "u": "6.0"
+ },
+ "poop": {
+ "c": "people",
+ "e": "💩",
+ "d": "pile of poo",
+ "u": "6.0"
+ },
+ "popcorn": {
+ "c": "food",
+ "e": "ðŸ¿",
+ "d": "popcorn",
+ "u": "8.0"
+ },
+ "post_office": {
+ "c": "travel",
+ "e": "ðŸ£",
+ "d": "japanese post office",
+ "u": "6.0"
+ },
+ "postal_horn": {
+ "c": "objects",
+ "e": "📯",
+ "d": "postal horn",
+ "u": "6.0"
+ },
+ "postbox": {
+ "c": "objects",
+ "e": "📮",
+ "d": "postbox",
+ "u": "6.0"
+ },
+ "potable_water": {
+ "c": "symbols",
+ "e": "🚰",
+ "d": "potable water symbol",
+ "u": "6.0"
+ },
+ "potato": {
+ "c": "food",
+ "e": "🥔",
+ "d": "potato",
+ "u": "9.0"
+ },
+ "pouch": {
+ "c": "people",
+ "e": "ðŸ‘",
+ "d": "pouch",
+ "u": "6.0"
+ },
+ "poultry_leg": {
+ "c": "food",
+ "e": "ðŸ—",
+ "d": "poultry leg",
+ "u": "6.0"
+ },
+ "pound": {
+ "c": "objects",
+ "e": "💷",
+ "d": "banknote with pound sign",
+ "u": "6.0"
+ },
+ "pouting_cat": {
+ "c": "people",
+ "e": "😾",
+ "d": "pouting cat face",
+ "u": "6.0"
+ },
+ "pray": {
+ "c": "people",
+ "e": "ðŸ™",
+ "d": "person with folded hands",
+ "u": "6.0"
+ },
+ "pray_tone1": {
+ "c": "people",
+ "e": "ðŸ™ðŸ»",
+ "d": "person with folded hands tone 1",
+ "u": "8.0"
+ },
+ "pray_tone2": {
+ "c": "people",
+ "e": "ðŸ™ðŸ¼",
+ "d": "person with folded hands tone 2",
+ "u": "8.0"
+ },
+ "pray_tone3": {
+ "c": "people",
+ "e": "ðŸ™ðŸ½",
+ "d": "person with folded hands tone 3",
+ "u": "8.0"
+ },
+ "pray_tone4": {
+ "c": "people",
+ "e": "ðŸ™ðŸ¾",
+ "d": "person with folded hands tone 4",
+ "u": "8.0"
+ },
+ "pray_tone5": {
+ "c": "people",
+ "e": "ðŸ™ðŸ¿",
+ "d": "person with folded hands tone 5",
+ "u": "8.0"
+ },
+ "prayer_beads": {
+ "c": "objects",
+ "e": "📿",
+ "d": "prayer beads",
+ "u": "8.0"
+ },
+ "pregnant_woman": {
+ "c": "people",
+ "e": "🤰",
+ "d": "pregnant woman",
+ "u": "9.0"
+ },
+ "pregnant_woman_tone1": {
+ "c": "people",
+ "e": "🤰ðŸ»",
+ "d": "pregnant woman tone 1",
+ "u": "9.0"
+ },
+ "pregnant_woman_tone2": {
+ "c": "people",
+ "e": "🤰ðŸ¼",
+ "d": "pregnant woman tone 2",
+ "u": "9.0"
+ },
+ "pregnant_woman_tone3": {
+ "c": "people",
+ "e": "🤰ðŸ½",
+ "d": "pregnant woman tone 3",
+ "u": "9.0"
+ },
+ "pregnant_woman_tone4": {
+ "c": "people",
+ "e": "🤰ðŸ¾",
+ "d": "pregnant woman tone 4",
+ "u": "9.0"
+ },
+ "pregnant_woman_tone5": {
+ "c": "people",
+ "e": "🤰ðŸ¿",
+ "d": "pregnant woman tone 5",
+ "u": "9.0"
+ },
+ "prince": {
+ "c": "people",
+ "e": "🤴",
+ "d": "prince",
+ "u": "9.0"
+ },
+ "prince_tone1": {
+ "c": "people",
+ "e": "🤴ðŸ»",
+ "d": "prince tone 1",
+ "u": "9.0"
+ },
+ "prince_tone2": {
+ "c": "people",
+ "e": "🤴ðŸ¼",
+ "d": "prince tone 2",
+ "u": "9.0"
+ },
+ "prince_tone3": {
+ "c": "people",
+ "e": "🤴ðŸ½",
+ "d": "prince tone 3",
+ "u": "9.0"
+ },
+ "prince_tone4": {
+ "c": "people",
+ "e": "🤴ðŸ¾",
+ "d": "prince tone 4",
+ "u": "9.0"
+ },
+ "prince_tone5": {
+ "c": "people",
+ "e": "🤴ðŸ¿",
+ "d": "prince tone 5",
+ "u": "9.0"
+ },
+ "princess": {
+ "c": "people",
+ "e": "👸",
+ "d": "princess",
+ "u": "6.0"
+ },
+ "princess_tone1": {
+ "c": "people",
+ "e": "👸ðŸ»",
+ "d": "princess tone 1",
+ "u": "8.0"
+ },
+ "princess_tone2": {
+ "c": "people",
+ "e": "👸ðŸ¼",
+ "d": "princess tone 2",
+ "u": "8.0"
+ },
+ "princess_tone3": {
+ "c": "people",
+ "e": "👸ðŸ½",
+ "d": "princess tone 3",
+ "u": "8.0"
+ },
+ "princess_tone4": {
+ "c": "people",
+ "e": "👸ðŸ¾",
+ "d": "princess tone 4",
+ "u": "8.0"
+ },
+ "princess_tone5": {
+ "c": "people",
+ "e": "👸ðŸ¿",
+ "d": "princess tone 5",
+ "u": "8.0"
+ },
+ "printer": {
+ "c": "objects",
+ "e": "🖨",
+ "d": "printer",
+ "u": "7.0"
+ },
+ "projector": {
+ "c": "objects",
+ "e": "📽",
+ "d": "film projector",
+ "u": "7.0"
+ },
+ "punch": {
+ "c": "people",
+ "e": "👊",
+ "d": "fisted hand sign",
+ "u": "6.0"
+ },
+ "punch_tone1": {
+ "c": "people",
+ "e": "👊ðŸ»",
+ "d": "fisted hand sign tone 1",
+ "u": "8.0"
+ },
+ "punch_tone2": {
+ "c": "people",
+ "e": "👊ðŸ¼",
+ "d": "fisted hand sign tone 2",
+ "u": "8.0"
+ },
+ "punch_tone3": {
+ "c": "people",
+ "e": "👊ðŸ½",
+ "d": "fisted hand sign tone 3",
+ "u": "8.0"
+ },
+ "punch_tone4": {
+ "c": "people",
+ "e": "👊ðŸ¾",
+ "d": "fisted hand sign tone 4",
+ "u": "8.0"
+ },
+ "punch_tone5": {
+ "c": "people",
+ "e": "👊ðŸ¿",
+ "d": "fisted hand sign tone 5",
+ "u": "8.0"
+ },
+ "purple_heart": {
+ "c": "symbols",
+ "e": "💜",
+ "d": "purple heart",
+ "u": "6.0"
+ },
+ "purse": {
+ "c": "people",
+ "e": "👛",
+ "d": "purse",
+ "u": "6.0"
+ },
+ "pushpin": {
+ "c": "objects",
+ "e": "📌",
+ "d": "pushpin",
+ "u": "6.0"
+ },
+ "put_litter_in_its_place": {
+ "c": "symbols",
+ "e": "🚮",
+ "d": "put litter in its place symbol",
+ "u": "6.0"
+ },
+ "question": {
+ "c": "symbols",
+ "e": "â“",
+ "d": "black question mark ornament",
+ "u": "6.0"
+ },
+ "rabbit": {
+ "c": "nature",
+ "e": "ðŸ°",
+ "d": "rabbit face",
+ "u": "6.0"
+ },
+ "rabbit2": {
+ "c": "nature",
+ "e": "ðŸ‡",
+ "d": "rabbit",
+ "u": "6.0"
+ },
+ "race_car": {
+ "c": "travel",
+ "e": "ðŸŽ",
+ "d": "racing car",
+ "u": "7.0"
+ },
+ "racehorse": {
+ "c": "nature",
+ "e": "ðŸŽ",
+ "d": "horse",
+ "u": "6.0"
+ },
+ "radio": {
+ "c": "objects",
+ "e": "📻",
+ "d": "radio",
+ "u": "6.0"
+ },
+ "radio_button": {
+ "c": "symbols",
+ "e": "🔘",
+ "d": "radio button",
+ "u": "6.0"
+ },
+ "radioactive": {
+ "c": "symbols",
+ "e": "☢",
+ "d": "radioactive sign",
+ "u": "1.1"
+ },
+ "rage": {
+ "c": "people",
+ "e": "😡",
+ "d": "pouting face",
+ "u": "6.0"
+ },
+ "railway_car": {
+ "c": "travel",
+ "e": "🚃",
+ "d": "railway car",
+ "u": "6.0"
+ },
+ "railway_track": {
+ "c": "travel",
+ "e": "🛤",
+ "d": "railway track",
+ "u": "7.0"
+ },
+ "rainbow": {
+ "c": "travel",
+ "e": "🌈",
+ "d": "rainbow",
+ "u": "6.0"
+ },
+ "raised_back_of_hand": {
+ "c": "people",
+ "e": "🤚",
+ "d": "raised back of hand",
+ "u": "9.0"
+ },
+ "raised_back_of_hand_tone1": {
+ "c": "people",
+ "e": "🤚ðŸ»",
+ "d": "raised back of hand tone 1",
+ "u": "9.0"
+ },
+ "raised_back_of_hand_tone2": {
+ "c": "people",
+ "e": "🤚ðŸ¼",
+ "d": "raised back of hand tone 2",
+ "u": "9.0"
+ },
+ "raised_back_of_hand_tone3": {
+ "c": "people",
+ "e": "🤚ðŸ½",
+ "d": "raised back of hand tone 3",
+ "u": "9.0"
+ },
+ "raised_back_of_hand_tone4": {
+ "c": "people",
+ "e": "🤚ðŸ¾",
+ "d": "raised back of hand tone 4",
+ "u": "9.0"
+ },
+ "raised_back_of_hand_tone5": {
+ "c": "people",
+ "e": "🤚ðŸ¿",
+ "d": "raised back of hand tone 5",
+ "u": "9.0"
+ },
+ "raised_hand": {
+ "c": "people",
+ "e": "✋",
+ "d": "raised hand",
+ "u": "6.0"
+ },
+ "raised_hand_tone1": {
+ "c": "people",
+ "e": "✋ðŸ»",
+ "d": "raised hand tone 1",
+ "u": "8.0"
+ },
+ "raised_hand_tone2": {
+ "c": "people",
+ "e": "✋ðŸ¼",
+ "d": "raised hand tone 2",
+ "u": "8.0"
+ },
+ "raised_hand_tone3": {
+ "c": "people",
+ "e": "✋ðŸ½",
+ "d": "raised hand tone 3",
+ "u": "8.0"
+ },
+ "raised_hand_tone4": {
+ "c": "people",
+ "e": "✋ðŸ¾",
+ "d": "raised hand tone 4",
+ "u": "8.0"
+ },
+ "raised_hand_tone5": {
+ "c": "people",
+ "e": "✋ðŸ¿",
+ "d": "raised hand tone 5",
+ "u": "8.0"
+ },
+ "raised_hands": {
+ "c": "people",
+ "e": "🙌",
+ "d": "person raising both hands in celebration",
+ "u": "6.0"
+ },
+ "raised_hands_tone1": {
+ "c": "people",
+ "e": "🙌ðŸ»",
+ "d": "person raising both hands in celebration tone 1",
+ "u": "8.0"
+ },
+ "raised_hands_tone2": {
+ "c": "people",
+ "e": "🙌ðŸ¼",
+ "d": "person raising both hands in celebration tone 2",
+ "u": "8.0"
+ },
+ "raised_hands_tone3": {
+ "c": "people",
+ "e": "🙌ðŸ½",
+ "d": "person raising both hands in celebration tone 3",
+ "u": "8.0"
+ },
+ "raised_hands_tone4": {
+ "c": "people",
+ "e": "🙌ðŸ¾",
+ "d": "person raising both hands in celebration tone 4",
+ "u": "8.0"
+ },
+ "raised_hands_tone5": {
+ "c": "people",
+ "e": "🙌ðŸ¿",
+ "d": "person raising both hands in celebration tone 5",
+ "u": "8.0"
+ },
+ "raising_hand": {
+ "c": "people",
+ "e": "🙋",
+ "d": "happy person raising one hand",
+ "u": "6.0"
+ },
+ "raising_hand_tone1": {
+ "c": "people",
+ "e": "🙋ðŸ»",
+ "d": "happy person raising one hand tone1",
+ "u": "8.0"
+ },
+ "raising_hand_tone2": {
+ "c": "people",
+ "e": "🙋ðŸ¼",
+ "d": "happy person raising one hand tone2",
+ "u": "8.0"
+ },
+ "raising_hand_tone3": {
+ "c": "people",
+ "e": "🙋ðŸ½",
+ "d": "happy person raising one hand tone3",
+ "u": "8.0"
+ },
+ "raising_hand_tone4": {
+ "c": "people",
+ "e": "🙋ðŸ¾",
+ "d": "happy person raising one hand tone4",
+ "u": "8.0"
+ },
+ "raising_hand_tone5": {
+ "c": "people",
+ "e": "🙋ðŸ¿",
+ "d": "happy person raising one hand tone5",
+ "u": "8.0"
+ },
+ "ram": {
+ "c": "nature",
+ "e": "ðŸ",
+ "d": "ram",
+ "u": "6.0"
+ },
+ "ramen": {
+ "c": "food",
+ "e": "ðŸœ",
+ "d": "steaming bowl",
+ "u": "6.0"
+ },
+ "rat": {
+ "c": "nature",
+ "e": "ðŸ€",
+ "d": "rat",
+ "u": "6.0"
+ },
+ "record_button": {
+ "c": "symbols",
+ "e": "âº",
+ "d": "black circle for record",
+ "u": "7.0"
+ },
+ "recycle": {
+ "c": "symbols",
+ "e": "â™»",
+ "d": "black universal recycling symbol",
+ "u": "3.2"
+ },
+ "red_car": {
+ "c": "travel",
+ "e": "🚗",
+ "d": "automobile",
+ "u": "6.0"
+ },
+ "red_circle": {
+ "c": "symbols",
+ "e": "🔴",
+ "d": "large red circle",
+ "u": "6.0"
+ },
+ "registered": {
+ "c": "symbols",
+ "e": "®",
+ "d": "registered sign",
+ "u": "1.1"
+ },
+ "relaxed": {
+ "c": "people",
+ "e": "☺",
+ "d": "white smiling face",
+ "u": "1.1"
+ },
+ "relieved": {
+ "c": "people",
+ "e": "😌",
+ "d": "relieved face",
+ "u": "6.0"
+ },
+ "reminder_ribbon": {
+ "c": "activity",
+ "e": "🎗",
+ "d": "reminder ribbon",
+ "u": "7.0"
+ },
+ "repeat": {
+ "c": "symbols",
+ "e": "ðŸ”",
+ "d": "clockwise rightwards and leftwards open circle arr",
+ "u": "6.0"
+ },
+ "repeat_one": {
+ "c": "symbols",
+ "e": "🔂",
+ "d": "clockwise rightwards and leftwards open circle arr",
+ "u": "6.0"
+ },
+ "restroom": {
+ "c": "symbols",
+ "e": "🚻",
+ "d": "restroom",
+ "u": "6.0"
+ },
+ "revolving_hearts": {
+ "c": "symbols",
+ "e": "💞",
+ "d": "revolving hearts",
+ "u": "6.0"
+ },
+ "rewind": {
+ "c": "symbols",
+ "e": "âª",
+ "d": "black left-pointing double triangle",
+ "u": "6.0"
+ },
+ "rhino": {
+ "c": "nature",
+ "e": "ðŸ¦",
+ "d": "rhinoceros",
+ "u": "9.0"
+ },
+ "ribbon": {
+ "c": "objects",
+ "e": "🎀",
+ "d": "ribbon",
+ "u": "6.0"
+ },
+ "rice": {
+ "c": "food",
+ "e": "ðŸš",
+ "d": "cooked rice",
+ "u": "6.0"
+ },
+ "rice_ball": {
+ "c": "food",
+ "e": "ðŸ™",
+ "d": "rice ball",
+ "u": "6.0"
+ },
+ "rice_cracker": {
+ "c": "food",
+ "e": "ðŸ˜",
+ "d": "rice cracker",
+ "u": "6.0"
+ },
+ "rice_scene": {
+ "c": "travel",
+ "e": "🎑",
+ "d": "moon viewing ceremony",
+ "u": "6.0"
+ },
+ "right_facing_fist": {
+ "c": "people",
+ "e": "🤜",
+ "d": "right-facing fist",
+ "u": "9.0"
+ },
+ "right_facing_fist_tone1": {
+ "c": "people",
+ "e": "🤜ðŸ»",
+ "d": "right facing fist tone 1",
+ "u": "9.0"
+ },
+ "right_facing_fist_tone2": {
+ "c": "people",
+ "e": "🤜ðŸ¼",
+ "d": "right facing fist tone 2",
+ "u": "9.0"
+ },
+ "right_facing_fist_tone3": {
+ "c": "people",
+ "e": "🤜ðŸ½",
+ "d": "right facing fist tone 3",
+ "u": "9.0"
+ },
+ "right_facing_fist_tone4": {
+ "c": "people",
+ "e": "🤜ðŸ¾",
+ "d": "right facing fist tone 4",
+ "u": "9.0"
+ },
+ "right_facing_fist_tone5": {
+ "c": "people",
+ "e": "🤜ðŸ¿",
+ "d": "right facing fist tone 5",
+ "u": "9.0"
+ },
+ "ring": {
+ "c": "people",
+ "e": "ðŸ’",
+ "d": "ring",
+ "u": "6.0"
+ },
+ "robot": {
+ "c": "people",
+ "e": "🤖",
+ "d": "robot face",
+ "u": "8.0"
+ },
+ "rocket": {
+ "c": "travel",
+ "e": "🚀",
+ "d": "rocket",
+ "u": "6.0"
+ },
+ "rofl": {
+ "c": "people",
+ "e": "🤣",
+ "d": "rolling on the floor laughing",
+ "u": "9.0"
+ },
+ "roller_coaster": {
+ "c": "travel",
+ "e": "🎢",
+ "d": "roller coaster",
+ "u": "6.0"
+ },
+ "rolling_eyes": {
+ "c": "people",
+ "e": "🙄",
+ "d": "face with rolling eyes",
+ "u": "8.0"
+ },
+ "rooster": {
+ "c": "nature",
+ "e": "ðŸ“",
+ "d": "rooster",
+ "u": "6.0"
+ },
+ "rose": {
+ "c": "nature",
+ "e": "🌹",
+ "d": "rose",
+ "u": "6.0"
+ },
+ "rosette": {
+ "c": "activity",
+ "e": "ðŸµ",
+ "d": "rosette",
+ "u": "7.0"
+ },
+ "rotating_light": {
+ "c": "travel",
+ "e": "🚨",
+ "d": "police cars revolving light",
+ "u": "6.0"
+ },
+ "round_pushpin": {
+ "c": "objects",
+ "e": "ðŸ“",
+ "d": "round pushpin",
+ "u": "6.0"
+ },
+ "rowboat": {
+ "c": "activity",
+ "e": "🚣",
+ "d": "rowboat",
+ "u": "6.0"
+ },
+ "rowboat_tone1": {
+ "c": "activity",
+ "e": "🚣ðŸ»",
+ "d": "rowboat tone 1",
+ "u": "8.0"
+ },
+ "rowboat_tone2": {
+ "c": "activity",
+ "e": "🚣ðŸ¼",
+ "d": "rowboat tone 2",
+ "u": "8.0"
+ },
+ "rowboat_tone3": {
+ "c": "activity",
+ "e": "🚣ðŸ½",
+ "d": "rowboat tone 3",
+ "u": "8.0"
+ },
+ "rowboat_tone4": {
+ "c": "activity",
+ "e": "🚣ðŸ¾",
+ "d": "rowboat tone 4",
+ "u": "8.0"
+ },
+ "rowboat_tone5": {
+ "c": "activity",
+ "e": "🚣ðŸ¿",
+ "d": "rowboat tone 5",
+ "u": "8.0"
+ },
+ "rugby_football": {
+ "c": "activity",
+ "e": "ðŸ‰",
+ "d": "rugby football",
+ "u": "6.0"
+ },
+ "runner": {
+ "c": "people",
+ "e": "ðŸƒ",
+ "d": "runner",
+ "u": "6.0"
+ },
+ "runner_tone1": {
+ "c": "people",
+ "e": "ðŸƒðŸ»",
+ "d": "runner tone 1",
+ "u": "8.0"
+ },
+ "runner_tone2": {
+ "c": "people",
+ "e": "ðŸƒðŸ¼",
+ "d": "runner tone 2",
+ "u": "8.0"
+ },
+ "runner_tone3": {
+ "c": "people",
+ "e": "ðŸƒðŸ½",
+ "d": "runner tone 3",
+ "u": "8.0"
+ },
+ "runner_tone4": {
+ "c": "people",
+ "e": "ðŸƒðŸ¾",
+ "d": "runner tone 4",
+ "u": "8.0"
+ },
+ "runner_tone5": {
+ "c": "people",
+ "e": "ðŸƒðŸ¿",
+ "d": "runner tone 5",
+ "u": "8.0"
+ },
+ "running_shirt_with_sash": {
+ "c": "activity",
+ "e": "🎽",
+ "d": "running shirt with sash",
+ "u": "6.0"
+ },
+ "sa": {
+ "c": "symbols",
+ "e": "🈂",
+ "d": "squared katakana sa",
+ "u": "6.0"
+ },
+ "sagittarius": {
+ "c": "symbols",
+ "e": "â™",
+ "d": "sagittarius",
+ "u": "1.1"
+ },
+ "sailboat": {
+ "c": "travel",
+ "e": "⛵",
+ "d": "sailboat",
+ "u": "5.2"
+ },
+ "sake": {
+ "c": "food",
+ "e": "ðŸ¶",
+ "d": "sake bottle and cup",
+ "u": "6.0"
+ },
+ "salad": {
+ "c": "food",
+ "e": "🥗",
+ "d": "green salad",
+ "u": "9.0"
+ },
+ "sandal": {
+ "c": "people",
+ "e": "👡",
+ "d": "womans sandal",
+ "u": "6.0"
+ },
+ "santa": {
+ "c": "people",
+ "e": "🎅",
+ "d": "father christmas",
+ "u": "6.0"
+ },
+ "santa_tone1": {
+ "c": "people",
+ "e": "🎅ðŸ»",
+ "d": "father christmas tone 1",
+ "u": "8.0"
+ },
+ "santa_tone2": {
+ "c": "people",
+ "e": "🎅ðŸ¼",
+ "d": "father christmas tone 2",
+ "u": "8.0"
+ },
+ "santa_tone3": {
+ "c": "people",
+ "e": "🎅ðŸ½",
+ "d": "father christmas tone 3",
+ "u": "8.0"
+ },
+ "santa_tone4": {
+ "c": "people",
+ "e": "🎅ðŸ¾",
+ "d": "father christmas tone 4",
+ "u": "8.0"
+ },
+ "santa_tone5": {
+ "c": "people",
+ "e": "🎅ðŸ¿",
+ "d": "father christmas tone 5",
+ "u": "8.0"
+ },
+ "satellite": {
+ "c": "objects",
+ "e": "📡",
+ "d": "satellite antenna",
+ "u": "6.0"
+ },
+ "satellite_orbital": {
+ "c": "travel",
+ "e": "🛰",
+ "d": "satellite",
+ "u": "7.0"
+ },
+ "saxophone": {
+ "c": "activity",
+ "e": "🎷",
+ "d": "saxophone",
+ "u": "6.0"
+ },
+ "scales": {
+ "c": "objects",
+ "e": "âš–",
+ "d": "scales",
+ "u": "4.1"
+ },
+ "school": {
+ "c": "travel",
+ "e": "ðŸ«",
+ "d": "school",
+ "u": "6.0"
+ },
+ "school_satchel": {
+ "c": "people",
+ "e": "🎒",
+ "d": "school satchel",
+ "u": "6.0"
+ },
+ "scissors": {
+ "c": "objects",
+ "e": "✂",
+ "d": "black scissors",
+ "u": "1.1"
+ },
+ "scooter": {
+ "c": "travel",
+ "e": "🛴",
+ "d": "scooter",
+ "u": "9.0"
+ },
+ "scorpion": {
+ "c": "nature",
+ "e": "🦂",
+ "d": "scorpion",
+ "u": "8.0"
+ },
+ "scorpius": {
+ "c": "symbols",
+ "e": "â™",
+ "d": "scorpius",
+ "u": "1.1"
+ },
+ "scream": {
+ "c": "people",
+ "e": "😱",
+ "d": "face screaming in fear",
+ "u": "6.0"
+ },
+ "scream_cat": {
+ "c": "people",
+ "e": "🙀",
+ "d": "weary cat face",
+ "u": "6.0"
+ },
+ "scroll": {
+ "c": "objects",
+ "e": "📜",
+ "d": "scroll",
+ "u": "6.0"
+ },
+ "seat": {
+ "c": "travel",
+ "e": "💺",
+ "d": "seat",
+ "u": "6.0"
+ },
+ "second_place": {
+ "c": "activity",
+ "e": "🥈",
+ "d": "second place medal",
+ "u": "9.0"
+ },
+ "secret": {
+ "c": "symbols",
+ "e": "㊙",
+ "d": "circled ideograph secret",
+ "u": "1.1"
+ },
+ "see_no_evil": {
+ "c": "nature",
+ "e": "🙈",
+ "d": "see-no-evil monkey",
+ "u": "6.0"
+ },
+ "seedling": {
+ "c": "nature",
+ "e": "🌱",
+ "d": "seedling",
+ "u": "6.0"
+ },
+ "selfie": {
+ "c": "people",
+ "e": "🤳",
+ "d": "selfie",
+ "u": "9.0"
+ },
+ "selfie_tone1": {
+ "c": "people",
+ "e": "🤳ðŸ»",
+ "d": "selfie tone 1",
+ "u": "9.0"
+ },
+ "selfie_tone2": {
+ "c": "people",
+ "e": "🤳ðŸ¼",
+ "d": "selfie tone 2",
+ "u": "9.0"
+ },
+ "selfie_tone3": {
+ "c": "people",
+ "e": "🤳ðŸ½",
+ "d": "selfie tone 3",
+ "u": "9.0"
+ },
+ "selfie_tone4": {
+ "c": "people",
+ "e": "🤳ðŸ¾",
+ "d": "selfie tone 4",
+ "u": "9.0"
+ },
+ "selfie_tone5": {
+ "c": "people",
+ "e": "🤳ðŸ¿",
+ "d": "selfie tone 5",
+ "u": "9.0"
+ },
+ "seven": {
+ "c": "symbols",
+ "e": "7ï¸âƒ£",
+ "d": "keycap digit seven",
+ "u": "3.0"
+ },
+ "shallow_pan_of_food": {
+ "c": "food",
+ "e": "🥘",
+ "d": "shallow pan of food",
+ "u": "9.0"
+ },
+ "shamrock": {
+ "c": "nature",
+ "e": "☘",
+ "d": "shamrock",
+ "u": "4.1"
+ },
+ "shark": {
+ "c": "nature",
+ "e": "🦈",
+ "d": "shark",
+ "u": "9.0"
+ },
+ "shaved_ice": {
+ "c": "food",
+ "e": "ðŸ§",
+ "d": "shaved ice",
+ "u": "6.0"
+ },
+ "sheep": {
+ "c": "nature",
+ "e": "ðŸ‘",
+ "d": "sheep",
+ "u": "6.0"
+ },
+ "shell": {
+ "c": "nature",
+ "e": "ðŸš",
+ "d": "spiral shell",
+ "u": "6.0"
+ },
+ "shield": {
+ "c": "objects",
+ "e": "🛡",
+ "d": "shield",
+ "u": "7.0"
+ },
+ "shinto_shrine": {
+ "c": "travel",
+ "e": "⛩",
+ "d": "shinto shrine",
+ "u": "5.2"
+ },
+ "ship": {
+ "c": "travel",
+ "e": "🚢",
+ "d": "ship",
+ "u": "6.0"
+ },
+ "shirt": {
+ "c": "people",
+ "e": "👕",
+ "d": "t-shirt",
+ "u": "6.0"
+ },
+ "shopping_bags": {
+ "c": "objects",
+ "e": "ðŸ›",
+ "d": "shopping bags",
+ "u": "7.0"
+ },
+ "shopping_cart": {
+ "c": "objects",
+ "e": "🛒",
+ "d": "shopping trolley",
+ "u": "9.0"
+ },
+ "shower": {
+ "c": "objects",
+ "e": "🚿",
+ "d": "shower",
+ "u": "6.0"
+ },
+ "shrimp": {
+ "c": "nature",
+ "e": "ðŸ¦",
+ "d": "shrimp",
+ "u": "9.0"
+ },
+ "shrug": {
+ "c": "people",
+ "e": "🤷",
+ "d": "shrug",
+ "u": "9.0"
+ },
+ "shrug_tone1": {
+ "c": "people",
+ "e": "🤷ðŸ»",
+ "d": "shrug tone 1",
+ "u": "9.0"
+ },
+ "shrug_tone2": {
+ "c": "people",
+ "e": "🤷ðŸ¼",
+ "d": "shrug tone 2",
+ "u": "9.0"
+ },
+ "shrug_tone3": {
+ "c": "people",
+ "e": "🤷ðŸ½",
+ "d": "shrug tone 3",
+ "u": "9.0"
+ },
+ "shrug_tone4": {
+ "c": "people",
+ "e": "🤷ðŸ¾",
+ "d": "shrug tone 4",
+ "u": "9.0"
+ },
+ "shrug_tone5": {
+ "c": "people",
+ "e": "🤷ðŸ¿",
+ "d": "shrug tone 5",
+ "u": "9.0"
+ },
+ "signal_strength": {
+ "c": "symbols",
+ "e": "📶",
+ "d": "antenna with bars",
+ "u": "6.0"
+ },
+ "six": {
+ "c": "symbols",
+ "e": "6ï¸âƒ£",
+ "d": "keycap digit six",
+ "u": "3.0"
+ },
+ "six_pointed_star": {
+ "c": "symbols",
+ "e": "🔯",
+ "d": "six pointed star with middle dot",
+ "u": "6.0"
+ },
+ "ski": {
+ "c": "activity",
+ "e": "🎿",
+ "d": "ski and ski boot",
+ "u": "6.0"
+ },
+ "skier": {
+ "c": "activity",
+ "e": "â›·",
+ "d": "skier",
+ "u": "5.2"
+ },
+ "skull": {
+ "c": "people",
+ "e": "💀",
+ "d": "skull",
+ "u": "6.0"
+ },
+ "skull_crossbones": {
+ "c": "objects",
+ "e": "☠",
+ "d": "skull and crossbones",
+ "u": "1.1"
+ },
+ "sleeping": {
+ "c": "people",
+ "e": "😴",
+ "d": "sleeping face",
+ "u": "6.1"
+ },
+ "sleeping_accommodation": {
+ "c": "objects",
+ "e": "🛌",
+ "d": "sleeping accommodation",
+ "u": "7.0"
+ },
+ "sleepy": {
+ "c": "people",
+ "e": "😪",
+ "d": "sleepy face",
+ "u": "6.0"
+ },
+ "slight_frown": {
+ "c": "people",
+ "e": "ðŸ™",
+ "d": "slightly frowning face",
+ "u": "7.0"
+ },
+ "slight_smile": {
+ "c": "people",
+ "e": "🙂",
+ "d": "slightly smiling face",
+ "u": "7.0"
+ },
+ "slot_machine": {
+ "c": "activity",
+ "e": "🎰",
+ "d": "slot machine",
+ "u": "6.0"
+ },
+ "small_blue_diamond": {
+ "c": "symbols",
+ "e": "🔹",
+ "d": "small blue diamond",
+ "u": "6.0"
+ },
+ "small_orange_diamond": {
+ "c": "symbols",
+ "e": "🔸",
+ "d": "small orange diamond",
+ "u": "6.0"
+ },
+ "small_red_triangle": {
+ "c": "symbols",
+ "e": "🔺",
+ "d": "up-pointing red triangle",
+ "u": "6.0"
+ },
+ "small_red_triangle_down": {
+ "c": "symbols",
+ "e": "🔻",
+ "d": "down-pointing red triangle",
+ "u": "6.0"
+ },
+ "smile": {
+ "c": "people",
+ "e": "😄",
+ "d": "smiling face with open mouth and smiling eyes",
+ "u": "6.0"
+ },
+ "smile_cat": {
+ "c": "people",
+ "e": "😸",
+ "d": "grinning cat face with smiling eyes",
+ "u": "6.0"
+ },
+ "smiley": {
+ "c": "people",
+ "e": "😃",
+ "d": "smiling face with open mouth",
+ "u": "6.0"
+ },
+ "smiley_cat": {
+ "c": "people",
+ "e": "😺",
+ "d": "smiling cat face with open mouth",
+ "u": "6.0"
+ },
+ "smiling_imp": {
+ "c": "people",
+ "e": "😈",
+ "d": "smiling face with horns",
+ "u": "6.0"
+ },
+ "smirk": {
+ "c": "people",
+ "e": "ðŸ˜",
+ "d": "smirking face",
+ "u": "6.0"
+ },
+ "smirk_cat": {
+ "c": "people",
+ "e": "😼",
+ "d": "cat face with wry smile",
+ "u": "6.0"
+ },
+ "smoking": {
+ "c": "objects",
+ "e": "🚬",
+ "d": "smoking symbol",
+ "u": "6.0"
+ },
+ "snail": {
+ "c": "nature",
+ "e": "ðŸŒ",
+ "d": "snail",
+ "u": "6.0"
+ },
+ "snake": {
+ "c": "nature",
+ "e": "ðŸ",
+ "d": "snake",
+ "u": "6.0"
+ },
+ "sneezing_face": {
+ "c": "people",
+ "e": "🤧",
+ "d": "sneezing face",
+ "u": "9.0"
+ },
+ "snowboarder": {
+ "c": "activity",
+ "e": "ðŸ‚",
+ "d": "snowboarder",
+ "u": "6.0"
+ },
+ "snowflake": {
+ "c": "nature",
+ "e": "â„",
+ "d": "snowflake",
+ "u": "1.1"
+ },
+ "snowman": {
+ "c": "nature",
+ "e": "⛄",
+ "d": "snowman without snow",
+ "u": "5.2"
+ },
+ "snowman2": {
+ "c": "nature",
+ "e": "☃",
+ "d": "snowman",
+ "u": "1.1"
+ },
+ "sob": {
+ "c": "people",
+ "e": "😭",
+ "d": "loudly crying face",
+ "u": "6.0"
+ },
+ "soccer": {
+ "c": "activity",
+ "e": "âš½",
+ "d": "soccer ball",
+ "u": "5.2"
+ },
+ "soon": {
+ "c": "symbols",
+ "e": "🔜",
+ "d": "soon with rightwards arrow above",
+ "u": "6.0"
+ },
+ "sos": {
+ "c": "symbols",
+ "e": "🆘",
+ "d": "squared sos",
+ "u": "6.0"
+ },
+ "sound": {
+ "c": "symbols",
+ "e": "🔉",
+ "d": "speaker with one sound wave",
+ "u": "6.0"
+ },
+ "space_invader": {
+ "c": "activity",
+ "e": "👾",
+ "d": "alien monster",
+ "u": "6.0"
+ },
+ "spades": {
+ "c": "symbols",
+ "e": "â™ ",
+ "d": "black spade suit",
+ "u": "1.1"
+ },
+ "spaghetti": {
+ "c": "food",
+ "e": "ðŸ",
+ "d": "spaghetti",
+ "u": "6.0"
+ },
+ "sparkle": {
+ "c": "symbols",
+ "e": "â‡",
+ "d": "sparkle",
+ "u": "1.1"
+ },
+ "sparkler": {
+ "c": "travel",
+ "e": "🎇",
+ "d": "firework sparkler",
+ "u": "6.0"
+ },
+ "sparkles": {
+ "c": "nature",
+ "e": "✨",
+ "d": "sparkles",
+ "u": "6.0"
+ },
+ "sparkling_heart": {
+ "c": "symbols",
+ "e": "💖",
+ "d": "sparkling heart",
+ "u": "6.0"
+ },
+ "speak_no_evil": {
+ "c": "nature",
+ "e": "🙊",
+ "d": "speak-no-evil monkey",
+ "u": "6.0"
+ },
+ "speaker": {
+ "c": "symbols",
+ "e": "🔈",
+ "d": "speaker",
+ "u": "6.0"
+ },
+ "speaking_head": {
+ "c": "people",
+ "e": "🗣",
+ "d": "speaking head in silhouette",
+ "u": "7.0"
+ },
+ "speech_balloon": {
+ "c": "symbols",
+ "e": "💬",
+ "d": "speech balloon",
+ "u": "6.0"
+ },
+ "speech_left": {
+ "c": "symbols",
+ "e": "🗨",
+ "d": "left speech bubble",
+ "u": "7.0"
+ },
+ "speedboat": {
+ "c": "travel",
+ "e": "🚤",
+ "d": "speedboat",
+ "u": "6.0"
+ },
+ "spider": {
+ "c": "nature",
+ "e": "🕷",
+ "d": "spider",
+ "u": "7.0"
+ },
+ "spider_web": {
+ "c": "nature",
+ "e": "🕸",
+ "d": "spider web",
+ "u": "7.0"
+ },
+ "spoon": {
+ "c": "food",
+ "e": "🥄",
+ "d": "spoon",
+ "u": "9.0"
+ },
+ "spy": {
+ "c": "people",
+ "e": "🕵",
+ "d": "sleuth or spy",
+ "u": "7.0"
+ },
+ "spy_tone1": {
+ "c": "people",
+ "e": "🕵ðŸ»",
+ "d": "sleuth or spy tone 1",
+ "u": "8.0"
+ },
+ "spy_tone2": {
+ "c": "people",
+ "e": "🕵ðŸ¼",
+ "d": "sleuth or spy tone 2",
+ "u": "8.0"
+ },
+ "spy_tone3": {
+ "c": "people",
+ "e": "🕵ðŸ½",
+ "d": "sleuth or spy tone 3",
+ "u": "8.0"
+ },
+ "spy_tone4": {
+ "c": "people",
+ "e": "🕵ðŸ¾",
+ "d": "sleuth or spy tone 4",
+ "u": "8.0"
+ },
+ "spy_tone5": {
+ "c": "people",
+ "e": "🕵ðŸ¿",
+ "d": "sleuth or spy tone 5",
+ "u": "8.0"
+ },
+ "squid": {
+ "c": "nature",
+ "e": "🦑",
+ "d": "squid",
+ "u": "9.0"
+ },
+ "stadium": {
+ "c": "travel",
+ "e": "ðŸŸ",
+ "d": "stadium",
+ "u": "7.0"
+ },
+ "star": {
+ "c": "nature",
+ "e": "â­",
+ "d": "white medium star",
+ "u": "5.1"
+ },
+ "star2": {
+ "c": "nature",
+ "e": "🌟",
+ "d": "glowing star",
+ "u": "6.0"
+ },
+ "star_and_crescent": {
+ "c": "symbols",
+ "e": "☪",
+ "d": "star and crescent",
+ "u": "1.1"
+ },
+ "star_of_david": {
+ "c": "symbols",
+ "e": "✡",
+ "d": "star of david",
+ "u": "1.1"
+ },
+ "stars": {
+ "c": "travel",
+ "e": "🌠",
+ "d": "shooting star",
+ "u": "6.0"
+ },
+ "station": {
+ "c": "travel",
+ "e": "🚉",
+ "d": "station",
+ "u": "6.0"
+ },
+ "statue_of_liberty": {
+ "c": "travel",
+ "e": "🗽",
+ "d": "statue of liberty",
+ "u": "6.0"
+ },
+ "steam_locomotive": {
+ "c": "travel",
+ "e": "🚂",
+ "d": "steam locomotive",
+ "u": "6.0"
+ },
+ "stew": {
+ "c": "food",
+ "e": "ðŸ²",
+ "d": "pot of food",
+ "u": "6.0"
+ },
+ "stop_button": {
+ "c": "symbols",
+ "e": "â¹",
+ "d": "black square for stop",
+ "u": "7.0"
+ },
+ "stopwatch": {
+ "c": "objects",
+ "e": "â±",
+ "d": "stopwatch",
+ "u": "6.0"
+ },
+ "straight_ruler": {
+ "c": "objects",
+ "e": "ðŸ“",
+ "d": "straight ruler",
+ "u": "6.0"
+ },
+ "strawberry": {
+ "c": "food",
+ "e": "ðŸ“",
+ "d": "strawberry",
+ "u": "6.0"
+ },
+ "stuck_out_tongue": {
+ "c": "people",
+ "e": "😛",
+ "d": "face with stuck-out tongue",
+ "u": "6.1"
+ },
+ "stuck_out_tongue_closed_eyes": {
+ "c": "people",
+ "e": "ðŸ˜",
+ "d": "face with stuck-out tongue and tightly-closed eyes",
+ "u": "6.0"
+ },
+ "stuck_out_tongue_winking_eye": {
+ "c": "people",
+ "e": "😜",
+ "d": "face with stuck-out tongue and winking eye",
+ "u": "6.0"
+ },
+ "stuffed_flatbread": {
+ "c": "food",
+ "e": "🥙",
+ "d": "stuffed flatbread",
+ "u": "9.0"
+ },
+ "sun_with_face": {
+ "c": "nature",
+ "e": "🌞",
+ "d": "sun with face",
+ "u": "6.0"
+ },
+ "sunflower": {
+ "c": "nature",
+ "e": "🌻",
+ "d": "sunflower",
+ "u": "6.0"
+ },
+ "sunglasses": {
+ "c": "people",
+ "e": "😎",
+ "d": "smiling face with sunglasses",
+ "u": "6.0"
+ },
+ "sunny": {
+ "c": "nature",
+ "e": "☀",
+ "d": "black sun with rays",
+ "u": "1.1"
+ },
+ "sunrise": {
+ "c": "travel",
+ "e": "🌅",
+ "d": "sunrise",
+ "u": "6.0"
+ },
+ "sunrise_over_mountains": {
+ "c": "travel",
+ "e": "🌄",
+ "d": "sunrise over mountains",
+ "u": "6.0"
+ },
+ "surfer": {
+ "c": "activity",
+ "e": "ðŸ„",
+ "d": "surfer",
+ "u": "6.0"
+ },
+ "surfer_tone1": {
+ "c": "activity",
+ "e": "ðŸ„ðŸ»",
+ "d": "surfer tone 1",
+ "u": "8.0"
+ },
+ "surfer_tone2": {
+ "c": "activity",
+ "e": "ðŸ„ðŸ¼",
+ "d": "surfer tone 2",
+ "u": "8.0"
+ },
+ "surfer_tone3": {
+ "c": "activity",
+ "e": "ðŸ„ðŸ½",
+ "d": "surfer tone 3",
+ "u": "8.0"
+ },
+ "surfer_tone4": {
+ "c": "activity",
+ "e": "ðŸ„ðŸ¾",
+ "d": "surfer tone 4",
+ "u": "8.0"
+ },
+ "surfer_tone5": {
+ "c": "activity",
+ "e": "ðŸ„ðŸ¿",
+ "d": "surfer tone 5",
+ "u": "8.0"
+ },
+ "sushi": {
+ "c": "food",
+ "e": "ðŸ£",
+ "d": "sushi",
+ "u": "6.0"
+ },
+ "suspension_railway": {
+ "c": "travel",
+ "e": "🚟",
+ "d": "suspension railway",
+ "u": "6.0"
+ },
+ "sweat": {
+ "c": "people",
+ "e": "😓",
+ "d": "face with cold sweat",
+ "u": "6.0"
+ },
+ "sweat_drops": {
+ "c": "nature",
+ "e": "💦",
+ "d": "splashing sweat symbol",
+ "u": "6.0"
+ },
+ "sweat_smile": {
+ "c": "people",
+ "e": "😅",
+ "d": "smiling face with open mouth and cold sweat",
+ "u": "6.0"
+ },
+ "sweet_potato": {
+ "c": "food",
+ "e": "ðŸ ",
+ "d": "roasted sweet potato",
+ "u": "6.0"
+ },
+ "swimmer": {
+ "c": "activity",
+ "e": "ðŸŠ",
+ "d": "swimmer",
+ "u": "6.0"
+ },
+ "swimmer_tone1": {
+ "c": "activity",
+ "e": "ðŸŠðŸ»",
+ "d": "swimmer tone 1",
+ "u": "8.0"
+ },
+ "swimmer_tone2": {
+ "c": "activity",
+ "e": "ðŸŠðŸ¼",
+ "d": "swimmer tone 2",
+ "u": "8.0"
+ },
+ "swimmer_tone3": {
+ "c": "activity",
+ "e": "ðŸŠðŸ½",
+ "d": "swimmer tone 3",
+ "u": "8.0"
+ },
+ "swimmer_tone4": {
+ "c": "activity",
+ "e": "ðŸŠðŸ¾",
+ "d": "swimmer tone 4",
+ "u": "8.0"
+ },
+ "swimmer_tone5": {
+ "c": "activity",
+ "e": "ðŸŠðŸ¿",
+ "d": "swimmer tone 5",
+ "u": "8.0"
+ },
+ "symbols": {
+ "c": "symbols",
+ "e": "🔣",
+ "d": "input symbol for symbols",
+ "u": "6.0"
+ },
+ "synagogue": {
+ "c": "travel",
+ "e": "ðŸ•",
+ "d": "synagogue",
+ "u": "8.0"
+ },
+ "syringe": {
+ "c": "objects",
+ "e": "💉",
+ "d": "syringe",
+ "u": "6.0"
+ },
+ "taco": {
+ "c": "food",
+ "e": "🌮",
+ "d": "taco",
+ "u": "8.0"
+ },
+ "tada": {
+ "c": "objects",
+ "e": "🎉",
+ "d": "party popper",
+ "u": "6.0"
+ },
+ "tanabata_tree": {
+ "c": "nature",
+ "e": "🎋",
+ "d": "tanabata tree",
+ "u": "6.0"
+ },
+ "tangerine": {
+ "c": "food",
+ "e": "ðŸŠ",
+ "d": "tangerine",
+ "u": "6.0"
+ },
+ "taurus": {
+ "c": "symbols",
+ "e": "♉",
+ "d": "taurus",
+ "u": "1.1"
+ },
+ "taxi": {
+ "c": "travel",
+ "e": "🚕",
+ "d": "taxi",
+ "u": "6.0"
+ },
+ "tea": {
+ "c": "food",
+ "e": "ðŸµ",
+ "d": "teacup without handle",
+ "u": "6.0"
+ },
+ "telephone": {
+ "c": "objects",
+ "e": "☎",
+ "d": "black telephone",
+ "u": "1.1"
+ },
+ "telephone_receiver": {
+ "c": "objects",
+ "e": "📞",
+ "d": "telephone receiver",
+ "u": "6.0"
+ },
+ "telescope": {
+ "c": "objects",
+ "e": "🔭",
+ "d": "telescope",
+ "u": "6.0"
+ },
+ "ten": {
+ "c": "symbols",
+ "e": "🔟",
+ "d": "keycap ten",
+ "u": "6.0"
+ },
+ "tennis": {
+ "c": "activity",
+ "e": "🎾",
+ "d": "tennis racquet and ball",
+ "u": "6.0"
+ },
+ "tent": {
+ "c": "travel",
+ "e": "⛺",
+ "d": "tent",
+ "u": "5.2"
+ },
+ "thermometer": {
+ "c": "objects",
+ "e": "🌡",
+ "d": "thermometer",
+ "u": "7.0"
+ },
+ "thermometer_face": {
+ "c": "people",
+ "e": "🤒",
+ "d": "face with thermometer",
+ "u": "8.0"
+ },
+ "thinking": {
+ "c": "people",
+ "e": "🤔",
+ "d": "thinking face",
+ "u": "8.0"
+ },
+ "third_place": {
+ "c": "activity",
+ "e": "🥉",
+ "d": "third place medal",
+ "u": "9.0"
+ },
+ "thought_balloon": {
+ "c": "symbols",
+ "e": "💭",
+ "d": "thought balloon",
+ "u": "6.0"
+ },
+ "three": {
+ "c": "symbols",
+ "e": "3ï¸âƒ£",
+ "d": "keycap digit three",
+ "u": "3.0"
+ },
+ "thumbsdown": {
+ "c": "people",
+ "e": "👎",
+ "d": "thumbs down sign",
+ "u": "6.0"
+ },
+ "thumbsdown_tone1": {
+ "c": "people",
+ "e": "👎ðŸ»",
+ "d": "thumbs down sign tone 1",
+ "u": "8.0"
+ },
+ "thumbsdown_tone2": {
+ "c": "people",
+ "e": "👎ðŸ¼",
+ "d": "thumbs down sign tone 2",
+ "u": "8.0"
+ },
+ "thumbsdown_tone3": {
+ "c": "people",
+ "e": "👎ðŸ½",
+ "d": "thumbs down sign tone 3",
+ "u": "8.0"
+ },
+ "thumbsdown_tone4": {
+ "c": "people",
+ "e": "👎ðŸ¾",
+ "d": "thumbs down sign tone 4",
+ "u": "8.0"
+ },
+ "thumbsdown_tone5": {
+ "c": "people",
+ "e": "👎ðŸ¿",
+ "d": "thumbs down sign tone 5",
+ "u": "8.0"
+ },
+ "thumbsup": {
+ "c": "people",
+ "e": "ðŸ‘",
+ "d": "thumbs up sign",
+ "u": "6.0"
+ },
+ "thumbsup_tone1": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ»",
+ "d": "thumbs up sign tone 1",
+ "u": "8.0"
+ },
+ "thumbsup_tone2": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¼",
+ "d": "thumbs up sign tone 2",
+ "u": "8.0"
+ },
+ "thumbsup_tone3": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ½",
+ "d": "thumbs up sign tone 3",
+ "u": "8.0"
+ },
+ "thumbsup_tone4": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¾",
+ "d": "thumbs up sign tone 4",
+ "u": "8.0"
+ },
+ "thumbsup_tone5": {
+ "c": "people",
+ "e": "ðŸ‘ðŸ¿",
+ "d": "thumbs up sign tone 5",
+ "u": "8.0"
+ },
+ "thunder_cloud_rain": {
+ "c": "nature",
+ "e": "⛈",
+ "d": "thunder cloud and rain",
+ "u": "5.2"
+ },
+ "ticket": {
+ "c": "activity",
+ "e": "🎫",
+ "d": "ticket",
+ "u": "6.0"
+ },
+ "tickets": {
+ "c": "activity",
+ "e": "🎟",
+ "d": "admission tickets",
+ "u": "7.0"
+ },
+ "tiger": {
+ "c": "nature",
+ "e": "ðŸ¯",
+ "d": "tiger face",
+ "u": "6.0"
+ },
+ "tiger2": {
+ "c": "nature",
+ "e": "ðŸ…",
+ "d": "tiger",
+ "u": "6.0"
+ },
+ "timer": {
+ "c": "objects",
+ "e": "â²",
+ "d": "timer clock",
+ "u": "6.0"
+ },
+ "tired_face": {
+ "c": "people",
+ "e": "😫",
+ "d": "tired face",
+ "u": "6.0"
+ },
+ "tm": {
+ "c": "symbols",
+ "e": "â„¢",
+ "d": "trade mark sign",
+ "u": "1.1"
+ },
+ "toilet": {
+ "c": "objects",
+ "e": "🚽",
+ "d": "toilet",
+ "u": "6.0"
+ },
+ "tokyo_tower": {
+ "c": "travel",
+ "e": "🗼",
+ "d": "tokyo tower",
+ "u": "6.0"
+ },
+ "tomato": {
+ "c": "food",
+ "e": "ðŸ…",
+ "d": "tomato",
+ "u": "6.0"
+ },
+ "tone1": {
+ "c": "modifier",
+ "e": "ðŸ»",
+ "d": "emoji modifier Fitzpatrick type-1-2",
+ "u": "8.0"
+ },
+ "tone2": {
+ "c": "modifier",
+ "e": "ðŸ¼",
+ "d": "emoji modifier Fitzpatrick type-3",
+ "u": "8.0"
+ },
+ "tone3": {
+ "c": "modifier",
+ "e": "ðŸ½",
+ "d": "emoji modifier Fitzpatrick type-4",
+ "u": "8.0"
+ },
+ "tone4": {
+ "c": "modifier",
+ "e": "ðŸ¾",
+ "d": "emoji modifier Fitzpatrick type-5",
+ "u": "8.0"
+ },
+ "tone5": {
+ "c": "modifier",
+ "e": "ðŸ¿",
+ "d": "emoji modifier Fitzpatrick type-6",
+ "u": "8.0"
+ },
+ "tongue": {
+ "c": "people",
+ "e": "👅",
+ "d": "tongue",
+ "u": "6.0"
+ },
+ "tools": {
+ "c": "objects",
+ "e": "🛠",
+ "d": "hammer and wrench",
+ "u": "7.0"
+ },
+ "top": {
+ "c": "symbols",
+ "e": "ðŸ”",
+ "d": "top with upwards arrow above",
+ "u": "6.0"
+ },
+ "tophat": {
+ "c": "people",
+ "e": "🎩",
+ "d": "top hat",
+ "u": "6.0"
+ },
+ "track_next": {
+ "c": "symbols",
+ "e": "â­",
+ "d": "black right-pointing double triangle with vertical bar",
+ "u": "6.0"
+ },
+ "track_previous": {
+ "c": "symbols",
+ "e": "â®",
+ "d": "black left-pointing double triangle with vertical bar",
+ "u": "6.0"
+ },
+ "trackball": {
+ "c": "objects",
+ "e": "🖲",
+ "d": "trackball",
+ "u": "7.0"
+ },
+ "tractor": {
+ "c": "travel",
+ "e": "🚜",
+ "d": "tractor",
+ "u": "6.0"
+ },
+ "traffic_light": {
+ "c": "travel",
+ "e": "🚥",
+ "d": "horizontal traffic light",
+ "u": "6.0"
+ },
+ "train": {
+ "c": "travel",
+ "e": "🚋",
+ "d": "Tram Car",
+ "u": "6.0"
+ },
+ "train2": {
+ "c": "travel",
+ "e": "🚆",
+ "d": "train",
+ "u": "6.0"
+ },
+ "tram": {
+ "c": "travel",
+ "e": "🚊",
+ "d": "tram",
+ "u": "6.0"
+ },
+ "triangular_flag_on_post": {
+ "c": "objects",
+ "e": "🚩",
+ "d": "triangular flag on post",
+ "u": "6.0"
+ },
+ "triangular_ruler": {
+ "c": "objects",
+ "e": "ðŸ“",
+ "d": "triangular ruler",
+ "u": "6.0"
+ },
+ "trident": {
+ "c": "symbols",
+ "e": "🔱",
+ "d": "trident emblem",
+ "u": "6.0"
+ },
+ "triumph": {
+ "c": "people",
+ "e": "😤",
+ "d": "face with look of triumph",
+ "u": "6.0"
+ },
+ "trolleybus": {
+ "c": "travel",
+ "e": "🚎",
+ "d": "trolleybus",
+ "u": "6.0"
+ },
+ "trophy": {
+ "c": "activity",
+ "e": "ðŸ†",
+ "d": "trophy",
+ "u": "6.0"
+ },
+ "tropical_drink": {
+ "c": "food",
+ "e": "ðŸ¹",
+ "d": "tropical drink",
+ "u": "6.0"
+ },
+ "tropical_fish": {
+ "c": "nature",
+ "e": "ðŸ ",
+ "d": "tropical fish",
+ "u": "6.0"
+ },
+ "truck": {
+ "c": "travel",
+ "e": "🚚",
+ "d": "delivery truck",
+ "u": "6.0"
+ },
+ "trumpet": {
+ "c": "activity",
+ "e": "🎺",
+ "d": "trumpet",
+ "u": "6.0"
+ },
+ "tulip": {
+ "c": "nature",
+ "e": "🌷",
+ "d": "tulip",
+ "u": "6.0"
+ },
+ "tumbler_glass": {
+ "c": "food",
+ "e": "🥃",
+ "d": "tumbler glass",
+ "u": "9.0"
+ },
+ "turkey": {
+ "c": "nature",
+ "e": "🦃",
+ "d": "turkey",
+ "u": "8.0"
+ },
+ "turtle": {
+ "c": "nature",
+ "e": "ðŸ¢",
+ "d": "turtle",
+ "u": "6.0"
+ },
+ "tv": {
+ "c": "objects",
+ "e": "📺",
+ "d": "television",
+ "u": "6.0"
+ },
+ "twisted_rightwards_arrows": {
+ "c": "symbols",
+ "e": "🔀",
+ "d": "twisted rightwards arrows",
+ "u": "6.0"
+ },
+ "two": {
+ "c": "symbols",
+ "e": "2ï¸âƒ£",
+ "d": "keycap digit two",
+ "u": "3.0"
+ },
+ "two_hearts": {
+ "c": "symbols",
+ "e": "💕",
+ "d": "two hearts",
+ "u": "6.0"
+ },
+ "two_men_holding_hands": {
+ "c": "people",
+ "e": "👬",
+ "d": "two men holding hands",
+ "u": "6.0"
+ },
+ "two_women_holding_hands": {
+ "c": "people",
+ "e": "👭",
+ "d": "two women holding hands",
+ "u": "6.0"
+ },
+ "u5272": {
+ "c": "symbols",
+ "e": "🈹",
+ "d": "squared cjk unified ideograph-5272",
+ "u": "6.0"
+ },
+ "u5408": {
+ "c": "symbols",
+ "e": "🈴",
+ "d": "squared cjk unified ideograph-5408",
+ "u": "6.0"
+ },
+ "u55b6": {
+ "c": "symbols",
+ "e": "🈺",
+ "d": "squared cjk unified ideograph-55b6",
+ "u": "6.0"
+ },
+ "u6307": {
+ "c": "symbols",
+ "e": "🈯",
+ "d": "squared cjk unified ideograph-6307",
+ "u": "5.2"
+ },
+ "u6708": {
+ "c": "symbols",
+ "e": "🈷",
+ "d": "squared cjk unified ideograph-6708",
+ "u": "6.0"
+ },
+ "u6709": {
+ "c": "symbols",
+ "e": "🈶",
+ "d": "squared cjk unified ideograph-6709",
+ "u": "6.0"
+ },
+ "u6e80": {
+ "c": "symbols",
+ "e": "🈵",
+ "d": "squared cjk unified ideograph-6e80",
+ "u": "6.0"
+ },
+ "u7121": {
+ "c": "symbols",
+ "e": "🈚",
+ "d": "squared cjk unified ideograph-7121",
+ "u": "5.2"
+ },
+ "u7533": {
+ "c": "symbols",
+ "e": "🈸",
+ "d": "squared cjk unified ideograph-7533",
+ "u": "6.0"
+ },
+ "u7981": {
+ "c": "symbols",
+ "e": "🈲",
+ "d": "squared cjk unified ideograph-7981",
+ "u": "6.0"
+ },
+ "u7a7a": {
+ "c": "symbols",
+ "e": "🈳",
+ "d": "squared cjk unified ideograph-7a7a",
+ "u": "6.0"
+ },
+ "umbrella": {
+ "c": "nature",
+ "e": "☔",
+ "d": "umbrella with rain drops",
+ "u": "4.0"
+ },
+ "umbrella2": {
+ "c": "nature",
+ "e": "☂",
+ "d": "umbrella",
+ "u": "1.1"
+ },
+ "unamused": {
+ "c": "people",
+ "e": "😒",
+ "d": "unamused face",
+ "u": "6.0"
+ },
+ "underage": {
+ "c": "symbols",
+ "e": "🔞",
+ "d": "no one under eighteen symbol",
+ "u": "6.0"
+ },
+ "unicorn": {
+ "c": "nature",
+ "e": "🦄",
+ "d": "unicorn face",
+ "u": "8.0"
+ },
+ "unlock": {
+ "c": "objects",
+ "e": "🔓",
+ "d": "open lock",
+ "u": "6.0"
+ },
+ "up": {
+ "c": "symbols",
+ "e": "🆙",
+ "d": "squared up with exclamation mark",
+ "u": "6.0"
+ },
+ "upside_down": {
+ "c": "people",
+ "e": "🙃",
+ "d": "upside-down face",
+ "u": "8.0"
+ },
+ "urn": {
+ "c": "objects",
+ "e": "âš±",
+ "d": "funeral urn",
+ "u": "4.1"
+ },
+ "v": {
+ "c": "people",
+ "e": "✌",
+ "d": "victory hand",
+ "u": "1.1"
+ },
+ "v_tone1": {
+ "c": "people",
+ "e": "✌ðŸ»",
+ "d": "victory hand tone 1",
+ "u": "8.0"
+ },
+ "v_tone2": {
+ "c": "people",
+ "e": "✌ðŸ¼",
+ "d": "victory hand tone 2",
+ "u": "8.0"
+ },
+ "v_tone3": {
+ "c": "people",
+ "e": "✌ðŸ½",
+ "d": "victory hand tone 3",
+ "u": "8.0"
+ },
+ "v_tone4": {
+ "c": "people",
+ "e": "✌ðŸ¾",
+ "d": "victory hand tone 4",
+ "u": "8.0"
+ },
+ "v_tone5": {
+ "c": "people",
+ "e": "✌ðŸ¿",
+ "d": "victory hand tone 5",
+ "u": "8.0"
+ },
+ "vertical_traffic_light": {
+ "c": "travel",
+ "e": "🚦",
+ "d": "vertical traffic light",
+ "u": "6.0"
+ },
+ "vhs": {
+ "c": "objects",
+ "e": "📼",
+ "d": "videocassette",
+ "u": "6.0"
+ },
+ "vibration_mode": {
+ "c": "symbols",
+ "e": "📳",
+ "d": "vibration mode",
+ "u": "6.0"
+ },
+ "video_camera": {
+ "c": "objects",
+ "e": "📹",
+ "d": "video camera",
+ "u": "6.0"
+ },
+ "video_game": {
+ "c": "activity",
+ "e": "🎮",
+ "d": "video game",
+ "u": "6.0"
+ },
+ "violin": {
+ "c": "activity",
+ "e": "🎻",
+ "d": "violin",
+ "u": "6.0"
+ },
+ "virgo": {
+ "c": "symbols",
+ "e": "â™",
+ "d": "virgo",
+ "u": "1.1"
+ },
+ "volcano": {
+ "c": "travel",
+ "e": "🌋",
+ "d": "volcano",
+ "u": "6.0"
+ },
+ "volleyball": {
+ "c": "activity",
+ "e": "ðŸ",
+ "d": "volleyball",
+ "u": "8.0"
+ },
+ "vs": {
+ "c": "symbols",
+ "e": "🆚",
+ "d": "squared vs",
+ "u": "6.0"
+ },
+ "vulcan": {
+ "c": "people",
+ "e": "🖖",
+ "d": "raised hand with part between middle and ring fingers",
+ "u": "7.0"
+ },
+ "vulcan_tone1": {
+ "c": "people",
+ "e": "🖖ðŸ»",
+ "d": "raised hand with part between middle and ring fingers tone 1",
+ "u": "8.0"
+ },
+ "vulcan_tone2": {
+ "c": "people",
+ "e": "🖖ðŸ¼",
+ "d": "raised hand with part between middle and ring fingers tone 2",
+ "u": "8.0"
+ },
+ "vulcan_tone3": {
+ "c": "people",
+ "e": "🖖ðŸ½",
+ "d": "raised hand with part between middle and ring fingers tone 3",
+ "u": "8.0"
+ },
+ "vulcan_tone4": {
+ "c": "people",
+ "e": "🖖ðŸ¾",
+ "d": "raised hand with part between middle and ring fingers tone 4",
+ "u": "8.0"
+ },
+ "vulcan_tone5": {
+ "c": "people",
+ "e": "🖖ðŸ¿",
+ "d": "raised hand with part between middle and ring fingers tone 5",
+ "u": "8.0"
+ },
+ "walking": {
+ "c": "people",
+ "e": "🚶",
+ "d": "pedestrian",
+ "u": "6.0"
+ },
+ "walking_tone1": {
+ "c": "people",
+ "e": "🚶ðŸ»",
+ "d": "pedestrian tone 1",
+ "u": "8.0"
+ },
+ "walking_tone2": {
+ "c": "people",
+ "e": "🚶ðŸ¼",
+ "d": "pedestrian tone 2",
+ "u": "8.0"
+ },
+ "walking_tone3": {
+ "c": "people",
+ "e": "🚶ðŸ½",
+ "d": "pedestrian tone 3",
+ "u": "8.0"
+ },
+ "walking_tone4": {
+ "c": "people",
+ "e": "🚶ðŸ¾",
+ "d": "pedestrian tone 4",
+ "u": "8.0"
+ },
+ "walking_tone5": {
+ "c": "people",
+ "e": "🚶ðŸ¿",
+ "d": "pedestrian tone 5",
+ "u": "8.0"
+ },
+ "waning_crescent_moon": {
+ "c": "nature",
+ "e": "🌘",
+ "d": "waning crescent moon symbol",
+ "u": "6.0"
+ },
+ "waning_gibbous_moon": {
+ "c": "nature",
+ "e": "🌖",
+ "d": "waning gibbous moon symbol",
+ "u": "6.0"
+ },
+ "warning": {
+ "c": "symbols",
+ "e": "âš ",
+ "d": "warning sign",
+ "u": "4.0"
+ },
+ "wastebasket": {
+ "c": "objects",
+ "e": "🗑",
+ "d": "wastebasket",
+ "u": "7.0"
+ },
+ "watch": {
+ "c": "objects",
+ "e": "⌚",
+ "d": "watch",
+ "u": "1.1"
+ },
+ "water_buffalo": {
+ "c": "nature",
+ "e": "ðŸƒ",
+ "d": "water buffalo",
+ "u": "6.0"
+ },
+ "water_polo": {
+ "c": "activity",
+ "e": "🤽",
+ "d": "water polo",
+ "u": "9.0"
+ },
+ "water_polo_tone1": {
+ "c": "activity",
+ "e": "🤽ðŸ»",
+ "d": "water polo tone 1",
+ "u": "9.0"
+ },
+ "water_polo_tone2": {
+ "c": "activity",
+ "e": "🤽ðŸ¼",
+ "d": "water polo tone 2",
+ "u": "9.0"
+ },
+ "water_polo_tone3": {
+ "c": "activity",
+ "e": "🤽ðŸ½",
+ "d": "water polo tone 3",
+ "u": "9.0"
+ },
+ "water_polo_tone4": {
+ "c": "activity",
+ "e": "🤽ðŸ¾",
+ "d": "water polo tone 4",
+ "u": "9.0"
+ },
+ "water_polo_tone5": {
+ "c": "activity",
+ "e": "🤽ðŸ¿",
+ "d": "water polo tone 5",
+ "u": "9.0"
+ },
+ "watermelon": {
+ "c": "food",
+ "e": "ðŸ‰",
+ "d": "watermelon",
+ "u": "6.0"
+ },
+ "wave": {
+ "c": "people",
+ "e": "👋",
+ "d": "waving hand sign",
+ "u": "6.0"
+ },
+ "wave_tone1": {
+ "c": "people",
+ "e": "👋ðŸ»",
+ "d": "waving hand sign tone 1",
+ "u": "8.0"
+ },
+ "wave_tone2": {
+ "c": "people",
+ "e": "👋ðŸ¼",
+ "d": "waving hand sign tone 2",
+ "u": "8.0"
+ },
+ "wave_tone3": {
+ "c": "people",
+ "e": "👋ðŸ½",
+ "d": "waving hand sign tone 3",
+ "u": "8.0"
+ },
+ "wave_tone4": {
+ "c": "people",
+ "e": "👋ðŸ¾",
+ "d": "waving hand sign tone 4",
+ "u": "8.0"
+ },
+ "wave_tone5": {
+ "c": "people",
+ "e": "👋ðŸ¿",
+ "d": "waving hand sign tone 5",
+ "u": "8.0"
+ },
+ "wavy_dash": {
+ "c": "symbols",
+ "e": "〰",
+ "d": "wavy dash",
+ "u": "1.1"
+ },
+ "waxing_crescent_moon": {
+ "c": "nature",
+ "e": "🌒",
+ "d": "waxing crescent moon symbol",
+ "u": "6.0"
+ },
+ "waxing_gibbous_moon": {
+ "c": "nature",
+ "e": "🌔",
+ "d": "waxing gibbous moon symbol",
+ "u": "6.0"
+ },
+ "wc": {
+ "c": "symbols",
+ "e": "🚾",
+ "d": "water closet",
+ "u": "6.0"
+ },
+ "weary": {
+ "c": "people",
+ "e": "😩",
+ "d": "weary face",
+ "u": "6.0"
+ },
+ "wedding": {
+ "c": "travel",
+ "e": "💒",
+ "d": "wedding",
+ "u": "6.0"
+ },
+ "whale": {
+ "c": "nature",
+ "e": "ðŸ³",
+ "d": "spouting whale",
+ "u": "6.0"
+ },
+ "whale2": {
+ "c": "nature",
+ "e": "ðŸ‹",
+ "d": "whale",
+ "u": "6.0"
+ },
+ "wheel_of_dharma": {
+ "c": "symbols",
+ "e": "☸",
+ "d": "wheel of dharma",
+ "u": "1.1"
+ },
+ "wheelchair": {
+ "c": "symbols",
+ "e": "♿",
+ "d": "wheelchair symbol",
+ "u": "4.1"
+ },
+ "white_check_mark": {
+ "c": "symbols",
+ "e": "✅",
+ "d": "white heavy check mark",
+ "u": "6.0"
+ },
+ "white_circle": {
+ "c": "symbols",
+ "e": "⚪",
+ "d": "medium white circle",
+ "u": "4.1"
+ },
+ "white_flower": {
+ "c": "symbols",
+ "e": "💮",
+ "d": "white flower",
+ "u": "6.0"
+ },
+ "white_large_square": {
+ "c": "symbols",
+ "e": "⬜",
+ "d": "white large square",
+ "u": "5.1"
+ },
+ "white_medium_small_square": {
+ "c": "symbols",
+ "e": "â—½",
+ "d": "white medium small square",
+ "u": "3.2"
+ },
+ "white_medium_square": {
+ "c": "symbols",
+ "e": "â—»",
+ "d": "white medium square",
+ "u": "3.2"
+ },
+ "white_small_square": {
+ "c": "symbols",
+ "e": "â–«",
+ "d": "white small square",
+ "u": "1.1"
+ },
+ "white_square_button": {
+ "c": "symbols",
+ "e": "🔳",
+ "d": "white square button",
+ "u": "6.0"
+ },
+ "white_sun_cloud": {
+ "c": "nature",
+ "e": "🌥",
+ "d": "white sun behind cloud",
+ "u": "7.0"
+ },
+ "white_sun_rain_cloud": {
+ "c": "nature",
+ "e": "🌦",
+ "d": "white sun behind cloud with rain",
+ "u": "7.0"
+ },
+ "white_sun_small_cloud": {
+ "c": "nature",
+ "e": "🌤",
+ "d": "white sun with small cloud",
+ "u": "7.0"
+ },
+ "wilted_rose": {
+ "c": "nature",
+ "e": "🥀",
+ "d": "wilted flower",
+ "u": "9.0"
+ },
+ "wind_blowing_face": {
+ "c": "nature",
+ "e": "🌬",
+ "d": "wind blowing face",
+ "u": "7.0"
+ },
+ "wind_chime": {
+ "c": "objects",
+ "e": "ðŸŽ",
+ "d": "wind chime",
+ "u": "6.0"
+ },
+ "wine_glass": {
+ "c": "food",
+ "e": "ðŸ·",
+ "d": "wine glass",
+ "u": "6.0"
+ },
+ "wink": {
+ "c": "people",
+ "e": "😉",
+ "d": "winking face",
+ "u": "6.0"
+ },
+ "wolf": {
+ "c": "nature",
+ "e": "ðŸº",
+ "d": "wolf face",
+ "u": "6.0"
+ },
+ "woman": {
+ "c": "people",
+ "e": "👩",
+ "d": "woman",
+ "u": "6.0"
+ },
+ "woman_tone1": {
+ "c": "people",
+ "e": "👩ðŸ»",
+ "d": "woman tone 1",
+ "u": "8.0"
+ },
+ "woman_tone2": {
+ "c": "people",
+ "e": "👩ðŸ¼",
+ "d": "woman tone 2",
+ "u": "8.0"
+ },
+ "woman_tone3": {
+ "c": "people",
+ "e": "👩ðŸ½",
+ "d": "woman tone 3",
+ "u": "8.0"
+ },
+ "woman_tone4": {
+ "c": "people",
+ "e": "👩ðŸ¾",
+ "d": "woman tone 4",
+ "u": "8.0"
+ },
+ "woman_tone5": {
+ "c": "people",
+ "e": "👩ðŸ¿",
+ "d": "woman tone 5",
+ "u": "8.0"
+ },
+ "womans_clothes": {
+ "c": "people",
+ "e": "👚",
+ "d": "womans clothes",
+ "u": "6.0"
+ },
+ "womans_hat": {
+ "c": "people",
+ "e": "👒",
+ "d": "womans hat",
+ "u": "6.0"
+ },
+ "womens": {
+ "c": "symbols",
+ "e": "🚺",
+ "d": "womens symbol",
+ "u": "6.0"
+ },
+ "worried": {
+ "c": "people",
+ "e": "😟",
+ "d": "worried face",
+ "u": "6.1"
+ },
+ "wrench": {
+ "c": "objects",
+ "e": "🔧",
+ "d": "wrench",
+ "u": "6.0"
+ },
+ "wrestlers": {
+ "c": "activity",
+ "e": "🤼",
+ "d": "wrestlers",
+ "u": "9.0"
+ },
+ "wrestlers_tone1": {
+ "c": "activity",
+ "e": "🤼ðŸ»",
+ "d": "wrestlers tone 1",
+ "u": "9.0"
+ },
+ "wrestlers_tone2": {
+ "c": "activity",
+ "e": "🤼ðŸ¼",
+ "d": "wrestlers tone 2",
+ "u": "9.0"
+ },
+ "wrestlers_tone3": {
+ "c": "activity",
+ "e": "🤼ðŸ½",
+ "d": "wrestlers tone 3",
+ "u": "9.0"
+ },
+ "wrestlers_tone4": {
+ "c": "activity",
+ "e": "🤼ðŸ¾",
+ "d": "wrestlers tone 4",
+ "u": "9.0"
+ },
+ "wrestlers_tone5": {
+ "c": "activity",
+ "e": "🤼ðŸ¿",
+ "d": "wrestlers tone 5",
+ "u": "9.0"
+ },
+ "writing_hand": {
+ "c": "people",
+ "e": "âœ",
+ "d": "writing hand",
+ "u": "1.1"
+ },
+ "writing_hand_tone1": {
+ "c": "people",
+ "e": "âœðŸ»",
+ "d": "writing hand tone 1",
+ "u": "8.0"
+ },
+ "writing_hand_tone2": {
+ "c": "people",
+ "e": "âœðŸ¼",
+ "d": "writing hand tone 2",
+ "u": "8.0"
+ },
+ "writing_hand_tone3": {
+ "c": "people",
+ "e": "âœðŸ½",
+ "d": "writing hand tone 3",
+ "u": "8.0"
+ },
+ "writing_hand_tone4": {
+ "c": "people",
+ "e": "âœðŸ¾",
+ "d": "writing hand tone 4",
+ "u": "8.0"
+ },
+ "writing_hand_tone5": {
+ "c": "people",
+ "e": "âœðŸ¿",
+ "d": "writing hand tone 5",
+ "u": "8.0"
+ },
+ "x": {
+ "c": "symbols",
+ "e": "âŒ",
+ "d": "cross mark",
+ "u": "6.0"
+ },
+ "yellow_heart": {
+ "c": "symbols",
+ "e": "💛",
+ "d": "yellow heart",
+ "u": "6.0"
+ },
+ "yen": {
+ "c": "objects",
+ "e": "💴",
+ "d": "banknote with yen sign",
+ "u": "6.0"
+ },
+ "yin_yang": {
+ "c": "symbols",
+ "e": "☯",
+ "d": "yin yang",
+ "u": "1.1"
+ },
+ "yum": {
+ "c": "people",
+ "e": "😋",
+ "d": "face savouring delicious food",
+ "u": "6.0"
+ },
+ "zap": {
+ "c": "nature",
+ "e": "âš¡",
+ "d": "high voltage sign",
+ "u": "4.0"
+ },
+ "zero": {
+ "c": "symbols",
+ "e": "0ï¸âƒ£",
+ "d": "keycap digit zero",
+ "u": "3.0"
+ },
+ "zipper_mouth": {
+ "c": "people",
+ "e": "ðŸ¤",
+ "d": "zipper-mouth face",
+ "u": "8.0"
+ },
+ "zzz": {
+ "c": "people",
+ "e": "💤",
+ "d": "sleeping symbol",
+ "u": "6.0"
+ }
+} \ No newline at end of file
diff --git a/app/assets/images/emoji/end.png b/public/-/emojis/1/end.png
index ef3ccd5f367..ef3ccd5f367 100644
--- a/app/assets/images/emoji/end.png
+++ b/public/-/emojis/1/end.png
Binary files differ
diff --git a/app/assets/images/emoji/envelope.png b/public/-/emojis/1/envelope.png
index ec77ac375a4..ec77ac375a4 100644
--- a/app/assets/images/emoji/envelope.png
+++ b/public/-/emojis/1/envelope.png
Binary files differ
diff --git a/app/assets/images/emoji/envelope_with_arrow.png b/public/-/emojis/1/envelope_with_arrow.png
index 7448a6b7673..7448a6b7673 100644
--- a/app/assets/images/emoji/envelope_with_arrow.png
+++ b/public/-/emojis/1/envelope_with_arrow.png
Binary files differ
diff --git a/app/assets/images/emoji/euro.png b/public/-/emojis/1/euro.png
index a49020820e1..a49020820e1 100644
--- a/app/assets/images/emoji/euro.png
+++ b/public/-/emojis/1/euro.png
Binary files differ
diff --git a/app/assets/images/emoji/european_castle.png b/public/-/emojis/1/european_castle.png
index 888d11332ce..888d11332ce 100644
--- a/app/assets/images/emoji/european_castle.png
+++ b/public/-/emojis/1/european_castle.png
Binary files differ
diff --git a/app/assets/images/emoji/european_post_office.png b/public/-/emojis/1/european_post_office.png
index 3745aff8dd2..3745aff8dd2 100644
--- a/app/assets/images/emoji/european_post_office.png
+++ b/public/-/emojis/1/european_post_office.png
Binary files differ
diff --git a/app/assets/images/emoji/evergreen_tree.png b/public/-/emojis/1/evergreen_tree.png
index f679d8dd772..f679d8dd772 100644
--- a/app/assets/images/emoji/evergreen_tree.png
+++ b/public/-/emojis/1/evergreen_tree.png
Binary files differ
diff --git a/app/assets/images/emoji/exclamation.png b/public/-/emojis/1/exclamation.png
index 2c14406422f..2c14406422f 100644
--- a/app/assets/images/emoji/exclamation.png
+++ b/public/-/emojis/1/exclamation.png
Binary files differ
diff --git a/app/assets/images/emoji/expressionless.png b/public/-/emojis/1/expressionless.png
index 2954017f6c2..2954017f6c2 100644
--- a/app/assets/images/emoji/expressionless.png
+++ b/public/-/emojis/1/expressionless.png
Binary files differ
diff --git a/app/assets/images/emoji/eye.png b/public/-/emojis/1/eye.png
index 9d989cdd375..9d989cdd375 100644
--- a/app/assets/images/emoji/eye.png
+++ b/public/-/emojis/1/eye.png
Binary files differ
diff --git a/app/assets/images/emoji/eye_in_speech_bubble.png b/public/-/emojis/1/eye_in_speech_bubble.png
index 21bd22bbcce..21bd22bbcce 100644
--- a/app/assets/images/emoji/eye_in_speech_bubble.png
+++ b/public/-/emojis/1/eye_in_speech_bubble.png
Binary files differ
diff --git a/app/assets/images/emoji/eyeglasses.png b/public/-/emojis/1/eyeglasses.png
index 865d8274acf..865d8274acf 100644
--- a/app/assets/images/emoji/eyeglasses.png
+++ b/public/-/emojis/1/eyeglasses.png
Binary files differ
diff --git a/app/assets/images/emoji/eyes.png b/public/-/emojis/1/eyes.png
index 2102ada7e09..2102ada7e09 100644
--- a/app/assets/images/emoji/eyes.png
+++ b/public/-/emojis/1/eyes.png
Binary files differ
diff --git a/app/assets/images/emoji/face_palm.png b/public/-/emojis/1/face_palm.png
index defc796cf16..defc796cf16 100644
--- a/app/assets/images/emoji/face_palm.png
+++ b/public/-/emojis/1/face_palm.png
Binary files differ
diff --git a/app/assets/images/emoji/face_palm_tone1.png b/public/-/emojis/1/face_palm_tone1.png
index 2f4b010bb40..2f4b010bb40 100644
--- a/app/assets/images/emoji/face_palm_tone1.png
+++ b/public/-/emojis/1/face_palm_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/face_palm_tone2.png b/public/-/emojis/1/face_palm_tone2.png
index 97fb6831687..97fb6831687 100644
--- a/app/assets/images/emoji/face_palm_tone2.png
+++ b/public/-/emojis/1/face_palm_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/face_palm_tone3.png b/public/-/emojis/1/face_palm_tone3.png
index b5b5c1e5306..b5b5c1e5306 100644
--- a/app/assets/images/emoji/face_palm_tone3.png
+++ b/public/-/emojis/1/face_palm_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/face_palm_tone4.png b/public/-/emojis/1/face_palm_tone4.png
index 2840b113483..2840b113483 100644
--- a/app/assets/images/emoji/face_palm_tone4.png
+++ b/public/-/emojis/1/face_palm_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/face_palm_tone5.png b/public/-/emojis/1/face_palm_tone5.png
index 6f070db98be..6f070db98be 100644
--- a/app/assets/images/emoji/face_palm_tone5.png
+++ b/public/-/emojis/1/face_palm_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/factory.png b/public/-/emojis/1/factory.png
index e1d2ddf4a27..e1d2ddf4a27 100644
--- a/app/assets/images/emoji/factory.png
+++ b/public/-/emojis/1/factory.png
Binary files differ
diff --git a/app/assets/images/emoji/fallen_leaf.png b/public/-/emojis/1/fallen_leaf.png
index 0d60e7bdf2d..0d60e7bdf2d 100644
--- a/app/assets/images/emoji/fallen_leaf.png
+++ b/public/-/emojis/1/fallen_leaf.png
Binary files differ
diff --git a/app/assets/images/emoji/family.png b/public/-/emojis/1/family.png
index 26421965791..26421965791 100644
--- a/app/assets/images/emoji/family.png
+++ b/public/-/emojis/1/family.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mmb.png b/public/-/emojis/1/family_mmb.png
index 7a2e4e2c491..7a2e4e2c491 100644
--- a/app/assets/images/emoji/family_mmb.png
+++ b/public/-/emojis/1/family_mmb.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mmbb.png b/public/-/emojis/1/family_mmbb.png
index 81e6c0fc0ee..81e6c0fc0ee 100644
--- a/app/assets/images/emoji/family_mmbb.png
+++ b/public/-/emojis/1/family_mmbb.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mmg.png b/public/-/emojis/1/family_mmg.png
index 932a85e1fe5..932a85e1fe5 100644
--- a/app/assets/images/emoji/family_mmg.png
+++ b/public/-/emojis/1/family_mmg.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mmgb.png b/public/-/emojis/1/family_mmgb.png
index 41e35166670..41e35166670 100644
--- a/app/assets/images/emoji/family_mmgb.png
+++ b/public/-/emojis/1/family_mmgb.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mmgg.png b/public/-/emojis/1/family_mmgg.png
index 8e8ccfe6c7f..8e8ccfe6c7f 100644
--- a/app/assets/images/emoji/family_mmgg.png
+++ b/public/-/emojis/1/family_mmgg.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mwbb.png b/public/-/emojis/1/family_mwbb.png
index b544fbe573f..b544fbe573f 100644
--- a/app/assets/images/emoji/family_mwbb.png
+++ b/public/-/emojis/1/family_mwbb.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mwg.png b/public/-/emojis/1/family_mwg.png
index 71d2681c32a..71d2681c32a 100644
--- a/app/assets/images/emoji/family_mwg.png
+++ b/public/-/emojis/1/family_mwg.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mwgb.png b/public/-/emojis/1/family_mwgb.png
index 40dbf1f7a18..40dbf1f7a18 100644
--- a/app/assets/images/emoji/family_mwgb.png
+++ b/public/-/emojis/1/family_mwgb.png
Binary files differ
diff --git a/app/assets/images/emoji/family_mwgg.png b/public/-/emojis/1/family_mwgg.png
index bfefa4879cb..bfefa4879cb 100644
--- a/app/assets/images/emoji/family_mwgg.png
+++ b/public/-/emojis/1/family_mwgg.png
Binary files differ
diff --git a/app/assets/images/emoji/family_wwb.png b/public/-/emojis/1/family_wwb.png
index 836feae7c78..836feae7c78 100644
--- a/app/assets/images/emoji/family_wwb.png
+++ b/public/-/emojis/1/family_wwb.png
Binary files differ
diff --git a/app/assets/images/emoji/family_wwbb.png b/public/-/emojis/1/family_wwbb.png
index 6c6ba45e7bb..6c6ba45e7bb 100644
--- a/app/assets/images/emoji/family_wwbb.png
+++ b/public/-/emojis/1/family_wwbb.png
Binary files differ
diff --git a/app/assets/images/emoji/family_wwg.png b/public/-/emojis/1/family_wwg.png
index 41225c6fa5a..41225c6fa5a 100644
--- a/app/assets/images/emoji/family_wwg.png
+++ b/public/-/emojis/1/family_wwg.png
Binary files differ
diff --git a/app/assets/images/emoji/family_wwgb.png b/public/-/emojis/1/family_wwgb.png
index 284d29ab5da..284d29ab5da 100644
--- a/app/assets/images/emoji/family_wwgb.png
+++ b/public/-/emojis/1/family_wwgb.png
Binary files differ
diff --git a/app/assets/images/emoji/family_wwgg.png b/public/-/emojis/1/family_wwgg.png
index d8d3f49b85f..d8d3f49b85f 100644
--- a/app/assets/images/emoji/family_wwgg.png
+++ b/public/-/emojis/1/family_wwgg.png
Binary files differ
diff --git a/app/assets/images/emoji/fast_forward.png b/public/-/emojis/1/fast_forward.png
index c406fedfdb1..c406fedfdb1 100644
--- a/app/assets/images/emoji/fast_forward.png
+++ b/public/-/emojis/1/fast_forward.png
Binary files differ
diff --git a/app/assets/images/emoji/fax.png b/public/-/emojis/1/fax.png
index 6f929e294c2..6f929e294c2 100644
--- a/app/assets/images/emoji/fax.png
+++ b/public/-/emojis/1/fax.png
Binary files differ
diff --git a/app/assets/images/emoji/fearful.png b/public/-/emojis/1/fearful.png
index eb8b347cef9..eb8b347cef9 100644
--- a/app/assets/images/emoji/fearful.png
+++ b/public/-/emojis/1/fearful.png
Binary files differ
diff --git a/app/assets/images/emoji/feet.png b/public/-/emojis/1/feet.png
index 5fe568cee93..5fe568cee93 100644
--- a/app/assets/images/emoji/feet.png
+++ b/public/-/emojis/1/feet.png
Binary files differ
diff --git a/app/assets/images/emoji/fencer.png b/public/-/emojis/1/fencer.png
index 5288c920eb9..5288c920eb9 100644
--- a/app/assets/images/emoji/fencer.png
+++ b/public/-/emojis/1/fencer.png
Binary files differ
diff --git a/app/assets/images/emoji/ferris_wheel.png b/public/-/emojis/1/ferris_wheel.png
index 55c8ff0475b..55c8ff0475b 100644
--- a/app/assets/images/emoji/ferris_wheel.png
+++ b/public/-/emojis/1/ferris_wheel.png
Binary files differ
diff --git a/app/assets/images/emoji/ferry.png b/public/-/emojis/1/ferry.png
index 41816b3ae34..41816b3ae34 100644
--- a/app/assets/images/emoji/ferry.png
+++ b/public/-/emojis/1/ferry.png
Binary files differ
diff --git a/app/assets/images/emoji/field_hockey.png b/public/-/emojis/1/field_hockey.png
index 839637716ee..839637716ee 100644
--- a/app/assets/images/emoji/field_hockey.png
+++ b/public/-/emojis/1/field_hockey.png
Binary files differ
diff --git a/app/assets/images/emoji/file_cabinet.png b/public/-/emojis/1/file_cabinet.png
index fddc65dde96..fddc65dde96 100644
--- a/app/assets/images/emoji/file_cabinet.png
+++ b/public/-/emojis/1/file_cabinet.png
Binary files differ
diff --git a/app/assets/images/emoji/file_folder.png b/public/-/emojis/1/file_folder.png
index addedaf0870..addedaf0870 100644
--- a/app/assets/images/emoji/file_folder.png
+++ b/public/-/emojis/1/file_folder.png
Binary files differ
diff --git a/app/assets/images/emoji/film_frames.png b/public/-/emojis/1/film_frames.png
index 30143aedbe6..30143aedbe6 100644
--- a/app/assets/images/emoji/film_frames.png
+++ b/public/-/emojis/1/film_frames.png
Binary files differ
diff --git a/app/assets/images/emoji/fingers_crossed.png b/public/-/emojis/1/fingers_crossed.png
index 4cd18514ea3..4cd18514ea3 100644
--- a/app/assets/images/emoji/fingers_crossed.png
+++ b/public/-/emojis/1/fingers_crossed.png
Binary files differ
diff --git a/app/assets/images/emoji/fingers_crossed_tone1.png b/public/-/emojis/1/fingers_crossed_tone1.png
index dd2384a6cd5..dd2384a6cd5 100644
--- a/app/assets/images/emoji/fingers_crossed_tone1.png
+++ b/public/-/emojis/1/fingers_crossed_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/fingers_crossed_tone2.png b/public/-/emojis/1/fingers_crossed_tone2.png
index 6228401befe..6228401befe 100644
--- a/app/assets/images/emoji/fingers_crossed_tone2.png
+++ b/public/-/emojis/1/fingers_crossed_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/fingers_crossed_tone3.png b/public/-/emojis/1/fingers_crossed_tone3.png
index b1074da15f5..b1074da15f5 100644
--- a/app/assets/images/emoji/fingers_crossed_tone3.png
+++ b/public/-/emojis/1/fingers_crossed_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/fingers_crossed_tone4.png b/public/-/emojis/1/fingers_crossed_tone4.png
index 75e05e4d332..75e05e4d332 100644
--- a/app/assets/images/emoji/fingers_crossed_tone4.png
+++ b/public/-/emojis/1/fingers_crossed_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/fingers_crossed_tone5.png b/public/-/emojis/1/fingers_crossed_tone5.png
index 761aebdc30f..761aebdc30f 100644
--- a/app/assets/images/emoji/fingers_crossed_tone5.png
+++ b/public/-/emojis/1/fingers_crossed_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/fire.png b/public/-/emojis/1/fire.png
index bd3775a460b..bd3775a460b 100644
--- a/app/assets/images/emoji/fire.png
+++ b/public/-/emojis/1/fire.png
Binary files differ
diff --git a/app/assets/images/emoji/fire_engine.png b/public/-/emojis/1/fire_engine.png
index 2cd45b7cf7e..2cd45b7cf7e 100644
--- a/app/assets/images/emoji/fire_engine.png
+++ b/public/-/emojis/1/fire_engine.png
Binary files differ
diff --git a/app/assets/images/emoji/fireworks.png b/public/-/emojis/1/fireworks.png
index 176c8b58265..176c8b58265 100644
--- a/app/assets/images/emoji/fireworks.png
+++ b/public/-/emojis/1/fireworks.png
Binary files differ
diff --git a/app/assets/images/emoji/first_place.png b/public/-/emojis/1/first_place.png
index 15612b66492..15612b66492 100644
--- a/app/assets/images/emoji/first_place.png
+++ b/public/-/emojis/1/first_place.png
Binary files differ
diff --git a/app/assets/images/emoji/first_quarter_moon.png b/public/-/emojis/1/first_quarter_moon.png
index 5dccaf72a4f..5dccaf72a4f 100644
--- a/app/assets/images/emoji/first_quarter_moon.png
+++ b/public/-/emojis/1/first_quarter_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/first_quarter_moon_with_face.png b/public/-/emojis/1/first_quarter_moon_with_face.png
index cd8a3d7acd8..cd8a3d7acd8 100644
--- a/app/assets/images/emoji/first_quarter_moon_with_face.png
+++ b/public/-/emojis/1/first_quarter_moon_with_face.png
Binary files differ
diff --git a/app/assets/images/emoji/fish.png b/public/-/emojis/1/fish.png
index c2d2faaacd4..c2d2faaacd4 100644
--- a/app/assets/images/emoji/fish.png
+++ b/public/-/emojis/1/fish.png
Binary files differ
diff --git a/app/assets/images/emoji/fish_cake.png b/public/-/emojis/1/fish_cake.png
index 157bded65db..157bded65db 100644
--- a/app/assets/images/emoji/fish_cake.png
+++ b/public/-/emojis/1/fish_cake.png
Binary files differ
diff --git a/app/assets/images/emoji/fishing_pole_and_fish.png b/public/-/emojis/1/fishing_pole_and_fish.png
index dfcdf07eb50..dfcdf07eb50 100644
--- a/app/assets/images/emoji/fishing_pole_and_fish.png
+++ b/public/-/emojis/1/fishing_pole_and_fish.png
Binary files differ
diff --git a/app/assets/images/emoji/fist.png b/public/-/emojis/1/fist.png
index de33592bf98..de33592bf98 100644
--- a/app/assets/images/emoji/fist.png
+++ b/public/-/emojis/1/fist.png
Binary files differ
diff --git a/app/assets/images/emoji/fist_tone1.png b/public/-/emojis/1/fist_tone1.png
index 02809e2dd68..02809e2dd68 100644
--- a/app/assets/images/emoji/fist_tone1.png
+++ b/public/-/emojis/1/fist_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/fist_tone2.png b/public/-/emojis/1/fist_tone2.png
index 5de34810383..5de34810383 100644
--- a/app/assets/images/emoji/fist_tone2.png
+++ b/public/-/emojis/1/fist_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/fist_tone3.png b/public/-/emojis/1/fist_tone3.png
index 0d5240129b1..0d5240129b1 100644
--- a/app/assets/images/emoji/fist_tone3.png
+++ b/public/-/emojis/1/fist_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/fist_tone4.png b/public/-/emojis/1/fist_tone4.png
index a95c0dd634b..a95c0dd634b 100644
--- a/app/assets/images/emoji/fist_tone4.png
+++ b/public/-/emojis/1/fist_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/fist_tone5.png b/public/-/emojis/1/fist_tone5.png
index a2f092fd8c7..a2f092fd8c7 100644
--- a/app/assets/images/emoji/fist_tone5.png
+++ b/public/-/emojis/1/fist_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/five.png b/public/-/emojis/1/five.png
index d14371f3f27..d14371f3f27 100644
--- a/app/assets/images/emoji/five.png
+++ b/public/-/emojis/1/five.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ac.png b/public/-/emojis/1/flag_ac.png
index 286239920c7..286239920c7 100644
--- a/app/assets/images/emoji/flag_ac.png
+++ b/public/-/emojis/1/flag_ac.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ad.png b/public/-/emojis/1/flag_ad.png
index 20f4b14e8ad..20f4b14e8ad 100644
--- a/app/assets/images/emoji/flag_ad.png
+++ b/public/-/emojis/1/flag_ad.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ae.png b/public/-/emojis/1/flag_ae.png
index d16ffe4b862..d16ffe4b862 100644
--- a/app/assets/images/emoji/flag_ae.png
+++ b/public/-/emojis/1/flag_ae.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_af.png b/public/-/emojis/1/flag_af.png
index a51533b554d..a51533b554d 100644
--- a/app/assets/images/emoji/flag_af.png
+++ b/public/-/emojis/1/flag_af.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ag.png b/public/-/emojis/1/flag_ag.png
index 07f2ce397d0..07f2ce397d0 100644
--- a/app/assets/images/emoji/flag_ag.png
+++ b/public/-/emojis/1/flag_ag.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ai.png b/public/-/emojis/1/flag_ai.png
index 500b5ab09fb..500b5ab09fb 100644
--- a/app/assets/images/emoji/flag_ai.png
+++ b/public/-/emojis/1/flag_ai.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_al.png b/public/-/emojis/1/flag_al.png
index 03a20132cc6..03a20132cc6 100644
--- a/app/assets/images/emoji/flag_al.png
+++ b/public/-/emojis/1/flag_al.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_am.png b/public/-/emojis/1/flag_am.png
index 2ad60a273ec..2ad60a273ec 100644
--- a/app/assets/images/emoji/flag_am.png
+++ b/public/-/emojis/1/flag_am.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ao.png b/public/-/emojis/1/flag_ao.png
index cb46c31f862..cb46c31f862 100644
--- a/app/assets/images/emoji/flag_ao.png
+++ b/public/-/emojis/1/flag_ao.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_aq.png b/public/-/emojis/1/flag_aq.png
index b272021d375..b272021d375 100644
--- a/app/assets/images/emoji/flag_aq.png
+++ b/public/-/emojis/1/flag_aq.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ar.png b/public/-/emojis/1/flag_ar.png
index 73136caf3b7..73136caf3b7 100644
--- a/app/assets/images/emoji/flag_ar.png
+++ b/public/-/emojis/1/flag_ar.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_as.png b/public/-/emojis/1/flag_as.png
index 3db45a0d9f3..3db45a0d9f3 100644
--- a/app/assets/images/emoji/flag_as.png
+++ b/public/-/emojis/1/flag_as.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_at.png b/public/-/emojis/1/flag_at.png
index c43769dcb19..c43769dcb19 100644
--- a/app/assets/images/emoji/flag_at.png
+++ b/public/-/emojis/1/flag_at.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_au.png b/public/-/emojis/1/flag_au.png
index 7794309c78c..7794309c78c 100644
--- a/app/assets/images/emoji/flag_au.png
+++ b/public/-/emojis/1/flag_au.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_aw.png b/public/-/emojis/1/flag_aw.png
index 02c840d12c9..02c840d12c9 100644
--- a/app/assets/images/emoji/flag_aw.png
+++ b/public/-/emojis/1/flag_aw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ax.png b/public/-/emojis/1/flag_ax.png
index fc5466174bb..fc5466174bb 100644
--- a/app/assets/images/emoji/flag_ax.png
+++ b/public/-/emojis/1/flag_ax.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_az.png b/public/-/emojis/1/flag_az.png
index 89d3d15fd9f..89d3d15fd9f 100644
--- a/app/assets/images/emoji/flag_az.png
+++ b/public/-/emojis/1/flag_az.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ba.png b/public/-/emojis/1/flag_ba.png
index 25fe407e13c..25fe407e13c 100644
--- a/app/assets/images/emoji/flag_ba.png
+++ b/public/-/emojis/1/flag_ba.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bb.png b/public/-/emojis/1/flag_bb.png
index bccd8c5c9b0..bccd8c5c9b0 100644
--- a/app/assets/images/emoji/flag_bb.png
+++ b/public/-/emojis/1/flag_bb.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bd.png b/public/-/emojis/1/flag_bd.png
index b0597a3149b..b0597a3149b 100644
--- a/app/assets/images/emoji/flag_bd.png
+++ b/public/-/emojis/1/flag_bd.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_be.png b/public/-/emojis/1/flag_be.png
index 551f086e3c4..551f086e3c4 100644
--- a/app/assets/images/emoji/flag_be.png
+++ b/public/-/emojis/1/flag_be.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bf.png b/public/-/emojis/1/flag_bf.png
index 444d4829f94..444d4829f94 100644
--- a/app/assets/images/emoji/flag_bf.png
+++ b/public/-/emojis/1/flag_bf.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bg.png b/public/-/emojis/1/flag_bg.png
index 821eee5e170..821eee5e170 100644
--- a/app/assets/images/emoji/flag_bg.png
+++ b/public/-/emojis/1/flag_bg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bh.png b/public/-/emojis/1/flag_bh.png
index f33724249f0..f33724249f0 100644
--- a/app/assets/images/emoji/flag_bh.png
+++ b/public/-/emojis/1/flag_bh.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bi.png b/public/-/emojis/1/flag_bi.png
index ea20ac93211..ea20ac93211 100644
--- a/app/assets/images/emoji/flag_bi.png
+++ b/public/-/emojis/1/flag_bi.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bj.png b/public/-/emojis/1/flag_bj.png
index 7cca4f80457..7cca4f80457 100644
--- a/app/assets/images/emoji/flag_bj.png
+++ b/public/-/emojis/1/flag_bj.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bl.png b/public/-/emojis/1/flag_bl.png
index 1082e78999f..1082e78999f 100644
--- a/app/assets/images/emoji/flag_bl.png
+++ b/public/-/emojis/1/flag_bl.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_black.png b/public/-/emojis/1/flag_black.png
index 0e28d05d5ac..0e28d05d5ac 100644
--- a/app/assets/images/emoji/flag_black.png
+++ b/public/-/emojis/1/flag_black.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bm.png b/public/-/emojis/1/flag_bm.png
index ab8cafdac63..ab8cafdac63 100644
--- a/app/assets/images/emoji/flag_bm.png
+++ b/public/-/emojis/1/flag_bm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bn.png b/public/-/emojis/1/flag_bn.png
index caa9329a896..caa9329a896 100644
--- a/app/assets/images/emoji/flag_bn.png
+++ b/public/-/emojis/1/flag_bn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bo.png b/public/-/emojis/1/flag_bo.png
index 98af62b3da7..98af62b3da7 100644
--- a/app/assets/images/emoji/flag_bo.png
+++ b/public/-/emojis/1/flag_bo.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bq.png b/public/-/emojis/1/flag_bq.png
index cb978ef9de9..cb978ef9de9 100644
--- a/app/assets/images/emoji/flag_bq.png
+++ b/public/-/emojis/1/flag_bq.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_br.png b/public/-/emojis/1/flag_br.png
index b139366a42b..b139366a42b 100644
--- a/app/assets/images/emoji/flag_br.png
+++ b/public/-/emojis/1/flag_br.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bs.png b/public/-/emojis/1/flag_bs.png
index d36bcd2fb52..d36bcd2fb52 100644
--- a/app/assets/images/emoji/flag_bs.png
+++ b/public/-/emojis/1/flag_bs.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bt.png b/public/-/emojis/1/flag_bt.png
index ed57aa0360e..ed57aa0360e 100644
--- a/app/assets/images/emoji/flag_bt.png
+++ b/public/-/emojis/1/flag_bt.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bv.png b/public/-/emojis/1/flag_bv.png
index 5884e648228..5884e648228 100644
--- a/app/assets/images/emoji/flag_bv.png
+++ b/public/-/emojis/1/flag_bv.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bw.png b/public/-/emojis/1/flag_bw.png
index cb12f34739d..cb12f34739d 100644
--- a/app/assets/images/emoji/flag_bw.png
+++ b/public/-/emojis/1/flag_bw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_by.png b/public/-/emojis/1/flag_by.png
index 859c05beb13..859c05beb13 100644
--- a/app/assets/images/emoji/flag_by.png
+++ b/public/-/emojis/1/flag_by.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_bz.png b/public/-/emojis/1/flag_bz.png
index 34761cd03d8..34761cd03d8 100644
--- a/app/assets/images/emoji/flag_bz.png
+++ b/public/-/emojis/1/flag_bz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ca.png b/public/-/emojis/1/flag_ca.png
index 7c5b390e85b..7c5b390e85b 100644
--- a/app/assets/images/emoji/flag_ca.png
+++ b/public/-/emojis/1/flag_ca.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cc.png b/public/-/emojis/1/flag_cc.png
index b6555a23d83..b6555a23d83 100644
--- a/app/assets/images/emoji/flag_cc.png
+++ b/public/-/emojis/1/flag_cc.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cd.png b/public/-/emojis/1/flag_cd.png
index fa92009771d..fa92009771d 100644
--- a/app/assets/images/emoji/flag_cd.png
+++ b/public/-/emojis/1/flag_cd.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cf.png b/public/-/emojis/1/flag_cf.png
index b969ae29ea9..b969ae29ea9 100644
--- a/app/assets/images/emoji/flag_cf.png
+++ b/public/-/emojis/1/flag_cf.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cg.png b/public/-/emojis/1/flag_cg.png
index 3a38a40a95e..3a38a40a95e 100644
--- a/app/assets/images/emoji/flag_cg.png
+++ b/public/-/emojis/1/flag_cg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ch.png b/public/-/emojis/1/flag_ch.png
index 5ff86b8a3b7..5ff86b8a3b7 100644
--- a/app/assets/images/emoji/flag_ch.png
+++ b/public/-/emojis/1/flag_ch.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ci.png b/public/-/emojis/1/flag_ci.png
index e3b4d15c7f1..e3b4d15c7f1 100644
--- a/app/assets/images/emoji/flag_ci.png
+++ b/public/-/emojis/1/flag_ci.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ck.png b/public/-/emojis/1/flag_ck.png
index b6b53dbc1c4..b6b53dbc1c4 100644
--- a/app/assets/images/emoji/flag_ck.png
+++ b/public/-/emojis/1/flag_ck.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cl.png b/public/-/emojis/1/flag_cl.png
index c9390da5499..c9390da5499 100644
--- a/app/assets/images/emoji/flag_cl.png
+++ b/public/-/emojis/1/flag_cl.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cm.png b/public/-/emojis/1/flag_cm.png
index 2d3f6ec4518..2d3f6ec4518 100644
--- a/app/assets/images/emoji/flag_cm.png
+++ b/public/-/emojis/1/flag_cm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cn.png b/public/-/emojis/1/flag_cn.png
index 0a7f350a6d2..0a7f350a6d2 100644
--- a/app/assets/images/emoji/flag_cn.png
+++ b/public/-/emojis/1/flag_cn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_co.png b/public/-/emojis/1/flag_co.png
index 7e0f5e0dc3c..7e0f5e0dc3c 100644
--- a/app/assets/images/emoji/flag_co.png
+++ b/public/-/emojis/1/flag_co.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cp.png b/public/-/emojis/1/flag_cp.png
index 70c761036bd..70c761036bd 100644
--- a/app/assets/images/emoji/flag_cp.png
+++ b/public/-/emojis/1/flag_cp.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cr.png b/public/-/emojis/1/flag_cr.png
index a5fce126515..a5fce126515 100644
--- a/app/assets/images/emoji/flag_cr.png
+++ b/public/-/emojis/1/flag_cr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cu.png b/public/-/emojis/1/flag_cu.png
index 447328f7dfd..447328f7dfd 100644
--- a/app/assets/images/emoji/flag_cu.png
+++ b/public/-/emojis/1/flag_cu.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cv.png b/public/-/emojis/1/flag_cv.png
index 43faf4d64d5..43faf4d64d5 100644
--- a/app/assets/images/emoji/flag_cv.png
+++ b/public/-/emojis/1/flag_cv.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cw.png b/public/-/emojis/1/flag_cw.png
index eb39e8d0078..eb39e8d0078 100644
--- a/app/assets/images/emoji/flag_cw.png
+++ b/public/-/emojis/1/flag_cw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cx.png b/public/-/emojis/1/flag_cx.png
index 09d21359f3a..09d21359f3a 100644
--- a/app/assets/images/emoji/flag_cx.png
+++ b/public/-/emojis/1/flag_cx.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cy.png b/public/-/emojis/1/flag_cy.png
index 154a7aa3176..154a7aa3176 100644
--- a/app/assets/images/emoji/flag_cy.png
+++ b/public/-/emojis/1/flag_cy.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_cz.png b/public/-/emojis/1/flag_cz.png
index 9737ca223c7..9737ca223c7 100644
--- a/app/assets/images/emoji/flag_cz.png
+++ b/public/-/emojis/1/flag_cz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_de.png b/public/-/emojis/1/flag_de.png
index 98ed76b3bab..98ed76b3bab 100644
--- a/app/assets/images/emoji/flag_de.png
+++ b/public/-/emojis/1/flag_de.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_dg.png b/public/-/emojis/1/flag_dg.png
index aae927d14b8..aae927d14b8 100644
--- a/app/assets/images/emoji/flag_dg.png
+++ b/public/-/emojis/1/flag_dg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_dj.png b/public/-/emojis/1/flag_dj.png
index 73c2a2acbd9..73c2a2acbd9 100644
--- a/app/assets/images/emoji/flag_dj.png
+++ b/public/-/emojis/1/flag_dj.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_dk.png b/public/-/emojis/1/flag_dk.png
index e5a60b06256..e5a60b06256 100644
--- a/app/assets/images/emoji/flag_dk.png
+++ b/public/-/emojis/1/flag_dk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_dm.png b/public/-/emojis/1/flag_dm.png
index 50f8a53981d..50f8a53981d 100644
--- a/app/assets/images/emoji/flag_dm.png
+++ b/public/-/emojis/1/flag_dm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_do.png b/public/-/emojis/1/flag_do.png
index 037a45d7c26..037a45d7c26 100644
--- a/app/assets/images/emoji/flag_do.png
+++ b/public/-/emojis/1/flag_do.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_dz.png b/public/-/emojis/1/flag_dz.png
index 24945b10f2d..24945b10f2d 100644
--- a/app/assets/images/emoji/flag_dz.png
+++ b/public/-/emojis/1/flag_dz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ea.png b/public/-/emojis/1/flag_ea.png
index 356ff347838..356ff347838 100644
--- a/app/assets/images/emoji/flag_ea.png
+++ b/public/-/emojis/1/flag_ea.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ec.png b/public/-/emojis/1/flag_ec.png
index 13814594619..13814594619 100644
--- a/app/assets/images/emoji/flag_ec.png
+++ b/public/-/emojis/1/flag_ec.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ee.png b/public/-/emojis/1/flag_ee.png
index 84f317e7747..84f317e7747 100644
--- a/app/assets/images/emoji/flag_ee.png
+++ b/public/-/emojis/1/flag_ee.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_eg.png b/public/-/emojis/1/flag_eg.png
index 57786064a95..57786064a95 100644
--- a/app/assets/images/emoji/flag_eg.png
+++ b/public/-/emojis/1/flag_eg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_eh.png b/public/-/emojis/1/flag_eh.png
index 4d7a76687f6..4d7a76687f6 100644
--- a/app/assets/images/emoji/flag_eh.png
+++ b/public/-/emojis/1/flag_eh.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_er.png b/public/-/emojis/1/flag_er.png
index 0c3c724c1fb..0c3c724c1fb 100644
--- a/app/assets/images/emoji/flag_er.png
+++ b/public/-/emojis/1/flag_er.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_es.png b/public/-/emojis/1/flag_es.png
index 3e73597a225..3e73597a225 100644
--- a/app/assets/images/emoji/flag_es.png
+++ b/public/-/emojis/1/flag_es.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_et.png b/public/-/emojis/1/flag_et.png
index 9560a134c97..9560a134c97 100644
--- a/app/assets/images/emoji/flag_et.png
+++ b/public/-/emojis/1/flag_et.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_eu.png b/public/-/emojis/1/flag_eu.png
index 0b456cf3330..0b456cf3330 100644
--- a/app/assets/images/emoji/flag_eu.png
+++ b/public/-/emojis/1/flag_eu.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_fi.png b/public/-/emojis/1/flag_fi.png
index ebcf58abfc5..ebcf58abfc5 100644
--- a/app/assets/images/emoji/flag_fi.png
+++ b/public/-/emojis/1/flag_fi.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_fj.png b/public/-/emojis/1/flag_fj.png
index 9cc8c37fe37..9cc8c37fe37 100644
--- a/app/assets/images/emoji/flag_fj.png
+++ b/public/-/emojis/1/flag_fj.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_fk.png b/public/-/emojis/1/flag_fk.png
index 61372fd2549..61372fd2549 100644
--- a/app/assets/images/emoji/flag_fk.png
+++ b/public/-/emojis/1/flag_fk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_fm.png b/public/-/emojis/1/flag_fm.png
index 0889825c8e1..0889825c8e1 100644
--- a/app/assets/images/emoji/flag_fm.png
+++ b/public/-/emojis/1/flag_fm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_fo.png b/public/-/emojis/1/flag_fo.png
index 9a4431b0831..9a4431b0831 100644
--- a/app/assets/images/emoji/flag_fo.png
+++ b/public/-/emojis/1/flag_fo.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_fr.png b/public/-/emojis/1/flag_fr.png
index 62ca19c3fcf..62ca19c3fcf 100644
--- a/app/assets/images/emoji/flag_fr.png
+++ b/public/-/emojis/1/flag_fr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ga.png b/public/-/emojis/1/flag_ga.png
index 2e68e527a3e..2e68e527a3e 100644
--- a/app/assets/images/emoji/flag_ga.png
+++ b/public/-/emojis/1/flag_ga.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gb.png b/public/-/emojis/1/flag_gb.png
index 3ed10f62347..3ed10f62347 100644
--- a/app/assets/images/emoji/flag_gb.png
+++ b/public/-/emojis/1/flag_gb.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gd.png b/public/-/emojis/1/flag_gd.png
index 527aad33807..527aad33807 100644
--- a/app/assets/images/emoji/flag_gd.png
+++ b/public/-/emojis/1/flag_gd.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ge.png b/public/-/emojis/1/flag_ge.png
index a75d142480d..a75d142480d 100644
--- a/app/assets/images/emoji/flag_ge.png
+++ b/public/-/emojis/1/flag_ge.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gf.png b/public/-/emojis/1/flag_gf.png
index 0cf96f327c0..0cf96f327c0 100644
--- a/app/assets/images/emoji/flag_gf.png
+++ b/public/-/emojis/1/flag_gf.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gg.png b/public/-/emojis/1/flag_gg.png
index 970002c7f76..970002c7f76 100644
--- a/app/assets/images/emoji/flag_gg.png
+++ b/public/-/emojis/1/flag_gg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gh.png b/public/-/emojis/1/flag_gh.png
index f31b5eb7b45..f31b5eb7b45 100644
--- a/app/assets/images/emoji/flag_gh.png
+++ b/public/-/emojis/1/flag_gh.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gi.png b/public/-/emojis/1/flag_gi.png
index e554a2a1d0c..e554a2a1d0c 100644
--- a/app/assets/images/emoji/flag_gi.png
+++ b/public/-/emojis/1/flag_gi.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gl.png b/public/-/emojis/1/flag_gl.png
index 2e795dd4e33..2e795dd4e33 100644
--- a/app/assets/images/emoji/flag_gl.png
+++ b/public/-/emojis/1/flag_gl.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gm.png b/public/-/emojis/1/flag_gm.png
index bb69c0975a3..bb69c0975a3 100644
--- a/app/assets/images/emoji/flag_gm.png
+++ b/public/-/emojis/1/flag_gm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gn.png b/public/-/emojis/1/flag_gn.png
index 1981f61dbf5..1981f61dbf5 100644
--- a/app/assets/images/emoji/flag_gn.png
+++ b/public/-/emojis/1/flag_gn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gp.png b/public/-/emojis/1/flag_gp.png
index 10e42e672bd..10e42e672bd 100644
--- a/app/assets/images/emoji/flag_gp.png
+++ b/public/-/emojis/1/flag_gp.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gq.png b/public/-/emojis/1/flag_gq.png
index 11475e61eeb..11475e61eeb 100644
--- a/app/assets/images/emoji/flag_gq.png
+++ b/public/-/emojis/1/flag_gq.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gr.png b/public/-/emojis/1/flag_gr.png
index 0f6bb1b6b94..0f6bb1b6b94 100644
--- a/app/assets/images/emoji/flag_gr.png
+++ b/public/-/emojis/1/flag_gr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gs.png b/public/-/emojis/1/flag_gs.png
index 6fc92780453..6fc92780453 100644
--- a/app/assets/images/emoji/flag_gs.png
+++ b/public/-/emojis/1/flag_gs.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gt.png b/public/-/emojis/1/flag_gt.png
index 7213d4139ed..7213d4139ed 100644
--- a/app/assets/images/emoji/flag_gt.png
+++ b/public/-/emojis/1/flag_gt.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gu.png b/public/-/emojis/1/flag_gu.png
index 4027549ca3c..4027549ca3c 100644
--- a/app/assets/images/emoji/flag_gu.png
+++ b/public/-/emojis/1/flag_gu.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gw.png b/public/-/emojis/1/flag_gw.png
index 6357f6225f4..6357f6225f4 100644
--- a/app/assets/images/emoji/flag_gw.png
+++ b/public/-/emojis/1/flag_gw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_gy.png b/public/-/emojis/1/flag_gy.png
index 746e2fb7e44..746e2fb7e44 100644
--- a/app/assets/images/emoji/flag_gy.png
+++ b/public/-/emojis/1/flag_gy.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_hk.png b/public/-/emojis/1/flag_hk.png
index cf0c7151b56..cf0c7151b56 100644
--- a/app/assets/images/emoji/flag_hk.png
+++ b/public/-/emojis/1/flag_hk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_hm.png b/public/-/emojis/1/flag_hm.png
index b613509e466..b613509e466 100644
--- a/app/assets/images/emoji/flag_hm.png
+++ b/public/-/emojis/1/flag_hm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_hn.png b/public/-/emojis/1/flag_hn.png
index 402cdcefdf8..402cdcefdf8 100644
--- a/app/assets/images/emoji/flag_hn.png
+++ b/public/-/emojis/1/flag_hn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_hr.png b/public/-/emojis/1/flag_hr.png
index 46f4f06b4f2..46f4f06b4f2 100644
--- a/app/assets/images/emoji/flag_hr.png
+++ b/public/-/emojis/1/flag_hr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ht.png b/public/-/emojis/1/flag_ht.png
index d8d0c888498..d8d0c888498 100644
--- a/app/assets/images/emoji/flag_ht.png
+++ b/public/-/emojis/1/flag_ht.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_hu.png b/public/-/emojis/1/flag_hu.png
index a898de636a5..a898de636a5 100644
--- a/app/assets/images/emoji/flag_hu.png
+++ b/public/-/emojis/1/flag_hu.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ic.png b/public/-/emojis/1/flag_ic.png
index 69fd990aa95..69fd990aa95 100644
--- a/app/assets/images/emoji/flag_ic.png
+++ b/public/-/emojis/1/flag_ic.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_id.png b/public/-/emojis/1/flag_id.png
index 85b4c063a45..85b4c063a45 100644
--- a/app/assets/images/emoji/flag_id.png
+++ b/public/-/emojis/1/flag_id.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ie.png b/public/-/emojis/1/flag_ie.png
index a28295838cc..a28295838cc 100644
--- a/app/assets/images/emoji/flag_ie.png
+++ b/public/-/emojis/1/flag_ie.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_il.png b/public/-/emojis/1/flag_il.png
index 85c410d45fb..85c410d45fb 100644
--- a/app/assets/images/emoji/flag_il.png
+++ b/public/-/emojis/1/flag_il.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_im.png b/public/-/emojis/1/flag_im.png
index 60a2458e38e..60a2458e38e 100644
--- a/app/assets/images/emoji/flag_im.png
+++ b/public/-/emojis/1/flag_im.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_in.png b/public/-/emojis/1/flag_in.png
index feccc8952ce..feccc8952ce 100644
--- a/app/assets/images/emoji/flag_in.png
+++ b/public/-/emojis/1/flag_in.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_io.png b/public/-/emojis/1/flag_io.png
index aae927d14b8..aae927d14b8 100644
--- a/app/assets/images/emoji/flag_io.png
+++ b/public/-/emojis/1/flag_io.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_iq.png b/public/-/emojis/1/flag_iq.png
index 41fd1db6f86..41fd1db6f86 100644
--- a/app/assets/images/emoji/flag_iq.png
+++ b/public/-/emojis/1/flag_iq.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ir.png b/public/-/emojis/1/flag_ir.png
index ff7aaf62ba6..ff7aaf62ba6 100644
--- a/app/assets/images/emoji/flag_ir.png
+++ b/public/-/emojis/1/flag_ir.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_is.png b/public/-/emojis/1/flag_is.png
index ad8d4131dd2..ad8d4131dd2 100644
--- a/app/assets/images/emoji/flag_is.png
+++ b/public/-/emojis/1/flag_is.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_it.png b/public/-/emojis/1/flag_it.png
index f21563ec533..f21563ec533 100644
--- a/app/assets/images/emoji/flag_it.png
+++ b/public/-/emojis/1/flag_it.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_je.png b/public/-/emojis/1/flag_je.png
index 198a918f6a4..198a918f6a4 100644
--- a/app/assets/images/emoji/flag_je.png
+++ b/public/-/emojis/1/flag_je.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_jm.png b/public/-/emojis/1/flag_jm.png
index f84e4f9e8db..f84e4f9e8db 100644
--- a/app/assets/images/emoji/flag_jm.png
+++ b/public/-/emojis/1/flag_jm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_jo.png b/public/-/emojis/1/flag_jo.png
index 20bfa147e3e..20bfa147e3e 100644
--- a/app/assets/images/emoji/flag_jo.png
+++ b/public/-/emojis/1/flag_jo.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_jp.png b/public/-/emojis/1/flag_jp.png
index 8d8838e4708..8d8838e4708 100644
--- a/app/assets/images/emoji/flag_jp.png
+++ b/public/-/emojis/1/flag_jp.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ke.png b/public/-/emojis/1/flag_ke.png
index 9e417ab3009..9e417ab3009 100644
--- a/app/assets/images/emoji/flag_ke.png
+++ b/public/-/emojis/1/flag_ke.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_kg.png b/public/-/emojis/1/flag_kg.png
index 2f2d848fe58..2f2d848fe58 100644
--- a/app/assets/images/emoji/flag_kg.png
+++ b/public/-/emojis/1/flag_kg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_kh.png b/public/-/emojis/1/flag_kh.png
index 9a2877dd620..9a2877dd620 100644
--- a/app/assets/images/emoji/flag_kh.png
+++ b/public/-/emojis/1/flag_kh.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ki.png b/public/-/emojis/1/flag_ki.png
index 10e507e3245..10e507e3245 100644
--- a/app/assets/images/emoji/flag_ki.png
+++ b/public/-/emojis/1/flag_ki.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_km.png b/public/-/emojis/1/flag_km.png
index bd5a0588e03..bd5a0588e03 100644
--- a/app/assets/images/emoji/flag_km.png
+++ b/public/-/emojis/1/flag_km.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_kn.png b/public/-/emojis/1/flag_kn.png
index 776207c9605..776207c9605 100644
--- a/app/assets/images/emoji/flag_kn.png
+++ b/public/-/emojis/1/flag_kn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_kp.png b/public/-/emojis/1/flag_kp.png
index 6b3fd89eaaa..6b3fd89eaaa 100644
--- a/app/assets/images/emoji/flag_kp.png
+++ b/public/-/emojis/1/flag_kp.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_kr.png b/public/-/emojis/1/flag_kr.png
index 833a88116e1..833a88116e1 100644
--- a/app/assets/images/emoji/flag_kr.png
+++ b/public/-/emojis/1/flag_kr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_kw.png b/public/-/emojis/1/flag_kw.png
index 4d19bfa6ca7..4d19bfa6ca7 100644
--- a/app/assets/images/emoji/flag_kw.png
+++ b/public/-/emojis/1/flag_kw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ky.png b/public/-/emojis/1/flag_ky.png
index 40daa4da597..40daa4da597 100644
--- a/app/assets/images/emoji/flag_ky.png
+++ b/public/-/emojis/1/flag_ky.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_kz.png b/public/-/emojis/1/flag_kz.png
index 2f97a8fd3c6..2f97a8fd3c6 100644
--- a/app/assets/images/emoji/flag_kz.png
+++ b/public/-/emojis/1/flag_kz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_la.png b/public/-/emojis/1/flag_la.png
index 4d4179f34f6..4d4179f34f6 100644
--- a/app/assets/images/emoji/flag_la.png
+++ b/public/-/emojis/1/flag_la.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_lb.png b/public/-/emojis/1/flag_lb.png
index 3d594467011..3d594467011 100644
--- a/app/assets/images/emoji/flag_lb.png
+++ b/public/-/emojis/1/flag_lb.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_lc.png b/public/-/emojis/1/flag_lc.png
index 45547b1e439..45547b1e439 100644
--- a/app/assets/images/emoji/flag_lc.png
+++ b/public/-/emojis/1/flag_lc.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_li.png b/public/-/emojis/1/flag_li.png
index 0eafa6a2215..0eafa6a2215 100644
--- a/app/assets/images/emoji/flag_li.png
+++ b/public/-/emojis/1/flag_li.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_lk.png b/public/-/emojis/1/flag_lk.png
index ab4fe10c40c..ab4fe10c40c 100644
--- a/app/assets/images/emoji/flag_lk.png
+++ b/public/-/emojis/1/flag_lk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_lr.png b/public/-/emojis/1/flag_lr.png
index f66f267fea2..f66f267fea2 100644
--- a/app/assets/images/emoji/flag_lr.png
+++ b/public/-/emojis/1/flag_lr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ls.png b/public/-/emojis/1/flag_ls.png
index 24745631e3c..24745631e3c 100644
--- a/app/assets/images/emoji/flag_ls.png
+++ b/public/-/emojis/1/flag_ls.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_lt.png b/public/-/emojis/1/flag_lt.png
index d644b56d62a..d644b56d62a 100644
--- a/app/assets/images/emoji/flag_lt.png
+++ b/public/-/emojis/1/flag_lt.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_lu.png b/public/-/emojis/1/flag_lu.png
index a2df9c92994..a2df9c92994 100644
--- a/app/assets/images/emoji/flag_lu.png
+++ b/public/-/emojis/1/flag_lu.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_lv.png b/public/-/emojis/1/flag_lv.png
index ae680d5f0e3..ae680d5f0e3 100644
--- a/app/assets/images/emoji/flag_lv.png
+++ b/public/-/emojis/1/flag_lv.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ly.png b/public/-/emojis/1/flag_ly.png
index f6e77b0f3ba..f6e77b0f3ba 100644
--- a/app/assets/images/emoji/flag_ly.png
+++ b/public/-/emojis/1/flag_ly.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ma.png b/public/-/emojis/1/flag_ma.png
index c4a056722cd..c4a056722cd 100644
--- a/app/assets/images/emoji/flag_ma.png
+++ b/public/-/emojis/1/flag_ma.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mc.png b/public/-/emojis/1/flag_mc.png
index d479eab98cb..d479eab98cb 100644
--- a/app/assets/images/emoji/flag_mc.png
+++ b/public/-/emojis/1/flag_mc.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_md.png b/public/-/emojis/1/flag_md.png
index a7a72539872..a7a72539872 100644
--- a/app/assets/images/emoji/flag_md.png
+++ b/public/-/emojis/1/flag_md.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_me.png b/public/-/emojis/1/flag_me.png
index 7c771e7e120..7c771e7e120 100644
--- a/app/assets/images/emoji/flag_me.png
+++ b/public/-/emojis/1/flag_me.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mf.png b/public/-/emojis/1/flag_mf.png
index 70c761036bd..70c761036bd 100644
--- a/app/assets/images/emoji/flag_mf.png
+++ b/public/-/emojis/1/flag_mf.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mg.png b/public/-/emojis/1/flag_mg.png
index 2f3ccdda76f..2f3ccdda76f 100644
--- a/app/assets/images/emoji/flag_mg.png
+++ b/public/-/emojis/1/flag_mg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mh.png b/public/-/emojis/1/flag_mh.png
index 598016481c1..598016481c1 100644
--- a/app/assets/images/emoji/flag_mh.png
+++ b/public/-/emojis/1/flag_mh.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mk.png b/public/-/emojis/1/flag_mk.png
index 7ba775ee75c..7ba775ee75c 100644
--- a/app/assets/images/emoji/flag_mk.png
+++ b/public/-/emojis/1/flag_mk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ml.png b/public/-/emojis/1/flag_ml.png
index 68343785468..68343785468 100644
--- a/app/assets/images/emoji/flag_ml.png
+++ b/public/-/emojis/1/flag_ml.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mm.png b/public/-/emojis/1/flag_mm.png
index 37dc7d71591..37dc7d71591 100644
--- a/app/assets/images/emoji/flag_mm.png
+++ b/public/-/emojis/1/flag_mm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mn.png b/public/-/emojis/1/flag_mn.png
index 1f146bbcd1a..1f146bbcd1a 100644
--- a/app/assets/images/emoji/flag_mn.png
+++ b/public/-/emojis/1/flag_mn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mo.png b/public/-/emojis/1/flag_mo.png
index 7edde31f64b..7edde31f64b 100644
--- a/app/assets/images/emoji/flag_mo.png
+++ b/public/-/emojis/1/flag_mo.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mp.png b/public/-/emojis/1/flag_mp.png
index 17ec1c441ed..17ec1c441ed 100644
--- a/app/assets/images/emoji/flag_mp.png
+++ b/public/-/emojis/1/flag_mp.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mq.png b/public/-/emojis/1/flag_mq.png
index 1e672dc9087..1e672dc9087 100644
--- a/app/assets/images/emoji/flag_mq.png
+++ b/public/-/emojis/1/flag_mq.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mr.png b/public/-/emojis/1/flag_mr.png
index f87de46effe..f87de46effe 100644
--- a/app/assets/images/emoji/flag_mr.png
+++ b/public/-/emojis/1/flag_mr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ms.png b/public/-/emojis/1/flag_ms.png
index 480b0d4ebda..480b0d4ebda 100644
--- a/app/assets/images/emoji/flag_ms.png
+++ b/public/-/emojis/1/flag_ms.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mt.png b/public/-/emojis/1/flag_mt.png
index c9e1dbdce82..c9e1dbdce82 100644
--- a/app/assets/images/emoji/flag_mt.png
+++ b/public/-/emojis/1/flag_mt.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mu.png b/public/-/emojis/1/flag_mu.png
index 55b33cb7c33..55b33cb7c33 100644
--- a/app/assets/images/emoji/flag_mu.png
+++ b/public/-/emojis/1/flag_mu.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mv.png b/public/-/emojis/1/flag_mv.png
index ce5867126ae..ce5867126ae 100644
--- a/app/assets/images/emoji/flag_mv.png
+++ b/public/-/emojis/1/flag_mv.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mw.png b/public/-/emojis/1/flag_mw.png
index 003d8548401..003d8548401 100644
--- a/app/assets/images/emoji/flag_mw.png
+++ b/public/-/emojis/1/flag_mw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mx.png b/public/-/emojis/1/flag_mx.png
index 42572bcd0ba..42572bcd0ba 100644
--- a/app/assets/images/emoji/flag_mx.png
+++ b/public/-/emojis/1/flag_mx.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_my.png b/public/-/emojis/1/flag_my.png
index 17526c26742..17526c26742 100644
--- a/app/assets/images/emoji/flag_my.png
+++ b/public/-/emojis/1/flag_my.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_mz.png b/public/-/emojis/1/flag_mz.png
index 2352a78e786..2352a78e786 100644
--- a/app/assets/images/emoji/flag_mz.png
+++ b/public/-/emojis/1/flag_mz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_na.png b/public/-/emojis/1/flag_na.png
index ed31c3df04d..ed31c3df04d 100644
--- a/app/assets/images/emoji/flag_na.png
+++ b/public/-/emojis/1/flag_na.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_nc.png b/public/-/emojis/1/flag_nc.png
index 90b3afebfa3..90b3afebfa3 100644
--- a/app/assets/images/emoji/flag_nc.png
+++ b/public/-/emojis/1/flag_nc.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ne.png b/public/-/emojis/1/flag_ne.png
index f98a1173c2a..f98a1173c2a 100644
--- a/app/assets/images/emoji/flag_ne.png
+++ b/public/-/emojis/1/flag_ne.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_nf.png b/public/-/emojis/1/flag_nf.png
index 9099e767420..9099e767420 100644
--- a/app/assets/images/emoji/flag_nf.png
+++ b/public/-/emojis/1/flag_nf.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ng.png b/public/-/emojis/1/flag_ng.png
index ea0abeff1a1..ea0abeff1a1 100644
--- a/app/assets/images/emoji/flag_ng.png
+++ b/public/-/emojis/1/flag_ng.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ni.png b/public/-/emojis/1/flag_ni.png
index 772920dfa10..772920dfa10 100644
--- a/app/assets/images/emoji/flag_ni.png
+++ b/public/-/emojis/1/flag_ni.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_nl.png b/public/-/emojis/1/flag_nl.png
index 83a0e817e41..83a0e817e41 100644
--- a/app/assets/images/emoji/flag_nl.png
+++ b/public/-/emojis/1/flag_nl.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_no.png b/public/-/emojis/1/flag_no.png
index 99d3142eb7b..99d3142eb7b 100644
--- a/app/assets/images/emoji/flag_no.png
+++ b/public/-/emojis/1/flag_no.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_np.png b/public/-/emojis/1/flag_np.png
index 87425a8dfef..87425a8dfef 100644
--- a/app/assets/images/emoji/flag_np.png
+++ b/public/-/emojis/1/flag_np.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_nr.png b/public/-/emojis/1/flag_nr.png
index b3e3a5d5621..b3e3a5d5621 100644
--- a/app/assets/images/emoji/flag_nr.png
+++ b/public/-/emojis/1/flag_nr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_nu.png b/public/-/emojis/1/flag_nu.png
index f03614443ee..f03614443ee 100644
--- a/app/assets/images/emoji/flag_nu.png
+++ b/public/-/emojis/1/flag_nu.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_nz.png b/public/-/emojis/1/flag_nz.png
index a4eeeab9cd9..a4eeeab9cd9 100644
--- a/app/assets/images/emoji/flag_nz.png
+++ b/public/-/emojis/1/flag_nz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_om.png b/public/-/emojis/1/flag_om.png
index ea824ba31e7..ea824ba31e7 100644
--- a/app/assets/images/emoji/flag_om.png
+++ b/public/-/emojis/1/flag_om.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pa.png b/public/-/emojis/1/flag_pa.png
index c3091d89889..c3091d89889 100644
--- a/app/assets/images/emoji/flag_pa.png
+++ b/public/-/emojis/1/flag_pa.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pe.png b/public/-/emojis/1/flag_pe.png
index 39223aa9dbb..39223aa9dbb 100644
--- a/app/assets/images/emoji/flag_pe.png
+++ b/public/-/emojis/1/flag_pe.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pf.png b/public/-/emojis/1/flag_pf.png
index 113445f8f6e..113445f8f6e 100644
--- a/app/assets/images/emoji/flag_pf.png
+++ b/public/-/emojis/1/flag_pf.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pg.png b/public/-/emojis/1/flag_pg.png
index 825e9dcb762..825e9dcb762 100644
--- a/app/assets/images/emoji/flag_pg.png
+++ b/public/-/emojis/1/flag_pg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ph.png b/public/-/emojis/1/flag_ph.png
index 8260e15bd2c..8260e15bd2c 100644
--- a/app/assets/images/emoji/flag_ph.png
+++ b/public/-/emojis/1/flag_ph.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pk.png b/public/-/emojis/1/flag_pk.png
index a7b6a1c5074..a7b6a1c5074 100644
--- a/app/assets/images/emoji/flag_pk.png
+++ b/public/-/emojis/1/flag_pk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pl.png b/public/-/emojis/1/flag_pl.png
index 19de2edec11..19de2edec11 100644
--- a/app/assets/images/emoji/flag_pl.png
+++ b/public/-/emojis/1/flag_pl.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pm.png b/public/-/emojis/1/flag_pm.png
index 2ca60554193..2ca60554193 100644
--- a/app/assets/images/emoji/flag_pm.png
+++ b/public/-/emojis/1/flag_pm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pn.png b/public/-/emojis/1/flag_pn.png
index f2263b154bc..f2263b154bc 100644
--- a/app/assets/images/emoji/flag_pn.png
+++ b/public/-/emojis/1/flag_pn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pr.png b/public/-/emojis/1/flag_pr.png
index d0209cddb79..d0209cddb79 100644
--- a/app/assets/images/emoji/flag_pr.png
+++ b/public/-/emojis/1/flag_pr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ps.png b/public/-/emojis/1/flag_ps.png
index 7ccab09778b..7ccab09778b 100644
--- a/app/assets/images/emoji/flag_ps.png
+++ b/public/-/emojis/1/flag_ps.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pt.png b/public/-/emojis/1/flag_pt.png
index cc93f27c64b..cc93f27c64b 100644
--- a/app/assets/images/emoji/flag_pt.png
+++ b/public/-/emojis/1/flag_pt.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_pw.png b/public/-/emojis/1/flag_pw.png
index 154b2f12d3c..154b2f12d3c 100644
--- a/app/assets/images/emoji/flag_pw.png
+++ b/public/-/emojis/1/flag_pw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_py.png b/public/-/emojis/1/flag_py.png
index 662ad2f6ff1..662ad2f6ff1 100644
--- a/app/assets/images/emoji/flag_py.png
+++ b/public/-/emojis/1/flag_py.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_qa.png b/public/-/emojis/1/flag_qa.png
index a01d8b05cc7..a01d8b05cc7 100644
--- a/app/assets/images/emoji/flag_qa.png
+++ b/public/-/emojis/1/flag_qa.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_re.png b/public/-/emojis/1/flag_re.png
index 57f2bbe9df8..57f2bbe9df8 100644
--- a/app/assets/images/emoji/flag_re.png
+++ b/public/-/emojis/1/flag_re.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ro.png b/public/-/emojis/1/flag_ro.png
index 3e48c447706..3e48c447706 100644
--- a/app/assets/images/emoji/flag_ro.png
+++ b/public/-/emojis/1/flag_ro.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_rs.png b/public/-/emojis/1/flag_rs.png
index 9df6c9a5235..9df6c9a5235 100644
--- a/app/assets/images/emoji/flag_rs.png
+++ b/public/-/emojis/1/flag_rs.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ru.png b/public/-/emojis/1/flag_ru.png
index e50c9db90e7..e50c9db90e7 100644
--- a/app/assets/images/emoji/flag_ru.png
+++ b/public/-/emojis/1/flag_ru.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_rw.png b/public/-/emojis/1/flag_rw.png
index c238c874e1d..c238c874e1d 100644
--- a/app/assets/images/emoji/flag_rw.png
+++ b/public/-/emojis/1/flag_rw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sa.png b/public/-/emojis/1/flag_sa.png
index 4941be7d198..4941be7d198 100644
--- a/app/assets/images/emoji/flag_sa.png
+++ b/public/-/emojis/1/flag_sa.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sb.png b/public/-/emojis/1/flag_sb.png
index 7d8f1ac6130..7d8f1ac6130 100644
--- a/app/assets/images/emoji/flag_sb.png
+++ b/public/-/emojis/1/flag_sb.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sc.png b/public/-/emojis/1/flag_sc.png
index 6ae4d90765e..6ae4d90765e 100644
--- a/app/assets/images/emoji/flag_sc.png
+++ b/public/-/emojis/1/flag_sc.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sd.png b/public/-/emojis/1/flag_sd.png
index 963be1b36fb..963be1b36fb 100644
--- a/app/assets/images/emoji/flag_sd.png
+++ b/public/-/emojis/1/flag_sd.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_se.png b/public/-/emojis/1/flag_se.png
index fc0d0e0ce89..fc0d0e0ce89 100644
--- a/app/assets/images/emoji/flag_se.png
+++ b/public/-/emojis/1/flag_se.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sg.png b/public/-/emojis/1/flag_sg.png
index de3c7737c42..de3c7737c42 100644
--- a/app/assets/images/emoji/flag_sg.png
+++ b/public/-/emojis/1/flag_sg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sh.png b/public/-/emojis/1/flag_sh.png
index 40cd9e44e96..40cd9e44e96 100644
--- a/app/assets/images/emoji/flag_sh.png
+++ b/public/-/emojis/1/flag_sh.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_si.png b/public/-/emojis/1/flag_si.png
index e308999dba2..e308999dba2 100644
--- a/app/assets/images/emoji/flag_si.png
+++ b/public/-/emojis/1/flag_si.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sj.png b/public/-/emojis/1/flag_sj.png
index 5884e648228..5884e648228 100644
--- a/app/assets/images/emoji/flag_sj.png
+++ b/public/-/emojis/1/flag_sj.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sk.png b/public/-/emojis/1/flag_sk.png
index 4259d0e1418..4259d0e1418 100644
--- a/app/assets/images/emoji/flag_sk.png
+++ b/public/-/emojis/1/flag_sk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sl.png b/public/-/emojis/1/flag_sl.png
index d2cc68830ab..d2cc68830ab 100644
--- a/app/assets/images/emoji/flag_sl.png
+++ b/public/-/emojis/1/flag_sl.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sm.png b/public/-/emojis/1/flag_sm.png
index 03b8708754e..03b8708754e 100644
--- a/app/assets/images/emoji/flag_sm.png
+++ b/public/-/emojis/1/flag_sm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sn.png b/public/-/emojis/1/flag_sn.png
index 5368bbe93df..5368bbe93df 100644
--- a/app/assets/images/emoji/flag_sn.png
+++ b/public/-/emojis/1/flag_sn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_so.png b/public/-/emojis/1/flag_so.png
index 68a0597365a..68a0597365a 100644
--- a/app/assets/images/emoji/flag_so.png
+++ b/public/-/emojis/1/flag_so.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sr.png b/public/-/emojis/1/flag_sr.png
index d3251327035..d3251327035 100644
--- a/app/assets/images/emoji/flag_sr.png
+++ b/public/-/emojis/1/flag_sr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ss.png b/public/-/emojis/1/flag_ss.png
index 122977e798f..122977e798f 100644
--- a/app/assets/images/emoji/flag_ss.png
+++ b/public/-/emojis/1/flag_ss.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_st.png b/public/-/emojis/1/flag_st.png
index f83a863d612..f83a863d612 100644
--- a/app/assets/images/emoji/flag_st.png
+++ b/public/-/emojis/1/flag_st.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sv.png b/public/-/emojis/1/flag_sv.png
index efb83e2f253..efb83e2f253 100644
--- a/app/assets/images/emoji/flag_sv.png
+++ b/public/-/emojis/1/flag_sv.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sx.png b/public/-/emojis/1/flag_sx.png
index 94b760fbedf..94b760fbedf 100644
--- a/app/assets/images/emoji/flag_sx.png
+++ b/public/-/emojis/1/flag_sx.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sy.png b/public/-/emojis/1/flag_sy.png
index 09a8ee8f78c..09a8ee8f78c 100644
--- a/app/assets/images/emoji/flag_sy.png
+++ b/public/-/emojis/1/flag_sy.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_sz.png b/public/-/emojis/1/flag_sz.png
index f74e82ea1fd..f74e82ea1fd 100644
--- a/app/assets/images/emoji/flag_sz.png
+++ b/public/-/emojis/1/flag_sz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ta.png b/public/-/emojis/1/flag_ta.png
index b44283e90e2..b44283e90e2 100644
--- a/app/assets/images/emoji/flag_ta.png
+++ b/public/-/emojis/1/flag_ta.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tc.png b/public/-/emojis/1/flag_tc.png
index 156b33d1ba6..156b33d1ba6 100644
--- a/app/assets/images/emoji/flag_tc.png
+++ b/public/-/emojis/1/flag_tc.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_td.png b/public/-/emojis/1/flag_td.png
index ebe7f592828..ebe7f592828 100644
--- a/app/assets/images/emoji/flag_td.png
+++ b/public/-/emojis/1/flag_td.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tf.png b/public/-/emojis/1/flag_tf.png
index a1a3ad68ee2..a1a3ad68ee2 100644
--- a/app/assets/images/emoji/flag_tf.png
+++ b/public/-/emojis/1/flag_tf.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tg.png b/public/-/emojis/1/flag_tg.png
index 826b73c9ac5..826b73c9ac5 100644
--- a/app/assets/images/emoji/flag_tg.png
+++ b/public/-/emojis/1/flag_tg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_th.png b/public/-/emojis/1/flag_th.png
index 93ff542c5a6..93ff542c5a6 100644
--- a/app/assets/images/emoji/flag_th.png
+++ b/public/-/emojis/1/flag_th.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tj.png b/public/-/emojis/1/flag_tj.png
index 7a8a0b6190a..7a8a0b6190a 100644
--- a/app/assets/images/emoji/flag_tj.png
+++ b/public/-/emojis/1/flag_tj.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tk.png b/public/-/emojis/1/flag_tk.png
index 2fa5a21b1bb..2fa5a21b1bb 100644
--- a/app/assets/images/emoji/flag_tk.png
+++ b/public/-/emojis/1/flag_tk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tl.png b/public/-/emojis/1/flag_tl.png
index 5b120eccc6f..5b120eccc6f 100644
--- a/app/assets/images/emoji/flag_tl.png
+++ b/public/-/emojis/1/flag_tl.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tm.png b/public/-/emojis/1/flag_tm.png
index c3c4f532302..c3c4f532302 100644
--- a/app/assets/images/emoji/flag_tm.png
+++ b/public/-/emojis/1/flag_tm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tn.png b/public/-/emojis/1/flag_tn.png
index 58ef161229f..58ef161229f 100644
--- a/app/assets/images/emoji/flag_tn.png
+++ b/public/-/emojis/1/flag_tn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_to.png b/public/-/emojis/1/flag_to.png
index 1ffa7bb9d19..1ffa7bb9d19 100644
--- a/app/assets/images/emoji/flag_to.png
+++ b/public/-/emojis/1/flag_to.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tr.png b/public/-/emojis/1/flag_tr.png
index 325251fae88..325251fae88 100644
--- a/app/assets/images/emoji/flag_tr.png
+++ b/public/-/emojis/1/flag_tr.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tt.png b/public/-/emojis/1/flag_tt.png
index ed3bb39a300..ed3bb39a300 100644
--- a/app/assets/images/emoji/flag_tt.png
+++ b/public/-/emojis/1/flag_tt.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tv.png b/public/-/emojis/1/flag_tv.png
index e82c65c7bb9..e82c65c7bb9 100644
--- a/app/assets/images/emoji/flag_tv.png
+++ b/public/-/emojis/1/flag_tv.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tw.png b/public/-/emojis/1/flag_tw.png
index 3a8f00b5928..3a8f00b5928 100644
--- a/app/assets/images/emoji/flag_tw.png
+++ b/public/-/emojis/1/flag_tw.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_tz.png b/public/-/emojis/1/flag_tz.png
index 2a020853d4e..2a020853d4e 100644
--- a/app/assets/images/emoji/flag_tz.png
+++ b/public/-/emojis/1/flag_tz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ua.png b/public/-/emojis/1/flag_ua.png
index cd84d1bbd36..cd84d1bbd36 100644
--- a/app/assets/images/emoji/flag_ua.png
+++ b/public/-/emojis/1/flag_ua.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ug.png b/public/-/emojis/1/flag_ug.png
index dc97690eb55..dc97690eb55 100644
--- a/app/assets/images/emoji/flag_ug.png
+++ b/public/-/emojis/1/flag_ug.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_um.png b/public/-/emojis/1/flag_um.png
index 4a7ee3cdf13..4a7ee3cdf13 100644
--- a/app/assets/images/emoji/flag_um.png
+++ b/public/-/emojis/1/flag_um.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_us.png b/public/-/emojis/1/flag_us.png
index 9f730305860..9f730305860 100644
--- a/app/assets/images/emoji/flag_us.png
+++ b/public/-/emojis/1/flag_us.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_uy.png b/public/-/emojis/1/flag_uy.png
index b8002a697a6..b8002a697a6 100644
--- a/app/assets/images/emoji/flag_uy.png
+++ b/public/-/emojis/1/flag_uy.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_uz.png b/public/-/emojis/1/flag_uz.png
index d56ca9bc424..d56ca9bc424 100644
--- a/app/assets/images/emoji/flag_uz.png
+++ b/public/-/emojis/1/flag_uz.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_va.png b/public/-/emojis/1/flag_va.png
index ddaf5e3141b..ddaf5e3141b 100644
--- a/app/assets/images/emoji/flag_va.png
+++ b/public/-/emojis/1/flag_va.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_vc.png b/public/-/emojis/1/flag_vc.png
index 43703c62a71..43703c62a71 100644
--- a/app/assets/images/emoji/flag_vc.png
+++ b/public/-/emojis/1/flag_vc.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ve.png b/public/-/emojis/1/flag_ve.png
index 1b62796824e..1b62796824e 100644
--- a/app/assets/images/emoji/flag_ve.png
+++ b/public/-/emojis/1/flag_ve.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_vg.png b/public/-/emojis/1/flag_vg.png
index 536f780f1c0..536f780f1c0 100644
--- a/app/assets/images/emoji/flag_vg.png
+++ b/public/-/emojis/1/flag_vg.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_vi.png b/public/-/emojis/1/flag_vi.png
index 64102012cfe..64102012cfe 100644
--- a/app/assets/images/emoji/flag_vi.png
+++ b/public/-/emojis/1/flag_vi.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_vn.png b/public/-/emojis/1/flag_vn.png
index 427036046b6..427036046b6 100644
--- a/app/assets/images/emoji/flag_vn.png
+++ b/public/-/emojis/1/flag_vn.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_vu.png b/public/-/emojis/1/flag_vu.png
index 706eba44070..706eba44070 100644
--- a/app/assets/images/emoji/flag_vu.png
+++ b/public/-/emojis/1/flag_vu.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_wf.png b/public/-/emojis/1/flag_wf.png
index 70c761036bd..70c761036bd 100644
--- a/app/assets/images/emoji/flag_wf.png
+++ b/public/-/emojis/1/flag_wf.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_white.png b/public/-/emojis/1/flag_white.png
index 86d6e96d5e9..86d6e96d5e9 100644
--- a/app/assets/images/emoji/flag_white.png
+++ b/public/-/emojis/1/flag_white.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ws.png b/public/-/emojis/1/flag_ws.png
index a1ea0703141..a1ea0703141 100644
--- a/app/assets/images/emoji/flag_ws.png
+++ b/public/-/emojis/1/flag_ws.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_xk.png b/public/-/emojis/1/flag_xk.png
index e587a446632..e587a446632 100644
--- a/app/assets/images/emoji/flag_xk.png
+++ b/public/-/emojis/1/flag_xk.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_ye.png b/public/-/emojis/1/flag_ye.png
index eadfebd5f67..eadfebd5f67 100644
--- a/app/assets/images/emoji/flag_ye.png
+++ b/public/-/emojis/1/flag_ye.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_yt.png b/public/-/emojis/1/flag_yt.png
index c81fa6d886e..c81fa6d886e 100644
--- a/app/assets/images/emoji/flag_yt.png
+++ b/public/-/emojis/1/flag_yt.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_za.png b/public/-/emojis/1/flag_za.png
index f397ef5072f..f397ef5072f 100644
--- a/app/assets/images/emoji/flag_za.png
+++ b/public/-/emojis/1/flag_za.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_zm.png b/public/-/emojis/1/flag_zm.png
index 2494a31f662..2494a31f662 100644
--- a/app/assets/images/emoji/flag_zm.png
+++ b/public/-/emojis/1/flag_zm.png
Binary files differ
diff --git a/app/assets/images/emoji/flag_zw.png b/public/-/emojis/1/flag_zw.png
index e09b9652be6..e09b9652be6 100644
--- a/app/assets/images/emoji/flag_zw.png
+++ b/public/-/emojis/1/flag_zw.png
Binary files differ
diff --git a/app/assets/images/emoji/flags.png b/public/-/emojis/1/flags.png
index 3b451035a3a..3b451035a3a 100644
--- a/app/assets/images/emoji/flags.png
+++ b/public/-/emojis/1/flags.png
Binary files differ
diff --git a/app/assets/images/emoji/flashlight.png b/public/-/emojis/1/flashlight.png
index eee36c25067..eee36c25067 100644
--- a/app/assets/images/emoji/flashlight.png
+++ b/public/-/emojis/1/flashlight.png
Binary files differ
diff --git a/app/assets/images/emoji/fleur-de-lis.png b/public/-/emojis/1/fleur-de-lis.png
index c9250d27fa7..c9250d27fa7 100644
--- a/app/assets/images/emoji/fleur-de-lis.png
+++ b/public/-/emojis/1/fleur-de-lis.png
Binary files differ
diff --git a/app/assets/images/emoji/floppy_disk.png b/public/-/emojis/1/floppy_disk.png
index 072a76d3c13..072a76d3c13 100644
--- a/app/assets/images/emoji/floppy_disk.png
+++ b/public/-/emojis/1/floppy_disk.png
Binary files differ
diff --git a/app/assets/images/emoji/flower_playing_cards.png b/public/-/emojis/1/flower_playing_cards.png
index 6766b044d95..6766b044d95 100644
--- a/app/assets/images/emoji/flower_playing_cards.png
+++ b/public/-/emojis/1/flower_playing_cards.png
Binary files differ
diff --git a/app/assets/images/emoji/flushed.png b/public/-/emojis/1/flushed.png
index 829220bc470..829220bc470 100644
--- a/app/assets/images/emoji/flushed.png
+++ b/public/-/emojis/1/flushed.png
Binary files differ
diff --git a/app/assets/images/emoji/fog.png b/public/-/emojis/1/fog.png
index 4e73c2de272..4e73c2de272 100644
--- a/app/assets/images/emoji/fog.png
+++ b/public/-/emojis/1/fog.png
Binary files differ
diff --git a/app/assets/images/emoji/foggy.png b/public/-/emojis/1/foggy.png
index 57702d8d3ac..57702d8d3ac 100644
--- a/app/assets/images/emoji/foggy.png
+++ b/public/-/emojis/1/foggy.png
Binary files differ
diff --git a/app/assets/images/emoji/football.png b/public/-/emojis/1/football.png
index 10366f41fce..10366f41fce 100644
--- a/app/assets/images/emoji/football.png
+++ b/public/-/emojis/1/football.png
Binary files differ
diff --git a/app/assets/images/emoji/footprints.png b/public/-/emojis/1/footprints.png
index b2673c5a1a8..b2673c5a1a8 100644
--- a/app/assets/images/emoji/footprints.png
+++ b/public/-/emojis/1/footprints.png
Binary files differ
diff --git a/app/assets/images/emoji/fork_and_knife.png b/public/-/emojis/1/fork_and_knife.png
index 09f1feaea1c..09f1feaea1c 100644
--- a/app/assets/images/emoji/fork_and_knife.png
+++ b/public/-/emojis/1/fork_and_knife.png
Binary files differ
diff --git a/app/assets/images/emoji/fork_knife_plate.png b/public/-/emojis/1/fork_knife_plate.png
index 7411755f708..7411755f708 100644
--- a/app/assets/images/emoji/fork_knife_plate.png
+++ b/public/-/emojis/1/fork_knife_plate.png
Binary files differ
diff --git a/app/assets/images/emoji/fountain.png b/public/-/emojis/1/fountain.png
index 293f5d91c0f..293f5d91c0f 100644
--- a/app/assets/images/emoji/fountain.png
+++ b/public/-/emojis/1/fountain.png
Binary files differ
diff --git a/app/assets/images/emoji/four.png b/public/-/emojis/1/four.png
index b0e914aac45..b0e914aac45 100644
--- a/app/assets/images/emoji/four.png
+++ b/public/-/emojis/1/four.png
Binary files differ
diff --git a/app/assets/images/emoji/four_leaf_clover.png b/public/-/emojis/1/four_leaf_clover.png
index fdedfcc2b4e..fdedfcc2b4e 100644
--- a/app/assets/images/emoji/four_leaf_clover.png
+++ b/public/-/emojis/1/four_leaf_clover.png
Binary files differ
diff --git a/app/assets/images/emoji/fox.png b/public/-/emojis/1/fox.png
index 1ab339bf054..1ab339bf054 100644
--- a/app/assets/images/emoji/fox.png
+++ b/public/-/emojis/1/fox.png
Binary files differ
diff --git a/app/assets/images/emoji/frame_photo.png b/public/-/emojis/1/frame_photo.png
index 9fe84607bfd..9fe84607bfd 100644
--- a/app/assets/images/emoji/frame_photo.png
+++ b/public/-/emojis/1/frame_photo.png
Binary files differ
diff --git a/app/assets/images/emoji/free.png b/public/-/emojis/1/free.png
index b71956eb48a..b71956eb48a 100644
--- a/app/assets/images/emoji/free.png
+++ b/public/-/emojis/1/free.png
Binary files differ
diff --git a/app/assets/images/emoji/french_bread.png b/public/-/emojis/1/french_bread.png
index 4c2c5639822..4c2c5639822 100644
--- a/app/assets/images/emoji/french_bread.png
+++ b/public/-/emojis/1/french_bread.png
Binary files differ
diff --git a/app/assets/images/emoji/fried_shrimp.png b/public/-/emojis/1/fried_shrimp.png
index 752ba7f1398..752ba7f1398 100644
--- a/app/assets/images/emoji/fried_shrimp.png
+++ b/public/-/emojis/1/fried_shrimp.png
Binary files differ
diff --git a/app/assets/images/emoji/fries.png b/public/-/emojis/1/fries.png
index 4e2a4caacef..4e2a4caacef 100644
--- a/app/assets/images/emoji/fries.png
+++ b/public/-/emojis/1/fries.png
Binary files differ
diff --git a/app/assets/images/emoji/frog.png b/public/-/emojis/1/frog.png
index 8825d1ad577..8825d1ad577 100644
--- a/app/assets/images/emoji/frog.png
+++ b/public/-/emojis/1/frog.png
Binary files differ
diff --git a/app/assets/images/emoji/frowning.png b/public/-/emojis/1/frowning.png
index 43ab6b0a1c1..43ab6b0a1c1 100644
--- a/app/assets/images/emoji/frowning.png
+++ b/public/-/emojis/1/frowning.png
Binary files differ
diff --git a/app/assets/images/emoji/frowning2.png b/public/-/emojis/1/frowning2.png
index 6ae71f233b9..6ae71f233b9 100644
--- a/app/assets/images/emoji/frowning2.png
+++ b/public/-/emojis/1/frowning2.png
Binary files differ
diff --git a/app/assets/images/emoji/fuelpump.png b/public/-/emojis/1/fuelpump.png
index 05b18794474..05b18794474 100644
--- a/app/assets/images/emoji/fuelpump.png
+++ b/public/-/emojis/1/fuelpump.png
Binary files differ
diff --git a/app/assets/images/emoji/full_moon.png b/public/-/emojis/1/full_moon.png
index c9a2d6aa7c9..c9a2d6aa7c9 100644
--- a/app/assets/images/emoji/full_moon.png
+++ b/public/-/emojis/1/full_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/full_moon_with_face.png b/public/-/emojis/1/full_moon_with_face.png
index a5c25bbaf64..a5c25bbaf64 100644
--- a/app/assets/images/emoji/full_moon_with_face.png
+++ b/public/-/emojis/1/full_moon_with_face.png
Binary files differ
diff --git a/app/assets/images/emoji/game_die.png b/public/-/emojis/1/game_die.png
index ad3626fe5e5..ad3626fe5e5 100644
--- a/app/assets/images/emoji/game_die.png
+++ b/public/-/emojis/1/game_die.png
Binary files differ
diff --git a/app/assets/images/emoji/gay_pride_flag.png b/public/-/emojis/1/gay_pride_flag.png
index 1bec5f2ffd7..1bec5f2ffd7 100644
--- a/app/assets/images/emoji/gay_pride_flag.png
+++ b/public/-/emojis/1/gay_pride_flag.png
Binary files differ
diff --git a/app/assets/images/emoji/gear.png b/public/-/emojis/1/gear.png
index 2a1cc2c0ff4..2a1cc2c0ff4 100644
--- a/app/assets/images/emoji/gear.png
+++ b/public/-/emojis/1/gear.png
Binary files differ
diff --git a/app/assets/images/emoji/gem.png b/public/-/emojis/1/gem.png
index db122d26a19..db122d26a19 100644
--- a/app/assets/images/emoji/gem.png
+++ b/public/-/emojis/1/gem.png
Binary files differ
diff --git a/app/assets/images/emoji/gemini.png b/public/-/emojis/1/gemini.png
index 1a09698cf00..1a09698cf00 100644
--- a/app/assets/images/emoji/gemini.png
+++ b/public/-/emojis/1/gemini.png
Binary files differ
diff --git a/app/assets/images/emoji/ghost.png b/public/-/emojis/1/ghost.png
index 5650bc0ed18..5650bc0ed18 100644
--- a/app/assets/images/emoji/ghost.png
+++ b/public/-/emojis/1/ghost.png
Binary files differ
diff --git a/app/assets/images/emoji/gift.png b/public/-/emojis/1/gift.png
index 844e2164560..844e2164560 100644
--- a/app/assets/images/emoji/gift.png
+++ b/public/-/emojis/1/gift.png
Binary files differ
diff --git a/app/assets/images/emoji/gift_heart.png b/public/-/emojis/1/gift_heart.png
index 902ceafe4d1..902ceafe4d1 100644
--- a/app/assets/images/emoji/gift_heart.png
+++ b/public/-/emojis/1/gift_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/girl.png b/public/-/emojis/1/girl.png
index dc1d4d08b39..dc1d4d08b39 100644
--- a/app/assets/images/emoji/girl.png
+++ b/public/-/emojis/1/girl.png
Binary files differ
diff --git a/app/assets/images/emoji/girl_tone1.png b/public/-/emojis/1/girl_tone1.png
index bb667e88651..bb667e88651 100644
--- a/app/assets/images/emoji/girl_tone1.png
+++ b/public/-/emojis/1/girl_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/girl_tone2.png b/public/-/emojis/1/girl_tone2.png
index a59ed4a3f0d..a59ed4a3f0d 100644
--- a/app/assets/images/emoji/girl_tone2.png
+++ b/public/-/emojis/1/girl_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/girl_tone3.png b/public/-/emojis/1/girl_tone3.png
index 517e7f2a7b0..517e7f2a7b0 100644
--- a/app/assets/images/emoji/girl_tone3.png
+++ b/public/-/emojis/1/girl_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/girl_tone4.png b/public/-/emojis/1/girl_tone4.png
index 542d96c8487..542d96c8487 100644
--- a/app/assets/images/emoji/girl_tone4.png
+++ b/public/-/emojis/1/girl_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/girl_tone5.png b/public/-/emojis/1/girl_tone5.png
index 66b7c28c2df..66b7c28c2df 100644
--- a/app/assets/images/emoji/girl_tone5.png
+++ b/public/-/emojis/1/girl_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/globe_with_meridians.png b/public/-/emojis/1/globe_with_meridians.png
index 82450c1a4ba..82450c1a4ba 100644
--- a/app/assets/images/emoji/globe_with_meridians.png
+++ b/public/-/emojis/1/globe_with_meridians.png
Binary files differ
diff --git a/app/assets/images/emoji/goal.png b/public/-/emojis/1/goal.png
index df3a53da0fb..df3a53da0fb 100644
--- a/app/assets/images/emoji/goal.png
+++ b/public/-/emojis/1/goal.png
Binary files differ
diff --git a/app/assets/images/emoji/goat.png b/public/-/emojis/1/goat.png
index f9d9e38a128..f9d9e38a128 100644
--- a/app/assets/images/emoji/goat.png
+++ b/public/-/emojis/1/goat.png
Binary files differ
diff --git a/app/assets/images/emoji/golf.png b/public/-/emojis/1/golf.png
index f65a21d8a46..f65a21d8a46 100644
--- a/app/assets/images/emoji/golf.png
+++ b/public/-/emojis/1/golf.png
Binary files differ
diff --git a/app/assets/images/emoji/golfer.png b/public/-/emojis/1/golfer.png
index 39c552de86d..39c552de86d 100644
--- a/app/assets/images/emoji/golfer.png
+++ b/public/-/emojis/1/golfer.png
Binary files differ
diff --git a/app/assets/images/emoji/gorilla.png b/public/-/emojis/1/gorilla.png
index acc51e13622..acc51e13622 100644
--- a/app/assets/images/emoji/gorilla.png
+++ b/public/-/emojis/1/gorilla.png
Binary files differ
diff --git a/app/assets/images/emoji/grapes.png b/public/-/emojis/1/grapes.png
index 30d22218896..30d22218896 100644
--- a/app/assets/images/emoji/grapes.png
+++ b/public/-/emojis/1/grapes.png
Binary files differ
diff --git a/app/assets/images/emoji/green_apple.png b/public/-/emojis/1/green_apple.png
index 5fd51bd3915..5fd51bd3915 100644
--- a/app/assets/images/emoji/green_apple.png
+++ b/public/-/emojis/1/green_apple.png
Binary files differ
diff --git a/app/assets/images/emoji/green_book.png b/public/-/emojis/1/green_book.png
index e5e411cf3b5..e5e411cf3b5 100644
--- a/app/assets/images/emoji/green_book.png
+++ b/public/-/emojis/1/green_book.png
Binary files differ
diff --git a/app/assets/images/emoji/green_heart.png b/public/-/emojis/1/green_heart.png
index c52d60a58be..c52d60a58be 100644
--- a/app/assets/images/emoji/green_heart.png
+++ b/public/-/emojis/1/green_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/grey_exclamation.png b/public/-/emojis/1/grey_exclamation.png
index 9b64da8bf7f..9b64da8bf7f 100644
--- a/app/assets/images/emoji/grey_exclamation.png
+++ b/public/-/emojis/1/grey_exclamation.png
Binary files differ
diff --git a/app/assets/images/emoji/grey_question.png b/public/-/emojis/1/grey_question.png
index 6e7824c75f6..6e7824c75f6 100644
--- a/app/assets/images/emoji/grey_question.png
+++ b/public/-/emojis/1/grey_question.png
Binary files differ
diff --git a/app/assets/images/emoji/grimacing.png b/public/-/emojis/1/grimacing.png
index 871b2f071c9..871b2f071c9 100644
--- a/app/assets/images/emoji/grimacing.png
+++ b/public/-/emojis/1/grimacing.png
Binary files differ
diff --git a/app/assets/images/emoji/grin.png b/public/-/emojis/1/grin.png
index 418d94c811b..418d94c811b 100644
--- a/app/assets/images/emoji/grin.png
+++ b/public/-/emojis/1/grin.png
Binary files differ
diff --git a/app/assets/images/emoji/grinning.png b/public/-/emojis/1/grinning.png
index 3e8e0dab78c..3e8e0dab78c 100644
--- a/app/assets/images/emoji/grinning.png
+++ b/public/-/emojis/1/grinning.png
Binary files differ
diff --git a/app/assets/images/emoji/guardsman.png b/public/-/emojis/1/guardsman.png
index 8d7ab3c473c..8d7ab3c473c 100644
--- a/app/assets/images/emoji/guardsman.png
+++ b/public/-/emojis/1/guardsman.png
Binary files differ
diff --git a/app/assets/images/emoji/guardsman_tone1.png b/public/-/emojis/1/guardsman_tone1.png
index cea9ba27468..cea9ba27468 100644
--- a/app/assets/images/emoji/guardsman_tone1.png
+++ b/public/-/emojis/1/guardsman_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/guardsman_tone2.png b/public/-/emojis/1/guardsman_tone2.png
index 037464e4028..037464e4028 100644
--- a/app/assets/images/emoji/guardsman_tone2.png
+++ b/public/-/emojis/1/guardsman_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/guardsman_tone3.png b/public/-/emojis/1/guardsman_tone3.png
index 0f6726fbe87..0f6726fbe87 100644
--- a/app/assets/images/emoji/guardsman_tone3.png
+++ b/public/-/emojis/1/guardsman_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/guardsman_tone4.png b/public/-/emojis/1/guardsman_tone4.png
index 85fcf9a3b97..85fcf9a3b97 100644
--- a/app/assets/images/emoji/guardsman_tone4.png
+++ b/public/-/emojis/1/guardsman_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/guardsman_tone5.png b/public/-/emojis/1/guardsman_tone5.png
index e5f9ca7d5a2..e5f9ca7d5a2 100644
--- a/app/assets/images/emoji/guardsman_tone5.png
+++ b/public/-/emojis/1/guardsman_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/guitar.png b/public/-/emojis/1/guitar.png
index 43d752f1e3d..43d752f1e3d 100644
--- a/app/assets/images/emoji/guitar.png
+++ b/public/-/emojis/1/guitar.png
Binary files differ
diff --git a/app/assets/images/emoji/gun.png b/public/-/emojis/1/gun.png
index 89c5c244c7b..89c5c244c7b 100644
--- a/app/assets/images/emoji/gun.png
+++ b/public/-/emojis/1/gun.png
Binary files differ
diff --git a/app/assets/images/emoji/haircut.png b/public/-/emojis/1/haircut.png
index 91266b12930..91266b12930 100644
--- a/app/assets/images/emoji/haircut.png
+++ b/public/-/emojis/1/haircut.png
Binary files differ
diff --git a/app/assets/images/emoji/haircut_tone1.png b/public/-/emojis/1/haircut_tone1.png
index c743b74abeb..c743b74abeb 100644
--- a/app/assets/images/emoji/haircut_tone1.png
+++ b/public/-/emojis/1/haircut_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/haircut_tone2.png b/public/-/emojis/1/haircut_tone2.png
index f144f8e55ce..f144f8e55ce 100644
--- a/app/assets/images/emoji/haircut_tone2.png
+++ b/public/-/emojis/1/haircut_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/haircut_tone3.png b/public/-/emojis/1/haircut_tone3.png
index d5ad19563ac..d5ad19563ac 100644
--- a/app/assets/images/emoji/haircut_tone3.png
+++ b/public/-/emojis/1/haircut_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/haircut_tone4.png b/public/-/emojis/1/haircut_tone4.png
index 244fd3af008..244fd3af008 100644
--- a/app/assets/images/emoji/haircut_tone4.png
+++ b/public/-/emojis/1/haircut_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/haircut_tone5.png b/public/-/emojis/1/haircut_tone5.png
index 20a94a88623..20a94a88623 100644
--- a/app/assets/images/emoji/haircut_tone5.png
+++ b/public/-/emojis/1/haircut_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/hamburger.png b/public/-/emojis/1/hamburger.png
index 3573b28a1fd..3573b28a1fd 100644
--- a/app/assets/images/emoji/hamburger.png
+++ b/public/-/emojis/1/hamburger.png
Binary files differ
diff --git a/app/assets/images/emoji/hammer.png b/public/-/emojis/1/hammer.png
index 00736cce47d..00736cce47d 100644
--- a/app/assets/images/emoji/hammer.png
+++ b/public/-/emojis/1/hammer.png
Binary files differ
diff --git a/app/assets/images/emoji/hammer_pick.png b/public/-/emojis/1/hammer_pick.png
index 3bee30ec588..3bee30ec588 100644
--- a/app/assets/images/emoji/hammer_pick.png
+++ b/public/-/emojis/1/hammer_pick.png
Binary files differ
diff --git a/app/assets/images/emoji/hamster.png b/public/-/emojis/1/hamster.png
index 9a04388e4e7..9a04388e4e7 100644
--- a/app/assets/images/emoji/hamster.png
+++ b/public/-/emojis/1/hamster.png
Binary files differ
diff --git a/app/assets/images/emoji/hand_splayed.png b/public/-/emojis/1/hand_splayed.png
index fb5ae8ebb5a..fb5ae8ebb5a 100644
--- a/app/assets/images/emoji/hand_splayed.png
+++ b/public/-/emojis/1/hand_splayed.png
Binary files differ
diff --git a/app/assets/images/emoji/hand_splayed_tone1.png b/public/-/emojis/1/hand_splayed_tone1.png
index a7888e6bd23..a7888e6bd23 100644
--- a/app/assets/images/emoji/hand_splayed_tone1.png
+++ b/public/-/emojis/1/hand_splayed_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/hand_splayed_tone2.png b/public/-/emojis/1/hand_splayed_tone2.png
index cc10fbc272d..cc10fbc272d 100644
--- a/app/assets/images/emoji/hand_splayed_tone2.png
+++ b/public/-/emojis/1/hand_splayed_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/hand_splayed_tone3.png b/public/-/emojis/1/hand_splayed_tone3.png
index 707236ae8a4..707236ae8a4 100644
--- a/app/assets/images/emoji/hand_splayed_tone3.png
+++ b/public/-/emojis/1/hand_splayed_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/hand_splayed_tone4.png b/public/-/emojis/1/hand_splayed_tone4.png
index 1430df9c61f..1430df9c61f 100644
--- a/app/assets/images/emoji/hand_splayed_tone4.png
+++ b/public/-/emojis/1/hand_splayed_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/hand_splayed_tone5.png b/public/-/emojis/1/hand_splayed_tone5.png
index 80bec971b6b..80bec971b6b 100644
--- a/app/assets/images/emoji/hand_splayed_tone5.png
+++ b/public/-/emojis/1/hand_splayed_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/handbag.png b/public/-/emojis/1/handbag.png
index cbf75c5d25e..cbf75c5d25e 100644
--- a/app/assets/images/emoji/handbag.png
+++ b/public/-/emojis/1/handbag.png
Binary files differ
diff --git a/app/assets/images/emoji/handball.png b/public/-/emojis/1/handball.png
index 1152f1344c7..1152f1344c7 100644
--- a/app/assets/images/emoji/handball.png
+++ b/public/-/emojis/1/handball.png
Binary files differ
diff --git a/app/assets/images/emoji/handball_tone1.png b/public/-/emojis/1/handball_tone1.png
index c26cac2df98..c26cac2df98 100644
--- a/app/assets/images/emoji/handball_tone1.png
+++ b/public/-/emojis/1/handball_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/handball_tone2.png b/public/-/emojis/1/handball_tone2.png
index 7baaf95a9a2..7baaf95a9a2 100644
--- a/app/assets/images/emoji/handball_tone2.png
+++ b/public/-/emojis/1/handball_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/handball_tone3.png b/public/-/emojis/1/handball_tone3.png
index 0e3a37c3d40..0e3a37c3d40 100644
--- a/app/assets/images/emoji/handball_tone3.png
+++ b/public/-/emojis/1/handball_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/handball_tone4.png b/public/-/emojis/1/handball_tone4.png
index e1233f38266..e1233f38266 100644
--- a/app/assets/images/emoji/handball_tone4.png
+++ b/public/-/emojis/1/handball_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/handball_tone5.png b/public/-/emojis/1/handball_tone5.png
index 6b1eb9b64b0..6b1eb9b64b0 100644
--- a/app/assets/images/emoji/handball_tone5.png
+++ b/public/-/emojis/1/handball_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/handshake.png b/public/-/emojis/1/handshake.png
index c5d35fd8138..c5d35fd8138 100644
--- a/app/assets/images/emoji/handshake.png
+++ b/public/-/emojis/1/handshake.png
Binary files differ
diff --git a/app/assets/images/emoji/handshake_tone1.png b/public/-/emojis/1/handshake_tone1.png
index 8f8fbb9bdca..8f8fbb9bdca 100644
--- a/app/assets/images/emoji/handshake_tone1.png
+++ b/public/-/emojis/1/handshake_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/handshake_tone2.png b/public/-/emojis/1/handshake_tone2.png
index 336a77a6d78..336a77a6d78 100644
--- a/app/assets/images/emoji/handshake_tone2.png
+++ b/public/-/emojis/1/handshake_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/handshake_tone3.png b/public/-/emojis/1/handshake_tone3.png
index 95f62d4fecd..95f62d4fecd 100644
--- a/app/assets/images/emoji/handshake_tone3.png
+++ b/public/-/emojis/1/handshake_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/handshake_tone4.png b/public/-/emojis/1/handshake_tone4.png
index 2b0a6433886..2b0a6433886 100644
--- a/app/assets/images/emoji/handshake_tone4.png
+++ b/public/-/emojis/1/handshake_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/handshake_tone5.png b/public/-/emojis/1/handshake_tone5.png
index 40189ee68e4..40189ee68e4 100644
--- a/app/assets/images/emoji/handshake_tone5.png
+++ b/public/-/emojis/1/handshake_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/hash.png b/public/-/emojis/1/hash.png
index 6e26f0070b0..6e26f0070b0 100644
--- a/app/assets/images/emoji/hash.png
+++ b/public/-/emojis/1/hash.png
Binary files differ
diff --git a/app/assets/images/emoji/hatched_chick.png b/public/-/emojis/1/hatched_chick.png
index 31dfb511e0e..31dfb511e0e 100644
--- a/app/assets/images/emoji/hatched_chick.png
+++ b/public/-/emojis/1/hatched_chick.png
Binary files differ
diff --git a/app/assets/images/emoji/hatching_chick.png b/public/-/emojis/1/hatching_chick.png
index c5b0e8f3bcc..c5b0e8f3bcc 100644
--- a/app/assets/images/emoji/hatching_chick.png
+++ b/public/-/emojis/1/hatching_chick.png
Binary files differ
diff --git a/app/assets/images/emoji/head_bandage.png b/public/-/emojis/1/head_bandage.png
index 0be723085e0..0be723085e0 100644
--- a/app/assets/images/emoji/head_bandage.png
+++ b/public/-/emojis/1/head_bandage.png
Binary files differ
diff --git a/app/assets/images/emoji/headphones.png b/public/-/emojis/1/headphones.png
index e9fd34041d8..e9fd34041d8 100644
--- a/app/assets/images/emoji/headphones.png
+++ b/public/-/emojis/1/headphones.png
Binary files differ
diff --git a/app/assets/images/emoji/hear_no_evil.png b/public/-/emojis/1/hear_no_evil.png
index 74b6be0c6c5..74b6be0c6c5 100644
--- a/app/assets/images/emoji/hear_no_evil.png
+++ b/public/-/emojis/1/hear_no_evil.png
Binary files differ
diff --git a/app/assets/images/emoji/heart.png b/public/-/emojis/1/heart.png
index 638cb72dc4e..638cb72dc4e 100644
--- a/app/assets/images/emoji/heart.png
+++ b/public/-/emojis/1/heart.png
Binary files differ
diff --git a/app/assets/images/emoji/heart_decoration.png b/public/-/emojis/1/heart_decoration.png
index 5443f60bc63..5443f60bc63 100644
--- a/app/assets/images/emoji/heart_decoration.png
+++ b/public/-/emojis/1/heart_decoration.png
Binary files differ
diff --git a/app/assets/images/emoji/heart_exclamation.png b/public/-/emojis/1/heart_exclamation.png
index 91b520be40b..91b520be40b 100644
--- a/app/assets/images/emoji/heart_exclamation.png
+++ b/public/-/emojis/1/heart_exclamation.png
Binary files differ
diff --git a/app/assets/images/emoji/heart_eyes.png b/public/-/emojis/1/heart_eyes.png
index 73fbee29d4e..73fbee29d4e 100644
--- a/app/assets/images/emoji/heart_eyes.png
+++ b/public/-/emojis/1/heart_eyes.png
Binary files differ
diff --git a/app/assets/images/emoji/heart_eyes_cat.png b/public/-/emojis/1/heart_eyes_cat.png
index bc5a833f9a1..bc5a833f9a1 100644
--- a/app/assets/images/emoji/heart_eyes_cat.png
+++ b/public/-/emojis/1/heart_eyes_cat.png
Binary files differ
diff --git a/app/assets/images/emoji/heartbeat.png b/public/-/emojis/1/heartbeat.png
index 0bcf2d1d567..0bcf2d1d567 100644
--- a/app/assets/images/emoji/heartbeat.png
+++ b/public/-/emojis/1/heartbeat.png
Binary files differ
diff --git a/app/assets/images/emoji/heartpulse.png b/public/-/emojis/1/heartpulse.png
index d6e694e972f..d6e694e972f 100644
--- a/app/assets/images/emoji/heartpulse.png
+++ b/public/-/emojis/1/heartpulse.png
Binary files differ
diff --git a/app/assets/images/emoji/hearts.png b/public/-/emojis/1/hearts.png
index 393c3ed5267..393c3ed5267 100644
--- a/app/assets/images/emoji/hearts.png
+++ b/public/-/emojis/1/hearts.png
Binary files differ
diff --git a/app/assets/images/emoji/heavy_check_mark.png b/public/-/emojis/1/heavy_check_mark.png
index 03bd695377e..03bd695377e 100644
--- a/app/assets/images/emoji/heavy_check_mark.png
+++ b/public/-/emojis/1/heavy_check_mark.png
Binary files differ
diff --git a/app/assets/images/emoji/heavy_division_sign.png b/public/-/emojis/1/heavy_division_sign.png
index df32ab21bea..df32ab21bea 100644
--- a/app/assets/images/emoji/heavy_division_sign.png
+++ b/public/-/emojis/1/heavy_division_sign.png
Binary files differ
diff --git a/app/assets/images/emoji/heavy_dollar_sign.png b/public/-/emojis/1/heavy_dollar_sign.png
index ef2c2e20590..ef2c2e20590 100644
--- a/app/assets/images/emoji/heavy_dollar_sign.png
+++ b/public/-/emojis/1/heavy_dollar_sign.png
Binary files differ
diff --git a/app/assets/images/emoji/heavy_minus_sign.png b/public/-/emojis/1/heavy_minus_sign.png
index 054211caf12..054211caf12 100644
--- a/app/assets/images/emoji/heavy_minus_sign.png
+++ b/public/-/emojis/1/heavy_minus_sign.png
Binary files differ
diff --git a/app/assets/images/emoji/heavy_multiplication_x.png b/public/-/emojis/1/heavy_multiplication_x.png
index e47cc1b685d..e47cc1b685d 100644
--- a/app/assets/images/emoji/heavy_multiplication_x.png
+++ b/public/-/emojis/1/heavy_multiplication_x.png
Binary files differ
diff --git a/app/assets/images/emoji/heavy_plus_sign.png b/public/-/emojis/1/heavy_plus_sign.png
index 40799798aaf..40799798aaf 100644
--- a/app/assets/images/emoji/heavy_plus_sign.png
+++ b/public/-/emojis/1/heavy_plus_sign.png
Binary files differ
diff --git a/app/assets/images/emoji/helicopter.png b/public/-/emojis/1/helicopter.png
index 7ec5f39a51a..7ec5f39a51a 100644
--- a/app/assets/images/emoji/helicopter.png
+++ b/public/-/emojis/1/helicopter.png
Binary files differ
diff --git a/app/assets/images/emoji/helmet_with_cross.png b/public/-/emojis/1/helmet_with_cross.png
index 7140a676038..7140a676038 100644
--- a/app/assets/images/emoji/helmet_with_cross.png
+++ b/public/-/emojis/1/helmet_with_cross.png
Binary files differ
diff --git a/app/assets/images/emoji/herb.png b/public/-/emojis/1/herb.png
index d984d1562bb..d984d1562bb 100644
--- a/app/assets/images/emoji/herb.png
+++ b/public/-/emojis/1/herb.png
Binary files differ
diff --git a/app/assets/images/emoji/hibiscus.png b/public/-/emojis/1/hibiscus.png
index 39dd3524233..39dd3524233 100644
--- a/app/assets/images/emoji/hibiscus.png
+++ b/public/-/emojis/1/hibiscus.png
Binary files differ
diff --git a/app/assets/images/emoji/high_brightness.png b/public/-/emojis/1/high_brightness.png
index c41f2d5fd50..c41f2d5fd50 100644
--- a/app/assets/images/emoji/high_brightness.png
+++ b/public/-/emojis/1/high_brightness.png
Binary files differ
diff --git a/app/assets/images/emoji/high_heel.png b/public/-/emojis/1/high_heel.png
index b331cbccc9d..b331cbccc9d 100644
--- a/app/assets/images/emoji/high_heel.png
+++ b/public/-/emojis/1/high_heel.png
Binary files differ
diff --git a/app/assets/images/emoji/hockey.png b/public/-/emojis/1/hockey.png
index be94e9cbf73..be94e9cbf73 100644
--- a/app/assets/images/emoji/hockey.png
+++ b/public/-/emojis/1/hockey.png
Binary files differ
diff --git a/app/assets/images/emoji/hole.png b/public/-/emojis/1/hole.png
index 517d2ae0deb..517d2ae0deb 100644
--- a/app/assets/images/emoji/hole.png
+++ b/public/-/emojis/1/hole.png
Binary files differ
diff --git a/app/assets/images/emoji/homes.png b/public/-/emojis/1/homes.png
index 6ab4a2a2651..6ab4a2a2651 100644
--- a/app/assets/images/emoji/homes.png
+++ b/public/-/emojis/1/homes.png
Binary files differ
diff --git a/app/assets/images/emoji/honey_pot.png b/public/-/emojis/1/honey_pot.png
index 9d8f592955e..9d8f592955e 100644
--- a/app/assets/images/emoji/honey_pot.png
+++ b/public/-/emojis/1/honey_pot.png
Binary files differ
diff --git a/app/assets/images/emoji/horse.png b/public/-/emojis/1/horse.png
index 7cb1172f4e4..7cb1172f4e4 100644
--- a/app/assets/images/emoji/horse.png
+++ b/public/-/emojis/1/horse.png
Binary files differ
diff --git a/app/assets/images/emoji/horse_racing.png b/public/-/emojis/1/horse_racing.png
index addf9edac56..addf9edac56 100644
--- a/app/assets/images/emoji/horse_racing.png
+++ b/public/-/emojis/1/horse_racing.png
Binary files differ
diff --git a/app/assets/images/emoji/horse_racing_tone1.png b/public/-/emojis/1/horse_racing_tone1.png
index e9bf4092e98..e9bf4092e98 100644
--- a/app/assets/images/emoji/horse_racing_tone1.png
+++ b/public/-/emojis/1/horse_racing_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/horse_racing_tone2.png b/public/-/emojis/1/horse_racing_tone2.png
index 031bbc3d867..031bbc3d867 100644
--- a/app/assets/images/emoji/horse_racing_tone2.png
+++ b/public/-/emojis/1/horse_racing_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/horse_racing_tone3.png b/public/-/emojis/1/horse_racing_tone3.png
index b40ef891f9b..b40ef891f9b 100644
--- a/app/assets/images/emoji/horse_racing_tone3.png
+++ b/public/-/emojis/1/horse_racing_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/horse_racing_tone4.png b/public/-/emojis/1/horse_racing_tone4.png
index e286cb85065..e286cb85065 100644
--- a/app/assets/images/emoji/horse_racing_tone4.png
+++ b/public/-/emojis/1/horse_racing_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/horse_racing_tone5.png b/public/-/emojis/1/horse_racing_tone5.png
index 453c51c6007..453c51c6007 100644
--- a/app/assets/images/emoji/horse_racing_tone5.png
+++ b/public/-/emojis/1/horse_racing_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/hospital.png b/public/-/emojis/1/hospital.png
index 1cbce4ae767..1cbce4ae767 100644
--- a/app/assets/images/emoji/hospital.png
+++ b/public/-/emojis/1/hospital.png
Binary files differ
diff --git a/app/assets/images/emoji/hot_pepper.png b/public/-/emojis/1/hot_pepper.png
index 266675bd577..266675bd577 100644
--- a/app/assets/images/emoji/hot_pepper.png
+++ b/public/-/emojis/1/hot_pepper.png
Binary files differ
diff --git a/app/assets/images/emoji/hotdog.png b/public/-/emojis/1/hotdog.png
index 3c3354d94cb..3c3354d94cb 100644
--- a/app/assets/images/emoji/hotdog.png
+++ b/public/-/emojis/1/hotdog.png
Binary files differ
diff --git a/app/assets/images/emoji/hotel.png b/public/-/emojis/1/hotel.png
index ea8f4c4979a..ea8f4c4979a 100644
--- a/app/assets/images/emoji/hotel.png
+++ b/public/-/emojis/1/hotel.png
Binary files differ
diff --git a/app/assets/images/emoji/hotsprings.png b/public/-/emojis/1/hotsprings.png
index 3d9df2d9475..3d9df2d9475 100644
--- a/app/assets/images/emoji/hotsprings.png
+++ b/public/-/emojis/1/hotsprings.png
Binary files differ
diff --git a/app/assets/images/emoji/hourglass.png b/public/-/emojis/1/hourglass.png
index a5db2d1d3f4..a5db2d1d3f4 100644
--- a/app/assets/images/emoji/hourglass.png
+++ b/public/-/emojis/1/hourglass.png
Binary files differ
diff --git a/app/assets/images/emoji/hourglass_flowing_sand.png b/public/-/emojis/1/hourglass_flowing_sand.png
index b93b15ed6d8..b93b15ed6d8 100644
--- a/app/assets/images/emoji/hourglass_flowing_sand.png
+++ b/public/-/emojis/1/hourglass_flowing_sand.png
Binary files differ
diff --git a/app/assets/images/emoji/house.png b/public/-/emojis/1/house.png
index 01c98a0ba92..01c98a0ba92 100644
--- a/app/assets/images/emoji/house.png
+++ b/public/-/emojis/1/house.png
Binary files differ
diff --git a/app/assets/images/emoji/house_abandoned.png b/public/-/emojis/1/house_abandoned.png
index c55e81de990..c55e81de990 100644
--- a/app/assets/images/emoji/house_abandoned.png
+++ b/public/-/emojis/1/house_abandoned.png
Binary files differ
diff --git a/app/assets/images/emoji/house_with_garden.png b/public/-/emojis/1/house_with_garden.png
index 0aae41598ef..0aae41598ef 100644
--- a/app/assets/images/emoji/house_with_garden.png
+++ b/public/-/emojis/1/house_with_garden.png
Binary files differ
diff --git a/app/assets/images/emoji/hugging.png b/public/-/emojis/1/hugging.png
index 5bba6dc6d51..5bba6dc6d51 100644
--- a/app/assets/images/emoji/hugging.png
+++ b/public/-/emojis/1/hugging.png
Binary files differ
diff --git a/app/assets/images/emoji/hushed.png b/public/-/emojis/1/hushed.png
index cad0e23132e..cad0e23132e 100644
--- a/app/assets/images/emoji/hushed.png
+++ b/public/-/emojis/1/hushed.png
Binary files differ
diff --git a/app/assets/images/emoji/ice_cream.png b/public/-/emojis/1/ice_cream.png
index 94267b9c434..94267b9c434 100644
--- a/app/assets/images/emoji/ice_cream.png
+++ b/public/-/emojis/1/ice_cream.png
Binary files differ
diff --git a/app/assets/images/emoji/ice_skate.png b/public/-/emojis/1/ice_skate.png
index 8c449b0c039..8c449b0c039 100644
--- a/app/assets/images/emoji/ice_skate.png
+++ b/public/-/emojis/1/ice_skate.png
Binary files differ
diff --git a/app/assets/images/emoji/icecream.png b/public/-/emojis/1/icecream.png
index 8f6546e31a5..8f6546e31a5 100644
--- a/app/assets/images/emoji/icecream.png
+++ b/public/-/emojis/1/icecream.png
Binary files differ
diff --git a/app/assets/images/emoji/id.png b/public/-/emojis/1/id.png
index 5bf69bf7ba8..5bf69bf7ba8 100644
--- a/app/assets/images/emoji/id.png
+++ b/public/-/emojis/1/id.png
Binary files differ
diff --git a/app/assets/images/emoji/ideograph_advantage.png b/public/-/emojis/1/ideograph_advantage.png
index 0c0d589caf0..0c0d589caf0 100644
--- a/app/assets/images/emoji/ideograph_advantage.png
+++ b/public/-/emojis/1/ideograph_advantage.png
Binary files differ
diff --git a/app/assets/images/emoji/imp.png b/public/-/emojis/1/imp.png
index 9f9a9605539..9f9a9605539 100644
--- a/app/assets/images/emoji/imp.png
+++ b/public/-/emojis/1/imp.png
Binary files differ
diff --git a/app/assets/images/emoji/inbox_tray.png b/public/-/emojis/1/inbox_tray.png
index 41a6be2b0ee..41a6be2b0ee 100644
--- a/app/assets/images/emoji/inbox_tray.png
+++ b/public/-/emojis/1/inbox_tray.png
Binary files differ
diff --git a/app/assets/images/emoji/incoming_envelope.png b/public/-/emojis/1/incoming_envelope.png
index fd22e88182e..fd22e88182e 100644
--- a/app/assets/images/emoji/incoming_envelope.png
+++ b/public/-/emojis/1/incoming_envelope.png
Binary files differ
diff --git a/app/assets/images/emoji/information_desk_person.png b/public/-/emojis/1/information_desk_person.png
index 55fc6294d25..55fc6294d25 100644
--- a/app/assets/images/emoji/information_desk_person.png
+++ b/public/-/emojis/1/information_desk_person.png
Binary files differ
diff --git a/app/assets/images/emoji/information_desk_person_tone1.png b/public/-/emojis/1/information_desk_person_tone1.png
index 3d9e2247940..3d9e2247940 100644
--- a/app/assets/images/emoji/information_desk_person_tone1.png
+++ b/public/-/emojis/1/information_desk_person_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/information_desk_person_tone2.png b/public/-/emojis/1/information_desk_person_tone2.png
index 879e8b7966d..879e8b7966d 100644
--- a/app/assets/images/emoji/information_desk_person_tone2.png
+++ b/public/-/emojis/1/information_desk_person_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/information_desk_person_tone3.png b/public/-/emojis/1/information_desk_person_tone3.png
index 307514eab67..307514eab67 100644
--- a/app/assets/images/emoji/information_desk_person_tone3.png
+++ b/public/-/emojis/1/information_desk_person_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/information_desk_person_tone4.png b/public/-/emojis/1/information_desk_person_tone4.png
index 297395dcb3f..297395dcb3f 100644
--- a/app/assets/images/emoji/information_desk_person_tone4.png
+++ b/public/-/emojis/1/information_desk_person_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/information_desk_person_tone5.png b/public/-/emojis/1/information_desk_person_tone5.png
index 26f8f22b28b..26f8f22b28b 100644
--- a/app/assets/images/emoji/information_desk_person_tone5.png
+++ b/public/-/emojis/1/information_desk_person_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/information_source.png b/public/-/emojis/1/information_source.png
index 871f2db9314..871f2db9314 100644
--- a/app/assets/images/emoji/information_source.png
+++ b/public/-/emojis/1/information_source.png
Binary files differ
diff --git a/app/assets/images/emoji/innocent.png b/public/-/emojis/1/innocent.png
index 57f5151124f..57f5151124f 100644
--- a/app/assets/images/emoji/innocent.png
+++ b/public/-/emojis/1/innocent.png
Binary files differ
diff --git a/app/assets/images/emoji/interrobang.png b/public/-/emojis/1/interrobang.png
index 509813e9bb2..509813e9bb2 100644
--- a/app/assets/images/emoji/interrobang.png
+++ b/public/-/emojis/1/interrobang.png
Binary files differ
diff --git a/app/assets/images/emoji/iphone.png b/public/-/emojis/1/iphone.png
index fd377acf872..fd377acf872 100644
--- a/app/assets/images/emoji/iphone.png
+++ b/public/-/emojis/1/iphone.png
Binary files differ
diff --git a/app/assets/images/emoji/island.png b/public/-/emojis/1/island.png
index 7fd834389b7..7fd834389b7 100644
--- a/app/assets/images/emoji/island.png
+++ b/public/-/emojis/1/island.png
Binary files differ
diff --git a/app/assets/images/emoji/izakaya_lantern.png b/public/-/emojis/1/izakaya_lantern.png
index dfd933f6f36..dfd933f6f36 100644
--- a/app/assets/images/emoji/izakaya_lantern.png
+++ b/public/-/emojis/1/izakaya_lantern.png
Binary files differ
diff --git a/app/assets/images/emoji/jack_o_lantern.png b/public/-/emojis/1/jack_o_lantern.png
index 44c3fc0aec9..44c3fc0aec9 100644
--- a/app/assets/images/emoji/jack_o_lantern.png
+++ b/public/-/emojis/1/jack_o_lantern.png
Binary files differ
diff --git a/app/assets/images/emoji/japan.png b/public/-/emojis/1/japan.png
index d86d0a59e12..d86d0a59e12 100644
--- a/app/assets/images/emoji/japan.png
+++ b/public/-/emojis/1/japan.png
Binary files differ
diff --git a/app/assets/images/emoji/japanese_castle.png b/public/-/emojis/1/japanese_castle.png
index 64b4e33a1ae..64b4e33a1ae 100644
--- a/app/assets/images/emoji/japanese_castle.png
+++ b/public/-/emojis/1/japanese_castle.png
Binary files differ
diff --git a/app/assets/images/emoji/japanese_goblin.png b/public/-/emojis/1/japanese_goblin.png
index 515c6a2250e..515c6a2250e 100644
--- a/app/assets/images/emoji/japanese_goblin.png
+++ b/public/-/emojis/1/japanese_goblin.png
Binary files differ
diff --git a/app/assets/images/emoji/japanese_ogre.png b/public/-/emojis/1/japanese_ogre.png
index fe8670fdaf1..fe8670fdaf1 100644
--- a/app/assets/images/emoji/japanese_ogre.png
+++ b/public/-/emojis/1/japanese_ogre.png
Binary files differ
diff --git a/app/assets/images/emoji/jeans.png b/public/-/emojis/1/jeans.png
index 2a6869d674c..2a6869d674c 100644
--- a/app/assets/images/emoji/jeans.png
+++ b/public/-/emojis/1/jeans.png
Binary files differ
diff --git a/app/assets/images/emoji/joy.png b/public/-/emojis/1/joy.png
index 0ba3b1859d8..0ba3b1859d8 100644
--- a/app/assets/images/emoji/joy.png
+++ b/public/-/emojis/1/joy.png
Binary files differ
diff --git a/app/assets/images/emoji/joy_cat.png b/public/-/emojis/1/joy_cat.png
index aac353179aa..aac353179aa 100644
--- a/app/assets/images/emoji/joy_cat.png
+++ b/public/-/emojis/1/joy_cat.png
Binary files differ
diff --git a/app/assets/images/emoji/joystick.png b/public/-/emojis/1/joystick.png
index 1ee1905434e..1ee1905434e 100644
--- a/app/assets/images/emoji/joystick.png
+++ b/public/-/emojis/1/joystick.png
Binary files differ
diff --git a/app/assets/images/emoji/juggling.png b/public/-/emojis/1/juggling.png
index a37f6224a42..a37f6224a42 100644
--- a/app/assets/images/emoji/juggling.png
+++ b/public/-/emojis/1/juggling.png
Binary files differ
diff --git a/app/assets/images/emoji/juggling_tone1.png b/public/-/emojis/1/juggling_tone1.png
index c18eda40031..c18eda40031 100644
--- a/app/assets/images/emoji/juggling_tone1.png
+++ b/public/-/emojis/1/juggling_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/juggling_tone2.png b/public/-/emojis/1/juggling_tone2.png
index de3b7a555b6..de3b7a555b6 100644
--- a/app/assets/images/emoji/juggling_tone2.png
+++ b/public/-/emojis/1/juggling_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/juggling_tone3.png b/public/-/emojis/1/juggling_tone3.png
index 74ab6d85458..74ab6d85458 100644
--- a/app/assets/images/emoji/juggling_tone3.png
+++ b/public/-/emojis/1/juggling_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/juggling_tone4.png b/public/-/emojis/1/juggling_tone4.png
index 1c57823203f..1c57823203f 100644
--- a/app/assets/images/emoji/juggling_tone4.png
+++ b/public/-/emojis/1/juggling_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/juggling_tone5.png b/public/-/emojis/1/juggling_tone5.png
index c343d6ee98a..c343d6ee98a 100644
--- a/app/assets/images/emoji/juggling_tone5.png
+++ b/public/-/emojis/1/juggling_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/kaaba.png b/public/-/emojis/1/kaaba.png
index 1778c1138e4..1778c1138e4 100644
--- a/app/assets/images/emoji/kaaba.png
+++ b/public/-/emojis/1/kaaba.png
Binary files differ
diff --git a/app/assets/images/emoji/key.png b/public/-/emojis/1/key.png
index 319cd1b884c..319cd1b884c 100644
--- a/app/assets/images/emoji/key.png
+++ b/public/-/emojis/1/key.png
Binary files differ
diff --git a/app/assets/images/emoji/key2.png b/public/-/emojis/1/key2.png
index e11d706c6c8..e11d706c6c8 100644
--- a/app/assets/images/emoji/key2.png
+++ b/public/-/emojis/1/key2.png
Binary files differ
diff --git a/app/assets/images/emoji/keyboard.png b/public/-/emojis/1/keyboard.png
index 75027cb9af7..75027cb9af7 100644
--- a/app/assets/images/emoji/keyboard.png
+++ b/public/-/emojis/1/keyboard.png
Binary files differ
diff --git a/app/assets/images/emoji/kimono.png b/public/-/emojis/1/kimono.png
index abe851115d1..abe851115d1 100644
--- a/app/assets/images/emoji/kimono.png
+++ b/public/-/emojis/1/kimono.png
Binary files differ
diff --git a/app/assets/images/emoji/kiss.png b/public/-/emojis/1/kiss.png
index 85e6dcfc4e8..85e6dcfc4e8 100644
--- a/app/assets/images/emoji/kiss.png
+++ b/public/-/emojis/1/kiss.png
Binary files differ
diff --git a/app/assets/images/emoji/kiss_mm.png b/public/-/emojis/1/kiss_mm.png
index a9a0edae17c..a9a0edae17c 100644
--- a/app/assets/images/emoji/kiss_mm.png
+++ b/public/-/emojis/1/kiss_mm.png
Binary files differ
diff --git a/app/assets/images/emoji/kiss_ww.png b/public/-/emojis/1/kiss_ww.png
index fdac73cbb1d..fdac73cbb1d 100644
--- a/app/assets/images/emoji/kiss_ww.png
+++ b/public/-/emojis/1/kiss_ww.png
Binary files differ
diff --git a/app/assets/images/emoji/kissing.png b/public/-/emojis/1/kissing.png
index 39d325fd8e3..39d325fd8e3 100644
--- a/app/assets/images/emoji/kissing.png
+++ b/public/-/emojis/1/kissing.png
Binary files differ
diff --git a/app/assets/images/emoji/kissing_cat.png b/public/-/emojis/1/kissing_cat.png
index 6e0bcc77540..6e0bcc77540 100644
--- a/app/assets/images/emoji/kissing_cat.png
+++ b/public/-/emojis/1/kissing_cat.png
Binary files differ
diff --git a/app/assets/images/emoji/kissing_closed_eyes.png b/public/-/emojis/1/kissing_closed_eyes.png
index b684d7d4d6c..b684d7d4d6c 100644
--- a/app/assets/images/emoji/kissing_closed_eyes.png
+++ b/public/-/emojis/1/kissing_closed_eyes.png
Binary files differ
diff --git a/app/assets/images/emoji/kissing_heart.png b/public/-/emojis/1/kissing_heart.png
index 0ff808fd614..0ff808fd614 100644
--- a/app/assets/images/emoji/kissing_heart.png
+++ b/public/-/emojis/1/kissing_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/kissing_smiling_eyes.png b/public/-/emojis/1/kissing_smiling_eyes.png
index e181f17099d..e181f17099d 100644
--- a/app/assets/images/emoji/kissing_smiling_eyes.png
+++ b/public/-/emojis/1/kissing_smiling_eyes.png
Binary files differ
diff --git a/app/assets/images/emoji/kiwi.png b/public/-/emojis/1/kiwi.png
index dfbd8258074..dfbd8258074 100644
--- a/app/assets/images/emoji/kiwi.png
+++ b/public/-/emojis/1/kiwi.png
Binary files differ
diff --git a/app/assets/images/emoji/knife.png b/public/-/emojis/1/knife.png
index 1acb9f3077b..1acb9f3077b 100644
--- a/app/assets/images/emoji/knife.png
+++ b/public/-/emojis/1/knife.png
Binary files differ
diff --git a/app/assets/images/emoji/koala.png b/public/-/emojis/1/koala.png
index a0aa437a98c..a0aa437a98c 100644
--- a/app/assets/images/emoji/koala.png
+++ b/public/-/emojis/1/koala.png
Binary files differ
diff --git a/app/assets/images/emoji/koko.png b/public/-/emojis/1/koko.png
index 6450eb44d90..6450eb44d90 100644
--- a/app/assets/images/emoji/koko.png
+++ b/public/-/emojis/1/koko.png
Binary files differ
diff --git a/app/assets/images/emoji/label.png b/public/-/emojis/1/label.png
index d41c9b4f1e1..d41c9b4f1e1 100644
--- a/app/assets/images/emoji/label.png
+++ b/public/-/emojis/1/label.png
Binary files differ
diff --git a/app/assets/images/emoji/large_blue_circle.png b/public/-/emojis/1/large_blue_circle.png
index 84078ef3127..84078ef3127 100644
--- a/app/assets/images/emoji/large_blue_circle.png
+++ b/public/-/emojis/1/large_blue_circle.png
Binary files differ
diff --git a/app/assets/images/emoji/large_blue_diamond.png b/public/-/emojis/1/large_blue_diamond.png
index 416a58bd5a8..416a58bd5a8 100644
--- a/app/assets/images/emoji/large_blue_diamond.png
+++ b/public/-/emojis/1/large_blue_diamond.png
Binary files differ
diff --git a/app/assets/images/emoji/large_orange_diamond.png b/public/-/emojis/1/large_orange_diamond.png
index 73ff0ac36c8..73ff0ac36c8 100644
--- a/app/assets/images/emoji/large_orange_diamond.png
+++ b/public/-/emojis/1/large_orange_diamond.png
Binary files differ
diff --git a/app/assets/images/emoji/last_quarter_moon.png b/public/-/emojis/1/last_quarter_moon.png
index 0842a0dd408..0842a0dd408 100644
--- a/app/assets/images/emoji/last_quarter_moon.png
+++ b/public/-/emojis/1/last_quarter_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/last_quarter_moon_with_face.png b/public/-/emojis/1/last_quarter_moon_with_face.png
index 94099343c5d..94099343c5d 100644
--- a/app/assets/images/emoji/last_quarter_moon_with_face.png
+++ b/public/-/emojis/1/last_quarter_moon_with_face.png
Binary files differ
diff --git a/app/assets/images/emoji/laughing.png b/public/-/emojis/1/laughing.png
index d94e9505ba1..d94e9505ba1 100644
--- a/app/assets/images/emoji/laughing.png
+++ b/public/-/emojis/1/laughing.png
Binary files differ
diff --git a/app/assets/images/emoji/leaves.png b/public/-/emojis/1/leaves.png
index 1e43e1af820..1e43e1af820 100644
--- a/app/assets/images/emoji/leaves.png
+++ b/public/-/emojis/1/leaves.png
Binary files differ
diff --git a/app/assets/images/emoji/ledger.png b/public/-/emojis/1/ledger.png
index 13e7561a4bd..13e7561a4bd 100644
--- a/app/assets/images/emoji/ledger.png
+++ b/public/-/emojis/1/ledger.png
Binary files differ
diff --git a/app/assets/images/emoji/left_facing_fist.png b/public/-/emojis/1/left_facing_fist.png
index a9d9fd8d59c..a9d9fd8d59c 100644
--- a/app/assets/images/emoji/left_facing_fist.png
+++ b/public/-/emojis/1/left_facing_fist.png
Binary files differ
diff --git a/app/assets/images/emoji/left_facing_fist_tone1.png b/public/-/emojis/1/left_facing_fist_tone1.png
index 1262a6b4b69..1262a6b4b69 100644
--- a/app/assets/images/emoji/left_facing_fist_tone1.png
+++ b/public/-/emojis/1/left_facing_fist_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/left_facing_fist_tone2.png b/public/-/emojis/1/left_facing_fist_tone2.png
index 40bf70b82b2..40bf70b82b2 100644
--- a/app/assets/images/emoji/left_facing_fist_tone2.png
+++ b/public/-/emojis/1/left_facing_fist_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/left_facing_fist_tone3.png b/public/-/emojis/1/left_facing_fist_tone3.png
index 93f58145111..93f58145111 100644
--- a/app/assets/images/emoji/left_facing_fist_tone3.png
+++ b/public/-/emojis/1/left_facing_fist_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/left_facing_fist_tone4.png b/public/-/emojis/1/left_facing_fist_tone4.png
index d82b5ec91f0..d82b5ec91f0 100644
--- a/app/assets/images/emoji/left_facing_fist_tone4.png
+++ b/public/-/emojis/1/left_facing_fist_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/left_facing_fist_tone5.png b/public/-/emojis/1/left_facing_fist_tone5.png
index 09ae4cd492b..09ae4cd492b 100644
--- a/app/assets/images/emoji/left_facing_fist_tone5.png
+++ b/public/-/emojis/1/left_facing_fist_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/left_luggage.png b/public/-/emojis/1/left_luggage.png
index 887b23f3f25..887b23f3f25 100644
--- a/app/assets/images/emoji/left_luggage.png
+++ b/public/-/emojis/1/left_luggage.png
Binary files differ
diff --git a/app/assets/images/emoji/left_right_arrow.png b/public/-/emojis/1/left_right_arrow.png
index 7937f24f2ac..7937f24f2ac 100644
--- a/app/assets/images/emoji/left_right_arrow.png
+++ b/public/-/emojis/1/left_right_arrow.png
Binary files differ
diff --git a/app/assets/images/emoji/leftwards_arrow_with_hook.png b/public/-/emojis/1/leftwards_arrow_with_hook.png
index ba45c2ad9e9..ba45c2ad9e9 100644
--- a/app/assets/images/emoji/leftwards_arrow_with_hook.png
+++ b/public/-/emojis/1/leftwards_arrow_with_hook.png
Binary files differ
diff --git a/app/assets/images/emoji/lemon.png b/public/-/emojis/1/lemon.png
index 9a7d95ca220..9a7d95ca220 100644
--- a/app/assets/images/emoji/lemon.png
+++ b/public/-/emojis/1/lemon.png
Binary files differ
diff --git a/app/assets/images/emoji/leo.png b/public/-/emojis/1/leo.png
index 30158d34de9..30158d34de9 100644
--- a/app/assets/images/emoji/leo.png
+++ b/public/-/emojis/1/leo.png
Binary files differ
diff --git a/app/assets/images/emoji/leopard.png b/public/-/emojis/1/leopard.png
index 8aac3d49448..8aac3d49448 100644
--- a/app/assets/images/emoji/leopard.png
+++ b/public/-/emojis/1/leopard.png
Binary files differ
diff --git a/app/assets/images/emoji/level_slider.png b/public/-/emojis/1/level_slider.png
index 720a3b34119..720a3b34119 100644
--- a/app/assets/images/emoji/level_slider.png
+++ b/public/-/emojis/1/level_slider.png
Binary files differ
diff --git a/app/assets/images/emoji/levitate.png b/public/-/emojis/1/levitate.png
index 3dc315a3d91..3dc315a3d91 100644
--- a/app/assets/images/emoji/levitate.png
+++ b/public/-/emojis/1/levitate.png
Binary files differ
diff --git a/app/assets/images/emoji/libra.png b/public/-/emojis/1/libra.png
index 8fd133a357c..8fd133a357c 100644
--- a/app/assets/images/emoji/libra.png
+++ b/public/-/emojis/1/libra.png
Binary files differ
diff --git a/app/assets/images/emoji/lifter.png b/public/-/emojis/1/lifter.png
index afdeaa476af..afdeaa476af 100644
--- a/app/assets/images/emoji/lifter.png
+++ b/public/-/emojis/1/lifter.png
Binary files differ
diff --git a/app/assets/images/emoji/lifter_tone1.png b/public/-/emojis/1/lifter_tone1.png
index febaad123ec..febaad123ec 100644
--- a/app/assets/images/emoji/lifter_tone1.png
+++ b/public/-/emojis/1/lifter_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/lifter_tone2.png b/public/-/emojis/1/lifter_tone2.png
index 27ae794a18e..27ae794a18e 100644
--- a/app/assets/images/emoji/lifter_tone2.png
+++ b/public/-/emojis/1/lifter_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/lifter_tone3.png b/public/-/emojis/1/lifter_tone3.png
index 45c4c22c709..45c4c22c709 100644
--- a/app/assets/images/emoji/lifter_tone3.png
+++ b/public/-/emojis/1/lifter_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/lifter_tone4.png b/public/-/emojis/1/lifter_tone4.png
index 67dd21d2464..67dd21d2464 100644
--- a/app/assets/images/emoji/lifter_tone4.png
+++ b/public/-/emojis/1/lifter_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/lifter_tone5.png b/public/-/emojis/1/lifter_tone5.png
index fa0152038b6..fa0152038b6 100644
--- a/app/assets/images/emoji/lifter_tone5.png
+++ b/public/-/emojis/1/lifter_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/light_rail.png b/public/-/emojis/1/light_rail.png
index a64829f5078..a64829f5078 100644
--- a/app/assets/images/emoji/light_rail.png
+++ b/public/-/emojis/1/light_rail.png
Binary files differ
diff --git a/app/assets/images/emoji/link.png b/public/-/emojis/1/link.png
index ae20f0f8eec..ae20f0f8eec 100644
--- a/app/assets/images/emoji/link.png
+++ b/public/-/emojis/1/link.png
Binary files differ
diff --git a/app/assets/images/emoji/lion_face.png b/public/-/emojis/1/lion_face.png
index 5062ab47ecf..5062ab47ecf 100644
--- a/app/assets/images/emoji/lion_face.png
+++ b/public/-/emojis/1/lion_face.png
Binary files differ
diff --git a/app/assets/images/emoji/lips.png b/public/-/emojis/1/lips.png
index 35f3cc2006f..35f3cc2006f 100644
--- a/app/assets/images/emoji/lips.png
+++ b/public/-/emojis/1/lips.png
Binary files differ
diff --git a/app/assets/images/emoji/lipstick.png b/public/-/emojis/1/lipstick.png
index 61a0c084c99..61a0c084c99 100644
--- a/app/assets/images/emoji/lipstick.png
+++ b/public/-/emojis/1/lipstick.png
Binary files differ
diff --git a/app/assets/images/emoji/lizard.png b/public/-/emojis/1/lizard.png
index 8363876050e..8363876050e 100644
--- a/app/assets/images/emoji/lizard.png
+++ b/public/-/emojis/1/lizard.png
Binary files differ
diff --git a/app/assets/images/emoji/lock.png b/public/-/emojis/1/lock.png
index 5a739c46644..5a739c46644 100644
--- a/app/assets/images/emoji/lock.png
+++ b/public/-/emojis/1/lock.png
Binary files differ
diff --git a/app/assets/images/emoji/lock_with_ink_pen.png b/public/-/emojis/1/lock_with_ink_pen.png
index 19a07d162fb..19a07d162fb 100644
--- a/app/assets/images/emoji/lock_with_ink_pen.png
+++ b/public/-/emojis/1/lock_with_ink_pen.png
Binary files differ
diff --git a/app/assets/images/emoji/lollipop.png b/public/-/emojis/1/lollipop.png
index ad76d7bf916..ad76d7bf916 100644
--- a/app/assets/images/emoji/lollipop.png
+++ b/public/-/emojis/1/lollipop.png
Binary files differ
diff --git a/app/assets/images/emoji/loop.png b/public/-/emojis/1/loop.png
index 0b82c8fe315..0b82c8fe315 100644
--- a/app/assets/images/emoji/loop.png
+++ b/public/-/emojis/1/loop.png
Binary files differ
diff --git a/app/assets/images/emoji/loud_sound.png b/public/-/emojis/1/loud_sound.png
index 8370033a539..8370033a539 100644
--- a/app/assets/images/emoji/loud_sound.png
+++ b/public/-/emojis/1/loud_sound.png
Binary files differ
diff --git a/app/assets/images/emoji/loudspeaker.png b/public/-/emojis/1/loudspeaker.png
index 5fd76a95b82..5fd76a95b82 100644
--- a/app/assets/images/emoji/loudspeaker.png
+++ b/public/-/emojis/1/loudspeaker.png
Binary files differ
diff --git a/app/assets/images/emoji/love_hotel.png b/public/-/emojis/1/love_hotel.png
index 5e136be6f8b..5e136be6f8b 100644
--- a/app/assets/images/emoji/love_hotel.png
+++ b/public/-/emojis/1/love_hotel.png
Binary files differ
diff --git a/app/assets/images/emoji/love_letter.png b/public/-/emojis/1/love_letter.png
index 3c3c767e784..3c3c767e784 100644
--- a/app/assets/images/emoji/love_letter.png
+++ b/public/-/emojis/1/love_letter.png
Binary files differ
diff --git a/app/assets/images/emoji/low_brightness.png b/public/-/emojis/1/low_brightness.png
index 543011d3961..543011d3961 100644
--- a/app/assets/images/emoji/low_brightness.png
+++ b/public/-/emojis/1/low_brightness.png
Binary files differ
diff --git a/app/assets/images/emoji/lying_face.png b/public/-/emojis/1/lying_face.png
index 02827e2628b..02827e2628b 100644
--- a/app/assets/images/emoji/lying_face.png
+++ b/public/-/emojis/1/lying_face.png
Binary files differ
diff --git a/app/assets/images/emoji/m.png b/public/-/emojis/1/m.png
index 8a3506fc1d7..8a3506fc1d7 100644
--- a/app/assets/images/emoji/m.png
+++ b/public/-/emojis/1/m.png
Binary files differ
diff --git a/app/assets/images/emoji/mag.png b/public/-/emojis/1/mag.png
index 55487156ac6..55487156ac6 100644
--- a/app/assets/images/emoji/mag.png
+++ b/public/-/emojis/1/mag.png
Binary files differ
diff --git a/app/assets/images/emoji/mag_right.png b/public/-/emojis/1/mag_right.png
index 0f4b1bca876..0f4b1bca876 100644
--- a/app/assets/images/emoji/mag_right.png
+++ b/public/-/emojis/1/mag_right.png
Binary files differ
diff --git a/app/assets/images/emoji/mahjong.png b/public/-/emojis/1/mahjong.png
index 66fd32025b2..66fd32025b2 100644
--- a/app/assets/images/emoji/mahjong.png
+++ b/public/-/emojis/1/mahjong.png
Binary files differ
diff --git a/app/assets/images/emoji/mailbox.png b/public/-/emojis/1/mailbox.png
index ef5174e40dd..ef5174e40dd 100644
--- a/app/assets/images/emoji/mailbox.png
+++ b/public/-/emojis/1/mailbox.png
Binary files differ
diff --git a/app/assets/images/emoji/mailbox_closed.png b/public/-/emojis/1/mailbox_closed.png
index ddc705db0d8..ddc705db0d8 100644
--- a/app/assets/images/emoji/mailbox_closed.png
+++ b/public/-/emojis/1/mailbox_closed.png
Binary files differ
diff --git a/app/assets/images/emoji/mailbox_with_mail.png b/public/-/emojis/1/mailbox_with_mail.png
index 5460616a5b1..5460616a5b1 100644
--- a/app/assets/images/emoji/mailbox_with_mail.png
+++ b/public/-/emojis/1/mailbox_with_mail.png
Binary files differ
diff --git a/app/assets/images/emoji/mailbox_with_no_mail.png b/public/-/emojis/1/mailbox_with_no_mail.png
index f9aeee6b15a..f9aeee6b15a 100644
--- a/app/assets/images/emoji/mailbox_with_no_mail.png
+++ b/public/-/emojis/1/mailbox_with_no_mail.png
Binary files differ
diff --git a/app/assets/images/emoji/man.png b/public/-/emojis/1/man.png
index 857a02e5146..857a02e5146 100644
--- a/app/assets/images/emoji/man.png
+++ b/public/-/emojis/1/man.png
Binary files differ
diff --git a/app/assets/images/emoji/man_dancing.png b/public/-/emojis/1/man_dancing.png
index ccff3bede5a..ccff3bede5a 100644
--- a/app/assets/images/emoji/man_dancing.png
+++ b/public/-/emojis/1/man_dancing.png
Binary files differ
diff --git a/app/assets/images/emoji/man_dancing_tone1.png b/public/-/emojis/1/man_dancing_tone1.png
index e0b9f82d905..e0b9f82d905 100644
--- a/app/assets/images/emoji/man_dancing_tone1.png
+++ b/public/-/emojis/1/man_dancing_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/man_dancing_tone2.png b/public/-/emojis/1/man_dancing_tone2.png
index a5beed56e2e..a5beed56e2e 100644
--- a/app/assets/images/emoji/man_dancing_tone2.png
+++ b/public/-/emojis/1/man_dancing_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/man_dancing_tone3.png b/public/-/emojis/1/man_dancing_tone3.png
index 2fa20180a6e..2fa20180a6e 100644
--- a/app/assets/images/emoji/man_dancing_tone3.png
+++ b/public/-/emojis/1/man_dancing_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/man_dancing_tone4.png b/public/-/emojis/1/man_dancing_tone4.png
index bd3528c83ba..bd3528c83ba 100644
--- a/app/assets/images/emoji/man_dancing_tone4.png
+++ b/public/-/emojis/1/man_dancing_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/man_dancing_tone5.png b/public/-/emojis/1/man_dancing_tone5.png
index 41fd4f880c9..41fd4f880c9 100644
--- a/app/assets/images/emoji/man_dancing_tone5.png
+++ b/public/-/emojis/1/man_dancing_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/man_in_tuxedo.png b/public/-/emojis/1/man_in_tuxedo.png
index 5f7e9303f89..5f7e9303f89 100644
--- a/app/assets/images/emoji/man_in_tuxedo.png
+++ b/public/-/emojis/1/man_in_tuxedo.png
Binary files differ
diff --git a/app/assets/images/emoji/man_in_tuxedo_tone1.png b/public/-/emojis/1/man_in_tuxedo_tone1.png
index 7b6b3acd99b..7b6b3acd99b 100644
--- a/app/assets/images/emoji/man_in_tuxedo_tone1.png
+++ b/public/-/emojis/1/man_in_tuxedo_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/man_in_tuxedo_tone2.png b/public/-/emojis/1/man_in_tuxedo_tone2.png
index 7975191b360..7975191b360 100644
--- a/app/assets/images/emoji/man_in_tuxedo_tone2.png
+++ b/public/-/emojis/1/man_in_tuxedo_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/man_in_tuxedo_tone3.png b/public/-/emojis/1/man_in_tuxedo_tone3.png
index a2816f600ae..a2816f600ae 100644
--- a/app/assets/images/emoji/man_in_tuxedo_tone3.png
+++ b/public/-/emojis/1/man_in_tuxedo_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/man_in_tuxedo_tone4.png b/public/-/emojis/1/man_in_tuxedo_tone4.png
index ea8291760f9..ea8291760f9 100644
--- a/app/assets/images/emoji/man_in_tuxedo_tone4.png
+++ b/public/-/emojis/1/man_in_tuxedo_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/man_in_tuxedo_tone5.png b/public/-/emojis/1/man_in_tuxedo_tone5.png
index c743e05fc5e..c743e05fc5e 100644
--- a/app/assets/images/emoji/man_in_tuxedo_tone5.png
+++ b/public/-/emojis/1/man_in_tuxedo_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/man_tone1.png b/public/-/emojis/1/man_tone1.png
index bb86e963a80..bb86e963a80 100644
--- a/app/assets/images/emoji/man_tone1.png
+++ b/public/-/emojis/1/man_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/man_tone2.png b/public/-/emojis/1/man_tone2.png
index fdeeaff46f5..fdeeaff46f5 100644
--- a/app/assets/images/emoji/man_tone2.png
+++ b/public/-/emojis/1/man_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/man_tone3.png b/public/-/emojis/1/man_tone3.png
index 7ae0b5df9cf..7ae0b5df9cf 100644
--- a/app/assets/images/emoji/man_tone3.png
+++ b/public/-/emojis/1/man_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/man_tone4.png b/public/-/emojis/1/man_tone4.png
index db14cde99b8..db14cde99b8 100644
--- a/app/assets/images/emoji/man_tone4.png
+++ b/public/-/emojis/1/man_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/man_tone5.png b/public/-/emojis/1/man_tone5.png
index 7c67a70529c..7c67a70529c 100644
--- a/app/assets/images/emoji/man_tone5.png
+++ b/public/-/emojis/1/man_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_gua_pi_mao.png b/public/-/emojis/1/man_with_gua_pi_mao.png
index 7841e13608d..7841e13608d 100644
--- a/app/assets/images/emoji/man_with_gua_pi_mao.png
+++ b/public/-/emojis/1/man_with_gua_pi_mao.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_gua_pi_mao_tone1.png b/public/-/emojis/1/man_with_gua_pi_mao_tone1.png
index 5b7b3def19c..5b7b3def19c 100644
--- a/app/assets/images/emoji/man_with_gua_pi_mao_tone1.png
+++ b/public/-/emojis/1/man_with_gua_pi_mao_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_gua_pi_mao_tone2.png b/public/-/emojis/1/man_with_gua_pi_mao_tone2.png
index c8b9cf87f4b..c8b9cf87f4b 100644
--- a/app/assets/images/emoji/man_with_gua_pi_mao_tone2.png
+++ b/public/-/emojis/1/man_with_gua_pi_mao_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_gua_pi_mao_tone3.png b/public/-/emojis/1/man_with_gua_pi_mao_tone3.png
index effdd0c4c84..effdd0c4c84 100644
--- a/app/assets/images/emoji/man_with_gua_pi_mao_tone3.png
+++ b/public/-/emojis/1/man_with_gua_pi_mao_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_gua_pi_mao_tone4.png b/public/-/emojis/1/man_with_gua_pi_mao_tone4.png
index f885ff46fa1..f885ff46fa1 100644
--- a/app/assets/images/emoji/man_with_gua_pi_mao_tone4.png
+++ b/public/-/emojis/1/man_with_gua_pi_mao_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_gua_pi_mao_tone5.png b/public/-/emojis/1/man_with_gua_pi_mao_tone5.png
index a6d55ca1380..a6d55ca1380 100644
--- a/app/assets/images/emoji/man_with_gua_pi_mao_tone5.png
+++ b/public/-/emojis/1/man_with_gua_pi_mao_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_turban.png b/public/-/emojis/1/man_with_turban.png
index 51cf047f966..51cf047f966 100644
--- a/app/assets/images/emoji/man_with_turban.png
+++ b/public/-/emojis/1/man_with_turban.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_turban_tone1.png b/public/-/emojis/1/man_with_turban_tone1.png
index 1e12ee4b231..1e12ee4b231 100644
--- a/app/assets/images/emoji/man_with_turban_tone1.png
+++ b/public/-/emojis/1/man_with_turban_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_turban_tone2.png b/public/-/emojis/1/man_with_turban_tone2.png
index 37de4cceb23..37de4cceb23 100644
--- a/app/assets/images/emoji/man_with_turban_tone2.png
+++ b/public/-/emojis/1/man_with_turban_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_turban_tone3.png b/public/-/emojis/1/man_with_turban_tone3.png
index f607afd3450..f607afd3450 100644
--- a/app/assets/images/emoji/man_with_turban_tone3.png
+++ b/public/-/emojis/1/man_with_turban_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_turban_tone4.png b/public/-/emojis/1/man_with_turban_tone4.png
index c05695888af..c05695888af 100644
--- a/app/assets/images/emoji/man_with_turban_tone4.png
+++ b/public/-/emojis/1/man_with_turban_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/man_with_turban_tone5.png b/public/-/emojis/1/man_with_turban_tone5.png
index 4b4ff64720b..4b4ff64720b 100644
--- a/app/assets/images/emoji/man_with_turban_tone5.png
+++ b/public/-/emojis/1/man_with_turban_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/mans_shoe.png b/public/-/emojis/1/mans_shoe.png
index 4bf7541032c..4bf7541032c 100644
--- a/app/assets/images/emoji/mans_shoe.png
+++ b/public/-/emojis/1/mans_shoe.png
Binary files differ
diff --git a/app/assets/images/emoji/map.png b/public/-/emojis/1/map.png
index 15efe32c798..15efe32c798 100644
--- a/app/assets/images/emoji/map.png
+++ b/public/-/emojis/1/map.png
Binary files differ
diff --git a/app/assets/images/emoji/maple_leaf.png b/public/-/emojis/1/maple_leaf.png
index c49acea67f7..c49acea67f7 100644
--- a/app/assets/images/emoji/maple_leaf.png
+++ b/public/-/emojis/1/maple_leaf.png
Binary files differ
diff --git a/app/assets/images/emoji/martial_arts_uniform.png b/public/-/emojis/1/martial_arts_uniform.png
index 8d6114761f6..8d6114761f6 100644
--- a/app/assets/images/emoji/martial_arts_uniform.png
+++ b/public/-/emojis/1/martial_arts_uniform.png
Binary files differ
diff --git a/app/assets/images/emoji/mask.png b/public/-/emojis/1/mask.png
index 1e800acd1c0..1e800acd1c0 100644
--- a/app/assets/images/emoji/mask.png
+++ b/public/-/emojis/1/mask.png
Binary files differ
diff --git a/app/assets/images/emoji/massage.png b/public/-/emojis/1/massage.png
index b91d845e374..b91d845e374 100644
--- a/app/assets/images/emoji/massage.png
+++ b/public/-/emojis/1/massage.png
Binary files differ
diff --git a/app/assets/images/emoji/massage_tone1.png b/public/-/emojis/1/massage_tone1.png
index e0f415d3186..e0f415d3186 100644
--- a/app/assets/images/emoji/massage_tone1.png
+++ b/public/-/emojis/1/massage_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/massage_tone2.png b/public/-/emojis/1/massage_tone2.png
index 0bb244a270b..0bb244a270b 100644
--- a/app/assets/images/emoji/massage_tone2.png
+++ b/public/-/emojis/1/massage_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/massage_tone3.png b/public/-/emojis/1/massage_tone3.png
index a117ee81a22..a117ee81a22 100644
--- a/app/assets/images/emoji/massage_tone3.png
+++ b/public/-/emojis/1/massage_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/massage_tone4.png b/public/-/emojis/1/massage_tone4.png
index 6f42ab017f4..6f42ab017f4 100644
--- a/app/assets/images/emoji/massage_tone4.png
+++ b/public/-/emojis/1/massage_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/massage_tone5.png b/public/-/emojis/1/massage_tone5.png
index 6a388c0d0b5..6a388c0d0b5 100644
--- a/app/assets/images/emoji/massage_tone5.png
+++ b/public/-/emojis/1/massage_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/meat_on_bone.png b/public/-/emojis/1/meat_on_bone.png
index b20a59d1690..b20a59d1690 100644
--- a/app/assets/images/emoji/meat_on_bone.png
+++ b/public/-/emojis/1/meat_on_bone.png
Binary files differ
diff --git a/app/assets/images/emoji/medal.png b/public/-/emojis/1/medal.png
index b85896b14da..b85896b14da 100644
--- a/app/assets/images/emoji/medal.png
+++ b/public/-/emojis/1/medal.png
Binary files differ
diff --git a/app/assets/images/emoji/mega.png b/public/-/emojis/1/mega.png
index 4e6735188e3..4e6735188e3 100644
--- a/app/assets/images/emoji/mega.png
+++ b/public/-/emojis/1/mega.png
Binary files differ
diff --git a/app/assets/images/emoji/melon.png b/public/-/emojis/1/melon.png
index c01232d419d..c01232d419d 100644
--- a/app/assets/images/emoji/melon.png
+++ b/public/-/emojis/1/melon.png
Binary files differ
diff --git a/app/assets/images/emoji/menorah.png b/public/-/emojis/1/menorah.png
index b4297362869..b4297362869 100644
--- a/app/assets/images/emoji/menorah.png
+++ b/public/-/emojis/1/menorah.png
Binary files differ
diff --git a/app/assets/images/emoji/mens.png b/public/-/emojis/1/mens.png
index f5a1e1ba0cd..f5a1e1ba0cd 100644
--- a/app/assets/images/emoji/mens.png
+++ b/public/-/emojis/1/mens.png
Binary files differ
diff --git a/app/assets/images/emoji/metal.png b/public/-/emojis/1/metal.png
index 4aa6e7e0a44..4aa6e7e0a44 100644
--- a/app/assets/images/emoji/metal.png
+++ b/public/-/emojis/1/metal.png
Binary files differ
diff --git a/app/assets/images/emoji/metal_tone1.png b/public/-/emojis/1/metal_tone1.png
index c080d2addbd..c080d2addbd 100644
--- a/app/assets/images/emoji/metal_tone1.png
+++ b/public/-/emojis/1/metal_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/metal_tone2.png b/public/-/emojis/1/metal_tone2.png
index 12313529bcf..12313529bcf 100644
--- a/app/assets/images/emoji/metal_tone2.png
+++ b/public/-/emojis/1/metal_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/metal_tone3.png b/public/-/emojis/1/metal_tone3.png
index ca9be6ae67b..ca9be6ae67b 100644
--- a/app/assets/images/emoji/metal_tone3.png
+++ b/public/-/emojis/1/metal_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/metal_tone4.png b/public/-/emojis/1/metal_tone4.png
index abe28cbf890..abe28cbf890 100644
--- a/app/assets/images/emoji/metal_tone4.png
+++ b/public/-/emojis/1/metal_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/metal_tone5.png b/public/-/emojis/1/metal_tone5.png
index 0c6b5dd34ed..0c6b5dd34ed 100644
--- a/app/assets/images/emoji/metal_tone5.png
+++ b/public/-/emojis/1/metal_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/metro.png b/public/-/emojis/1/metro.png
index 1de8f0551f3..1de8f0551f3 100644
--- a/app/assets/images/emoji/metro.png
+++ b/public/-/emojis/1/metro.png
Binary files differ
diff --git a/app/assets/images/emoji/microphone.png b/public/-/emojis/1/microphone.png
index d4e6b0def25..d4e6b0def25 100644
--- a/app/assets/images/emoji/microphone.png
+++ b/public/-/emojis/1/microphone.png
Binary files differ
diff --git a/app/assets/images/emoji/microphone2.png b/public/-/emojis/1/microphone2.png
index cd9167654ff..cd9167654ff 100644
--- a/app/assets/images/emoji/microphone2.png
+++ b/public/-/emojis/1/microphone2.png
Binary files differ
diff --git a/app/assets/images/emoji/microscope.png b/public/-/emojis/1/microscope.png
index 90f5acf6a78..90f5acf6a78 100644
--- a/app/assets/images/emoji/microscope.png
+++ b/public/-/emojis/1/microscope.png
Binary files differ
diff --git a/app/assets/images/emoji/middle_finger.png b/public/-/emojis/1/middle_finger.png
index 697f7a25eb2..697f7a25eb2 100644
--- a/app/assets/images/emoji/middle_finger.png
+++ b/public/-/emojis/1/middle_finger.png
Binary files differ
diff --git a/app/assets/images/emoji/middle_finger_tone1.png b/public/-/emojis/1/middle_finger_tone1.png
index 61ef12a1548..61ef12a1548 100644
--- a/app/assets/images/emoji/middle_finger_tone1.png
+++ b/public/-/emojis/1/middle_finger_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/middle_finger_tone2.png b/public/-/emojis/1/middle_finger_tone2.png
index c31a69be9af..c31a69be9af 100644
--- a/app/assets/images/emoji/middle_finger_tone2.png
+++ b/public/-/emojis/1/middle_finger_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/middle_finger_tone3.png b/public/-/emojis/1/middle_finger_tone3.png
index 73ac216ce63..73ac216ce63 100644
--- a/app/assets/images/emoji/middle_finger_tone3.png
+++ b/public/-/emojis/1/middle_finger_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/middle_finger_tone4.png b/public/-/emojis/1/middle_finger_tone4.png
index 80b8ab7706d..80b8ab7706d 100644
--- a/app/assets/images/emoji/middle_finger_tone4.png
+++ b/public/-/emojis/1/middle_finger_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/middle_finger_tone5.png b/public/-/emojis/1/middle_finger_tone5.png
index a8826b196e8..a8826b196e8 100644
--- a/app/assets/images/emoji/middle_finger_tone5.png
+++ b/public/-/emojis/1/middle_finger_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/military_medal.png b/public/-/emojis/1/military_medal.png
index ecd3fb03584..ecd3fb03584 100644
--- a/app/assets/images/emoji/military_medal.png
+++ b/public/-/emojis/1/military_medal.png
Binary files differ
diff --git a/app/assets/images/emoji/milk.png b/public/-/emojis/1/milk.png
index e4fcf2e64f3..e4fcf2e64f3 100644
--- a/app/assets/images/emoji/milk.png
+++ b/public/-/emojis/1/milk.png
Binary files differ
diff --git a/app/assets/images/emoji/milky_way.png b/public/-/emojis/1/milky_way.png
index b2b8ac59c5e..b2b8ac59c5e 100644
--- a/app/assets/images/emoji/milky_way.png
+++ b/public/-/emojis/1/milky_way.png
Binary files differ
diff --git a/app/assets/images/emoji/minibus.png b/public/-/emojis/1/minibus.png
index c60dd8f47ab..c60dd8f47ab 100644
--- a/app/assets/images/emoji/minibus.png
+++ b/public/-/emojis/1/minibus.png
Binary files differ
diff --git a/app/assets/images/emoji/minidisc.png b/public/-/emojis/1/minidisc.png
index 9fa94cfbe74..9fa94cfbe74 100644
--- a/app/assets/images/emoji/minidisc.png
+++ b/public/-/emojis/1/minidisc.png
Binary files differ
diff --git a/app/assets/images/emoji/mobile_phone_off.png b/public/-/emojis/1/mobile_phone_off.png
index 8b661ec1c94..8b661ec1c94 100644
--- a/app/assets/images/emoji/mobile_phone_off.png
+++ b/public/-/emojis/1/mobile_phone_off.png
Binary files differ
diff --git a/app/assets/images/emoji/money_mouth.png b/public/-/emojis/1/money_mouth.png
index 75fd1e90cb0..75fd1e90cb0 100644
--- a/app/assets/images/emoji/money_mouth.png
+++ b/public/-/emojis/1/money_mouth.png
Binary files differ
diff --git a/app/assets/images/emoji/money_with_wings.png b/public/-/emojis/1/money_with_wings.png
index f022b04b3c2..f022b04b3c2 100644
--- a/app/assets/images/emoji/money_with_wings.png
+++ b/public/-/emojis/1/money_with_wings.png
Binary files differ
diff --git a/app/assets/images/emoji/moneybag.png b/public/-/emojis/1/moneybag.png
index b9296be0902..b9296be0902 100644
--- a/app/assets/images/emoji/moneybag.png
+++ b/public/-/emojis/1/moneybag.png
Binary files differ
diff --git a/app/assets/images/emoji/monkey.png b/public/-/emojis/1/monkey.png
index 9fae29448e3..9fae29448e3 100644
--- a/app/assets/images/emoji/monkey.png
+++ b/public/-/emojis/1/monkey.png
Binary files differ
diff --git a/app/assets/images/emoji/monkey_face.png b/public/-/emojis/1/monkey_face.png
index 7cab9b91a82..7cab9b91a82 100644
--- a/app/assets/images/emoji/monkey_face.png
+++ b/public/-/emojis/1/monkey_face.png
Binary files differ
diff --git a/app/assets/images/emoji/monorail.png b/public/-/emojis/1/monorail.png
index 11eb1f574bf..11eb1f574bf 100644
--- a/app/assets/images/emoji/monorail.png
+++ b/public/-/emojis/1/monorail.png
Binary files differ
diff --git a/app/assets/images/emoji/mortar_board.png b/public/-/emojis/1/mortar_board.png
index 8b17ddd9d00..8b17ddd9d00 100644
--- a/app/assets/images/emoji/mortar_board.png
+++ b/public/-/emojis/1/mortar_board.png
Binary files differ
diff --git a/app/assets/images/emoji/mosque.png b/public/-/emojis/1/mosque.png
index ef770b26d96..ef770b26d96 100644
--- a/app/assets/images/emoji/mosque.png
+++ b/public/-/emojis/1/mosque.png
Binary files differ
diff --git a/app/assets/images/emoji/motor_scooter.png b/public/-/emojis/1/motor_scooter.png
index c5afa72d807..c5afa72d807 100644
--- a/app/assets/images/emoji/motor_scooter.png
+++ b/public/-/emojis/1/motor_scooter.png
Binary files differ
diff --git a/app/assets/images/emoji/motorboat.png b/public/-/emojis/1/motorboat.png
index 0506db1a40f..0506db1a40f 100644
--- a/app/assets/images/emoji/motorboat.png
+++ b/public/-/emojis/1/motorboat.png
Binary files differ
diff --git a/app/assets/images/emoji/motorcycle.png b/public/-/emojis/1/motorcycle.png
index 3d1d567e8ec..3d1d567e8ec 100644
--- a/app/assets/images/emoji/motorcycle.png
+++ b/public/-/emojis/1/motorcycle.png
Binary files differ
diff --git a/app/assets/images/emoji/motorway.png b/public/-/emojis/1/motorway.png
index 8c3d3d03e3f..8c3d3d03e3f 100644
--- a/app/assets/images/emoji/motorway.png
+++ b/public/-/emojis/1/motorway.png
Binary files differ
diff --git a/app/assets/images/emoji/mount_fuji.png b/public/-/emojis/1/mount_fuji.png
index 88a54752458..88a54752458 100644
--- a/app/assets/images/emoji/mount_fuji.png
+++ b/public/-/emojis/1/mount_fuji.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain.png b/public/-/emojis/1/mountain.png
index 6722ebdd294..6722ebdd294 100644
--- a/app/assets/images/emoji/mountain.png
+++ b/public/-/emojis/1/mountain.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_bicyclist.png b/public/-/emojis/1/mountain_bicyclist.png
index 41d3dc3ac6f..41d3dc3ac6f 100644
--- a/app/assets/images/emoji/mountain_bicyclist.png
+++ b/public/-/emojis/1/mountain_bicyclist.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_bicyclist_tone1.png b/public/-/emojis/1/mountain_bicyclist_tone1.png
index e9f1daf5e40..e9f1daf5e40 100644
--- a/app/assets/images/emoji/mountain_bicyclist_tone1.png
+++ b/public/-/emojis/1/mountain_bicyclist_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_bicyclist_tone2.png b/public/-/emojis/1/mountain_bicyclist_tone2.png
index 555b9e29d4d..555b9e29d4d 100644
--- a/app/assets/images/emoji/mountain_bicyclist_tone2.png
+++ b/public/-/emojis/1/mountain_bicyclist_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_bicyclist_tone3.png b/public/-/emojis/1/mountain_bicyclist_tone3.png
index 7df5508ec8c..7df5508ec8c 100644
--- a/app/assets/images/emoji/mountain_bicyclist_tone3.png
+++ b/public/-/emojis/1/mountain_bicyclist_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_bicyclist_tone4.png b/public/-/emojis/1/mountain_bicyclist_tone4.png
index f94b3450697..f94b3450697 100644
--- a/app/assets/images/emoji/mountain_bicyclist_tone4.png
+++ b/public/-/emojis/1/mountain_bicyclist_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_bicyclist_tone5.png b/public/-/emojis/1/mountain_bicyclist_tone5.png
index 16a45861e1f..16a45861e1f 100644
--- a/app/assets/images/emoji/mountain_bicyclist_tone5.png
+++ b/public/-/emojis/1/mountain_bicyclist_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_cableway.png b/public/-/emojis/1/mountain_cableway.png
index 1dea73ca53b..1dea73ca53b 100644
--- a/app/assets/images/emoji/mountain_cableway.png
+++ b/public/-/emojis/1/mountain_cableway.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_railway.png b/public/-/emojis/1/mountain_railway.png
index ade2218e469..ade2218e469 100644
--- a/app/assets/images/emoji/mountain_railway.png
+++ b/public/-/emojis/1/mountain_railway.png
Binary files differ
diff --git a/app/assets/images/emoji/mountain_snow.png b/public/-/emojis/1/mountain_snow.png
index 76e1cfd8313..76e1cfd8313 100644
--- a/app/assets/images/emoji/mountain_snow.png
+++ b/public/-/emojis/1/mountain_snow.png
Binary files differ
diff --git a/app/assets/images/emoji/mouse.png b/public/-/emojis/1/mouse.png
index 50afcd3262e..50afcd3262e 100644
--- a/app/assets/images/emoji/mouse.png
+++ b/public/-/emojis/1/mouse.png
Binary files differ
diff --git a/app/assets/images/emoji/mouse2.png b/public/-/emojis/1/mouse2.png
index 20fb041f09f..20fb041f09f 100644
--- a/app/assets/images/emoji/mouse2.png
+++ b/public/-/emojis/1/mouse2.png
Binary files differ
diff --git a/app/assets/images/emoji/mouse_three_button.png b/public/-/emojis/1/mouse_three_button.png
index e84e96ff6e8..e84e96ff6e8 100644
--- a/app/assets/images/emoji/mouse_three_button.png
+++ b/public/-/emojis/1/mouse_three_button.png
Binary files differ
diff --git a/app/assets/images/emoji/movie_camera.png b/public/-/emojis/1/movie_camera.png
index 4e73b130155..4e73b130155 100644
--- a/app/assets/images/emoji/movie_camera.png
+++ b/public/-/emojis/1/movie_camera.png
Binary files differ
diff --git a/app/assets/images/emoji/moyai.png b/public/-/emojis/1/moyai.png
index e6a7779c45b..e6a7779c45b 100644
--- a/app/assets/images/emoji/moyai.png
+++ b/public/-/emojis/1/moyai.png
Binary files differ
diff --git a/app/assets/images/emoji/mrs_claus.png b/public/-/emojis/1/mrs_claus.png
index 9cf2458df1a..9cf2458df1a 100644
--- a/app/assets/images/emoji/mrs_claus.png
+++ b/public/-/emojis/1/mrs_claus.png
Binary files differ
diff --git a/app/assets/images/emoji/mrs_claus_tone1.png b/public/-/emojis/1/mrs_claus_tone1.png
index d8a695d7035..d8a695d7035 100644
--- a/app/assets/images/emoji/mrs_claus_tone1.png
+++ b/public/-/emojis/1/mrs_claus_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/mrs_claus_tone2.png b/public/-/emojis/1/mrs_claus_tone2.png
index 0e17e8c51f3..0e17e8c51f3 100644
--- a/app/assets/images/emoji/mrs_claus_tone2.png
+++ b/public/-/emojis/1/mrs_claus_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/mrs_claus_tone3.png b/public/-/emojis/1/mrs_claus_tone3.png
index c3ee4d1dfae..c3ee4d1dfae 100644
--- a/app/assets/images/emoji/mrs_claus_tone3.png
+++ b/public/-/emojis/1/mrs_claus_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/mrs_claus_tone4.png b/public/-/emojis/1/mrs_claus_tone4.png
index 68a556da2fe..68a556da2fe 100644
--- a/app/assets/images/emoji/mrs_claus_tone4.png
+++ b/public/-/emojis/1/mrs_claus_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/mrs_claus_tone5.png b/public/-/emojis/1/mrs_claus_tone5.png
index ccab3c40ff2..ccab3c40ff2 100644
--- a/app/assets/images/emoji/mrs_claus_tone5.png
+++ b/public/-/emojis/1/mrs_claus_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/muscle.png b/public/-/emojis/1/muscle.png
index 7e67c1880f7..7e67c1880f7 100644
--- a/app/assets/images/emoji/muscle.png
+++ b/public/-/emojis/1/muscle.png
Binary files differ
diff --git a/app/assets/images/emoji/muscle_tone1.png b/public/-/emojis/1/muscle_tone1.png
index 1522942ce51..1522942ce51 100644
--- a/app/assets/images/emoji/muscle_tone1.png
+++ b/public/-/emojis/1/muscle_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/muscle_tone2.png b/public/-/emojis/1/muscle_tone2.png
index 569c6e832ca..569c6e832ca 100644
--- a/app/assets/images/emoji/muscle_tone2.png
+++ b/public/-/emojis/1/muscle_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/muscle_tone3.png b/public/-/emojis/1/muscle_tone3.png
index 0a76b00fa89..0a76b00fa89 100644
--- a/app/assets/images/emoji/muscle_tone3.png
+++ b/public/-/emojis/1/muscle_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/muscle_tone4.png b/public/-/emojis/1/muscle_tone4.png
index f0cf31328e0..f0cf31328e0 100644
--- a/app/assets/images/emoji/muscle_tone4.png
+++ b/public/-/emojis/1/muscle_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/muscle_tone5.png b/public/-/emojis/1/muscle_tone5.png
index 4fda92460e8..4fda92460e8 100644
--- a/app/assets/images/emoji/muscle_tone5.png
+++ b/public/-/emojis/1/muscle_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/mushroom.png b/public/-/emojis/1/mushroom.png
index dd85742ba2c..dd85742ba2c 100644
--- a/app/assets/images/emoji/mushroom.png
+++ b/public/-/emojis/1/mushroom.png
Binary files differ
diff --git a/app/assets/images/emoji/musical_keyboard.png b/public/-/emojis/1/musical_keyboard.png
index 442b7456842..442b7456842 100644
--- a/app/assets/images/emoji/musical_keyboard.png
+++ b/public/-/emojis/1/musical_keyboard.png
Binary files differ
diff --git a/app/assets/images/emoji/musical_note.png b/public/-/emojis/1/musical_note.png
index 06691ef61bb..06691ef61bb 100644
--- a/app/assets/images/emoji/musical_note.png
+++ b/public/-/emojis/1/musical_note.png
Binary files differ
diff --git a/app/assets/images/emoji/musical_score.png b/public/-/emojis/1/musical_score.png
index 47dc05a8ef5..47dc05a8ef5 100644
--- a/app/assets/images/emoji/musical_score.png
+++ b/public/-/emojis/1/musical_score.png
Binary files differ
diff --git a/app/assets/images/emoji/mute.png b/public/-/emojis/1/mute.png
index 7c1788e5075..7c1788e5075 100644
--- a/app/assets/images/emoji/mute.png
+++ b/public/-/emojis/1/mute.png
Binary files differ
diff --git a/app/assets/images/emoji/nail_care.png b/public/-/emojis/1/nail_care.png
index aa52af7050d..aa52af7050d 100644
--- a/app/assets/images/emoji/nail_care.png
+++ b/public/-/emojis/1/nail_care.png
Binary files differ
diff --git a/app/assets/images/emoji/nail_care_tone1.png b/public/-/emojis/1/nail_care_tone1.png
index 26e883dd244..26e883dd244 100644
--- a/app/assets/images/emoji/nail_care_tone1.png
+++ b/public/-/emojis/1/nail_care_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/nail_care_tone2.png b/public/-/emojis/1/nail_care_tone2.png
index 61257b47ea3..61257b47ea3 100644
--- a/app/assets/images/emoji/nail_care_tone2.png
+++ b/public/-/emojis/1/nail_care_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/nail_care_tone3.png b/public/-/emojis/1/nail_care_tone3.png
index 29871b05f62..29871b05f62 100644
--- a/app/assets/images/emoji/nail_care_tone3.png
+++ b/public/-/emojis/1/nail_care_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/nail_care_tone4.png b/public/-/emojis/1/nail_care_tone4.png
index 2881de0b17d..2881de0b17d 100644
--- a/app/assets/images/emoji/nail_care_tone4.png
+++ b/public/-/emojis/1/nail_care_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/nail_care_tone5.png b/public/-/emojis/1/nail_care_tone5.png
index a0b7c0a45a6..a0b7c0a45a6 100644
--- a/app/assets/images/emoji/nail_care_tone5.png
+++ b/public/-/emojis/1/nail_care_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/name_badge.png b/public/-/emojis/1/name_badge.png
index ec5ee213e20..ec5ee213e20 100644
--- a/app/assets/images/emoji/name_badge.png
+++ b/public/-/emojis/1/name_badge.png
Binary files differ
diff --git a/app/assets/images/emoji/nauseated_face.png b/public/-/emojis/1/nauseated_face.png
index a566c109c28..a566c109c28 100644
--- a/app/assets/images/emoji/nauseated_face.png
+++ b/public/-/emojis/1/nauseated_face.png
Binary files differ
diff --git a/app/assets/images/emoji/necktie.png b/public/-/emojis/1/necktie.png
index 1804e7f3ff3..1804e7f3ff3 100644
--- a/app/assets/images/emoji/necktie.png
+++ b/public/-/emojis/1/necktie.png
Binary files differ
diff --git a/app/assets/images/emoji/negative_squared_cross_mark.png b/public/-/emojis/1/negative_squared_cross_mark.png
index dae487f1f98..dae487f1f98 100644
--- a/app/assets/images/emoji/negative_squared_cross_mark.png
+++ b/public/-/emojis/1/negative_squared_cross_mark.png
Binary files differ
diff --git a/app/assets/images/emoji/nerd.png b/public/-/emojis/1/nerd.png
index 7820bd581dc..7820bd581dc 100644
--- a/app/assets/images/emoji/nerd.png
+++ b/public/-/emojis/1/nerd.png
Binary files differ
diff --git a/app/assets/images/emoji/neutral_face.png b/public/-/emojis/1/neutral_face.png
index 065d193afe4..065d193afe4 100644
--- a/app/assets/images/emoji/neutral_face.png
+++ b/public/-/emojis/1/neutral_face.png
Binary files differ
diff --git a/app/assets/images/emoji/new.png b/public/-/emojis/1/new.png
index b4f85488d1a..b4f85488d1a 100644
--- a/app/assets/images/emoji/new.png
+++ b/public/-/emojis/1/new.png
Binary files differ
diff --git a/app/assets/images/emoji/new_moon.png b/public/-/emojis/1/new_moon.png
index ecff72caa42..ecff72caa42 100644
--- a/app/assets/images/emoji/new_moon.png
+++ b/public/-/emojis/1/new_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/new_moon_with_face.png b/public/-/emojis/1/new_moon_with_face.png
index 150dd12400c..150dd12400c 100644
--- a/app/assets/images/emoji/new_moon_with_face.png
+++ b/public/-/emojis/1/new_moon_with_face.png
Binary files differ
diff --git a/app/assets/images/emoji/newspaper.png b/public/-/emojis/1/newspaper.png
index 2aa8f060bde..2aa8f060bde 100644
--- a/app/assets/images/emoji/newspaper.png
+++ b/public/-/emojis/1/newspaper.png
Binary files differ
diff --git a/app/assets/images/emoji/newspaper2.png b/public/-/emojis/1/newspaper2.png
index f64748df2b2..f64748df2b2 100644
--- a/app/assets/images/emoji/newspaper2.png
+++ b/public/-/emojis/1/newspaper2.png
Binary files differ
diff --git a/app/assets/images/emoji/ng.png b/public/-/emojis/1/ng.png
index ee8d20f5ebc..ee8d20f5ebc 100644
--- a/app/assets/images/emoji/ng.png
+++ b/public/-/emojis/1/ng.png
Binary files differ
diff --git a/app/assets/images/emoji/night_with_stars.png b/public/-/emojis/1/night_with_stars.png
index ca2018f456d..ca2018f456d 100644
--- a/app/assets/images/emoji/night_with_stars.png
+++ b/public/-/emojis/1/night_with_stars.png
Binary files differ
diff --git a/app/assets/images/emoji/nine.png b/public/-/emojis/1/nine.png
index 9fce3d1eca9..9fce3d1eca9 100644
--- a/app/assets/images/emoji/nine.png
+++ b/public/-/emojis/1/nine.png
Binary files differ
diff --git a/app/assets/images/emoji/no_bell.png b/public/-/emojis/1/no_bell.png
index 15cb38dd1e7..15cb38dd1e7 100644
--- a/app/assets/images/emoji/no_bell.png
+++ b/public/-/emojis/1/no_bell.png
Binary files differ
diff --git a/app/assets/images/emoji/no_bicycles.png b/public/-/emojis/1/no_bicycles.png
index 19c85421ce9..19c85421ce9 100644
--- a/app/assets/images/emoji/no_bicycles.png
+++ b/public/-/emojis/1/no_bicycles.png
Binary files differ
diff --git a/app/assets/images/emoji/no_entry.png b/public/-/emojis/1/no_entry.png
index 476800fc5c6..476800fc5c6 100644
--- a/app/assets/images/emoji/no_entry.png
+++ b/public/-/emojis/1/no_entry.png
Binary files differ
diff --git a/app/assets/images/emoji/no_entry_sign.png b/public/-/emojis/1/no_entry_sign.png
index d2efd65e74b..d2efd65e74b 100644
--- a/app/assets/images/emoji/no_entry_sign.png
+++ b/public/-/emojis/1/no_entry_sign.png
Binary files differ
diff --git a/app/assets/images/emoji/no_good.png b/public/-/emojis/1/no_good.png
index ed577100322..ed577100322 100644
--- a/app/assets/images/emoji/no_good.png
+++ b/public/-/emojis/1/no_good.png
Binary files differ
diff --git a/app/assets/images/emoji/no_good_tone1.png b/public/-/emojis/1/no_good_tone1.png
index 5c1a3cbb884..5c1a3cbb884 100644
--- a/app/assets/images/emoji/no_good_tone1.png
+++ b/public/-/emojis/1/no_good_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/no_good_tone2.png b/public/-/emojis/1/no_good_tone2.png
index 80d8021f8fe..80d8021f8fe 100644
--- a/app/assets/images/emoji/no_good_tone2.png
+++ b/public/-/emojis/1/no_good_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/no_good_tone3.png b/public/-/emojis/1/no_good_tone3.png
index 635e6a00815..635e6a00815 100644
--- a/app/assets/images/emoji/no_good_tone3.png
+++ b/public/-/emojis/1/no_good_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/no_good_tone4.png b/public/-/emojis/1/no_good_tone4.png
index b96e412a374..b96e412a374 100644
--- a/app/assets/images/emoji/no_good_tone4.png
+++ b/public/-/emojis/1/no_good_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/no_good_tone5.png b/public/-/emojis/1/no_good_tone5.png
index 9a7084afa0a..9a7084afa0a 100644
--- a/app/assets/images/emoji/no_good_tone5.png
+++ b/public/-/emojis/1/no_good_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/no_mobile_phones.png b/public/-/emojis/1/no_mobile_phones.png
index 7b1ae6ea579..7b1ae6ea579 100644
--- a/app/assets/images/emoji/no_mobile_phones.png
+++ b/public/-/emojis/1/no_mobile_phones.png
Binary files differ
diff --git a/app/assets/images/emoji/no_mouth.png b/public/-/emojis/1/no_mouth.png
index b642f6c1172..b642f6c1172 100644
--- a/app/assets/images/emoji/no_mouth.png
+++ b/public/-/emojis/1/no_mouth.png
Binary files differ
diff --git a/app/assets/images/emoji/no_pedestrians.png b/public/-/emojis/1/no_pedestrians.png
index 286aa577a23..286aa577a23 100644
--- a/app/assets/images/emoji/no_pedestrians.png
+++ b/public/-/emojis/1/no_pedestrians.png
Binary files differ
diff --git a/app/assets/images/emoji/no_smoking.png b/public/-/emojis/1/no_smoking.png
index 586b8d29d05..586b8d29d05 100644
--- a/app/assets/images/emoji/no_smoking.png
+++ b/public/-/emojis/1/no_smoking.png
Binary files differ
diff --git a/app/assets/images/emoji/non-potable_water.png b/public/-/emojis/1/non-potable_water.png
index 827d4193f4e..827d4193f4e 100644
--- a/app/assets/images/emoji/non-potable_water.png
+++ b/public/-/emojis/1/non-potable_water.png
Binary files differ
diff --git a/app/assets/images/emoji/nose.png b/public/-/emojis/1/nose.png
index 2f04ac5f98f..2f04ac5f98f 100644
--- a/app/assets/images/emoji/nose.png
+++ b/public/-/emojis/1/nose.png
Binary files differ
diff --git a/app/assets/images/emoji/nose_tone1.png b/public/-/emojis/1/nose_tone1.png
index 8008d17506e..8008d17506e 100644
--- a/app/assets/images/emoji/nose_tone1.png
+++ b/public/-/emojis/1/nose_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/nose_tone2.png b/public/-/emojis/1/nose_tone2.png
index ac17f26e827..ac17f26e827 100644
--- a/app/assets/images/emoji/nose_tone2.png
+++ b/public/-/emojis/1/nose_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/nose_tone3.png b/public/-/emojis/1/nose_tone3.png
index d8b6cbe0f8e..d8b6cbe0f8e 100644
--- a/app/assets/images/emoji/nose_tone3.png
+++ b/public/-/emojis/1/nose_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/nose_tone4.png b/public/-/emojis/1/nose_tone4.png
index 004b2631e2e..004b2631e2e 100644
--- a/app/assets/images/emoji/nose_tone4.png
+++ b/public/-/emojis/1/nose_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/nose_tone5.png b/public/-/emojis/1/nose_tone5.png
index 7b33821f6c9..7b33821f6c9 100644
--- a/app/assets/images/emoji/nose_tone5.png
+++ b/public/-/emojis/1/nose_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/notebook.png b/public/-/emojis/1/notebook.png
index f6c28b4915d..f6c28b4915d 100644
--- a/app/assets/images/emoji/notebook.png
+++ b/public/-/emojis/1/notebook.png
Binary files differ
diff --git a/app/assets/images/emoji/notebook_with_decorative_cover.png b/public/-/emojis/1/notebook_with_decorative_cover.png
index 03f566b6d2c..03f566b6d2c 100644
--- a/app/assets/images/emoji/notebook_with_decorative_cover.png
+++ b/public/-/emojis/1/notebook_with_decorative_cover.png
Binary files differ
diff --git a/app/assets/images/emoji/notepad_spiral.png b/public/-/emojis/1/notepad_spiral.png
index 85faa10d8ea..85faa10d8ea 100644
--- a/app/assets/images/emoji/notepad_spiral.png
+++ b/public/-/emojis/1/notepad_spiral.png
Binary files differ
diff --git a/app/assets/images/emoji/notes.png b/public/-/emojis/1/notes.png
index 57d499aa181..57d499aa181 100644
--- a/app/assets/images/emoji/notes.png
+++ b/public/-/emojis/1/notes.png
Binary files differ
diff --git a/app/assets/images/emoji/nut_and_bolt.png b/public/-/emojis/1/nut_and_bolt.png
index 4b9ae155319..4b9ae155319 100644
--- a/app/assets/images/emoji/nut_and_bolt.png
+++ b/public/-/emojis/1/nut_and_bolt.png
Binary files differ
diff --git a/app/assets/images/emoji/o.png b/public/-/emojis/1/o.png
index 3fe75ce4675..3fe75ce4675 100644
--- a/app/assets/images/emoji/o.png
+++ b/public/-/emojis/1/o.png
Binary files differ
diff --git a/app/assets/images/emoji/o2.png b/public/-/emojis/1/o2.png
index 73278ba194a..73278ba194a 100644
--- a/app/assets/images/emoji/o2.png
+++ b/public/-/emojis/1/o2.png
Binary files differ
diff --git a/app/assets/images/emoji/ocean.png b/public/-/emojis/1/ocean.png
index 45ff1e87703..45ff1e87703 100644
--- a/app/assets/images/emoji/ocean.png
+++ b/public/-/emojis/1/ocean.png
Binary files differ
diff --git a/app/assets/images/emoji/octagonal_sign.png b/public/-/emojis/1/octagonal_sign.png
index 5ed61004045..5ed61004045 100644
--- a/app/assets/images/emoji/octagonal_sign.png
+++ b/public/-/emojis/1/octagonal_sign.png
Binary files differ
diff --git a/app/assets/images/emoji/octopus.png b/public/-/emojis/1/octopus.png
index 72c84074aac..72c84074aac 100644
--- a/app/assets/images/emoji/octopus.png
+++ b/public/-/emojis/1/octopus.png
Binary files differ
diff --git a/app/assets/images/emoji/oden.png b/public/-/emojis/1/oden.png
index d38a849fece..d38a849fece 100644
--- a/app/assets/images/emoji/oden.png
+++ b/public/-/emojis/1/oden.png
Binary files differ
diff --git a/app/assets/images/emoji/office.png b/public/-/emojis/1/office.png
index 7eee927d1b0..7eee927d1b0 100644
--- a/app/assets/images/emoji/office.png
+++ b/public/-/emojis/1/office.png
Binary files differ
diff --git a/app/assets/images/emoji/oil.png b/public/-/emojis/1/oil.png
index c4c4d42da8b..c4c4d42da8b 100644
--- a/app/assets/images/emoji/oil.png
+++ b/public/-/emojis/1/oil.png
Binary files differ
diff --git a/app/assets/images/emoji/ok.png b/public/-/emojis/1/ok.png
index d0d775532ff..d0d775532ff 100644
--- a/app/assets/images/emoji/ok.png
+++ b/public/-/emojis/1/ok.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_hand.png b/public/-/emojis/1/ok_hand.png
index 028d69b0de3..028d69b0de3 100644
--- a/app/assets/images/emoji/ok_hand.png
+++ b/public/-/emojis/1/ok_hand.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_hand_tone1.png b/public/-/emojis/1/ok_hand_tone1.png
index cecf7b2ab5a..cecf7b2ab5a 100644
--- a/app/assets/images/emoji/ok_hand_tone1.png
+++ b/public/-/emojis/1/ok_hand_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_hand_tone2.png b/public/-/emojis/1/ok_hand_tone2.png
index c19239bcd3d..c19239bcd3d 100644
--- a/app/assets/images/emoji/ok_hand_tone2.png
+++ b/public/-/emojis/1/ok_hand_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_hand_tone3.png b/public/-/emojis/1/ok_hand_tone3.png
index 94b65b03ecd..94b65b03ecd 100644
--- a/app/assets/images/emoji/ok_hand_tone3.png
+++ b/public/-/emojis/1/ok_hand_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_hand_tone4.png b/public/-/emojis/1/ok_hand_tone4.png
index 03d26f08e6a..03d26f08e6a 100644
--- a/app/assets/images/emoji/ok_hand_tone4.png
+++ b/public/-/emojis/1/ok_hand_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_hand_tone5.png b/public/-/emojis/1/ok_hand_tone5.png
index d4b24086364..d4b24086364 100644
--- a/app/assets/images/emoji/ok_hand_tone5.png
+++ b/public/-/emojis/1/ok_hand_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_woman.png b/public/-/emojis/1/ok_woman.png
index 90a2c7469c4..90a2c7469c4 100644
--- a/app/assets/images/emoji/ok_woman.png
+++ b/public/-/emojis/1/ok_woman.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_woman_tone1.png b/public/-/emojis/1/ok_woman_tone1.png
index c99543e785b..c99543e785b 100644
--- a/app/assets/images/emoji/ok_woman_tone1.png
+++ b/public/-/emojis/1/ok_woman_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_woman_tone2.png b/public/-/emojis/1/ok_woman_tone2.png
index ad5fae813db..ad5fae813db 100644
--- a/app/assets/images/emoji/ok_woman_tone2.png
+++ b/public/-/emojis/1/ok_woman_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_woman_tone3.png b/public/-/emojis/1/ok_woman_tone3.png
index 51bf4fab406..51bf4fab406 100644
--- a/app/assets/images/emoji/ok_woman_tone3.png
+++ b/public/-/emojis/1/ok_woman_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_woman_tone4.png b/public/-/emojis/1/ok_woman_tone4.png
index ee3f9dc640a..ee3f9dc640a 100644
--- a/app/assets/images/emoji/ok_woman_tone4.png
+++ b/public/-/emojis/1/ok_woman_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/ok_woman_tone5.png b/public/-/emojis/1/ok_woman_tone5.png
index 62a9d9237f7..62a9d9237f7 100644
--- a/app/assets/images/emoji/ok_woman_tone5.png
+++ b/public/-/emojis/1/ok_woman_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/older_man.png b/public/-/emojis/1/older_man.png
index 4ace4e6f308..4ace4e6f308 100644
--- a/app/assets/images/emoji/older_man.png
+++ b/public/-/emojis/1/older_man.png
Binary files differ
diff --git a/app/assets/images/emoji/older_man_tone1.png b/public/-/emojis/1/older_man_tone1.png
index ab459baace8..ab459baace8 100644
--- a/app/assets/images/emoji/older_man_tone1.png
+++ b/public/-/emojis/1/older_man_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/older_man_tone2.png b/public/-/emojis/1/older_man_tone2.png
index f4dfc7694ea..f4dfc7694ea 100644
--- a/app/assets/images/emoji/older_man_tone2.png
+++ b/public/-/emojis/1/older_man_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/older_man_tone3.png b/public/-/emojis/1/older_man_tone3.png
index 5ffd11792f4..5ffd11792f4 100644
--- a/app/assets/images/emoji/older_man_tone3.png
+++ b/public/-/emojis/1/older_man_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/older_man_tone4.png b/public/-/emojis/1/older_man_tone4.png
index b350a764bfd..b350a764bfd 100644
--- a/app/assets/images/emoji/older_man_tone4.png
+++ b/public/-/emojis/1/older_man_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/older_man_tone5.png b/public/-/emojis/1/older_man_tone5.png
index 05fe24a1708..05fe24a1708 100644
--- a/app/assets/images/emoji/older_man_tone5.png
+++ b/public/-/emojis/1/older_man_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/older_woman.png b/public/-/emojis/1/older_woman.png
index 52dc4987143..52dc4987143 100644
--- a/app/assets/images/emoji/older_woman.png
+++ b/public/-/emojis/1/older_woman.png
Binary files differ
diff --git a/app/assets/images/emoji/older_woman_tone1.png b/public/-/emojis/1/older_woman_tone1.png
index b49e821402c..b49e821402c 100644
--- a/app/assets/images/emoji/older_woman_tone1.png
+++ b/public/-/emojis/1/older_woman_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/older_woman_tone2.png b/public/-/emojis/1/older_woman_tone2.png
index e86bf5ab3b7..e86bf5ab3b7 100644
--- a/app/assets/images/emoji/older_woman_tone2.png
+++ b/public/-/emojis/1/older_woman_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/older_woman_tone3.png b/public/-/emojis/1/older_woman_tone3.png
index 83fc14b0874..83fc14b0874 100644
--- a/app/assets/images/emoji/older_woman_tone3.png
+++ b/public/-/emojis/1/older_woman_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/older_woman_tone4.png b/public/-/emojis/1/older_woman_tone4.png
index e4aa8a424d4..e4aa8a424d4 100644
--- a/app/assets/images/emoji/older_woman_tone4.png
+++ b/public/-/emojis/1/older_woman_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/older_woman_tone5.png b/public/-/emojis/1/older_woman_tone5.png
index 4009012bb0a..4009012bb0a 100644
--- a/app/assets/images/emoji/older_woman_tone5.png
+++ b/public/-/emojis/1/older_woman_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/om_symbol.png b/public/-/emojis/1/om_symbol.png
index a35c63c459c..a35c63c459c 100644
--- a/app/assets/images/emoji/om_symbol.png
+++ b/public/-/emojis/1/om_symbol.png
Binary files differ
diff --git a/app/assets/images/emoji/on.png b/public/-/emojis/1/on.png
index a0c371ae21e..a0c371ae21e 100644
--- a/app/assets/images/emoji/on.png
+++ b/public/-/emojis/1/on.png
Binary files differ
diff --git a/app/assets/images/emoji/oncoming_automobile.png b/public/-/emojis/1/oncoming_automobile.png
index 3c7e1d52e63..3c7e1d52e63 100644
--- a/app/assets/images/emoji/oncoming_automobile.png
+++ b/public/-/emojis/1/oncoming_automobile.png
Binary files differ
diff --git a/app/assets/images/emoji/oncoming_bus.png b/public/-/emojis/1/oncoming_bus.png
index ad91e256c7f..ad91e256c7f 100644
--- a/app/assets/images/emoji/oncoming_bus.png
+++ b/public/-/emojis/1/oncoming_bus.png
Binary files differ
diff --git a/app/assets/images/emoji/oncoming_police_car.png b/public/-/emojis/1/oncoming_police_car.png
index c9109c85b5d..c9109c85b5d 100644
--- a/app/assets/images/emoji/oncoming_police_car.png
+++ b/public/-/emojis/1/oncoming_police_car.png
Binary files differ
diff --git a/app/assets/images/emoji/oncoming_taxi.png b/public/-/emojis/1/oncoming_taxi.png
index fea14e45846..fea14e45846 100644
--- a/app/assets/images/emoji/oncoming_taxi.png
+++ b/public/-/emojis/1/oncoming_taxi.png
Binary files differ
diff --git a/app/assets/images/emoji/one.png b/public/-/emojis/1/one.png
index e6d84b80128..e6d84b80128 100644
--- a/app/assets/images/emoji/one.png
+++ b/public/-/emojis/1/one.png
Binary files differ
diff --git a/app/assets/images/emoji/open_file_folder.png b/public/-/emojis/1/open_file_folder.png
index 3993b09222f..3993b09222f 100644
--- a/app/assets/images/emoji/open_file_folder.png
+++ b/public/-/emojis/1/open_file_folder.png
Binary files differ
diff --git a/app/assets/images/emoji/open_hands.png b/public/-/emojis/1/open_hands.png
index 1cf75c9101e..1cf75c9101e 100644
--- a/app/assets/images/emoji/open_hands.png
+++ b/public/-/emojis/1/open_hands.png
Binary files differ
diff --git a/app/assets/images/emoji/open_hands_tone1.png b/public/-/emojis/1/open_hands_tone1.png
index 352d2614f11..352d2614f11 100644
--- a/app/assets/images/emoji/open_hands_tone1.png
+++ b/public/-/emojis/1/open_hands_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/open_hands_tone2.png b/public/-/emojis/1/open_hands_tone2.png
index 70824a50c73..70824a50c73 100644
--- a/app/assets/images/emoji/open_hands_tone2.png
+++ b/public/-/emojis/1/open_hands_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/open_hands_tone3.png b/public/-/emojis/1/open_hands_tone3.png
index d7d136bd3db..d7d136bd3db 100644
--- a/app/assets/images/emoji/open_hands_tone3.png
+++ b/public/-/emojis/1/open_hands_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/open_hands_tone4.png b/public/-/emojis/1/open_hands_tone4.png
index df4eaa711e7..df4eaa711e7 100644
--- a/app/assets/images/emoji/open_hands_tone4.png
+++ b/public/-/emojis/1/open_hands_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/open_hands_tone5.png b/public/-/emojis/1/open_hands_tone5.png
index 7dc04eaebd8..7dc04eaebd8 100644
--- a/app/assets/images/emoji/open_hands_tone5.png
+++ b/public/-/emojis/1/open_hands_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/open_mouth.png b/public/-/emojis/1/open_mouth.png
index a62cd27e148..a62cd27e148 100644
--- a/app/assets/images/emoji/open_mouth.png
+++ b/public/-/emojis/1/open_mouth.png
Binary files differ
diff --git a/app/assets/images/emoji/ophiuchus.png b/public/-/emojis/1/ophiuchus.png
index 0a780a700da..0a780a700da 100644
--- a/app/assets/images/emoji/ophiuchus.png
+++ b/public/-/emojis/1/ophiuchus.png
Binary files differ
diff --git a/app/assets/images/emoji/orange_book.png b/public/-/emojis/1/orange_book.png
index ab40e6ae6a2..ab40e6ae6a2 100644
--- a/app/assets/images/emoji/orange_book.png
+++ b/public/-/emojis/1/orange_book.png
Binary files differ
diff --git a/app/assets/images/emoji/orthodox_cross.png b/public/-/emojis/1/orthodox_cross.png
index 0530e33a4d4..0530e33a4d4 100644
--- a/app/assets/images/emoji/orthodox_cross.png
+++ b/public/-/emojis/1/orthodox_cross.png
Binary files differ
diff --git a/app/assets/images/emoji/outbox_tray.png b/public/-/emojis/1/outbox_tray.png
index 46493ed5b2c..46493ed5b2c 100644
--- a/app/assets/images/emoji/outbox_tray.png
+++ b/public/-/emojis/1/outbox_tray.png
Binary files differ
diff --git a/app/assets/images/emoji/owl.png b/public/-/emojis/1/owl.png
index fa6815480c3..fa6815480c3 100644
--- a/app/assets/images/emoji/owl.png
+++ b/public/-/emojis/1/owl.png
Binary files differ
diff --git a/app/assets/images/emoji/ox.png b/public/-/emojis/1/ox.png
index badf5708f2f..badf5708f2f 100644
--- a/app/assets/images/emoji/ox.png
+++ b/public/-/emojis/1/ox.png
Binary files differ
diff --git a/app/assets/images/emoji/package.png b/public/-/emojis/1/package.png
index 85431756ad8..85431756ad8 100644
--- a/app/assets/images/emoji/package.png
+++ b/public/-/emojis/1/package.png
Binary files differ
diff --git a/app/assets/images/emoji/page_facing_up.png b/public/-/emojis/1/page_facing_up.png
index ba4ed757e01..ba4ed757e01 100644
--- a/app/assets/images/emoji/page_facing_up.png
+++ b/public/-/emojis/1/page_facing_up.png
Binary files differ
diff --git a/app/assets/images/emoji/page_with_curl.png b/public/-/emojis/1/page_with_curl.png
index 06355319c74..06355319c74 100644
--- a/app/assets/images/emoji/page_with_curl.png
+++ b/public/-/emojis/1/page_with_curl.png
Binary files differ
diff --git a/app/assets/images/emoji/pager.png b/public/-/emojis/1/pager.png
index b24b99306a2..b24b99306a2 100644
--- a/app/assets/images/emoji/pager.png
+++ b/public/-/emojis/1/pager.png
Binary files differ
diff --git a/app/assets/images/emoji/paintbrush.png b/public/-/emojis/1/paintbrush.png
index 28bffbaa3c9..28bffbaa3c9 100644
--- a/app/assets/images/emoji/paintbrush.png
+++ b/public/-/emojis/1/paintbrush.png
Binary files differ
diff --git a/app/assets/images/emoji/palm_tree.png b/public/-/emojis/1/palm_tree.png
index 4bbb10f4f19..4bbb10f4f19 100644
--- a/app/assets/images/emoji/palm_tree.png
+++ b/public/-/emojis/1/palm_tree.png
Binary files differ
diff --git a/app/assets/images/emoji/pancakes.png b/public/-/emojis/1/pancakes.png
index 6223d1a28e9..6223d1a28e9 100644
--- a/app/assets/images/emoji/pancakes.png
+++ b/public/-/emojis/1/pancakes.png
Binary files differ
diff --git a/app/assets/images/emoji/panda_face.png b/public/-/emojis/1/panda_face.png
index 978382775ce..978382775ce 100644
--- a/app/assets/images/emoji/panda_face.png
+++ b/public/-/emojis/1/panda_face.png
Binary files differ
diff --git a/app/assets/images/emoji/paperclip.png b/public/-/emojis/1/paperclip.png
index 8cd8d4f8750..8cd8d4f8750 100644
--- a/app/assets/images/emoji/paperclip.png
+++ b/public/-/emojis/1/paperclip.png
Binary files differ
diff --git a/app/assets/images/emoji/paperclips.png b/public/-/emojis/1/paperclips.png
index 76021e8c705..76021e8c705 100644
--- a/app/assets/images/emoji/paperclips.png
+++ b/public/-/emojis/1/paperclips.png
Binary files differ
diff --git a/app/assets/images/emoji/park.png b/public/-/emojis/1/park.png
index 63ec7016301..63ec7016301 100644
--- a/app/assets/images/emoji/park.png
+++ b/public/-/emojis/1/park.png
Binary files differ
diff --git a/app/assets/images/emoji/parking.png b/public/-/emojis/1/parking.png
index 7be7dac27e8..7be7dac27e8 100644
--- a/app/assets/images/emoji/parking.png
+++ b/public/-/emojis/1/parking.png
Binary files differ
diff --git a/app/assets/images/emoji/part_alternation_mark.png b/public/-/emojis/1/part_alternation_mark.png
index 70453d41528..70453d41528 100644
--- a/app/assets/images/emoji/part_alternation_mark.png
+++ b/public/-/emojis/1/part_alternation_mark.png
Binary files differ
diff --git a/app/assets/images/emoji/partly_sunny.png b/public/-/emojis/1/partly_sunny.png
index a55e59c344c..a55e59c344c 100644
--- a/app/assets/images/emoji/partly_sunny.png
+++ b/public/-/emojis/1/partly_sunny.png
Binary files differ
diff --git a/app/assets/images/emoji/passport_control.png b/public/-/emojis/1/passport_control.png
index 079e34ee4d4..079e34ee4d4 100644
--- a/app/assets/images/emoji/passport_control.png
+++ b/public/-/emojis/1/passport_control.png
Binary files differ
diff --git a/app/assets/images/emoji/pause_button.png b/public/-/emojis/1/pause_button.png
index 4f07e7ebfd7..4f07e7ebfd7 100644
--- a/app/assets/images/emoji/pause_button.png
+++ b/public/-/emojis/1/pause_button.png
Binary files differ
diff --git a/app/assets/images/emoji/peace.png b/public/-/emojis/1/peace.png
index 86033faf477..86033faf477 100644
--- a/app/assets/images/emoji/peace.png
+++ b/public/-/emojis/1/peace.png
Binary files differ
diff --git a/app/assets/images/emoji/peach.png b/public/-/emojis/1/peach.png
index 9ab57cbb758..9ab57cbb758 100644
--- a/app/assets/images/emoji/peach.png
+++ b/public/-/emojis/1/peach.png
Binary files differ
diff --git a/app/assets/images/emoji/peanuts.png b/public/-/emojis/1/peanuts.png
index b64fadad010..b64fadad010 100644
--- a/app/assets/images/emoji/peanuts.png
+++ b/public/-/emojis/1/peanuts.png
Binary files differ
diff --git a/app/assets/images/emoji/pear.png b/public/-/emojis/1/pear.png
index 3869f718bcf..3869f718bcf 100644
--- a/app/assets/images/emoji/pear.png
+++ b/public/-/emojis/1/pear.png
Binary files differ
diff --git a/app/assets/images/emoji/pen_ballpoint.png b/public/-/emojis/1/pen_ballpoint.png
index 6ef7a342433..6ef7a342433 100644
--- a/app/assets/images/emoji/pen_ballpoint.png
+++ b/public/-/emojis/1/pen_ballpoint.png
Binary files differ
diff --git a/app/assets/images/emoji/pen_fountain.png b/public/-/emojis/1/pen_fountain.png
index 3ca4bd2c231..3ca4bd2c231 100644
--- a/app/assets/images/emoji/pen_fountain.png
+++ b/public/-/emojis/1/pen_fountain.png
Binary files differ
diff --git a/app/assets/images/emoji/pencil.png b/public/-/emojis/1/pencil.png
index edc6155e168..edc6155e168 100644
--- a/app/assets/images/emoji/pencil.png
+++ b/public/-/emojis/1/pencil.png
Binary files differ
diff --git a/app/assets/images/emoji/pencil2.png b/public/-/emojis/1/pencil2.png
index 3833d590fa2..3833d590fa2 100644
--- a/app/assets/images/emoji/pencil2.png
+++ b/public/-/emojis/1/pencil2.png
Binary files differ
diff --git a/app/assets/images/emoji/penguin.png b/public/-/emojis/1/penguin.png
index c0064fb9734..c0064fb9734 100644
--- a/app/assets/images/emoji/penguin.png
+++ b/public/-/emojis/1/penguin.png
Binary files differ
diff --git a/app/assets/images/emoji/pensive.png b/public/-/emojis/1/pensive.png
index 490fb566954..490fb566954 100644
--- a/app/assets/images/emoji/pensive.png
+++ b/public/-/emojis/1/pensive.png
Binary files differ
diff --git a/app/assets/images/emoji/performing_arts.png b/public/-/emojis/1/performing_arts.png
index 685441fdaa1..685441fdaa1 100644
--- a/app/assets/images/emoji/performing_arts.png
+++ b/public/-/emojis/1/performing_arts.png
Binary files differ
diff --git a/app/assets/images/emoji/persevere.png b/public/-/emojis/1/persevere.png
index 646a05fe908..646a05fe908 100644
--- a/app/assets/images/emoji/persevere.png
+++ b/public/-/emojis/1/persevere.png
Binary files differ
diff --git a/app/assets/images/emoji/person_frowning.png b/public/-/emojis/1/person_frowning.png
index 579324959a1..579324959a1 100644
--- a/app/assets/images/emoji/person_frowning.png
+++ b/public/-/emojis/1/person_frowning.png
Binary files differ
diff --git a/app/assets/images/emoji/person_frowning_tone1.png b/public/-/emojis/1/person_frowning_tone1.png
index 21d3bb43923..21d3bb43923 100644
--- a/app/assets/images/emoji/person_frowning_tone1.png
+++ b/public/-/emojis/1/person_frowning_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/person_frowning_tone2.png b/public/-/emojis/1/person_frowning_tone2.png
index 973f5fc8382..973f5fc8382 100644
--- a/app/assets/images/emoji/person_frowning_tone2.png
+++ b/public/-/emojis/1/person_frowning_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/person_frowning_tone3.png b/public/-/emojis/1/person_frowning_tone3.png
index 41fbcc78816..41fbcc78816 100644
--- a/app/assets/images/emoji/person_frowning_tone3.png
+++ b/public/-/emojis/1/person_frowning_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/person_frowning_tone4.png b/public/-/emojis/1/person_frowning_tone4.png
index 5a37c741030..5a37c741030 100644
--- a/app/assets/images/emoji/person_frowning_tone4.png
+++ b/public/-/emojis/1/person_frowning_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/person_frowning_tone5.png b/public/-/emojis/1/person_frowning_tone5.png
index e08141f3efe..e08141f3efe 100644
--- a/app/assets/images/emoji/person_frowning_tone5.png
+++ b/public/-/emojis/1/person_frowning_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_blond_hair.png b/public/-/emojis/1/person_with_blond_hair.png
index ad6f01a7dda..ad6f01a7dda 100644
--- a/app/assets/images/emoji/person_with_blond_hair.png
+++ b/public/-/emojis/1/person_with_blond_hair.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_blond_hair_tone1.png b/public/-/emojis/1/person_with_blond_hair_tone1.png
index 7d18ef24445..7d18ef24445 100644
--- a/app/assets/images/emoji/person_with_blond_hair_tone1.png
+++ b/public/-/emojis/1/person_with_blond_hair_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_blond_hair_tone2.png b/public/-/emojis/1/person_with_blond_hair_tone2.png
index dae1307315c..dae1307315c 100644
--- a/app/assets/images/emoji/person_with_blond_hair_tone2.png
+++ b/public/-/emojis/1/person_with_blond_hair_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_blond_hair_tone3.png b/public/-/emojis/1/person_with_blond_hair_tone3.png
index 684677e8e5a..684677e8e5a 100644
--- a/app/assets/images/emoji/person_with_blond_hair_tone3.png
+++ b/public/-/emojis/1/person_with_blond_hair_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_blond_hair_tone4.png b/public/-/emojis/1/person_with_blond_hair_tone4.png
index 012be0b51f8..012be0b51f8 100644
--- a/app/assets/images/emoji/person_with_blond_hair_tone4.png
+++ b/public/-/emojis/1/person_with_blond_hair_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_blond_hair_tone5.png b/public/-/emojis/1/person_with_blond_hair_tone5.png
index d4ecc4cf44b..d4ecc4cf44b 100644
--- a/app/assets/images/emoji/person_with_blond_hair_tone5.png
+++ b/public/-/emojis/1/person_with_blond_hair_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_pouting_face.png b/public/-/emojis/1/person_with_pouting_face.png
index 10eb0571078..10eb0571078 100644
--- a/app/assets/images/emoji/person_with_pouting_face.png
+++ b/public/-/emojis/1/person_with_pouting_face.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_pouting_face_tone1.png b/public/-/emojis/1/person_with_pouting_face_tone1.png
index 57e826b75a4..57e826b75a4 100644
--- a/app/assets/images/emoji/person_with_pouting_face_tone1.png
+++ b/public/-/emojis/1/person_with_pouting_face_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_pouting_face_tone2.png b/public/-/emojis/1/person_with_pouting_face_tone2.png
index 3f317c0c25f..3f317c0c25f 100644
--- a/app/assets/images/emoji/person_with_pouting_face_tone2.png
+++ b/public/-/emojis/1/person_with_pouting_face_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_pouting_face_tone3.png b/public/-/emojis/1/person_with_pouting_face_tone3.png
index d2fbb6c20bf..d2fbb6c20bf 100644
--- a/app/assets/images/emoji/person_with_pouting_face_tone3.png
+++ b/public/-/emojis/1/person_with_pouting_face_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_pouting_face_tone4.png b/public/-/emojis/1/person_with_pouting_face_tone4.png
index 643ceb4a5c5..643ceb4a5c5 100644
--- a/app/assets/images/emoji/person_with_pouting_face_tone4.png
+++ b/public/-/emojis/1/person_with_pouting_face_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/person_with_pouting_face_tone5.png b/public/-/emojis/1/person_with_pouting_face_tone5.png
index b2eb6859c32..b2eb6859c32 100644
--- a/app/assets/images/emoji/person_with_pouting_face_tone5.png
+++ b/public/-/emojis/1/person_with_pouting_face_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/pick.png b/public/-/emojis/1/pick.png
index 6370fe6d791..6370fe6d791 100644
--- a/app/assets/images/emoji/pick.png
+++ b/public/-/emojis/1/pick.png
Binary files differ
diff --git a/app/assets/images/emoji/pig.png b/public/-/emojis/1/pig.png
index afe05ca1676..afe05ca1676 100644
--- a/app/assets/images/emoji/pig.png
+++ b/public/-/emojis/1/pig.png
Binary files differ
diff --git a/app/assets/images/emoji/pig2.png b/public/-/emojis/1/pig2.png
index 5f31c1a2d75..5f31c1a2d75 100644
--- a/app/assets/images/emoji/pig2.png
+++ b/public/-/emojis/1/pig2.png
Binary files differ
diff --git a/app/assets/images/emoji/pig_nose.png b/public/-/emojis/1/pig_nose.png
index 3610ae4a910..3610ae4a910 100644
--- a/app/assets/images/emoji/pig_nose.png
+++ b/public/-/emojis/1/pig_nose.png
Binary files differ
diff --git a/app/assets/images/emoji/pill.png b/public/-/emojis/1/pill.png
index 1d4530e77a3..1d4530e77a3 100644
--- a/app/assets/images/emoji/pill.png
+++ b/public/-/emojis/1/pill.png
Binary files differ
diff --git a/app/assets/images/emoji/pineapple.png b/public/-/emojis/1/pineapple.png
index c89a1606462..c89a1606462 100644
--- a/app/assets/images/emoji/pineapple.png
+++ b/public/-/emojis/1/pineapple.png
Binary files differ
diff --git a/app/assets/images/emoji/ping_pong.png b/public/-/emojis/1/ping_pong.png
index ff3c51727d1..ff3c51727d1 100644
--- a/app/assets/images/emoji/ping_pong.png
+++ b/public/-/emojis/1/ping_pong.png
Binary files differ
diff --git a/app/assets/images/emoji/pisces.png b/public/-/emojis/1/pisces.png
index 7f6f646a95c..7f6f646a95c 100644
--- a/app/assets/images/emoji/pisces.png
+++ b/public/-/emojis/1/pisces.png
Binary files differ
diff --git a/app/assets/images/emoji/pizza.png b/public/-/emojis/1/pizza.png
index e07365cb398..e07365cb398 100644
--- a/app/assets/images/emoji/pizza.png
+++ b/public/-/emojis/1/pizza.png
Binary files differ
diff --git a/app/assets/images/emoji/place_of_worship.png b/public/-/emojis/1/place_of_worship.png
index 207d59cce85..207d59cce85 100644
--- a/app/assets/images/emoji/place_of_worship.png
+++ b/public/-/emojis/1/place_of_worship.png
Binary files differ
diff --git a/app/assets/images/emoji/play_pause.png b/public/-/emojis/1/play_pause.png
index a9f857139ac..a9f857139ac 100644
--- a/app/assets/images/emoji/play_pause.png
+++ b/public/-/emojis/1/play_pause.png
Binary files differ
diff --git a/app/assets/images/emoji/point_down.png b/public/-/emojis/1/point_down.png
index 00d3d13ab5c..00d3d13ab5c 100644
--- a/app/assets/images/emoji/point_down.png
+++ b/public/-/emojis/1/point_down.png
Binary files differ
diff --git a/app/assets/images/emoji/point_down_tone1.png b/public/-/emojis/1/point_down_tone1.png
index 140f157d8c7..140f157d8c7 100644
--- a/app/assets/images/emoji/point_down_tone1.png
+++ b/public/-/emojis/1/point_down_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/point_down_tone2.png b/public/-/emojis/1/point_down_tone2.png
index d518544f7fa..d518544f7fa 100644
--- a/app/assets/images/emoji/point_down_tone2.png
+++ b/public/-/emojis/1/point_down_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/point_down_tone3.png b/public/-/emojis/1/point_down_tone3.png
index 018b688b8b7..018b688b8b7 100644
--- a/app/assets/images/emoji/point_down_tone3.png
+++ b/public/-/emojis/1/point_down_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/point_down_tone4.png b/public/-/emojis/1/point_down_tone4.png
index 98845bf6f72..98845bf6f72 100644
--- a/app/assets/images/emoji/point_down_tone4.png
+++ b/public/-/emojis/1/point_down_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/point_down_tone5.png b/public/-/emojis/1/point_down_tone5.png
index 9a9b039a9fc..9a9b039a9fc 100644
--- a/app/assets/images/emoji/point_down_tone5.png
+++ b/public/-/emojis/1/point_down_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/point_left.png b/public/-/emojis/1/point_left.png
index 599fa2e3cf1..599fa2e3cf1 100644
--- a/app/assets/images/emoji/point_left.png
+++ b/public/-/emojis/1/point_left.png
Binary files differ
diff --git a/app/assets/images/emoji/point_left_tone1.png b/public/-/emojis/1/point_left_tone1.png
index 88e2c306076..88e2c306076 100644
--- a/app/assets/images/emoji/point_left_tone1.png
+++ b/public/-/emojis/1/point_left_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/point_left_tone2.png b/public/-/emojis/1/point_left_tone2.png
index d3c89d87c5f..d3c89d87c5f 100644
--- a/app/assets/images/emoji/point_left_tone2.png
+++ b/public/-/emojis/1/point_left_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/point_left_tone3.png b/public/-/emojis/1/point_left_tone3.png
index b23b9167358..b23b9167358 100644
--- a/app/assets/images/emoji/point_left_tone3.png
+++ b/public/-/emojis/1/point_left_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/point_left_tone4.png b/public/-/emojis/1/point_left_tone4.png
index 3093f325c27..3093f325c27 100644
--- a/app/assets/images/emoji/point_left_tone4.png
+++ b/public/-/emojis/1/point_left_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/point_left_tone5.png b/public/-/emojis/1/point_left_tone5.png
index 2b4cbfa120c..2b4cbfa120c 100644
--- a/app/assets/images/emoji/point_left_tone5.png
+++ b/public/-/emojis/1/point_left_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/point_right.png b/public/-/emojis/1/point_right.png
index 93a3cd34aa5..93a3cd34aa5 100644
--- a/app/assets/images/emoji/point_right.png
+++ b/public/-/emojis/1/point_right.png
Binary files differ
diff --git a/app/assets/images/emoji/point_right_tone1.png b/public/-/emojis/1/point_right_tone1.png
index 4a28c6bbc89..4a28c6bbc89 100644
--- a/app/assets/images/emoji/point_right_tone1.png
+++ b/public/-/emojis/1/point_right_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/point_right_tone2.png b/public/-/emojis/1/point_right_tone2.png
index 7cb13231733..7cb13231733 100644
--- a/app/assets/images/emoji/point_right_tone2.png
+++ b/public/-/emojis/1/point_right_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/point_right_tone3.png b/public/-/emojis/1/point_right_tone3.png
index 5514807d71a..5514807d71a 100644
--- a/app/assets/images/emoji/point_right_tone3.png
+++ b/public/-/emojis/1/point_right_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/point_right_tone4.png b/public/-/emojis/1/point_right_tone4.png
index b8541d6440d..b8541d6440d 100644
--- a/app/assets/images/emoji/point_right_tone4.png
+++ b/public/-/emojis/1/point_right_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/point_right_tone5.png b/public/-/emojis/1/point_right_tone5.png
index 1b7aab07bb1..1b7aab07bb1 100644
--- a/app/assets/images/emoji/point_right_tone5.png
+++ b/public/-/emojis/1/point_right_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up.png b/public/-/emojis/1/point_up.png
index f4978ff0f00..f4978ff0f00 100644
--- a/app/assets/images/emoji/point_up.png
+++ b/public/-/emojis/1/point_up.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_2.png b/public/-/emojis/1/point_up_2.png
index bc496dfeae4..bc496dfeae4 100644
--- a/app/assets/images/emoji/point_up_2.png
+++ b/public/-/emojis/1/point_up_2.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_2_tone1.png b/public/-/emojis/1/point_up_2_tone1.png
index a12a7e78430..a12a7e78430 100644
--- a/app/assets/images/emoji/point_up_2_tone1.png
+++ b/public/-/emojis/1/point_up_2_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_2_tone2.png b/public/-/emojis/1/point_up_2_tone2.png
index cdff40ceab0..cdff40ceab0 100644
--- a/app/assets/images/emoji/point_up_2_tone2.png
+++ b/public/-/emojis/1/point_up_2_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_2_tone3.png b/public/-/emojis/1/point_up_2_tone3.png
index a07ce9e5ae8..a07ce9e5ae8 100644
--- a/app/assets/images/emoji/point_up_2_tone3.png
+++ b/public/-/emojis/1/point_up_2_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_2_tone4.png b/public/-/emojis/1/point_up_2_tone4.png
index 4f86c88ba42..4f86c88ba42 100644
--- a/app/assets/images/emoji/point_up_2_tone4.png
+++ b/public/-/emojis/1/point_up_2_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_2_tone5.png b/public/-/emojis/1/point_up_2_tone5.png
index ed1b26c35d3..ed1b26c35d3 100644
--- a/app/assets/images/emoji/point_up_2_tone5.png
+++ b/public/-/emojis/1/point_up_2_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_tone1.png b/public/-/emojis/1/point_up_tone1.png
index 6a9db21d64c..6a9db21d64c 100644
--- a/app/assets/images/emoji/point_up_tone1.png
+++ b/public/-/emojis/1/point_up_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_tone2.png b/public/-/emojis/1/point_up_tone2.png
index 15aa9ea0e05..15aa9ea0e05 100644
--- a/app/assets/images/emoji/point_up_tone2.png
+++ b/public/-/emojis/1/point_up_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_tone3.png b/public/-/emojis/1/point_up_tone3.png
index 652b73a9c5d..652b73a9c5d 100644
--- a/app/assets/images/emoji/point_up_tone3.png
+++ b/public/-/emojis/1/point_up_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_tone4.png b/public/-/emojis/1/point_up_tone4.png
index 692bad926e9..692bad926e9 100644
--- a/app/assets/images/emoji/point_up_tone4.png
+++ b/public/-/emojis/1/point_up_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/point_up_tone5.png b/public/-/emojis/1/point_up_tone5.png
index 1e1b10fb71c..1e1b10fb71c 100644
--- a/app/assets/images/emoji/point_up_tone5.png
+++ b/public/-/emojis/1/point_up_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/police_car.png b/public/-/emojis/1/police_car.png
index 3da4253de7e..3da4253de7e 100644
--- a/app/assets/images/emoji/police_car.png
+++ b/public/-/emojis/1/police_car.png
Binary files differ
diff --git a/app/assets/images/emoji/poodle.png b/public/-/emojis/1/poodle.png
index 8ec39e396af..8ec39e396af 100644
--- a/app/assets/images/emoji/poodle.png
+++ b/public/-/emojis/1/poodle.png
Binary files differ
diff --git a/app/assets/images/emoji/poop.png b/public/-/emojis/1/poop.png
index 10b15e72d56..10b15e72d56 100644
--- a/app/assets/images/emoji/poop.png
+++ b/public/-/emojis/1/poop.png
Binary files differ
diff --git a/app/assets/images/emoji/popcorn.png b/public/-/emojis/1/popcorn.png
index 36853e381d4..36853e381d4 100644
--- a/app/assets/images/emoji/popcorn.png
+++ b/public/-/emojis/1/popcorn.png
Binary files differ
diff --git a/app/assets/images/emoji/post_office.png b/public/-/emojis/1/post_office.png
index a23848f9aa0..a23848f9aa0 100644
--- a/app/assets/images/emoji/post_office.png
+++ b/public/-/emojis/1/post_office.png
Binary files differ
diff --git a/app/assets/images/emoji/postal_horn.png b/public/-/emojis/1/postal_horn.png
index c173b8dbd67..c173b8dbd67 100644
--- a/app/assets/images/emoji/postal_horn.png
+++ b/public/-/emojis/1/postal_horn.png
Binary files differ
diff --git a/app/assets/images/emoji/postbox.png b/public/-/emojis/1/postbox.png
index 07c9c4ab3d6..07c9c4ab3d6 100644
--- a/app/assets/images/emoji/postbox.png
+++ b/public/-/emojis/1/postbox.png
Binary files differ
diff --git a/app/assets/images/emoji/potable_water.png b/public/-/emojis/1/potable_water.png
index 2c610049459..2c610049459 100644
--- a/app/assets/images/emoji/potable_water.png
+++ b/public/-/emojis/1/potable_water.png
Binary files differ
diff --git a/app/assets/images/emoji/potato.png b/public/-/emojis/1/potato.png
index 70350ca2c0a..70350ca2c0a 100644
--- a/app/assets/images/emoji/potato.png
+++ b/public/-/emojis/1/potato.png
Binary files differ
diff --git a/app/assets/images/emoji/pouch.png b/public/-/emojis/1/pouch.png
index 8795c6c66ff..8795c6c66ff 100644
--- a/app/assets/images/emoji/pouch.png
+++ b/public/-/emojis/1/pouch.png
Binary files differ
diff --git a/app/assets/images/emoji/poultry_leg.png b/public/-/emojis/1/poultry_leg.png
index eea4a53a2f9..eea4a53a2f9 100644
--- a/app/assets/images/emoji/poultry_leg.png
+++ b/public/-/emojis/1/poultry_leg.png
Binary files differ
diff --git a/app/assets/images/emoji/pound.png b/public/-/emojis/1/pound.png
index a0d4c4099e9..a0d4c4099e9 100644
--- a/app/assets/images/emoji/pound.png
+++ b/public/-/emojis/1/pound.png
Binary files differ
diff --git a/app/assets/images/emoji/pouting_cat.png b/public/-/emojis/1/pouting_cat.png
index 41ddfeab42b..41ddfeab42b 100644
--- a/app/assets/images/emoji/pouting_cat.png
+++ b/public/-/emojis/1/pouting_cat.png
Binary files differ
diff --git a/app/assets/images/emoji/pray.png b/public/-/emojis/1/pray.png
index 8347f2435be..8347f2435be 100644
--- a/app/assets/images/emoji/pray.png
+++ b/public/-/emojis/1/pray.png
Binary files differ
diff --git a/app/assets/images/emoji/pray_tone1.png b/public/-/emojis/1/pray_tone1.png
index 060ef257172..060ef257172 100644
--- a/app/assets/images/emoji/pray_tone1.png
+++ b/public/-/emojis/1/pray_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/pray_tone2.png b/public/-/emojis/1/pray_tone2.png
index 56dc607c07a..56dc607c07a 100644
--- a/app/assets/images/emoji/pray_tone2.png
+++ b/public/-/emojis/1/pray_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/pray_tone3.png b/public/-/emojis/1/pray_tone3.png
index 0f33b862008..0f33b862008 100644
--- a/app/assets/images/emoji/pray_tone3.png
+++ b/public/-/emojis/1/pray_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/pray_tone4.png b/public/-/emojis/1/pray_tone4.png
index 2ea8dc11657..2ea8dc11657 100644
--- a/app/assets/images/emoji/pray_tone4.png
+++ b/public/-/emojis/1/pray_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/pray_tone5.png b/public/-/emojis/1/pray_tone5.png
index 2128a6c4703..2128a6c4703 100644
--- a/app/assets/images/emoji/pray_tone5.png
+++ b/public/-/emojis/1/pray_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/prayer_beads.png b/public/-/emojis/1/prayer_beads.png
index a4b6dfcc62e..a4b6dfcc62e 100644
--- a/app/assets/images/emoji/prayer_beads.png
+++ b/public/-/emojis/1/prayer_beads.png
Binary files differ
diff --git a/app/assets/images/emoji/pregnant_woman.png b/public/-/emojis/1/pregnant_woman.png
index 084e83a414a..084e83a414a 100644
--- a/app/assets/images/emoji/pregnant_woman.png
+++ b/public/-/emojis/1/pregnant_woman.png
Binary files differ
diff --git a/app/assets/images/emoji/pregnant_woman_tone1.png b/public/-/emojis/1/pregnant_woman_tone1.png
index a78703b33aa..a78703b33aa 100644
--- a/app/assets/images/emoji/pregnant_woman_tone1.png
+++ b/public/-/emojis/1/pregnant_woman_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/pregnant_woman_tone2.png b/public/-/emojis/1/pregnant_woman_tone2.png
index 0068c6c4a77..0068c6c4a77 100644
--- a/app/assets/images/emoji/pregnant_woman_tone2.png
+++ b/public/-/emojis/1/pregnant_woman_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/pregnant_woman_tone3.png b/public/-/emojis/1/pregnant_woman_tone3.png
index 3206296b684..3206296b684 100644
--- a/app/assets/images/emoji/pregnant_woman_tone3.png
+++ b/public/-/emojis/1/pregnant_woman_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/pregnant_woman_tone4.png b/public/-/emojis/1/pregnant_woman_tone4.png
index 120fda5cd8c..120fda5cd8c 100644
--- a/app/assets/images/emoji/pregnant_woman_tone4.png
+++ b/public/-/emojis/1/pregnant_woman_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/pregnant_woman_tone5.png b/public/-/emojis/1/pregnant_woman_tone5.png
index 569bfdf05ce..569bfdf05ce 100644
--- a/app/assets/images/emoji/pregnant_woman_tone5.png
+++ b/public/-/emojis/1/pregnant_woman_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/prince.png b/public/-/emojis/1/prince.png
index 38d69344c84..38d69344c84 100644
--- a/app/assets/images/emoji/prince.png
+++ b/public/-/emojis/1/prince.png
Binary files differ
diff --git a/app/assets/images/emoji/prince_tone1.png b/public/-/emojis/1/prince_tone1.png
index 849930c8887..849930c8887 100644
--- a/app/assets/images/emoji/prince_tone1.png
+++ b/public/-/emojis/1/prince_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/prince_tone2.png b/public/-/emojis/1/prince_tone2.png
index 23d8b3b1285..23d8b3b1285 100644
--- a/app/assets/images/emoji/prince_tone2.png
+++ b/public/-/emojis/1/prince_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/prince_tone3.png b/public/-/emojis/1/prince_tone3.png
index db6dfff0647..db6dfff0647 100644
--- a/app/assets/images/emoji/prince_tone3.png
+++ b/public/-/emojis/1/prince_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/prince_tone4.png b/public/-/emojis/1/prince_tone4.png
index 8e10f8be6a8..8e10f8be6a8 100644
--- a/app/assets/images/emoji/prince_tone4.png
+++ b/public/-/emojis/1/prince_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/prince_tone5.png b/public/-/emojis/1/prince_tone5.png
index 138d4ea7048..138d4ea7048 100644
--- a/app/assets/images/emoji/prince_tone5.png
+++ b/public/-/emojis/1/prince_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/princess.png b/public/-/emojis/1/princess.png
index 879e9fa8c5d..879e9fa8c5d 100644
--- a/app/assets/images/emoji/princess.png
+++ b/public/-/emojis/1/princess.png
Binary files differ
diff --git a/app/assets/images/emoji/princess_tone1.png b/public/-/emojis/1/princess_tone1.png
index c28078cdc36..c28078cdc36 100644
--- a/app/assets/images/emoji/princess_tone1.png
+++ b/public/-/emojis/1/princess_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/princess_tone2.png b/public/-/emojis/1/princess_tone2.png
index dcd20e6ecd4..dcd20e6ecd4 100644
--- a/app/assets/images/emoji/princess_tone2.png
+++ b/public/-/emojis/1/princess_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/princess_tone3.png b/public/-/emojis/1/princess_tone3.png
index cde6f315c56..cde6f315c56 100644
--- a/app/assets/images/emoji/princess_tone3.png
+++ b/public/-/emojis/1/princess_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/princess_tone4.png b/public/-/emojis/1/princess_tone4.png
index c71e69caaef..c71e69caaef 100644
--- a/app/assets/images/emoji/princess_tone4.png
+++ b/public/-/emojis/1/princess_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/princess_tone5.png b/public/-/emojis/1/princess_tone5.png
index 063e2645910..063e2645910 100644
--- a/app/assets/images/emoji/princess_tone5.png
+++ b/public/-/emojis/1/princess_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/printer.png b/public/-/emojis/1/printer.png
index 027c830f0fe..027c830f0fe 100644
--- a/app/assets/images/emoji/printer.png
+++ b/public/-/emojis/1/printer.png
Binary files differ
diff --git a/app/assets/images/emoji/projector.png b/public/-/emojis/1/projector.png
index ce9ab0daa28..ce9ab0daa28 100644
--- a/app/assets/images/emoji/projector.png
+++ b/public/-/emojis/1/projector.png
Binary files differ
diff --git a/app/assets/images/emoji/punch.png b/public/-/emojis/1/punch.png
index b14ca5f5211..b14ca5f5211 100644
--- a/app/assets/images/emoji/punch.png
+++ b/public/-/emojis/1/punch.png
Binary files differ
diff --git a/app/assets/images/emoji/punch_tone1.png b/public/-/emojis/1/punch_tone1.png
index 93c7d17fb47..93c7d17fb47 100644
--- a/app/assets/images/emoji/punch_tone1.png
+++ b/public/-/emojis/1/punch_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/punch_tone2.png b/public/-/emojis/1/punch_tone2.png
index c0a1af6e10a..c0a1af6e10a 100644
--- a/app/assets/images/emoji/punch_tone2.png
+++ b/public/-/emojis/1/punch_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/punch_tone3.png b/public/-/emojis/1/punch_tone3.png
index 1458b021201..1458b021201 100644
--- a/app/assets/images/emoji/punch_tone3.png
+++ b/public/-/emojis/1/punch_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/punch_tone4.png b/public/-/emojis/1/punch_tone4.png
index c1466bfcdef..c1466bfcdef 100644
--- a/app/assets/images/emoji/punch_tone4.png
+++ b/public/-/emojis/1/punch_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/punch_tone5.png b/public/-/emojis/1/punch_tone5.png
index 00b4ddb8953..00b4ddb8953 100644
--- a/app/assets/images/emoji/punch_tone5.png
+++ b/public/-/emojis/1/punch_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/purple_heart.png b/public/-/emojis/1/purple_heart.png
index 95c53a9ade6..95c53a9ade6 100644
--- a/app/assets/images/emoji/purple_heart.png
+++ b/public/-/emojis/1/purple_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/purse.png b/public/-/emojis/1/purse.png
index 981346193c5..981346193c5 100644
--- a/app/assets/images/emoji/purse.png
+++ b/public/-/emojis/1/purse.png
Binary files differ
diff --git a/app/assets/images/emoji/pushpin.png b/public/-/emojis/1/pushpin.png
index 57e07d7f4cc..57e07d7f4cc 100644
--- a/app/assets/images/emoji/pushpin.png
+++ b/public/-/emojis/1/pushpin.png
Binary files differ
diff --git a/app/assets/images/emoji/put_litter_in_its_place.png b/public/-/emojis/1/put_litter_in_its_place.png
index 82a84f9a375..82a84f9a375 100644
--- a/app/assets/images/emoji/put_litter_in_its_place.png
+++ b/public/-/emojis/1/put_litter_in_its_place.png
Binary files differ
diff --git a/app/assets/images/emoji/question.png b/public/-/emojis/1/question.png
index 5a58f3458aa..5a58f3458aa 100644
--- a/app/assets/images/emoji/question.png
+++ b/public/-/emojis/1/question.png
Binary files differ
diff --git a/app/assets/images/emoji/rabbit.png b/public/-/emojis/1/rabbit.png
index ea75ab0426e..ea75ab0426e 100644
--- a/app/assets/images/emoji/rabbit.png
+++ b/public/-/emojis/1/rabbit.png
Binary files differ
diff --git a/app/assets/images/emoji/rabbit2.png b/public/-/emojis/1/rabbit2.png
index 2c8a29c642f..2c8a29c642f 100644
--- a/app/assets/images/emoji/rabbit2.png
+++ b/public/-/emojis/1/rabbit2.png
Binary files differ
diff --git a/app/assets/images/emoji/race_car.png b/public/-/emojis/1/race_car.png
index fe3f045f446..fe3f045f446 100644
--- a/app/assets/images/emoji/race_car.png
+++ b/public/-/emojis/1/race_car.png
Binary files differ
diff --git a/app/assets/images/emoji/racehorse.png b/public/-/emojis/1/racehorse.png
index b3e73cc8903..b3e73cc8903 100644
--- a/app/assets/images/emoji/racehorse.png
+++ b/public/-/emojis/1/racehorse.png
Binary files differ
diff --git a/app/assets/images/emoji/radio.png b/public/-/emojis/1/radio.png
index dec381fa242..dec381fa242 100644
--- a/app/assets/images/emoji/radio.png
+++ b/public/-/emojis/1/radio.png
Binary files differ
diff --git a/app/assets/images/emoji/radio_button.png b/public/-/emojis/1/radio_button.png
index 3a23449d917..3a23449d917 100644
--- a/app/assets/images/emoji/radio_button.png
+++ b/public/-/emojis/1/radio_button.png
Binary files differ
diff --git a/app/assets/images/emoji/radioactive.png b/public/-/emojis/1/radioactive.png
index 3b46199fe37..3b46199fe37 100644
--- a/app/assets/images/emoji/radioactive.png
+++ b/public/-/emojis/1/radioactive.png
Binary files differ
diff --git a/app/assets/images/emoji/rage.png b/public/-/emojis/1/rage.png
index 9d739bd40ad..9d739bd40ad 100644
--- a/app/assets/images/emoji/rage.png
+++ b/public/-/emojis/1/rage.png
Binary files differ
diff --git a/app/assets/images/emoji/railway_car.png b/public/-/emojis/1/railway_car.png
index a9acbf13008..a9acbf13008 100644
--- a/app/assets/images/emoji/railway_car.png
+++ b/public/-/emojis/1/railway_car.png
Binary files differ
diff --git a/app/assets/images/emoji/railway_track.png b/public/-/emojis/1/railway_track.png
index e1a7a0d1430..e1a7a0d1430 100644
--- a/app/assets/images/emoji/railway_track.png
+++ b/public/-/emojis/1/railway_track.png
Binary files differ
diff --git a/app/assets/images/emoji/rainbow.png b/public/-/emojis/1/rainbow.png
index 154735d7147..154735d7147 100644
--- a/app/assets/images/emoji/rainbow.png
+++ b/public/-/emojis/1/rainbow.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_back_of_hand.png b/public/-/emojis/1/raised_back_of_hand.png
index 479234294b4..479234294b4 100644
--- a/app/assets/images/emoji/raised_back_of_hand.png
+++ b/public/-/emojis/1/raised_back_of_hand.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_back_of_hand_tone1.png b/public/-/emojis/1/raised_back_of_hand_tone1.png
index 813d28499b5..813d28499b5 100644
--- a/app/assets/images/emoji/raised_back_of_hand_tone1.png
+++ b/public/-/emojis/1/raised_back_of_hand_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_back_of_hand_tone2.png b/public/-/emojis/1/raised_back_of_hand_tone2.png
index 192ff795e37..192ff795e37 100644
--- a/app/assets/images/emoji/raised_back_of_hand_tone2.png
+++ b/public/-/emojis/1/raised_back_of_hand_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_back_of_hand_tone3.png b/public/-/emojis/1/raised_back_of_hand_tone3.png
index 61a727abe6b..61a727abe6b 100644
--- a/app/assets/images/emoji/raised_back_of_hand_tone3.png
+++ b/public/-/emojis/1/raised_back_of_hand_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_back_of_hand_tone4.png b/public/-/emojis/1/raised_back_of_hand_tone4.png
index 2e83da511f5..2e83da511f5 100644
--- a/app/assets/images/emoji/raised_back_of_hand_tone4.png
+++ b/public/-/emojis/1/raised_back_of_hand_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_back_of_hand_tone5.png b/public/-/emojis/1/raised_back_of_hand_tone5.png
index d7a5b95a02c..d7a5b95a02c 100644
--- a/app/assets/images/emoji/raised_back_of_hand_tone5.png
+++ b/public/-/emojis/1/raised_back_of_hand_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hand.png b/public/-/emojis/1/raised_hand.png
index 6b2954315d1..6b2954315d1 100644
--- a/app/assets/images/emoji/raised_hand.png
+++ b/public/-/emojis/1/raised_hand.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hand_tone1.png b/public/-/emojis/1/raised_hand_tone1.png
index 3b752902c07..3b752902c07 100644
--- a/app/assets/images/emoji/raised_hand_tone1.png
+++ b/public/-/emojis/1/raised_hand_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hand_tone2.png b/public/-/emojis/1/raised_hand_tone2.png
index 44e2a514c60..44e2a514c60 100644
--- a/app/assets/images/emoji/raised_hand_tone2.png
+++ b/public/-/emojis/1/raised_hand_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hand_tone3.png b/public/-/emojis/1/raised_hand_tone3.png
index 5bb62a7528a..5bb62a7528a 100644
--- a/app/assets/images/emoji/raised_hand_tone3.png
+++ b/public/-/emojis/1/raised_hand_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hand_tone4.png b/public/-/emojis/1/raised_hand_tone4.png
index c7f8c9ec270..c7f8c9ec270 100644
--- a/app/assets/images/emoji/raised_hand_tone4.png
+++ b/public/-/emojis/1/raised_hand_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hand_tone5.png b/public/-/emojis/1/raised_hand_tone5.png
index c601b58a73e..c601b58a73e 100644
--- a/app/assets/images/emoji/raised_hand_tone5.png
+++ b/public/-/emojis/1/raised_hand_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hands.png b/public/-/emojis/1/raised_hands.png
index c0155f728e7..c0155f728e7 100644
--- a/app/assets/images/emoji/raised_hands.png
+++ b/public/-/emojis/1/raised_hands.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hands_tone1.png b/public/-/emojis/1/raised_hands_tone1.png
index 1168b8236b6..1168b8236b6 100644
--- a/app/assets/images/emoji/raised_hands_tone1.png
+++ b/public/-/emojis/1/raised_hands_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hands_tone2.png b/public/-/emojis/1/raised_hands_tone2.png
index 322de622903..322de622903 100644
--- a/app/assets/images/emoji/raised_hands_tone2.png
+++ b/public/-/emojis/1/raised_hands_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hands_tone3.png b/public/-/emojis/1/raised_hands_tone3.png
index 2aa24e05ae1..2aa24e05ae1 100644
--- a/app/assets/images/emoji/raised_hands_tone3.png
+++ b/public/-/emojis/1/raised_hands_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hands_tone4.png b/public/-/emojis/1/raised_hands_tone4.png
index f31bf0db992..f31bf0db992 100644
--- a/app/assets/images/emoji/raised_hands_tone4.png
+++ b/public/-/emojis/1/raised_hands_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/raised_hands_tone5.png b/public/-/emojis/1/raised_hands_tone5.png
index 5e95067f98b..5e95067f98b 100644
--- a/app/assets/images/emoji/raised_hands_tone5.png
+++ b/public/-/emojis/1/raised_hands_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/raising_hand.png b/public/-/emojis/1/raising_hand.png
index 2880708c0cc..2880708c0cc 100644
--- a/app/assets/images/emoji/raising_hand.png
+++ b/public/-/emojis/1/raising_hand.png
Binary files differ
diff --git a/app/assets/images/emoji/raising_hand_tone1.png b/public/-/emojis/1/raising_hand_tone1.png
index 1c90e3e2689..1c90e3e2689 100644
--- a/app/assets/images/emoji/raising_hand_tone1.png
+++ b/public/-/emojis/1/raising_hand_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/raising_hand_tone2.png b/public/-/emojis/1/raising_hand_tone2.png
index 82c3ef2bfc5..82c3ef2bfc5 100644
--- a/app/assets/images/emoji/raising_hand_tone2.png
+++ b/public/-/emojis/1/raising_hand_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/raising_hand_tone3.png b/public/-/emojis/1/raising_hand_tone3.png
index 1b1da2aa0ca..1b1da2aa0ca 100644
--- a/app/assets/images/emoji/raising_hand_tone3.png
+++ b/public/-/emojis/1/raising_hand_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/raising_hand_tone4.png b/public/-/emojis/1/raising_hand_tone4.png
index e453855c01f..e453855c01f 100644
--- a/app/assets/images/emoji/raising_hand_tone4.png
+++ b/public/-/emojis/1/raising_hand_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/raising_hand_tone5.png b/public/-/emojis/1/raising_hand_tone5.png
index b86200fd844..b86200fd844 100644
--- a/app/assets/images/emoji/raising_hand_tone5.png
+++ b/public/-/emojis/1/raising_hand_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/ram.png b/public/-/emojis/1/ram.png
index 52a44464c9b..52a44464c9b 100644
--- a/app/assets/images/emoji/ram.png
+++ b/public/-/emojis/1/ram.png
Binary files differ
diff --git a/app/assets/images/emoji/ramen.png b/public/-/emojis/1/ramen.png
index c1cb7cd7384..c1cb7cd7384 100644
--- a/app/assets/images/emoji/ramen.png
+++ b/public/-/emojis/1/ramen.png
Binary files differ
diff --git a/app/assets/images/emoji/rat.png b/public/-/emojis/1/rat.png
index 86219144f10..86219144f10 100644
--- a/app/assets/images/emoji/rat.png
+++ b/public/-/emojis/1/rat.png
Binary files differ
diff --git a/app/assets/images/emoji/record_button.png b/public/-/emojis/1/record_button.png
index ada52830fce..ada52830fce 100644
--- a/app/assets/images/emoji/record_button.png
+++ b/public/-/emojis/1/record_button.png
Binary files differ
diff --git a/app/assets/images/emoji/recycle.png b/public/-/emojis/1/recycle.png
index 9221f095c37..9221f095c37 100644
--- a/app/assets/images/emoji/recycle.png
+++ b/public/-/emojis/1/recycle.png
Binary files differ
diff --git a/app/assets/images/emoji/red_car.png b/public/-/emojis/1/red_car.png
index b3e6a774dea..b3e6a774dea 100644
--- a/app/assets/images/emoji/red_car.png
+++ b/public/-/emojis/1/red_car.png
Binary files differ
diff --git a/app/assets/images/emoji/red_circle.png b/public/-/emojis/1/red_circle.png
index 4bef930d92f..4bef930d92f 100644
--- a/app/assets/images/emoji/red_circle.png
+++ b/public/-/emojis/1/red_circle.png
Binary files differ
diff --git a/app/assets/images/emoji/registered.png b/public/-/emojis/1/registered.png
index 53ef9f2d4e6..53ef9f2d4e6 100644
--- a/app/assets/images/emoji/registered.png
+++ b/public/-/emojis/1/registered.png
Binary files differ
diff --git a/app/assets/images/emoji/relaxed.png b/public/-/emojis/1/relaxed.png
index e9e53c03d45..e9e53c03d45 100644
--- a/app/assets/images/emoji/relaxed.png
+++ b/public/-/emojis/1/relaxed.png
Binary files differ
diff --git a/app/assets/images/emoji/relieved.png b/public/-/emojis/1/relieved.png
index 715ad0bf53f..715ad0bf53f 100644
--- a/app/assets/images/emoji/relieved.png
+++ b/public/-/emojis/1/relieved.png
Binary files differ
diff --git a/app/assets/images/emoji/reminder_ribbon.png b/public/-/emojis/1/reminder_ribbon.png
index 3988bbd094c..3988bbd094c 100644
--- a/app/assets/images/emoji/reminder_ribbon.png
+++ b/public/-/emojis/1/reminder_ribbon.png
Binary files differ
diff --git a/app/assets/images/emoji/repeat.png b/public/-/emojis/1/repeat.png
index 540ce4e0fba..540ce4e0fba 100644
--- a/app/assets/images/emoji/repeat.png
+++ b/public/-/emojis/1/repeat.png
Binary files differ
diff --git a/app/assets/images/emoji/repeat_one.png b/public/-/emojis/1/repeat_one.png
index 9567e83337f..9567e83337f 100644
--- a/app/assets/images/emoji/repeat_one.png
+++ b/public/-/emojis/1/repeat_one.png
Binary files differ
diff --git a/app/assets/images/emoji/restroom.png b/public/-/emojis/1/restroom.png
index 9588e0f0ef7..9588e0f0ef7 100644
--- a/app/assets/images/emoji/restroom.png
+++ b/public/-/emojis/1/restroom.png
Binary files differ
diff --git a/app/assets/images/emoji/revolving_hearts.png b/public/-/emojis/1/revolving_hearts.png
index 7b9d1948f73..7b9d1948f73 100644
--- a/app/assets/images/emoji/revolving_hearts.png
+++ b/public/-/emojis/1/revolving_hearts.png
Binary files differ
diff --git a/app/assets/images/emoji/rewind.png b/public/-/emojis/1/rewind.png
index e22e2bd3da5..e22e2bd3da5 100644
--- a/app/assets/images/emoji/rewind.png
+++ b/public/-/emojis/1/rewind.png
Binary files differ
diff --git a/app/assets/images/emoji/rhino.png b/public/-/emojis/1/rhino.png
index 12f4e0d9d9b..12f4e0d9d9b 100644
--- a/app/assets/images/emoji/rhino.png
+++ b/public/-/emojis/1/rhino.png
Binary files differ
diff --git a/app/assets/images/emoji/ribbon.png b/public/-/emojis/1/ribbon.png
index 0f253c3d8c8..0f253c3d8c8 100644
--- a/app/assets/images/emoji/ribbon.png
+++ b/public/-/emojis/1/ribbon.png
Binary files differ
diff --git a/app/assets/images/emoji/rice.png b/public/-/emojis/1/rice.png
index 6e3ac7956b1..6e3ac7956b1 100644
--- a/app/assets/images/emoji/rice.png
+++ b/public/-/emojis/1/rice.png
Binary files differ
diff --git a/app/assets/images/emoji/rice_ball.png b/public/-/emojis/1/rice_ball.png
index d3d8ee25cb8..d3d8ee25cb8 100644
--- a/app/assets/images/emoji/rice_ball.png
+++ b/public/-/emojis/1/rice_ball.png
Binary files differ
diff --git a/app/assets/images/emoji/rice_cracker.png b/public/-/emojis/1/rice_cracker.png
index 7fbd08e4ff9..7fbd08e4ff9 100644
--- a/app/assets/images/emoji/rice_cracker.png
+++ b/public/-/emojis/1/rice_cracker.png
Binary files differ
diff --git a/app/assets/images/emoji/rice_scene.png b/public/-/emojis/1/rice_scene.png
index 1a28426592a..1a28426592a 100644
--- a/app/assets/images/emoji/rice_scene.png
+++ b/public/-/emojis/1/rice_scene.png
Binary files differ
diff --git a/app/assets/images/emoji/right_facing_fist.png b/public/-/emojis/1/right_facing_fist.png
index 754ed066d2c..754ed066d2c 100644
--- a/app/assets/images/emoji/right_facing_fist.png
+++ b/public/-/emojis/1/right_facing_fist.png
Binary files differ
diff --git a/app/assets/images/emoji/right_facing_fist_tone1.png b/public/-/emojis/1/right_facing_fist_tone1.png
index 33ded2f61a6..33ded2f61a6 100644
--- a/app/assets/images/emoji/right_facing_fist_tone1.png
+++ b/public/-/emojis/1/right_facing_fist_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/right_facing_fist_tone2.png b/public/-/emojis/1/right_facing_fist_tone2.png
index 88054e335c7..88054e335c7 100644
--- a/app/assets/images/emoji/right_facing_fist_tone2.png
+++ b/public/-/emojis/1/right_facing_fist_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/right_facing_fist_tone3.png b/public/-/emojis/1/right_facing_fist_tone3.png
index 84b9f5da7f7..84b9f5da7f7 100644
--- a/app/assets/images/emoji/right_facing_fist_tone3.png
+++ b/public/-/emojis/1/right_facing_fist_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/right_facing_fist_tone4.png b/public/-/emojis/1/right_facing_fist_tone4.png
index e741cfea68b..e741cfea68b 100644
--- a/app/assets/images/emoji/right_facing_fist_tone4.png
+++ b/public/-/emojis/1/right_facing_fist_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/right_facing_fist_tone5.png b/public/-/emojis/1/right_facing_fist_tone5.png
index cf66d760c1f..cf66d760c1f 100644
--- a/app/assets/images/emoji/right_facing_fist_tone5.png
+++ b/public/-/emojis/1/right_facing_fist_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/ring.png b/public/-/emojis/1/ring.png
index 87d227adb74..87d227adb74 100644
--- a/app/assets/images/emoji/ring.png
+++ b/public/-/emojis/1/ring.png
Binary files differ
diff --git a/app/assets/images/emoji/robot.png b/public/-/emojis/1/robot.png
index 7cc62612c6a..7cc62612c6a 100644
--- a/app/assets/images/emoji/robot.png
+++ b/public/-/emojis/1/robot.png
Binary files differ
diff --git a/app/assets/images/emoji/rocket.png b/public/-/emojis/1/rocket.png
index 0d8da089a37..0d8da089a37 100644
--- a/app/assets/images/emoji/rocket.png
+++ b/public/-/emojis/1/rocket.png
Binary files differ
diff --git a/app/assets/images/emoji/rofl.png b/public/-/emojis/1/rofl.png
index b1736fedfeb..b1736fedfeb 100644
--- a/app/assets/images/emoji/rofl.png
+++ b/public/-/emojis/1/rofl.png
Binary files differ
diff --git a/app/assets/images/emoji/roller_coaster.png b/public/-/emojis/1/roller_coaster.png
index 5b849e071e8..5b849e071e8 100644
--- a/app/assets/images/emoji/roller_coaster.png
+++ b/public/-/emojis/1/roller_coaster.png
Binary files differ
diff --git a/app/assets/images/emoji/rolling_eyes.png b/public/-/emojis/1/rolling_eyes.png
index 2f77b9fc3b9..2f77b9fc3b9 100644
--- a/app/assets/images/emoji/rolling_eyes.png
+++ b/public/-/emojis/1/rolling_eyes.png
Binary files differ
diff --git a/app/assets/images/emoji/rooster.png b/public/-/emojis/1/rooster.png
index bbf2bbff97a..bbf2bbff97a 100644
--- a/app/assets/images/emoji/rooster.png
+++ b/public/-/emojis/1/rooster.png
Binary files differ
diff --git a/app/assets/images/emoji/rose.png b/public/-/emojis/1/rose.png
index 52c286d31ce..52c286d31ce 100644
--- a/app/assets/images/emoji/rose.png
+++ b/public/-/emojis/1/rose.png
Binary files differ
diff --git a/app/assets/images/emoji/rosette.png b/public/-/emojis/1/rosette.png
index 8030e494bcf..8030e494bcf 100644
--- a/app/assets/images/emoji/rosette.png
+++ b/public/-/emojis/1/rosette.png
Binary files differ
diff --git a/app/assets/images/emoji/rotating_light.png b/public/-/emojis/1/rotating_light.png
index cad66b0afef..cad66b0afef 100644
--- a/app/assets/images/emoji/rotating_light.png
+++ b/public/-/emojis/1/rotating_light.png
Binary files differ
diff --git a/app/assets/images/emoji/round_pushpin.png b/public/-/emojis/1/round_pushpin.png
index 28b9d72866e..28b9d72866e 100644
--- a/app/assets/images/emoji/round_pushpin.png
+++ b/public/-/emojis/1/round_pushpin.png
Binary files differ
diff --git a/app/assets/images/emoji/rowboat.png b/public/-/emojis/1/rowboat.png
index dd4dfc095d9..dd4dfc095d9 100644
--- a/app/assets/images/emoji/rowboat.png
+++ b/public/-/emojis/1/rowboat.png
Binary files differ
diff --git a/app/assets/images/emoji/rowboat_tone1.png b/public/-/emojis/1/rowboat_tone1.png
index 5e5d18548cb..5e5d18548cb 100644
--- a/app/assets/images/emoji/rowboat_tone1.png
+++ b/public/-/emojis/1/rowboat_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/rowboat_tone2.png b/public/-/emojis/1/rowboat_tone2.png
index 9b123ef8871..9b123ef8871 100644
--- a/app/assets/images/emoji/rowboat_tone2.png
+++ b/public/-/emojis/1/rowboat_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/rowboat_tone3.png b/public/-/emojis/1/rowboat_tone3.png
index 8ebd89a55f5..8ebd89a55f5 100644
--- a/app/assets/images/emoji/rowboat_tone3.png
+++ b/public/-/emojis/1/rowboat_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/rowboat_tone4.png b/public/-/emojis/1/rowboat_tone4.png
index 2b0d04f8725..2b0d04f8725 100644
--- a/app/assets/images/emoji/rowboat_tone4.png
+++ b/public/-/emojis/1/rowboat_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/rowboat_tone5.png b/public/-/emojis/1/rowboat_tone5.png
index b346f2dfc84..b346f2dfc84 100644
--- a/app/assets/images/emoji/rowboat_tone5.png
+++ b/public/-/emojis/1/rowboat_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/rugby_football.png b/public/-/emojis/1/rugby_football.png
index b1872273436..b1872273436 100644
--- a/app/assets/images/emoji/rugby_football.png
+++ b/public/-/emojis/1/rugby_football.png
Binary files differ
diff --git a/app/assets/images/emoji/runner.png b/public/-/emojis/1/runner.png
index e914915976a..e914915976a 100644
--- a/app/assets/images/emoji/runner.png
+++ b/public/-/emojis/1/runner.png
Binary files differ
diff --git a/app/assets/images/emoji/runner_tone1.png b/public/-/emojis/1/runner_tone1.png
index 9355239a52d..9355239a52d 100644
--- a/app/assets/images/emoji/runner_tone1.png
+++ b/public/-/emojis/1/runner_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/runner_tone2.png b/public/-/emojis/1/runner_tone2.png
index 6112fd5c376..6112fd5c376 100644
--- a/app/assets/images/emoji/runner_tone2.png
+++ b/public/-/emojis/1/runner_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/runner_tone3.png b/public/-/emojis/1/runner_tone3.png
index 625ec708f48..625ec708f48 100644
--- a/app/assets/images/emoji/runner_tone3.png
+++ b/public/-/emojis/1/runner_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/runner_tone4.png b/public/-/emojis/1/runner_tone4.png
index 242f1b56337..242f1b56337 100644
--- a/app/assets/images/emoji/runner_tone4.png
+++ b/public/-/emojis/1/runner_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/runner_tone5.png b/public/-/emojis/1/runner_tone5.png
index 2976c6f019f..2976c6f019f 100644
--- a/app/assets/images/emoji/runner_tone5.png
+++ b/public/-/emojis/1/runner_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/running_shirt_with_sash.png b/public/-/emojis/1/running_shirt_with_sash.png
index 6d83c06b803..6d83c06b803 100644
--- a/app/assets/images/emoji/running_shirt_with_sash.png
+++ b/public/-/emojis/1/running_shirt_with_sash.png
Binary files differ
diff --git a/app/assets/images/emoji/sa.png b/public/-/emojis/1/sa.png
index 900f9633247..900f9633247 100644
--- a/app/assets/images/emoji/sa.png
+++ b/public/-/emojis/1/sa.png
Binary files differ
diff --git a/app/assets/images/emoji/sagittarius.png b/public/-/emojis/1/sagittarius.png
index f8d94ff2923..f8d94ff2923 100644
--- a/app/assets/images/emoji/sagittarius.png
+++ b/public/-/emojis/1/sagittarius.png
Binary files differ
diff --git a/app/assets/images/emoji/sailboat.png b/public/-/emojis/1/sailboat.png
index 772ef11da5d..772ef11da5d 100644
--- a/app/assets/images/emoji/sailboat.png
+++ b/public/-/emojis/1/sailboat.png
Binary files differ
diff --git a/app/assets/images/emoji/sake.png b/public/-/emojis/1/sake.png
index 2933f5672c4..2933f5672c4 100644
--- a/app/assets/images/emoji/sake.png
+++ b/public/-/emojis/1/sake.png
Binary files differ
diff --git a/app/assets/images/emoji/salad.png b/public/-/emojis/1/salad.png
index c89f9341158..c89f9341158 100644
--- a/app/assets/images/emoji/salad.png
+++ b/public/-/emojis/1/salad.png
Binary files differ
diff --git a/app/assets/images/emoji/sandal.png b/public/-/emojis/1/sandal.png
index 9d9f5122b7a..9d9f5122b7a 100644
--- a/app/assets/images/emoji/sandal.png
+++ b/public/-/emojis/1/sandal.png
Binary files differ
diff --git a/app/assets/images/emoji/santa.png b/public/-/emojis/1/santa.png
index bc83ab80d52..bc83ab80d52 100644
--- a/app/assets/images/emoji/santa.png
+++ b/public/-/emojis/1/santa.png
Binary files differ
diff --git a/app/assets/images/emoji/santa_tone1.png b/public/-/emojis/1/santa_tone1.png
index 5233ffb7174..5233ffb7174 100644
--- a/app/assets/images/emoji/santa_tone1.png
+++ b/public/-/emojis/1/santa_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/santa_tone2.png b/public/-/emojis/1/santa_tone2.png
index 4e845438197..4e845438197 100644
--- a/app/assets/images/emoji/santa_tone2.png
+++ b/public/-/emojis/1/santa_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/santa_tone3.png b/public/-/emojis/1/santa_tone3.png
index 7fc4f33b60f..7fc4f33b60f 100644
--- a/app/assets/images/emoji/santa_tone3.png
+++ b/public/-/emojis/1/santa_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/santa_tone4.png b/public/-/emojis/1/santa_tone4.png
index d1d5a15132d..d1d5a15132d 100644
--- a/app/assets/images/emoji/santa_tone4.png
+++ b/public/-/emojis/1/santa_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/santa_tone5.png b/public/-/emojis/1/santa_tone5.png
index 4d697a01f24..4d697a01f24 100644
--- a/app/assets/images/emoji/santa_tone5.png
+++ b/public/-/emojis/1/santa_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/satellite.png b/public/-/emojis/1/satellite.png
index db0372795f4..db0372795f4 100644
--- a/app/assets/images/emoji/satellite.png
+++ b/public/-/emojis/1/satellite.png
Binary files differ
diff --git a/app/assets/images/emoji/satellite_orbital.png b/public/-/emojis/1/satellite_orbital.png
index 4ba55d6e297..4ba55d6e297 100644
--- a/app/assets/images/emoji/satellite_orbital.png
+++ b/public/-/emojis/1/satellite_orbital.png
Binary files differ
diff --git a/app/assets/images/emoji/saxophone.png b/public/-/emojis/1/saxophone.png
index a392faec291..a392faec291 100644
--- a/app/assets/images/emoji/saxophone.png
+++ b/public/-/emojis/1/saxophone.png
Binary files differ
diff --git a/app/assets/images/emoji/scales.png b/public/-/emojis/1/scales.png
index 0757eda1684..0757eda1684 100644
--- a/app/assets/images/emoji/scales.png
+++ b/public/-/emojis/1/scales.png
Binary files differ
diff --git a/app/assets/images/emoji/school.png b/public/-/emojis/1/school.png
index 269759534f0..269759534f0 100644
--- a/app/assets/images/emoji/school.png
+++ b/public/-/emojis/1/school.png
Binary files differ
diff --git a/app/assets/images/emoji/school_satchel.png b/public/-/emojis/1/school_satchel.png
index 9997c86e7dc..9997c86e7dc 100644
--- a/app/assets/images/emoji/school_satchel.png
+++ b/public/-/emojis/1/school_satchel.png
Binary files differ
diff --git a/app/assets/images/emoji/scissors.png b/public/-/emojis/1/scissors.png
index 270571c8cdd..270571c8cdd 100644
--- a/app/assets/images/emoji/scissors.png
+++ b/public/-/emojis/1/scissors.png
Binary files differ
diff --git a/app/assets/images/emoji/scooter.png b/public/-/emojis/1/scooter.png
index 4ab7ef59cd2..4ab7ef59cd2 100644
--- a/app/assets/images/emoji/scooter.png
+++ b/public/-/emojis/1/scooter.png
Binary files differ
diff --git a/app/assets/images/emoji/scorpion.png b/public/-/emojis/1/scorpion.png
index 449a6b281c9..449a6b281c9 100644
--- a/app/assets/images/emoji/scorpion.png
+++ b/public/-/emojis/1/scorpion.png
Binary files differ
diff --git a/app/assets/images/emoji/scorpius.png b/public/-/emojis/1/scorpius.png
index c31a9920455..c31a9920455 100644
--- a/app/assets/images/emoji/scorpius.png
+++ b/public/-/emojis/1/scorpius.png
Binary files differ
diff --git a/app/assets/images/emoji/scream.png b/public/-/emojis/1/scream.png
index c3bea9f2510..c3bea9f2510 100644
--- a/app/assets/images/emoji/scream.png
+++ b/public/-/emojis/1/scream.png
Binary files differ
diff --git a/app/assets/images/emoji/scream_cat.png b/public/-/emojis/1/scream_cat.png
index 15803ad8e6e..15803ad8e6e 100644
--- a/app/assets/images/emoji/scream_cat.png
+++ b/public/-/emojis/1/scream_cat.png
Binary files differ
diff --git a/app/assets/images/emoji/scroll.png b/public/-/emojis/1/scroll.png
index 50ee5dcd4b9..50ee5dcd4b9 100644
--- a/app/assets/images/emoji/scroll.png
+++ b/public/-/emojis/1/scroll.png
Binary files differ
diff --git a/app/assets/images/emoji/seat.png b/public/-/emojis/1/seat.png
index a6d72d95adb..a6d72d95adb 100644
--- a/app/assets/images/emoji/seat.png
+++ b/public/-/emojis/1/seat.png
Binary files differ
diff --git a/app/assets/images/emoji/second_place.png b/public/-/emojis/1/second_place.png
index 17b011268b6..17b011268b6 100644
--- a/app/assets/images/emoji/second_place.png
+++ b/public/-/emojis/1/second_place.png
Binary files differ
diff --git a/app/assets/images/emoji/secret.png b/public/-/emojis/1/secret.png
index 5fd72608e60..5fd72608e60 100644
--- a/app/assets/images/emoji/secret.png
+++ b/public/-/emojis/1/secret.png
Binary files differ
diff --git a/app/assets/images/emoji/see_no_evil.png b/public/-/emojis/1/see_no_evil.png
index 5187e474531..5187e474531 100644
--- a/app/assets/images/emoji/see_no_evil.png
+++ b/public/-/emojis/1/see_no_evil.png
Binary files differ
diff --git a/app/assets/images/emoji/seedling.png b/public/-/emojis/1/seedling.png
index ae0948bcfd6..ae0948bcfd6 100644
--- a/app/assets/images/emoji/seedling.png
+++ b/public/-/emojis/1/seedling.png
Binary files differ
diff --git a/app/assets/images/emoji/selfie.png b/public/-/emojis/1/selfie.png
index 6a1ba75c7e3..6a1ba75c7e3 100644
--- a/app/assets/images/emoji/selfie.png
+++ b/public/-/emojis/1/selfie.png
Binary files differ
diff --git a/app/assets/images/emoji/selfie_tone1.png b/public/-/emojis/1/selfie_tone1.png
index 290e075b56f..290e075b56f 100644
--- a/app/assets/images/emoji/selfie_tone1.png
+++ b/public/-/emojis/1/selfie_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/selfie_tone2.png b/public/-/emojis/1/selfie_tone2.png
index fcd9595b643..fcd9595b643 100644
--- a/app/assets/images/emoji/selfie_tone2.png
+++ b/public/-/emojis/1/selfie_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/selfie_tone3.png b/public/-/emojis/1/selfie_tone3.png
index f3a22fdf435..f3a22fdf435 100644
--- a/app/assets/images/emoji/selfie_tone3.png
+++ b/public/-/emojis/1/selfie_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/selfie_tone4.png b/public/-/emojis/1/selfie_tone4.png
index cdecf6d9f4e..cdecf6d9f4e 100644
--- a/app/assets/images/emoji/selfie_tone4.png
+++ b/public/-/emojis/1/selfie_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/selfie_tone5.png b/public/-/emojis/1/selfie_tone5.png
index 86acbb6c202..86acbb6c202 100644
--- a/app/assets/images/emoji/selfie_tone5.png
+++ b/public/-/emojis/1/selfie_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/seven.png b/public/-/emojis/1/seven.png
index 9b3476ae7c7..9b3476ae7c7 100644
--- a/app/assets/images/emoji/seven.png
+++ b/public/-/emojis/1/seven.png
Binary files differ
diff --git a/app/assets/images/emoji/shallow_pan_of_food.png b/public/-/emojis/1/shallow_pan_of_food.png
index 663a1006acd..663a1006acd 100644
--- a/app/assets/images/emoji/shallow_pan_of_food.png
+++ b/public/-/emojis/1/shallow_pan_of_food.png
Binary files differ
diff --git a/app/assets/images/emoji/shamrock.png b/public/-/emojis/1/shamrock.png
index f202aecfe6f..f202aecfe6f 100644
--- a/app/assets/images/emoji/shamrock.png
+++ b/public/-/emojis/1/shamrock.png
Binary files differ
diff --git a/app/assets/images/emoji/shark.png b/public/-/emojis/1/shark.png
index c75076d57d8..c75076d57d8 100644
--- a/app/assets/images/emoji/shark.png
+++ b/public/-/emojis/1/shark.png
Binary files differ
diff --git a/app/assets/images/emoji/shaved_ice.png b/public/-/emojis/1/shaved_ice.png
index 36dfb53ca93..36dfb53ca93 100644
--- a/app/assets/images/emoji/shaved_ice.png
+++ b/public/-/emojis/1/shaved_ice.png
Binary files differ
diff --git a/app/assets/images/emoji/sheep.png b/public/-/emojis/1/sheep.png
index 102b8a52b28..102b8a52b28 100644
--- a/app/assets/images/emoji/sheep.png
+++ b/public/-/emojis/1/sheep.png
Binary files differ
diff --git a/app/assets/images/emoji/shell.png b/public/-/emojis/1/shell.png
index 55721629f62..55721629f62 100644
--- a/app/assets/images/emoji/shell.png
+++ b/public/-/emojis/1/shell.png
Binary files differ
diff --git a/app/assets/images/emoji/shield.png b/public/-/emojis/1/shield.png
index 610bf033ce0..610bf033ce0 100644
--- a/app/assets/images/emoji/shield.png
+++ b/public/-/emojis/1/shield.png
Binary files differ
diff --git a/app/assets/images/emoji/shinto_shrine.png b/public/-/emojis/1/shinto_shrine.png
index 5a344975bf3..5a344975bf3 100644
--- a/app/assets/images/emoji/shinto_shrine.png
+++ b/public/-/emojis/1/shinto_shrine.png
Binary files differ
diff --git a/app/assets/images/emoji/ship.png b/public/-/emojis/1/ship.png
index 62d54f7d6c9..62d54f7d6c9 100644
--- a/app/assets/images/emoji/ship.png
+++ b/public/-/emojis/1/ship.png
Binary files differ
diff --git a/app/assets/images/emoji/shirt.png b/public/-/emojis/1/shirt.png
index af08dec8b59..af08dec8b59 100644
--- a/app/assets/images/emoji/shirt.png
+++ b/public/-/emojis/1/shirt.png
Binary files differ
diff --git a/app/assets/images/emoji/shopping_bags.png b/public/-/emojis/1/shopping_bags.png
index 99f2a2b13ac..99f2a2b13ac 100644
--- a/app/assets/images/emoji/shopping_bags.png
+++ b/public/-/emojis/1/shopping_bags.png
Binary files differ
diff --git a/app/assets/images/emoji/shopping_cart.png b/public/-/emojis/1/shopping_cart.png
index 1086fe6e456..1086fe6e456 100644
--- a/app/assets/images/emoji/shopping_cart.png
+++ b/public/-/emojis/1/shopping_cart.png
Binary files differ
diff --git a/app/assets/images/emoji/shower.png b/public/-/emojis/1/shower.png
index 156776a2e52..156776a2e52 100644
--- a/app/assets/images/emoji/shower.png
+++ b/public/-/emojis/1/shower.png
Binary files differ
diff --git a/app/assets/images/emoji/shrimp.png b/public/-/emojis/1/shrimp.png
index 49eff28a71e..49eff28a71e 100644
--- a/app/assets/images/emoji/shrimp.png
+++ b/public/-/emojis/1/shrimp.png
Binary files differ
diff --git a/app/assets/images/emoji/shrug.png b/public/-/emojis/1/shrug.png
index 76e63bfac77..76e63bfac77 100644
--- a/app/assets/images/emoji/shrug.png
+++ b/public/-/emojis/1/shrug.png
Binary files differ
diff --git a/app/assets/images/emoji/shrug_tone1.png b/public/-/emojis/1/shrug_tone1.png
index 1c895e64468..1c895e64468 100644
--- a/app/assets/images/emoji/shrug_tone1.png
+++ b/public/-/emojis/1/shrug_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/shrug_tone2.png b/public/-/emojis/1/shrug_tone2.png
index 4e3ca8f8bac..4e3ca8f8bac 100644
--- a/app/assets/images/emoji/shrug_tone2.png
+++ b/public/-/emojis/1/shrug_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/shrug_tone3.png b/public/-/emojis/1/shrug_tone3.png
index d1b16a19bb5..d1b16a19bb5 100644
--- a/app/assets/images/emoji/shrug_tone3.png
+++ b/public/-/emojis/1/shrug_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/shrug_tone4.png b/public/-/emojis/1/shrug_tone4.png
index 5fbef3f2255..5fbef3f2255 100644
--- a/app/assets/images/emoji/shrug_tone4.png
+++ b/public/-/emojis/1/shrug_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/shrug_tone5.png b/public/-/emojis/1/shrug_tone5.png
index 4af2e28bc5c..4af2e28bc5c 100644
--- a/app/assets/images/emoji/shrug_tone5.png
+++ b/public/-/emojis/1/shrug_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/signal_strength.png b/public/-/emojis/1/signal_strength.png
index ee2b5a4b519..ee2b5a4b519 100644
--- a/app/assets/images/emoji/signal_strength.png
+++ b/public/-/emojis/1/signal_strength.png
Binary files differ
diff --git a/app/assets/images/emoji/six.png b/public/-/emojis/1/six.png
index 371b3acef2c..371b3acef2c 100644
--- a/app/assets/images/emoji/six.png
+++ b/public/-/emojis/1/six.png
Binary files differ
diff --git a/app/assets/images/emoji/six_pointed_star.png b/public/-/emojis/1/six_pointed_star.png
index 2eb1707458b..2eb1707458b 100644
--- a/app/assets/images/emoji/six_pointed_star.png
+++ b/public/-/emojis/1/six_pointed_star.png
Binary files differ
diff --git a/app/assets/images/emoji/ski.png b/public/-/emojis/1/ski.png
index 4a2d2c12306..4a2d2c12306 100644
--- a/app/assets/images/emoji/ski.png
+++ b/public/-/emojis/1/ski.png
Binary files differ
diff --git a/app/assets/images/emoji/skier.png b/public/-/emojis/1/skier.png
index 2eb3bdce2af..2eb3bdce2af 100644
--- a/app/assets/images/emoji/skier.png
+++ b/public/-/emojis/1/skier.png
Binary files differ
diff --git a/app/assets/images/emoji/skull.png b/public/-/emojis/1/skull.png
index 26abb17296a..26abb17296a 100644
--- a/app/assets/images/emoji/skull.png
+++ b/public/-/emojis/1/skull.png
Binary files differ
diff --git a/app/assets/images/emoji/skull_crossbones.png b/public/-/emojis/1/skull_crossbones.png
index b459df9227a..b459df9227a 100644
--- a/app/assets/images/emoji/skull_crossbones.png
+++ b/public/-/emojis/1/skull_crossbones.png
Binary files differ
diff --git a/app/assets/images/emoji/sleeping.png b/public/-/emojis/1/sleeping.png
index 9ecf600d6d8..9ecf600d6d8 100644
--- a/app/assets/images/emoji/sleeping.png
+++ b/public/-/emojis/1/sleeping.png
Binary files differ
diff --git a/app/assets/images/emoji/sleeping_accommodation.png b/public/-/emojis/1/sleeping_accommodation.png
index c739e7fb69b..c739e7fb69b 100644
--- a/app/assets/images/emoji/sleeping_accommodation.png
+++ b/public/-/emojis/1/sleeping_accommodation.png
Binary files differ
diff --git a/app/assets/images/emoji/sleepy.png b/public/-/emojis/1/sleepy.png
index 836b4107717..836b4107717 100644
--- a/app/assets/images/emoji/sleepy.png
+++ b/public/-/emojis/1/sleepy.png
Binary files differ
diff --git a/app/assets/images/emoji/slight_frown.png b/public/-/emojis/1/slight_frown.png
index b2f1d983d36..b2f1d983d36 100644
--- a/app/assets/images/emoji/slight_frown.png
+++ b/public/-/emojis/1/slight_frown.png
Binary files differ
diff --git a/app/assets/images/emoji/slight_smile.png b/public/-/emojis/1/slight_smile.png
index ddd7d65dd3d..ddd7d65dd3d 100644
--- a/app/assets/images/emoji/slight_smile.png
+++ b/public/-/emojis/1/slight_smile.png
Binary files differ
diff --git a/app/assets/images/emoji/slot_machine.png b/public/-/emojis/1/slot_machine.png
index ee71b6c268c..ee71b6c268c 100644
--- a/app/assets/images/emoji/slot_machine.png
+++ b/public/-/emojis/1/slot_machine.png
Binary files differ
diff --git a/app/assets/images/emoji/small_blue_diamond.png b/public/-/emojis/1/small_blue_diamond.png
index b86b5bc4db3..b86b5bc4db3 100644
--- a/app/assets/images/emoji/small_blue_diamond.png
+++ b/public/-/emojis/1/small_blue_diamond.png
Binary files differ
diff --git a/app/assets/images/emoji/small_orange_diamond.png b/public/-/emojis/1/small_orange_diamond.png
index e1c6ed9b2f8..e1c6ed9b2f8 100644
--- a/app/assets/images/emoji/small_orange_diamond.png
+++ b/public/-/emojis/1/small_orange_diamond.png
Binary files differ
diff --git a/app/assets/images/emoji/small_red_triangle.png b/public/-/emojis/1/small_red_triangle.png
index 785887c195a..785887c195a 100644
--- a/app/assets/images/emoji/small_red_triangle.png
+++ b/public/-/emojis/1/small_red_triangle.png
Binary files differ
diff --git a/app/assets/images/emoji/small_red_triangle_down.png b/public/-/emojis/1/small_red_triangle_down.png
index a83beff1914..a83beff1914 100644
--- a/app/assets/images/emoji/small_red_triangle_down.png
+++ b/public/-/emojis/1/small_red_triangle_down.png
Binary files differ
diff --git a/app/assets/images/emoji/smile.png b/public/-/emojis/1/smile.png
index aa47ffe978c..aa47ffe978c 100644
--- a/app/assets/images/emoji/smile.png
+++ b/public/-/emojis/1/smile.png
Binary files differ
diff --git a/app/assets/images/emoji/smile_cat.png b/public/-/emojis/1/smile_cat.png
index 6f25f11dd3a..6f25f11dd3a 100644
--- a/app/assets/images/emoji/smile_cat.png
+++ b/public/-/emojis/1/smile_cat.png
Binary files differ
diff --git a/app/assets/images/emoji/smiley.png b/public/-/emojis/1/smiley.png
index 30957a65968..30957a65968 100644
--- a/app/assets/images/emoji/smiley.png
+++ b/public/-/emojis/1/smiley.png
Binary files differ
diff --git a/app/assets/images/emoji/smiley_cat.png b/public/-/emojis/1/smiley_cat.png
index 163b57a3427..163b57a3427 100644
--- a/app/assets/images/emoji/smiley_cat.png
+++ b/public/-/emojis/1/smiley_cat.png
Binary files differ
diff --git a/app/assets/images/emoji/smiling_imp.png b/public/-/emojis/1/smiling_imp.png
index cc2c5f1ec72..cc2c5f1ec72 100644
--- a/app/assets/images/emoji/smiling_imp.png
+++ b/public/-/emojis/1/smiling_imp.png
Binary files differ
diff --git a/app/assets/images/emoji/smirk.png b/public/-/emojis/1/smirk.png
index 87852109988..87852109988 100644
--- a/app/assets/images/emoji/smirk.png
+++ b/public/-/emojis/1/smirk.png
Binary files differ
diff --git a/app/assets/images/emoji/smirk_cat.png b/public/-/emojis/1/smirk_cat.png
index 9ac5954c199..9ac5954c199 100644
--- a/app/assets/images/emoji/smirk_cat.png
+++ b/public/-/emojis/1/smirk_cat.png
Binary files differ
diff --git a/app/assets/images/emoji/smoking.png b/public/-/emojis/1/smoking.png
index 910f648c8f9..910f648c8f9 100644
--- a/app/assets/images/emoji/smoking.png
+++ b/public/-/emojis/1/smoking.png
Binary files differ
diff --git a/app/assets/images/emoji/snail.png b/public/-/emojis/1/snail.png
index f4ea071e2d3..f4ea071e2d3 100644
--- a/app/assets/images/emoji/snail.png
+++ b/public/-/emojis/1/snail.png
Binary files differ
diff --git a/app/assets/images/emoji/snake.png b/public/-/emojis/1/snake.png
index d0278a28d8c..d0278a28d8c 100644
--- a/app/assets/images/emoji/snake.png
+++ b/public/-/emojis/1/snake.png
Binary files differ
diff --git a/app/assets/images/emoji/sneezing_face.png b/public/-/emojis/1/sneezing_face.png
index ccf07d4b64d..ccf07d4b64d 100644
--- a/app/assets/images/emoji/sneezing_face.png
+++ b/public/-/emojis/1/sneezing_face.png
Binary files differ
diff --git a/app/assets/images/emoji/snowboarder.png b/public/-/emojis/1/snowboarder.png
index 6361c0f2c9d..6361c0f2c9d 100644
--- a/app/assets/images/emoji/snowboarder.png
+++ b/public/-/emojis/1/snowboarder.png
Binary files differ
diff --git a/app/assets/images/emoji/snowflake.png b/public/-/emojis/1/snowflake.png
index db319a77ec6..db319a77ec6 100644
--- a/app/assets/images/emoji/snowflake.png
+++ b/public/-/emojis/1/snowflake.png
Binary files differ
diff --git a/app/assets/images/emoji/snowman.png b/public/-/emojis/1/snowman.png
index 20c177c2aff..20c177c2aff 100644
--- a/app/assets/images/emoji/snowman.png
+++ b/public/-/emojis/1/snowman.png
Binary files differ
diff --git a/app/assets/images/emoji/snowman2.png b/public/-/emojis/1/snowman2.png
index 896f28502af..896f28502af 100644
--- a/app/assets/images/emoji/snowman2.png
+++ b/public/-/emojis/1/snowman2.png
Binary files differ
diff --git a/app/assets/images/emoji/sob.png b/public/-/emojis/1/sob.png
index 52e3517a1ee..52e3517a1ee 100644
--- a/app/assets/images/emoji/sob.png
+++ b/public/-/emojis/1/sob.png
Binary files differ
diff --git a/app/assets/images/emoji/soccer.png b/public/-/emojis/1/soccer.png
index 28cfa218d6d..28cfa218d6d 100644
--- a/app/assets/images/emoji/soccer.png
+++ b/public/-/emojis/1/soccer.png
Binary files differ
diff --git a/app/assets/images/emoji/soon.png b/public/-/emojis/1/soon.png
index 8cdfd86690d..8cdfd86690d 100644
--- a/app/assets/images/emoji/soon.png
+++ b/public/-/emojis/1/soon.png
Binary files differ
diff --git a/app/assets/images/emoji/sos.png b/public/-/emojis/1/sos.png
index d7d8c9953e4..d7d8c9953e4 100644
--- a/app/assets/images/emoji/sos.png
+++ b/public/-/emojis/1/sos.png
Binary files differ
diff --git a/app/assets/images/emoji/sound.png b/public/-/emojis/1/sound.png
index e75ddca53ba..e75ddca53ba 100644
--- a/app/assets/images/emoji/sound.png
+++ b/public/-/emojis/1/sound.png
Binary files differ
diff --git a/app/assets/images/emoji/space_invader.png b/public/-/emojis/1/space_invader.png
index 2e73f5f32e5..2e73f5f32e5 100644
--- a/app/assets/images/emoji/space_invader.png
+++ b/public/-/emojis/1/space_invader.png
Binary files differ
diff --git a/app/assets/images/emoji/spades.png b/public/-/emojis/1/spades.png
index f822f184cb0..f822f184cb0 100644
--- a/app/assets/images/emoji/spades.png
+++ b/public/-/emojis/1/spades.png
Binary files differ
diff --git a/app/assets/images/emoji/spaghetti.png b/public/-/emojis/1/spaghetti.png
index 89c24a321f1..89c24a321f1 100644
--- a/app/assets/images/emoji/spaghetti.png
+++ b/public/-/emojis/1/spaghetti.png
Binary files differ
diff --git a/app/assets/images/emoji/sparkle.png b/public/-/emojis/1/sparkle.png
index 6aa7b6ec9cf..6aa7b6ec9cf 100644
--- a/app/assets/images/emoji/sparkle.png
+++ b/public/-/emojis/1/sparkle.png
Binary files differ
diff --git a/app/assets/images/emoji/sparkler.png b/public/-/emojis/1/sparkler.png
index 30339cd6e09..30339cd6e09 100644
--- a/app/assets/images/emoji/sparkler.png
+++ b/public/-/emojis/1/sparkler.png
Binary files differ
diff --git a/app/assets/images/emoji/sparkles.png b/public/-/emojis/1/sparkles.png
index 169bc10b023..169bc10b023 100644
--- a/app/assets/images/emoji/sparkles.png
+++ b/public/-/emojis/1/sparkles.png
Binary files differ
diff --git a/app/assets/images/emoji/sparkling_heart.png b/public/-/emojis/1/sparkling_heart.png
index 6709269454e..6709269454e 100644
--- a/app/assets/images/emoji/sparkling_heart.png
+++ b/public/-/emojis/1/sparkling_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/speak_no_evil.png b/public/-/emojis/1/speak_no_evil.png
index 9d9e07c974b..9d9e07c974b 100644
--- a/app/assets/images/emoji/speak_no_evil.png
+++ b/public/-/emojis/1/speak_no_evil.png
Binary files differ
diff --git a/app/assets/images/emoji/speaker.png b/public/-/emojis/1/speaker.png
index 7bcffb8fc43..7bcffb8fc43 100644
--- a/app/assets/images/emoji/speaker.png
+++ b/public/-/emojis/1/speaker.png
Binary files differ
diff --git a/app/assets/images/emoji/speaking_head.png b/public/-/emojis/1/speaking_head.png
index 2df93aaae09..2df93aaae09 100644
--- a/app/assets/images/emoji/speaking_head.png
+++ b/public/-/emojis/1/speaking_head.png
Binary files differ
diff --git a/app/assets/images/emoji/speech_balloon.png b/public/-/emojis/1/speech_balloon.png
index a34ef741733..a34ef741733 100644
--- a/app/assets/images/emoji/speech_balloon.png
+++ b/public/-/emojis/1/speech_balloon.png
Binary files differ
diff --git a/app/assets/images/emoji/speech_left.png b/public/-/emojis/1/speech_left.png
index 00c05959bcd..00c05959bcd 100644
--- a/app/assets/images/emoji/speech_left.png
+++ b/public/-/emojis/1/speech_left.png
Binary files differ
diff --git a/app/assets/images/emoji/speedboat.png b/public/-/emojis/1/speedboat.png
index 74059d12de1..74059d12de1 100644
--- a/app/assets/images/emoji/speedboat.png
+++ b/public/-/emojis/1/speedboat.png
Binary files differ
diff --git a/app/assets/images/emoji/spider.png b/public/-/emojis/1/spider.png
index 3849fa90b94..3849fa90b94 100644
--- a/app/assets/images/emoji/spider.png
+++ b/public/-/emojis/1/spider.png
Binary files differ
diff --git a/app/assets/images/emoji/spider_web.png b/public/-/emojis/1/spider_web.png
index ba448ee7fba..ba448ee7fba 100644
--- a/app/assets/images/emoji/spider_web.png
+++ b/public/-/emojis/1/spider_web.png
Binary files differ
diff --git a/app/assets/images/emoji/spoon.png b/public/-/emojis/1/spoon.png
index 3c4da766aee..3c4da766aee 100644
--- a/app/assets/images/emoji/spoon.png
+++ b/public/-/emojis/1/spoon.png
Binary files differ
diff --git a/app/assets/images/emoji/spy.png b/public/-/emojis/1/spy.png
index a729e9584d6..a729e9584d6 100644
--- a/app/assets/images/emoji/spy.png
+++ b/public/-/emojis/1/spy.png
Binary files differ
diff --git a/app/assets/images/emoji/spy_tone1.png b/public/-/emojis/1/spy_tone1.png
index 2d1c022caee..2d1c022caee 100644
--- a/app/assets/images/emoji/spy_tone1.png
+++ b/public/-/emojis/1/spy_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/spy_tone2.png b/public/-/emojis/1/spy_tone2.png
index 548b9c26f5d..548b9c26f5d 100644
--- a/app/assets/images/emoji/spy_tone2.png
+++ b/public/-/emojis/1/spy_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/spy_tone3.png b/public/-/emojis/1/spy_tone3.png
index b023f4b18e1..b023f4b18e1 100644
--- a/app/assets/images/emoji/spy_tone3.png
+++ b/public/-/emojis/1/spy_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/spy_tone4.png b/public/-/emojis/1/spy_tone4.png
index d8300af492d..d8300af492d 100644
--- a/app/assets/images/emoji/spy_tone4.png
+++ b/public/-/emojis/1/spy_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/spy_tone5.png b/public/-/emojis/1/spy_tone5.png
index ca1462595fa..ca1462595fa 100644
--- a/app/assets/images/emoji/spy_tone5.png
+++ b/public/-/emojis/1/spy_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/squid.png b/public/-/emojis/1/squid.png
index d2af223f0cb..d2af223f0cb 100644
--- a/app/assets/images/emoji/squid.png
+++ b/public/-/emojis/1/squid.png
Binary files differ
diff --git a/app/assets/images/emoji/stadium.png b/public/-/emojis/1/stadium.png
index 00cd6db5e29..00cd6db5e29 100644
--- a/app/assets/images/emoji/stadium.png
+++ b/public/-/emojis/1/stadium.png
Binary files differ
diff --git a/app/assets/images/emoji/star.png b/public/-/emojis/1/star.png
index c930947076e..c930947076e 100644
--- a/app/assets/images/emoji/star.png
+++ b/public/-/emojis/1/star.png
Binary files differ
diff --git a/app/assets/images/emoji/star2.png b/public/-/emojis/1/star2.png
index 2f5cba592db..2f5cba592db 100644
--- a/app/assets/images/emoji/star2.png
+++ b/public/-/emojis/1/star2.png
Binary files differ
diff --git a/app/assets/images/emoji/star_and_crescent.png b/public/-/emojis/1/star_and_crescent.png
index e182636457d..e182636457d 100644
--- a/app/assets/images/emoji/star_and_crescent.png
+++ b/public/-/emojis/1/star_and_crescent.png
Binary files differ
diff --git a/app/assets/images/emoji/star_of_david.png b/public/-/emojis/1/star_of_david.png
index fc59d0dde24..fc59d0dde24 100644
--- a/app/assets/images/emoji/star_of_david.png
+++ b/public/-/emojis/1/star_of_david.png
Binary files differ
diff --git a/app/assets/images/emoji/stars.png b/public/-/emojis/1/stars.png
index aa45384d1c6..aa45384d1c6 100644
--- a/app/assets/images/emoji/stars.png
+++ b/public/-/emojis/1/stars.png
Binary files differ
diff --git a/app/assets/images/emoji/station.png b/public/-/emojis/1/station.png
index 5c26fee529c..5c26fee529c 100644
--- a/app/assets/images/emoji/station.png
+++ b/public/-/emojis/1/station.png
Binary files differ
diff --git a/app/assets/images/emoji/statue_of_liberty.png b/public/-/emojis/1/statue_of_liberty.png
index 05df8289b59..05df8289b59 100644
--- a/app/assets/images/emoji/statue_of_liberty.png
+++ b/public/-/emojis/1/statue_of_liberty.png
Binary files differ
diff --git a/app/assets/images/emoji/steam_locomotive.png b/public/-/emojis/1/steam_locomotive.png
index 9ac0d999c4c..9ac0d999c4c 100644
--- a/app/assets/images/emoji/steam_locomotive.png
+++ b/public/-/emojis/1/steam_locomotive.png
Binary files differ
diff --git a/app/assets/images/emoji/stew.png b/public/-/emojis/1/stew.png
index 6b3f010c17a..6b3f010c17a 100644
--- a/app/assets/images/emoji/stew.png
+++ b/public/-/emojis/1/stew.png
Binary files differ
diff --git a/app/assets/images/emoji/stop_button.png b/public/-/emojis/1/stop_button.png
index cfa99988ac2..cfa99988ac2 100644
--- a/app/assets/images/emoji/stop_button.png
+++ b/public/-/emojis/1/stop_button.png
Binary files differ
diff --git a/app/assets/images/emoji/stopwatch.png b/public/-/emojis/1/stopwatch.png
index 8fae1c9a898..8fae1c9a898 100644
--- a/app/assets/images/emoji/stopwatch.png
+++ b/public/-/emojis/1/stopwatch.png
Binary files differ
diff --git a/app/assets/images/emoji/straight_ruler.png b/public/-/emojis/1/straight_ruler.png
index 1017b7433a1..1017b7433a1 100644
--- a/app/assets/images/emoji/straight_ruler.png
+++ b/public/-/emojis/1/straight_ruler.png
Binary files differ
diff --git a/app/assets/images/emoji/strawberry.png b/public/-/emojis/1/strawberry.png
index 7bb86f0b29c..7bb86f0b29c 100644
--- a/app/assets/images/emoji/strawberry.png
+++ b/public/-/emojis/1/strawberry.png
Binary files differ
diff --git a/app/assets/images/emoji/stuck_out_tongue.png b/public/-/emojis/1/stuck_out_tongue.png
index 25757341f96..25757341f96 100644
--- a/app/assets/images/emoji/stuck_out_tongue.png
+++ b/public/-/emojis/1/stuck_out_tongue.png
Binary files differ
diff --git a/app/assets/images/emoji/stuck_out_tongue_closed_eyes.png b/public/-/emojis/1/stuck_out_tongue_closed_eyes.png
index 5c0401e9b1d..5c0401e9b1d 100644
--- a/app/assets/images/emoji/stuck_out_tongue_closed_eyes.png
+++ b/public/-/emojis/1/stuck_out_tongue_closed_eyes.png
Binary files differ
diff --git a/app/assets/images/emoji/stuck_out_tongue_winking_eye.png b/public/-/emojis/1/stuck_out_tongue_winking_eye.png
index 4817eaa3dc6..4817eaa3dc6 100644
--- a/app/assets/images/emoji/stuck_out_tongue_winking_eye.png
+++ b/public/-/emojis/1/stuck_out_tongue_winking_eye.png
Binary files differ
diff --git a/app/assets/images/emoji/stuffed_flatbread.png b/public/-/emojis/1/stuffed_flatbread.png
index a2e10df40a5..a2e10df40a5 100644
--- a/app/assets/images/emoji/stuffed_flatbread.png
+++ b/public/-/emojis/1/stuffed_flatbread.png
Binary files differ
diff --git a/app/assets/images/emoji/sun_with_face.png b/public/-/emojis/1/sun_with_face.png
index 14a4ea971db..14a4ea971db 100644
--- a/app/assets/images/emoji/sun_with_face.png
+++ b/public/-/emojis/1/sun_with_face.png
Binary files differ
diff --git a/app/assets/images/emoji/sunflower.png b/public/-/emojis/1/sunflower.png
index 08cc07761ea..08cc07761ea 100644
--- a/app/assets/images/emoji/sunflower.png
+++ b/public/-/emojis/1/sunflower.png
Binary files differ
diff --git a/app/assets/images/emoji/sunglasses.png b/public/-/emojis/1/sunglasses.png
index 20011735110..20011735110 100644
--- a/app/assets/images/emoji/sunglasses.png
+++ b/public/-/emojis/1/sunglasses.png
Binary files differ
diff --git a/app/assets/images/emoji/sunny.png b/public/-/emojis/1/sunny.png
index fd521ae31a7..fd521ae31a7 100644
--- a/app/assets/images/emoji/sunny.png
+++ b/public/-/emojis/1/sunny.png
Binary files differ
diff --git a/app/assets/images/emoji/sunrise.png b/public/-/emojis/1/sunrise.png
index 4ad36003c20..4ad36003c20 100644
--- a/app/assets/images/emoji/sunrise.png
+++ b/public/-/emojis/1/sunrise.png
Binary files differ
diff --git a/app/assets/images/emoji/sunrise_over_mountains.png b/public/-/emojis/1/sunrise_over_mountains.png
index 2b99307344d..2b99307344d 100644
--- a/app/assets/images/emoji/sunrise_over_mountains.png
+++ b/public/-/emojis/1/sunrise_over_mountains.png
Binary files differ
diff --git a/app/assets/images/emoji/surfer.png b/public/-/emojis/1/surfer.png
index 3ab017adf4b..3ab017adf4b 100644
--- a/app/assets/images/emoji/surfer.png
+++ b/public/-/emojis/1/surfer.png
Binary files differ
diff --git a/app/assets/images/emoji/surfer_tone1.png b/public/-/emojis/1/surfer_tone1.png
index b5faaa524cc..b5faaa524cc 100644
--- a/app/assets/images/emoji/surfer_tone1.png
+++ b/public/-/emojis/1/surfer_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/surfer_tone2.png b/public/-/emojis/1/surfer_tone2.png
index 6d92e412ff1..6d92e412ff1 100644
--- a/app/assets/images/emoji/surfer_tone2.png
+++ b/public/-/emojis/1/surfer_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/surfer_tone3.png b/public/-/emojis/1/surfer_tone3.png
index f05ef59496e..f05ef59496e 100644
--- a/app/assets/images/emoji/surfer_tone3.png
+++ b/public/-/emojis/1/surfer_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/surfer_tone4.png b/public/-/emojis/1/surfer_tone4.png
index 35e143d19dc..35e143d19dc 100644
--- a/app/assets/images/emoji/surfer_tone4.png
+++ b/public/-/emojis/1/surfer_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/surfer_tone5.png b/public/-/emojis/1/surfer_tone5.png
index 38917658eac..38917658eac 100644
--- a/app/assets/images/emoji/surfer_tone5.png
+++ b/public/-/emojis/1/surfer_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/sushi.png b/public/-/emojis/1/sushi.png
index f171fd2f7a1..f171fd2f7a1 100644
--- a/app/assets/images/emoji/sushi.png
+++ b/public/-/emojis/1/sushi.png
Binary files differ
diff --git a/app/assets/images/emoji/suspension_railway.png b/public/-/emojis/1/suspension_railway.png
index a59d5f48c24..a59d5f48c24 100644
--- a/app/assets/images/emoji/suspension_railway.png
+++ b/public/-/emojis/1/suspension_railway.png
Binary files differ
diff --git a/app/assets/images/emoji/sweat.png b/public/-/emojis/1/sweat.png
index f0dae7b7893..f0dae7b7893 100644
--- a/app/assets/images/emoji/sweat.png
+++ b/public/-/emojis/1/sweat.png
Binary files differ
diff --git a/app/assets/images/emoji/sweat_drops.png b/public/-/emojis/1/sweat_drops.png
index 4106117ebc8..4106117ebc8 100644
--- a/app/assets/images/emoji/sweat_drops.png
+++ b/public/-/emojis/1/sweat_drops.png
Binary files differ
diff --git a/app/assets/images/emoji/sweat_smile.png b/public/-/emojis/1/sweat_smile.png
index cb18d9c899b..cb18d9c899b 100644
--- a/app/assets/images/emoji/sweat_smile.png
+++ b/public/-/emojis/1/sweat_smile.png
Binary files differ
diff --git a/app/assets/images/emoji/sweet_potato.png b/public/-/emojis/1/sweet_potato.png
index 92a425f2e20..92a425f2e20 100644
--- a/app/assets/images/emoji/sweet_potato.png
+++ b/public/-/emojis/1/sweet_potato.png
Binary files differ
diff --git a/app/assets/images/emoji/swimmer.png b/public/-/emojis/1/swimmer.png
index 55b4d72f9a7..55b4d72f9a7 100644
--- a/app/assets/images/emoji/swimmer.png
+++ b/public/-/emojis/1/swimmer.png
Binary files differ
diff --git a/app/assets/images/emoji/swimmer_tone1.png b/public/-/emojis/1/swimmer_tone1.png
index 38441c9ca9a..38441c9ca9a 100644
--- a/app/assets/images/emoji/swimmer_tone1.png
+++ b/public/-/emojis/1/swimmer_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/swimmer_tone2.png b/public/-/emojis/1/swimmer_tone2.png
index b0d43112444..b0d43112444 100644
--- a/app/assets/images/emoji/swimmer_tone2.png
+++ b/public/-/emojis/1/swimmer_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/swimmer_tone3.png b/public/-/emojis/1/swimmer_tone3.png
index 211e77e2aa0..211e77e2aa0 100644
--- a/app/assets/images/emoji/swimmer_tone3.png
+++ b/public/-/emojis/1/swimmer_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/swimmer_tone4.png b/public/-/emojis/1/swimmer_tone4.png
index f34c34db9d2..f34c34db9d2 100644
--- a/app/assets/images/emoji/swimmer_tone4.png
+++ b/public/-/emojis/1/swimmer_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/swimmer_tone5.png b/public/-/emojis/1/swimmer_tone5.png
index 3e9231ff868..3e9231ff868 100644
--- a/app/assets/images/emoji/swimmer_tone5.png
+++ b/public/-/emojis/1/swimmer_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/symbols.png b/public/-/emojis/1/symbols.png
index ac2fc1f358f..ac2fc1f358f 100644
--- a/app/assets/images/emoji/symbols.png
+++ b/public/-/emojis/1/symbols.png
Binary files differ
diff --git a/app/assets/images/emoji/synagogue.png b/public/-/emojis/1/synagogue.png
index ee347904c80..ee347904c80 100644
--- a/app/assets/images/emoji/synagogue.png
+++ b/public/-/emojis/1/synagogue.png
Binary files differ
diff --git a/app/assets/images/emoji/syringe.png b/public/-/emojis/1/syringe.png
index 71c1a9528d5..71c1a9528d5 100644
--- a/app/assets/images/emoji/syringe.png
+++ b/public/-/emojis/1/syringe.png
Binary files differ
diff --git a/app/assets/images/emoji/taco.png b/public/-/emojis/1/taco.png
index 10e847a4619..10e847a4619 100644
--- a/app/assets/images/emoji/taco.png
+++ b/public/-/emojis/1/taco.png
Binary files differ
diff --git a/app/assets/images/emoji/tada.png b/public/-/emojis/1/tada.png
index 0244d60f269..0244d60f269 100644
--- a/app/assets/images/emoji/tada.png
+++ b/public/-/emojis/1/tada.png
Binary files differ
diff --git a/app/assets/images/emoji/tanabata_tree.png b/public/-/emojis/1/tanabata_tree.png
index 46fcb3a1aac..46fcb3a1aac 100644
--- a/app/assets/images/emoji/tanabata_tree.png
+++ b/public/-/emojis/1/tanabata_tree.png
Binary files differ
diff --git a/app/assets/images/emoji/tangerine.png b/public/-/emojis/1/tangerine.png
index ab14e5378db..ab14e5378db 100644
--- a/app/assets/images/emoji/tangerine.png
+++ b/public/-/emojis/1/tangerine.png
Binary files differ
diff --git a/app/assets/images/emoji/taurus.png b/public/-/emojis/1/taurus.png
index b2a370df42b..b2a370df42b 100644
--- a/app/assets/images/emoji/taurus.png
+++ b/public/-/emojis/1/taurus.png
Binary files differ
diff --git a/app/assets/images/emoji/taxi.png b/public/-/emojis/1/taxi.png
index 55f4cc84797..55f4cc84797 100644
--- a/app/assets/images/emoji/taxi.png
+++ b/public/-/emojis/1/taxi.png
Binary files differ
diff --git a/app/assets/images/emoji/tea.png b/public/-/emojis/1/tea.png
index b53b98f0c45..b53b98f0c45 100644
--- a/app/assets/images/emoji/tea.png
+++ b/public/-/emojis/1/tea.png
Binary files differ
diff --git a/app/assets/images/emoji/telephone.png b/public/-/emojis/1/telephone.png
index a1e69f566bc..a1e69f566bc 100644
--- a/app/assets/images/emoji/telephone.png
+++ b/public/-/emojis/1/telephone.png
Binary files differ
diff --git a/app/assets/images/emoji/telephone_receiver.png b/public/-/emojis/1/telephone_receiver.png
index 69388316c35..69388316c35 100644
--- a/app/assets/images/emoji/telephone_receiver.png
+++ b/public/-/emojis/1/telephone_receiver.png
Binary files differ
diff --git a/app/assets/images/emoji/telescope.png b/public/-/emojis/1/telescope.png
index d63154614b5..d63154614b5 100644
--- a/app/assets/images/emoji/telescope.png
+++ b/public/-/emojis/1/telescope.png
Binary files differ
diff --git a/app/assets/images/emoji/ten.png b/public/-/emojis/1/ten.png
index 782d4004962..782d4004962 100644
--- a/app/assets/images/emoji/ten.png
+++ b/public/-/emojis/1/ten.png
Binary files differ
diff --git a/app/assets/images/emoji/tennis.png b/public/-/emojis/1/tennis.png
index 7e68ba8f301..7e68ba8f301 100644
--- a/app/assets/images/emoji/tennis.png
+++ b/public/-/emojis/1/tennis.png
Binary files differ
diff --git a/app/assets/images/emoji/tent.png b/public/-/emojis/1/tent.png
index 3fddcfc56eb..3fddcfc56eb 100644
--- a/app/assets/images/emoji/tent.png
+++ b/public/-/emojis/1/tent.png
Binary files differ
diff --git a/app/assets/images/emoji/thermometer.png b/public/-/emojis/1/thermometer.png
index b1147392426..b1147392426 100644
--- a/app/assets/images/emoji/thermometer.png
+++ b/public/-/emojis/1/thermometer.png
Binary files differ
diff --git a/app/assets/images/emoji/thermometer_face.png b/public/-/emojis/1/thermometer_face.png
index 8fc57387563..8fc57387563 100644
--- a/app/assets/images/emoji/thermometer_face.png
+++ b/public/-/emojis/1/thermometer_face.png
Binary files differ
diff --git a/app/assets/images/emoji/thinking.png b/public/-/emojis/1/thinking.png
index c18f6fd14ad..c18f6fd14ad 100644
--- a/app/assets/images/emoji/thinking.png
+++ b/public/-/emojis/1/thinking.png
Binary files differ
diff --git a/app/assets/images/emoji/third_place.png b/public/-/emojis/1/third_place.png
index 636e04a5950..636e04a5950 100644
--- a/app/assets/images/emoji/third_place.png
+++ b/public/-/emojis/1/third_place.png
Binary files differ
diff --git a/app/assets/images/emoji/thought_balloon.png b/public/-/emojis/1/thought_balloon.png
index 72fe8fa7022..72fe8fa7022 100644
--- a/app/assets/images/emoji/thought_balloon.png
+++ b/public/-/emojis/1/thought_balloon.png
Binary files differ
diff --git a/app/assets/images/emoji/three.png b/public/-/emojis/1/three.png
index dbaa6183e72..dbaa6183e72 100644
--- a/app/assets/images/emoji/three.png
+++ b/public/-/emojis/1/three.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsdown.png b/public/-/emojis/1/thumbsdown.png
index b63da2f20a8..b63da2f20a8 100644
--- a/app/assets/images/emoji/thumbsdown.png
+++ b/public/-/emojis/1/thumbsdown.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsdown_tone1.png b/public/-/emojis/1/thumbsdown_tone1.png
index a1631af8e92..a1631af8e92 100644
--- a/app/assets/images/emoji/thumbsdown_tone1.png
+++ b/public/-/emojis/1/thumbsdown_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsdown_tone2.png b/public/-/emojis/1/thumbsdown_tone2.png
index 85fff82d595..85fff82d595 100644
--- a/app/assets/images/emoji/thumbsdown_tone2.png
+++ b/public/-/emojis/1/thumbsdown_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsdown_tone3.png b/public/-/emojis/1/thumbsdown_tone3.png
index eeba3be80fd..eeba3be80fd 100644
--- a/app/assets/images/emoji/thumbsdown_tone3.png
+++ b/public/-/emojis/1/thumbsdown_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsdown_tone4.png b/public/-/emojis/1/thumbsdown_tone4.png
index 1addafdaed0..1addafdaed0 100644
--- a/app/assets/images/emoji/thumbsdown_tone4.png
+++ b/public/-/emojis/1/thumbsdown_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsdown_tone5.png b/public/-/emojis/1/thumbsdown_tone5.png
index 37ec07b5721..37ec07b5721 100644
--- a/app/assets/images/emoji/thumbsdown_tone5.png
+++ b/public/-/emojis/1/thumbsdown_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsup.png b/public/-/emojis/1/thumbsup.png
index f9e6f13a34f..f9e6f13a34f 100644
--- a/app/assets/images/emoji/thumbsup.png
+++ b/public/-/emojis/1/thumbsup.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsup_tone1.png b/public/-/emojis/1/thumbsup_tone1.png
index 39684cd5cc7..39684cd5cc7 100644
--- a/app/assets/images/emoji/thumbsup_tone1.png
+++ b/public/-/emojis/1/thumbsup_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsup_tone2.png b/public/-/emojis/1/thumbsup_tone2.png
index a9b59723573..a9b59723573 100644
--- a/app/assets/images/emoji/thumbsup_tone2.png
+++ b/public/-/emojis/1/thumbsup_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsup_tone3.png b/public/-/emojis/1/thumbsup_tone3.png
index c5e29167015..c5e29167015 100644
--- a/app/assets/images/emoji/thumbsup_tone3.png
+++ b/public/-/emojis/1/thumbsup_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsup_tone4.png b/public/-/emojis/1/thumbsup_tone4.png
index 5bf4857a884..5bf4857a884 100644
--- a/app/assets/images/emoji/thumbsup_tone4.png
+++ b/public/-/emojis/1/thumbsup_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/thumbsup_tone5.png b/public/-/emojis/1/thumbsup_tone5.png
index d829f787c61..d829f787c61 100644
--- a/app/assets/images/emoji/thumbsup_tone5.png
+++ b/public/-/emojis/1/thumbsup_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/thunder_cloud_rain.png b/public/-/emojis/1/thunder_cloud_rain.png
index 31a26a1b6ee..31a26a1b6ee 100644
--- a/app/assets/images/emoji/thunder_cloud_rain.png
+++ b/public/-/emojis/1/thunder_cloud_rain.png
Binary files differ
diff --git a/app/assets/images/emoji/ticket.png b/public/-/emojis/1/ticket.png
index 605936bb6b3..605936bb6b3 100644
--- a/app/assets/images/emoji/ticket.png
+++ b/public/-/emojis/1/ticket.png
Binary files differ
diff --git a/app/assets/images/emoji/tickets.png b/public/-/emojis/1/tickets.png
index e510f4a7a50..e510f4a7a50 100644
--- a/app/assets/images/emoji/tickets.png
+++ b/public/-/emojis/1/tickets.png
Binary files differ
diff --git a/app/assets/images/emoji/tiger.png b/public/-/emojis/1/tiger.png
index a4d3ef086d4..a4d3ef086d4 100644
--- a/app/assets/images/emoji/tiger.png
+++ b/public/-/emojis/1/tiger.png
Binary files differ
diff --git a/app/assets/images/emoji/tiger2.png b/public/-/emojis/1/tiger2.png
index 871a8b74d56..871a8b74d56 100644
--- a/app/assets/images/emoji/tiger2.png
+++ b/public/-/emojis/1/tiger2.png
Binary files differ
diff --git a/app/assets/images/emoji/timer.png b/public/-/emojis/1/timer.png
index 8a3be574c24..8a3be574c24 100644
--- a/app/assets/images/emoji/timer.png
+++ b/public/-/emojis/1/timer.png
Binary files differ
diff --git a/app/assets/images/emoji/tired_face.png b/public/-/emojis/1/tired_face.png
index 4e01eff5b23..4e01eff5b23 100644
--- a/app/assets/images/emoji/tired_face.png
+++ b/public/-/emojis/1/tired_face.png
Binary files differ
diff --git a/app/assets/images/emoji/tm.png b/public/-/emojis/1/tm.png
index 7a0c44a2c2b..7a0c44a2c2b 100644
--- a/app/assets/images/emoji/tm.png
+++ b/public/-/emojis/1/tm.png
Binary files differ
diff --git a/app/assets/images/emoji/toilet.png b/public/-/emojis/1/toilet.png
index 1392f761835..1392f761835 100644
--- a/app/assets/images/emoji/toilet.png
+++ b/public/-/emojis/1/toilet.png
Binary files differ
diff --git a/app/assets/images/emoji/tokyo_tower.png b/public/-/emojis/1/tokyo_tower.png
index 37df7fc65b1..37df7fc65b1 100644
--- a/app/assets/images/emoji/tokyo_tower.png
+++ b/public/-/emojis/1/tokyo_tower.png
Binary files differ
diff --git a/app/assets/images/emoji/tomato.png b/public/-/emojis/1/tomato.png
index 497da8f6b22..497da8f6b22 100644
--- a/app/assets/images/emoji/tomato.png
+++ b/public/-/emojis/1/tomato.png
Binary files differ
diff --git a/app/assets/images/emoji/tone1.png b/public/-/emojis/1/tone1.png
index c395f3d0d68..c395f3d0d68 100644
--- a/app/assets/images/emoji/tone1.png
+++ b/public/-/emojis/1/tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/tone2.png b/public/-/emojis/1/tone2.png
index 080847431c1..080847431c1 100644
--- a/app/assets/images/emoji/tone2.png
+++ b/public/-/emojis/1/tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/tone3.png b/public/-/emojis/1/tone3.png
index 482dd403475..482dd403475 100644
--- a/app/assets/images/emoji/tone3.png
+++ b/public/-/emojis/1/tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/tone4.png b/public/-/emojis/1/tone4.png
index 5cae8bb20b0..5cae8bb20b0 100644
--- a/app/assets/images/emoji/tone4.png
+++ b/public/-/emojis/1/tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/tone5.png b/public/-/emojis/1/tone5.png
index 49d1a8c3a64..49d1a8c3a64 100644
--- a/app/assets/images/emoji/tone5.png
+++ b/public/-/emojis/1/tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/tongue.png b/public/-/emojis/1/tongue.png
index 70ce9c1225f..70ce9c1225f 100644
--- a/app/assets/images/emoji/tongue.png
+++ b/public/-/emojis/1/tongue.png
Binary files differ
diff --git a/app/assets/images/emoji/tools.png b/public/-/emojis/1/tools.png
index 3c6049273a9..3c6049273a9 100644
--- a/app/assets/images/emoji/tools.png
+++ b/public/-/emojis/1/tools.png
Binary files differ
diff --git a/app/assets/images/emoji/top.png b/public/-/emojis/1/top.png
index 49dea8c08b5..49dea8c08b5 100644
--- a/app/assets/images/emoji/top.png
+++ b/public/-/emojis/1/top.png
Binary files differ
diff --git a/app/assets/images/emoji/tophat.png b/public/-/emojis/1/tophat.png
index 131b657b109..131b657b109 100644
--- a/app/assets/images/emoji/tophat.png
+++ b/public/-/emojis/1/tophat.png
Binary files differ
diff --git a/app/assets/images/emoji/track_next.png b/public/-/emojis/1/track_next.png
index f8880d33bab..f8880d33bab 100644
--- a/app/assets/images/emoji/track_next.png
+++ b/public/-/emojis/1/track_next.png
Binary files differ
diff --git a/app/assets/images/emoji/track_previous.png b/public/-/emojis/1/track_previous.png
index 1ffd0566cfc..1ffd0566cfc 100644
--- a/app/assets/images/emoji/track_previous.png
+++ b/public/-/emojis/1/track_previous.png
Binary files differ
diff --git a/app/assets/images/emoji/trackball.png b/public/-/emojis/1/trackball.png
index 3bea84ad7ce..3bea84ad7ce 100644
--- a/app/assets/images/emoji/trackball.png
+++ b/public/-/emojis/1/trackball.png
Binary files differ
diff --git a/app/assets/images/emoji/tractor.png b/public/-/emojis/1/tractor.png
index c1bf8cae44f..c1bf8cae44f 100644
--- a/app/assets/images/emoji/tractor.png
+++ b/public/-/emojis/1/tractor.png
Binary files differ
diff --git a/app/assets/images/emoji/traffic_light.png b/public/-/emojis/1/traffic_light.png
index 6b312285b00..6b312285b00 100644
--- a/app/assets/images/emoji/traffic_light.png
+++ b/public/-/emojis/1/traffic_light.png
Binary files differ
diff --git a/app/assets/images/emoji/train.png b/public/-/emojis/1/train.png
index 3c80321f7e8..3c80321f7e8 100644
--- a/app/assets/images/emoji/train.png
+++ b/public/-/emojis/1/train.png
Binary files differ
diff --git a/app/assets/images/emoji/train2.png b/public/-/emojis/1/train2.png
index 367c7bc5d39..367c7bc5d39 100644
--- a/app/assets/images/emoji/train2.png
+++ b/public/-/emojis/1/train2.png
Binary files differ
diff --git a/app/assets/images/emoji/tram.png b/public/-/emojis/1/tram.png
index b6f0e69038f..b6f0e69038f 100644
--- a/app/assets/images/emoji/tram.png
+++ b/public/-/emojis/1/tram.png
Binary files differ
diff --git a/app/assets/images/emoji/triangular_flag_on_post.png b/public/-/emojis/1/triangular_flag_on_post.png
index c12d8b06886..c12d8b06886 100644
--- a/app/assets/images/emoji/triangular_flag_on_post.png
+++ b/public/-/emojis/1/triangular_flag_on_post.png
Binary files differ
diff --git a/app/assets/images/emoji/triangular_ruler.png b/public/-/emojis/1/triangular_ruler.png
index 77dee9ee843..77dee9ee843 100644
--- a/app/assets/images/emoji/triangular_ruler.png
+++ b/public/-/emojis/1/triangular_ruler.png
Binary files differ
diff --git a/app/assets/images/emoji/trident.png b/public/-/emojis/1/trident.png
index 777a1dad121..777a1dad121 100644
--- a/app/assets/images/emoji/trident.png
+++ b/public/-/emojis/1/trident.png
Binary files differ
diff --git a/app/assets/images/emoji/triumph.png b/public/-/emojis/1/triumph.png
index 0be7a501969..0be7a501969 100644
--- a/app/assets/images/emoji/triumph.png
+++ b/public/-/emojis/1/triumph.png
Binary files differ
diff --git a/app/assets/images/emoji/trolleybus.png b/public/-/emojis/1/trolleybus.png
index 139a9931b52..139a9931b52 100644
--- a/app/assets/images/emoji/trolleybus.png
+++ b/public/-/emojis/1/trolleybus.png
Binary files differ
diff --git a/app/assets/images/emoji/trophy.png b/public/-/emojis/1/trophy.png
index ac2895c1896..ac2895c1896 100644
--- a/app/assets/images/emoji/trophy.png
+++ b/public/-/emojis/1/trophy.png
Binary files differ
diff --git a/app/assets/images/emoji/tropical_drink.png b/public/-/emojis/1/tropical_drink.png
index cd714f81b36..cd714f81b36 100644
--- a/app/assets/images/emoji/tropical_drink.png
+++ b/public/-/emojis/1/tropical_drink.png
Binary files differ
diff --git a/app/assets/images/emoji/tropical_fish.png b/public/-/emojis/1/tropical_fish.png
index 252105235a6..252105235a6 100644
--- a/app/assets/images/emoji/tropical_fish.png
+++ b/public/-/emojis/1/tropical_fish.png
Binary files differ
diff --git a/app/assets/images/emoji/truck.png b/public/-/emojis/1/truck.png
index 130de047f8b..130de047f8b 100644
--- a/app/assets/images/emoji/truck.png
+++ b/public/-/emojis/1/truck.png
Binary files differ
diff --git a/app/assets/images/emoji/trumpet.png b/public/-/emojis/1/trumpet.png
index 864ccbcd04a..864ccbcd04a 100644
--- a/app/assets/images/emoji/trumpet.png
+++ b/public/-/emojis/1/trumpet.png
Binary files differ
diff --git a/app/assets/images/emoji/tulip.png b/public/-/emojis/1/tulip.png
index f799d75c182..f799d75c182 100644
--- a/app/assets/images/emoji/tulip.png
+++ b/public/-/emojis/1/tulip.png
Binary files differ
diff --git a/app/assets/images/emoji/tumbler_glass.png b/public/-/emojis/1/tumbler_glass.png
index 7bf09229879..7bf09229879 100644
--- a/app/assets/images/emoji/tumbler_glass.png
+++ b/public/-/emojis/1/tumbler_glass.png
Binary files differ
diff --git a/app/assets/images/emoji/turkey.png b/public/-/emojis/1/turkey.png
index 344af94c9ec..344af94c9ec 100644
--- a/app/assets/images/emoji/turkey.png
+++ b/public/-/emojis/1/turkey.png
Binary files differ
diff --git a/app/assets/images/emoji/turtle.png b/public/-/emojis/1/turtle.png
index c22f7519fe8..c22f7519fe8 100644
--- a/app/assets/images/emoji/turtle.png
+++ b/public/-/emojis/1/turtle.png
Binary files differ
diff --git a/app/assets/images/emoji/tv.png b/public/-/emojis/1/tv.png
index 999f1fb5c6d..999f1fb5c6d 100644
--- a/app/assets/images/emoji/tv.png
+++ b/public/-/emojis/1/tv.png
Binary files differ
diff --git a/app/assets/images/emoji/twisted_rightwards_arrows.png b/public/-/emojis/1/twisted_rightwards_arrows.png
index 5904badde65..5904badde65 100644
--- a/app/assets/images/emoji/twisted_rightwards_arrows.png
+++ b/public/-/emojis/1/twisted_rightwards_arrows.png
Binary files differ
diff --git a/app/assets/images/emoji/two.png b/public/-/emojis/1/two.png
index 927339c9bff..927339c9bff 100644
--- a/app/assets/images/emoji/two.png
+++ b/public/-/emojis/1/two.png
Binary files differ
diff --git a/app/assets/images/emoji/two_hearts.png b/public/-/emojis/1/two_hearts.png
index 4d8c3386042..4d8c3386042 100644
--- a/app/assets/images/emoji/two_hearts.png
+++ b/public/-/emojis/1/two_hearts.png
Binary files differ
diff --git a/app/assets/images/emoji/two_men_holding_hands.png b/public/-/emojis/1/two_men_holding_hands.png
index a511fda822a..a511fda822a 100644
--- a/app/assets/images/emoji/two_men_holding_hands.png
+++ b/public/-/emojis/1/two_men_holding_hands.png
Binary files differ
diff --git a/app/assets/images/emoji/two_women_holding_hands.png b/public/-/emojis/1/two_women_holding_hands.png
index b077cd3e40f..b077cd3e40f 100644
--- a/app/assets/images/emoji/two_women_holding_hands.png
+++ b/public/-/emojis/1/two_women_holding_hands.png
Binary files differ
diff --git a/app/assets/images/emoji/u5272.png b/public/-/emojis/1/u5272.png
index c4f837fe684..c4f837fe684 100644
--- a/app/assets/images/emoji/u5272.png
+++ b/public/-/emojis/1/u5272.png
Binary files differ
diff --git a/app/assets/images/emoji/u5408.png b/public/-/emojis/1/u5408.png
index 8375ad9d9af..8375ad9d9af 100644
--- a/app/assets/images/emoji/u5408.png
+++ b/public/-/emojis/1/u5408.png
Binary files differ
diff --git a/app/assets/images/emoji/u55b6.png b/public/-/emojis/1/u55b6.png
index d21cb30eaf3..d21cb30eaf3 100644
--- a/app/assets/images/emoji/u55b6.png
+++ b/public/-/emojis/1/u55b6.png
Binary files differ
diff --git a/app/assets/images/emoji/u6307.png b/public/-/emojis/1/u6307.png
index 078e23e4ff3..078e23e4ff3 100644
--- a/app/assets/images/emoji/u6307.png
+++ b/public/-/emojis/1/u6307.png
Binary files differ
diff --git a/app/assets/images/emoji/u6708.png b/public/-/emojis/1/u6708.png
index c41bd36a26a..c41bd36a26a 100644
--- a/app/assets/images/emoji/u6708.png
+++ b/public/-/emojis/1/u6708.png
Binary files differ
diff --git a/app/assets/images/emoji/u6709.png b/public/-/emojis/1/u6709.png
index a4510de41c0..a4510de41c0 100644
--- a/app/assets/images/emoji/u6709.png
+++ b/public/-/emojis/1/u6709.png
Binary files differ
diff --git a/app/assets/images/emoji/u6e80.png b/public/-/emojis/1/u6e80.png
index f9dea8b8833..f9dea8b8833 100644
--- a/app/assets/images/emoji/u6e80.png
+++ b/public/-/emojis/1/u6e80.png
Binary files differ
diff --git a/app/assets/images/emoji/u7121.png b/public/-/emojis/1/u7121.png
index d3a19b420de..d3a19b420de 100644
--- a/app/assets/images/emoji/u7121.png
+++ b/public/-/emojis/1/u7121.png
Binary files differ
diff --git a/app/assets/images/emoji/u7533.png b/public/-/emojis/1/u7533.png
index 6b7af0ee222..6b7af0ee222 100644
--- a/app/assets/images/emoji/u7533.png
+++ b/public/-/emojis/1/u7533.png
Binary files differ
diff --git a/app/assets/images/emoji/u7981.png b/public/-/emojis/1/u7981.png
index 4c704e03433..4c704e03433 100644
--- a/app/assets/images/emoji/u7981.png
+++ b/public/-/emojis/1/u7981.png
Binary files differ
diff --git a/app/assets/images/emoji/u7a7a.png b/public/-/emojis/1/u7a7a.png
index 47966c1ea93..47966c1ea93 100644
--- a/app/assets/images/emoji/u7a7a.png
+++ b/public/-/emojis/1/u7a7a.png
Binary files differ
diff --git a/app/assets/images/emoji/umbrella.png b/public/-/emojis/1/umbrella.png
index 5b35b7ff6a4..5b35b7ff6a4 100644
--- a/app/assets/images/emoji/umbrella.png
+++ b/public/-/emojis/1/umbrella.png
Binary files differ
diff --git a/app/assets/images/emoji/umbrella2.png b/public/-/emojis/1/umbrella2.png
index 97fe859e74f..97fe859e74f 100644
--- a/app/assets/images/emoji/umbrella2.png
+++ b/public/-/emojis/1/umbrella2.png
Binary files differ
diff --git a/app/assets/images/emoji/unamused.png b/public/-/emojis/1/unamused.png
index 25e3677f2eb..25e3677f2eb 100644
--- a/app/assets/images/emoji/unamused.png
+++ b/public/-/emojis/1/unamused.png
Binary files differ
diff --git a/app/assets/images/emoji/underage.png b/public/-/emojis/1/underage.png
index 6dfe6da51e2..6dfe6da51e2 100644
--- a/app/assets/images/emoji/underage.png
+++ b/public/-/emojis/1/underage.png
Binary files differ
diff --git a/app/assets/images/emoji/unicorn.png b/public/-/emojis/1/unicorn.png
index 05a97969f7e..05a97969f7e 100644
--- a/app/assets/images/emoji/unicorn.png
+++ b/public/-/emojis/1/unicorn.png
Binary files differ
diff --git a/app/assets/images/emoji/unlock.png b/public/-/emojis/1/unlock.png
index 4a74a693911..4a74a693911 100644
--- a/app/assets/images/emoji/unlock.png
+++ b/public/-/emojis/1/unlock.png
Binary files differ
diff --git a/app/assets/images/emoji/up.png b/public/-/emojis/1/up.png
index 0d42142ba04..0d42142ba04 100644
--- a/app/assets/images/emoji/up.png
+++ b/public/-/emojis/1/up.png
Binary files differ
diff --git a/app/assets/images/emoji/upside_down.png b/public/-/emojis/1/upside_down.png
index 128f31c9828..128f31c9828 100644
--- a/app/assets/images/emoji/upside_down.png
+++ b/public/-/emojis/1/upside_down.png
Binary files differ
diff --git a/app/assets/images/emoji/urn.png b/public/-/emojis/1/urn.png
index 6b5b3503438..6b5b3503438 100644
--- a/app/assets/images/emoji/urn.png
+++ b/public/-/emojis/1/urn.png
Binary files differ
diff --git a/app/assets/images/emoji/v.png b/public/-/emojis/1/v.png
index 70c5516ffee..70c5516ffee 100644
--- a/app/assets/images/emoji/v.png
+++ b/public/-/emojis/1/v.png
Binary files differ
diff --git a/app/assets/images/emoji/v_tone1.png b/public/-/emojis/1/v_tone1.png
index 6ac54a745f4..6ac54a745f4 100644
--- a/app/assets/images/emoji/v_tone1.png
+++ b/public/-/emojis/1/v_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/v_tone2.png b/public/-/emojis/1/v_tone2.png
index 6dd9669866d..6dd9669866d 100644
--- a/app/assets/images/emoji/v_tone2.png
+++ b/public/-/emojis/1/v_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/v_tone3.png b/public/-/emojis/1/v_tone3.png
index a615e53f02f..a615e53f02f 100644
--- a/app/assets/images/emoji/v_tone3.png
+++ b/public/-/emojis/1/v_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/v_tone4.png b/public/-/emojis/1/v_tone4.png
index 33a34bd5a78..33a34bd5a78 100644
--- a/app/assets/images/emoji/v_tone4.png
+++ b/public/-/emojis/1/v_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/v_tone5.png b/public/-/emojis/1/v_tone5.png
index 45ad14b6c9c..45ad14b6c9c 100644
--- a/app/assets/images/emoji/v_tone5.png
+++ b/public/-/emojis/1/v_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/vertical_traffic_light.png b/public/-/emojis/1/vertical_traffic_light.png
index 8085973eecf..8085973eecf 100644
--- a/app/assets/images/emoji/vertical_traffic_light.png
+++ b/public/-/emojis/1/vertical_traffic_light.png
Binary files differ
diff --git a/app/assets/images/emoji/vhs.png b/public/-/emojis/1/vhs.png
index b9eb78ecd92..b9eb78ecd92 100644
--- a/app/assets/images/emoji/vhs.png
+++ b/public/-/emojis/1/vhs.png
Binary files differ
diff --git a/app/assets/images/emoji/vibration_mode.png b/public/-/emojis/1/vibration_mode.png
index cc46510e48e..cc46510e48e 100644
--- a/app/assets/images/emoji/vibration_mode.png
+++ b/public/-/emojis/1/vibration_mode.png
Binary files differ
diff --git a/app/assets/images/emoji/video_camera.png b/public/-/emojis/1/video_camera.png
index 85b300d425c..85b300d425c 100644
--- a/app/assets/images/emoji/video_camera.png
+++ b/public/-/emojis/1/video_camera.png
Binary files differ
diff --git a/app/assets/images/emoji/video_game.png b/public/-/emojis/1/video_game.png
index 316a9106a55..316a9106a55 100644
--- a/app/assets/images/emoji/video_game.png
+++ b/public/-/emojis/1/video_game.png
Binary files differ
diff --git a/app/assets/images/emoji/violin.png b/public/-/emojis/1/violin.png
index e1e76cce242..e1e76cce242 100644
--- a/app/assets/images/emoji/violin.png
+++ b/public/-/emojis/1/violin.png
Binary files differ
diff --git a/app/assets/images/emoji/virgo.png b/public/-/emojis/1/virgo.png
index a6b56c2cb5e..a6b56c2cb5e 100644
--- a/app/assets/images/emoji/virgo.png
+++ b/public/-/emojis/1/virgo.png
Binary files differ
diff --git a/app/assets/images/emoji/volcano.png b/public/-/emojis/1/volcano.png
index 931d569294c..931d569294c 100644
--- a/app/assets/images/emoji/volcano.png
+++ b/public/-/emojis/1/volcano.png
Binary files differ
diff --git a/app/assets/images/emoji/volleyball.png b/public/-/emojis/1/volleyball.png
index 7a0e49d4b07..7a0e49d4b07 100644
--- a/app/assets/images/emoji/volleyball.png
+++ b/public/-/emojis/1/volleyball.png
Binary files differ
diff --git a/app/assets/images/emoji/vs.png b/public/-/emojis/1/vs.png
index e1180f4a464..e1180f4a464 100644
--- a/app/assets/images/emoji/vs.png
+++ b/public/-/emojis/1/vs.png
Binary files differ
diff --git a/app/assets/images/emoji/vulcan.png b/public/-/emojis/1/vulcan.png
index 54728bcaf5c..54728bcaf5c 100644
--- a/app/assets/images/emoji/vulcan.png
+++ b/public/-/emojis/1/vulcan.png
Binary files differ
diff --git a/app/assets/images/emoji/vulcan_tone1.png b/public/-/emojis/1/vulcan_tone1.png
index 8aff5d8fa16..8aff5d8fa16 100644
--- a/app/assets/images/emoji/vulcan_tone1.png
+++ b/public/-/emojis/1/vulcan_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/vulcan_tone2.png b/public/-/emojis/1/vulcan_tone2.png
index 82b7ad519b4..82b7ad519b4 100644
--- a/app/assets/images/emoji/vulcan_tone2.png
+++ b/public/-/emojis/1/vulcan_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/vulcan_tone3.png b/public/-/emojis/1/vulcan_tone3.png
index d1400e1dd28..d1400e1dd28 100644
--- a/app/assets/images/emoji/vulcan_tone3.png
+++ b/public/-/emojis/1/vulcan_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/vulcan_tone4.png b/public/-/emojis/1/vulcan_tone4.png
index 47e2b280148..47e2b280148 100644
--- a/app/assets/images/emoji/vulcan_tone4.png
+++ b/public/-/emojis/1/vulcan_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/vulcan_tone5.png b/public/-/emojis/1/vulcan_tone5.png
index 60b5c6077be..60b5c6077be 100644
--- a/app/assets/images/emoji/vulcan_tone5.png
+++ b/public/-/emojis/1/vulcan_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/walking.png b/public/-/emojis/1/walking.png
index 06dc169a3fd..06dc169a3fd 100644
--- a/app/assets/images/emoji/walking.png
+++ b/public/-/emojis/1/walking.png
Binary files differ
diff --git a/app/assets/images/emoji/walking_tone1.png b/public/-/emojis/1/walking_tone1.png
index 4e391b45a0b..4e391b45a0b 100644
--- a/app/assets/images/emoji/walking_tone1.png
+++ b/public/-/emojis/1/walking_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/walking_tone2.png b/public/-/emojis/1/walking_tone2.png
index 31f94a1bce1..31f94a1bce1 100644
--- a/app/assets/images/emoji/walking_tone2.png
+++ b/public/-/emojis/1/walking_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/walking_tone3.png b/public/-/emojis/1/walking_tone3.png
index f7ed8e39c2e..f7ed8e39c2e 100644
--- a/app/assets/images/emoji/walking_tone3.png
+++ b/public/-/emojis/1/walking_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/walking_tone4.png b/public/-/emojis/1/walking_tone4.png
index e58dc04c7b2..e58dc04c7b2 100644
--- a/app/assets/images/emoji/walking_tone4.png
+++ b/public/-/emojis/1/walking_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/walking_tone5.png b/public/-/emojis/1/walking_tone5.png
index ba4e1b58fcb..ba4e1b58fcb 100644
--- a/app/assets/images/emoji/walking_tone5.png
+++ b/public/-/emojis/1/walking_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/waning_crescent_moon.png b/public/-/emojis/1/waning_crescent_moon.png
index cf68706b871..cf68706b871 100644
--- a/app/assets/images/emoji/waning_crescent_moon.png
+++ b/public/-/emojis/1/waning_crescent_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/waning_gibbous_moon.png b/public/-/emojis/1/waning_gibbous_moon.png
index 24e16266119..24e16266119 100644
--- a/app/assets/images/emoji/waning_gibbous_moon.png
+++ b/public/-/emojis/1/waning_gibbous_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/warning.png b/public/-/emojis/1/warning.png
index 35691c2ed97..35691c2ed97 100644
--- a/app/assets/images/emoji/warning.png
+++ b/public/-/emojis/1/warning.png
Binary files differ
diff --git a/app/assets/images/emoji/wastebasket.png b/public/-/emojis/1/wastebasket.png
index 2b3c484b498..2b3c484b498 100644
--- a/app/assets/images/emoji/wastebasket.png
+++ b/public/-/emojis/1/wastebasket.png
Binary files differ
diff --git a/app/assets/images/emoji/watch.png b/public/-/emojis/1/watch.png
index 64819bc6e21..64819bc6e21 100644
--- a/app/assets/images/emoji/watch.png
+++ b/public/-/emojis/1/watch.png
Binary files differ
diff --git a/app/assets/images/emoji/water_buffalo.png b/public/-/emojis/1/water_buffalo.png
index 80446615caf..80446615caf 100644
--- a/app/assets/images/emoji/water_buffalo.png
+++ b/public/-/emojis/1/water_buffalo.png
Binary files differ
diff --git a/app/assets/images/emoji/water_polo.png b/public/-/emojis/1/water_polo.png
index cb44576780d..cb44576780d 100644
--- a/app/assets/images/emoji/water_polo.png
+++ b/public/-/emojis/1/water_polo.png
Binary files differ
diff --git a/app/assets/images/emoji/water_polo_tone1.png b/public/-/emojis/1/water_polo_tone1.png
index bed1a908d6a..bed1a908d6a 100644
--- a/app/assets/images/emoji/water_polo_tone1.png
+++ b/public/-/emojis/1/water_polo_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/water_polo_tone2.png b/public/-/emojis/1/water_polo_tone2.png
index ec5a43b4d4a..ec5a43b4d4a 100644
--- a/app/assets/images/emoji/water_polo_tone2.png
+++ b/public/-/emojis/1/water_polo_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/water_polo_tone3.png b/public/-/emojis/1/water_polo_tone3.png
index b081a4a5a96..b081a4a5a96 100644
--- a/app/assets/images/emoji/water_polo_tone3.png
+++ b/public/-/emojis/1/water_polo_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/water_polo_tone4.png b/public/-/emojis/1/water_polo_tone4.png
index 82cfbc3b0c7..82cfbc3b0c7 100644
--- a/app/assets/images/emoji/water_polo_tone4.png
+++ b/public/-/emojis/1/water_polo_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/water_polo_tone5.png b/public/-/emojis/1/water_polo_tone5.png
index bd3366eb06c..bd3366eb06c 100644
--- a/app/assets/images/emoji/water_polo_tone5.png
+++ b/public/-/emojis/1/water_polo_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/watermelon.png b/public/-/emojis/1/watermelon.png
index 0761488b4c9..0761488b4c9 100644
--- a/app/assets/images/emoji/watermelon.png
+++ b/public/-/emojis/1/watermelon.png
Binary files differ
diff --git a/app/assets/images/emoji/wave.png b/public/-/emojis/1/wave.png
index e0cd79b45f5..e0cd79b45f5 100644
--- a/app/assets/images/emoji/wave.png
+++ b/public/-/emojis/1/wave.png
Binary files differ
diff --git a/app/assets/images/emoji/wave_tone1.png b/public/-/emojis/1/wave_tone1.png
index 6b2b34b106e..6b2b34b106e 100644
--- a/app/assets/images/emoji/wave_tone1.png
+++ b/public/-/emojis/1/wave_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/wave_tone2.png b/public/-/emojis/1/wave_tone2.png
index b857119732e..b857119732e 100644
--- a/app/assets/images/emoji/wave_tone2.png
+++ b/public/-/emojis/1/wave_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/wave_tone3.png b/public/-/emojis/1/wave_tone3.png
index 6283b670f43..6283b670f43 100644
--- a/app/assets/images/emoji/wave_tone3.png
+++ b/public/-/emojis/1/wave_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/wave_tone4.png b/public/-/emojis/1/wave_tone4.png
index fe6b2baa747..fe6b2baa747 100644
--- a/app/assets/images/emoji/wave_tone4.png
+++ b/public/-/emojis/1/wave_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/wave_tone5.png b/public/-/emojis/1/wave_tone5.png
index 4bd168ebb78..4bd168ebb78 100644
--- a/app/assets/images/emoji/wave_tone5.png
+++ b/public/-/emojis/1/wave_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/wavy_dash.png b/public/-/emojis/1/wavy_dash.png
index 001c8d6e47d..001c8d6e47d 100644
--- a/app/assets/images/emoji/wavy_dash.png
+++ b/public/-/emojis/1/wavy_dash.png
Binary files differ
diff --git a/app/assets/images/emoji/waxing_crescent_moon.png b/public/-/emojis/1/waxing_crescent_moon.png
index 687125173d9..687125173d9 100644
--- a/app/assets/images/emoji/waxing_crescent_moon.png
+++ b/public/-/emojis/1/waxing_crescent_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/waxing_gibbous_moon.png b/public/-/emojis/1/waxing_gibbous_moon.png
index 3a808156318..3a808156318 100644
--- a/app/assets/images/emoji/waxing_gibbous_moon.png
+++ b/public/-/emojis/1/waxing_gibbous_moon.png
Binary files differ
diff --git a/app/assets/images/emoji/wc.png b/public/-/emojis/1/wc.png
index aa433e84ba6..aa433e84ba6 100644
--- a/app/assets/images/emoji/wc.png
+++ b/public/-/emojis/1/wc.png
Binary files differ
diff --git a/app/assets/images/emoji/weary.png b/public/-/emojis/1/weary.png
index 98bfbd24a16..98bfbd24a16 100644
--- a/app/assets/images/emoji/weary.png
+++ b/public/-/emojis/1/weary.png
Binary files differ
diff --git a/app/assets/images/emoji/wedding.png b/public/-/emojis/1/wedding.png
index d0d8aa0bfae..d0d8aa0bfae 100644
--- a/app/assets/images/emoji/wedding.png
+++ b/public/-/emojis/1/wedding.png
Binary files differ
diff --git a/app/assets/images/emoji/whale.png b/public/-/emojis/1/whale.png
index 9f19b44257c..9f19b44257c 100644
--- a/app/assets/images/emoji/whale.png
+++ b/public/-/emojis/1/whale.png
Binary files differ
diff --git a/app/assets/images/emoji/whale2.png b/public/-/emojis/1/whale2.png
index 0df9d3c73a4..0df9d3c73a4 100644
--- a/app/assets/images/emoji/whale2.png
+++ b/public/-/emojis/1/whale2.png
Binary files differ
diff --git a/app/assets/images/emoji/wheel_of_dharma.png b/public/-/emojis/1/wheel_of_dharma.png
index 3666db0016b..3666db0016b 100644
--- a/app/assets/images/emoji/wheel_of_dharma.png
+++ b/public/-/emojis/1/wheel_of_dharma.png
Binary files differ
diff --git a/app/assets/images/emoji/wheelchair.png b/public/-/emojis/1/wheelchair.png
index 4e5b2698eac..4e5b2698eac 100644
--- a/app/assets/images/emoji/wheelchair.png
+++ b/public/-/emojis/1/wheelchair.png
Binary files differ
diff --git a/app/assets/images/emoji/white_check_mark.png b/public/-/emojis/1/white_check_mark.png
index e55f087e544..e55f087e544 100644
--- a/app/assets/images/emoji/white_check_mark.png
+++ b/public/-/emojis/1/white_check_mark.png
Binary files differ
diff --git a/app/assets/images/emoji/white_circle.png b/public/-/emojis/1/white_circle.png
index c19e15684dd..c19e15684dd 100644
--- a/app/assets/images/emoji/white_circle.png
+++ b/public/-/emojis/1/white_circle.png
Binary files differ
diff --git a/app/assets/images/emoji/white_flower.png b/public/-/emojis/1/white_flower.png
index d6af8b60077..d6af8b60077 100644
--- a/app/assets/images/emoji/white_flower.png
+++ b/public/-/emojis/1/white_flower.png
Binary files differ
diff --git a/app/assets/images/emoji/white_large_square.png b/public/-/emojis/1/white_large_square.png
index 6f06c1c79de..6f06c1c79de 100644
--- a/app/assets/images/emoji/white_large_square.png
+++ b/public/-/emojis/1/white_large_square.png
Binary files differ
diff --git a/app/assets/images/emoji/white_medium_small_square.png b/public/-/emojis/1/white_medium_small_square.png
index ae874126750..ae874126750 100644
--- a/app/assets/images/emoji/white_medium_small_square.png
+++ b/public/-/emojis/1/white_medium_small_square.png
Binary files differ
diff --git a/app/assets/images/emoji/white_medium_square.png b/public/-/emojis/1/white_medium_square.png
index 8daacf57059..8daacf57059 100644
--- a/app/assets/images/emoji/white_medium_square.png
+++ b/public/-/emojis/1/white_medium_square.png
Binary files differ
diff --git a/app/assets/images/emoji/white_small_square.png b/public/-/emojis/1/white_small_square.png
index d7ebdb0c0ed..d7ebdb0c0ed 100644
--- a/app/assets/images/emoji/white_small_square.png
+++ b/public/-/emojis/1/white_small_square.png
Binary files differ
diff --git a/app/assets/images/emoji/white_square_button.png b/public/-/emojis/1/white_square_button.png
index 934b1cedfd2..934b1cedfd2 100644
--- a/app/assets/images/emoji/white_square_button.png
+++ b/public/-/emojis/1/white_square_button.png
Binary files differ
diff --git a/app/assets/images/emoji/white_sun_cloud.png b/public/-/emojis/1/white_sun_cloud.png
index 0a4cc100269..0a4cc100269 100644
--- a/app/assets/images/emoji/white_sun_cloud.png
+++ b/public/-/emojis/1/white_sun_cloud.png
Binary files differ
diff --git a/app/assets/images/emoji/white_sun_rain_cloud.png b/public/-/emojis/1/white_sun_rain_cloud.png
index 491f9ca4839..491f9ca4839 100644
--- a/app/assets/images/emoji/white_sun_rain_cloud.png
+++ b/public/-/emojis/1/white_sun_rain_cloud.png
Binary files differ
diff --git a/app/assets/images/emoji/white_sun_small_cloud.png b/public/-/emojis/1/white_sun_small_cloud.png
index cead0bfa521..cead0bfa521 100644
--- a/app/assets/images/emoji/white_sun_small_cloud.png
+++ b/public/-/emojis/1/white_sun_small_cloud.png
Binary files differ
diff --git a/app/assets/images/emoji/wilted_rose.png b/public/-/emojis/1/wilted_rose.png
index 62412b143ae..62412b143ae 100644
--- a/app/assets/images/emoji/wilted_rose.png
+++ b/public/-/emojis/1/wilted_rose.png
Binary files differ
diff --git a/app/assets/images/emoji/wind_blowing_face.png b/public/-/emojis/1/wind_blowing_face.png
index df81b652eb6..df81b652eb6 100644
--- a/app/assets/images/emoji/wind_blowing_face.png
+++ b/public/-/emojis/1/wind_blowing_face.png
Binary files differ
diff --git a/app/assets/images/emoji/wind_chime.png b/public/-/emojis/1/wind_chime.png
index 3c9ef3a95f6..3c9ef3a95f6 100644
--- a/app/assets/images/emoji/wind_chime.png
+++ b/public/-/emojis/1/wind_chime.png
Binary files differ
diff --git a/app/assets/images/emoji/wine_glass.png b/public/-/emojis/1/wine_glass.png
index 3cc98689192..3cc98689192 100644
--- a/app/assets/images/emoji/wine_glass.png
+++ b/public/-/emojis/1/wine_glass.png
Binary files differ
diff --git a/app/assets/images/emoji/wink.png b/public/-/emojis/1/wink.png
index 7ea7810a37d..7ea7810a37d 100644
--- a/app/assets/images/emoji/wink.png
+++ b/public/-/emojis/1/wink.png
Binary files differ
diff --git a/app/assets/images/emoji/wolf.png b/public/-/emojis/1/wolf.png
index ba7220f2de9..ba7220f2de9 100644
--- a/app/assets/images/emoji/wolf.png
+++ b/public/-/emojis/1/wolf.png
Binary files differ
diff --git a/app/assets/images/emoji/woman.png b/public/-/emojis/1/woman.png
index ece440e7a61..ece440e7a61 100644
--- a/app/assets/images/emoji/woman.png
+++ b/public/-/emojis/1/woman.png
Binary files differ
diff --git a/app/assets/images/emoji/woman_tone1.png b/public/-/emojis/1/woman_tone1.png
index ff089b8889b..ff089b8889b 100644
--- a/app/assets/images/emoji/woman_tone1.png
+++ b/public/-/emojis/1/woman_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/woman_tone2.png b/public/-/emojis/1/woman_tone2.png
index 0719c378016..0719c378016 100644
--- a/app/assets/images/emoji/woman_tone2.png
+++ b/public/-/emojis/1/woman_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/woman_tone3.png b/public/-/emojis/1/woman_tone3.png
index 5672e2fd52d..5672e2fd52d 100644
--- a/app/assets/images/emoji/woman_tone3.png
+++ b/public/-/emojis/1/woman_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/woman_tone4.png b/public/-/emojis/1/woman_tone4.png
index 5754aab558b..5754aab558b 100644
--- a/app/assets/images/emoji/woman_tone4.png
+++ b/public/-/emojis/1/woman_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/woman_tone5.png b/public/-/emojis/1/woman_tone5.png
index fc252af3a39..fc252af3a39 100644
--- a/app/assets/images/emoji/woman_tone5.png
+++ b/public/-/emojis/1/woman_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/womans_clothes.png b/public/-/emojis/1/womans_clothes.png
index 01410dc8107..01410dc8107 100644
--- a/app/assets/images/emoji/womans_clothes.png
+++ b/public/-/emojis/1/womans_clothes.png
Binary files differ
diff --git a/app/assets/images/emoji/womans_hat.png b/public/-/emojis/1/womans_hat.png
index b837b6a2e47..b837b6a2e47 100644
--- a/app/assets/images/emoji/womans_hat.png
+++ b/public/-/emojis/1/womans_hat.png
Binary files differ
diff --git a/app/assets/images/emoji/womens.png b/public/-/emojis/1/womens.png
index d4ecc22e7b3..d4ecc22e7b3 100644
--- a/app/assets/images/emoji/womens.png
+++ b/public/-/emojis/1/womens.png
Binary files differ
diff --git a/app/assets/images/emoji/worried.png b/public/-/emojis/1/worried.png
index 7074afcf5b7..7074afcf5b7 100644
--- a/app/assets/images/emoji/worried.png
+++ b/public/-/emojis/1/worried.png
Binary files differ
diff --git a/app/assets/images/emoji/wrench.png b/public/-/emojis/1/wrench.png
index c16b7439697..c16b7439697 100644
--- a/app/assets/images/emoji/wrench.png
+++ b/public/-/emojis/1/wrench.png
Binary files differ
diff --git a/app/assets/images/emoji/wrestlers.png b/public/-/emojis/1/wrestlers.png
index 71e67cfad85..71e67cfad85 100644
--- a/app/assets/images/emoji/wrestlers.png
+++ b/public/-/emojis/1/wrestlers.png
Binary files differ
diff --git a/app/assets/images/emoji/wrestlers_tone1.png b/public/-/emojis/1/wrestlers_tone1.png
index 379070fd03b..379070fd03b 100644
--- a/app/assets/images/emoji/wrestlers_tone1.png
+++ b/public/-/emojis/1/wrestlers_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/wrestlers_tone2.png b/public/-/emojis/1/wrestlers_tone2.png
index 6863ea9209d..6863ea9209d 100644
--- a/app/assets/images/emoji/wrestlers_tone2.png
+++ b/public/-/emojis/1/wrestlers_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/wrestlers_tone3.png b/public/-/emojis/1/wrestlers_tone3.png
index b7e62910127..b7e62910127 100644
--- a/app/assets/images/emoji/wrestlers_tone3.png
+++ b/public/-/emojis/1/wrestlers_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/wrestlers_tone4.png b/public/-/emojis/1/wrestlers_tone4.png
index 750f9589233..750f9589233 100644
--- a/app/assets/images/emoji/wrestlers_tone4.png
+++ b/public/-/emojis/1/wrestlers_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/wrestlers_tone5.png b/public/-/emojis/1/wrestlers_tone5.png
index 36ab9bb3f42..36ab9bb3f42 100644
--- a/app/assets/images/emoji/wrestlers_tone5.png
+++ b/public/-/emojis/1/wrestlers_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/writing_hand.png b/public/-/emojis/1/writing_hand.png
index 85639f8ac40..85639f8ac40 100644
--- a/app/assets/images/emoji/writing_hand.png
+++ b/public/-/emojis/1/writing_hand.png
Binary files differ
diff --git a/app/assets/images/emoji/writing_hand_tone1.png b/public/-/emojis/1/writing_hand_tone1.png
index 7923d8ebb17..7923d8ebb17 100644
--- a/app/assets/images/emoji/writing_hand_tone1.png
+++ b/public/-/emojis/1/writing_hand_tone1.png
Binary files differ
diff --git a/app/assets/images/emoji/writing_hand_tone2.png b/public/-/emojis/1/writing_hand_tone2.png
index bcb304e15d2..bcb304e15d2 100644
--- a/app/assets/images/emoji/writing_hand_tone2.png
+++ b/public/-/emojis/1/writing_hand_tone2.png
Binary files differ
diff --git a/app/assets/images/emoji/writing_hand_tone3.png b/public/-/emojis/1/writing_hand_tone3.png
index fd885fd2d90..fd885fd2d90 100644
--- a/app/assets/images/emoji/writing_hand_tone3.png
+++ b/public/-/emojis/1/writing_hand_tone3.png
Binary files differ
diff --git a/app/assets/images/emoji/writing_hand_tone4.png b/public/-/emojis/1/writing_hand_tone4.png
index d065b8c64ab..d065b8c64ab 100644
--- a/app/assets/images/emoji/writing_hand_tone4.png
+++ b/public/-/emojis/1/writing_hand_tone4.png
Binary files differ
diff --git a/app/assets/images/emoji/writing_hand_tone5.png b/public/-/emojis/1/writing_hand_tone5.png
index a44b3dd757c..a44b3dd757c 100644
--- a/app/assets/images/emoji/writing_hand_tone5.png
+++ b/public/-/emojis/1/writing_hand_tone5.png
Binary files differ
diff --git a/app/assets/images/emoji/x.png b/public/-/emojis/1/x.png
index 9f9ed0f7ad2..9f9ed0f7ad2 100644
--- a/app/assets/images/emoji/x.png
+++ b/public/-/emojis/1/x.png
Binary files differ
diff --git a/app/assets/images/emoji/yellow_heart.png b/public/-/emojis/1/yellow_heart.png
index 7901a9d0103..7901a9d0103 100644
--- a/app/assets/images/emoji/yellow_heart.png
+++ b/public/-/emojis/1/yellow_heart.png
Binary files differ
diff --git a/app/assets/images/emoji/yen.png b/public/-/emojis/1/yen.png
index 63ee4799d66..63ee4799d66 100644
--- a/app/assets/images/emoji/yen.png
+++ b/public/-/emojis/1/yen.png
Binary files differ
diff --git a/app/assets/images/emoji/yin_yang.png b/public/-/emojis/1/yin_yang.png
index f2900f6338f..f2900f6338f 100644
--- a/app/assets/images/emoji/yin_yang.png
+++ b/public/-/emojis/1/yin_yang.png
Binary files differ
diff --git a/app/assets/images/emoji/yum.png b/public/-/emojis/1/yum.png
index 2df15753ca1..2df15753ca1 100644
--- a/app/assets/images/emoji/yum.png
+++ b/public/-/emojis/1/yum.png
Binary files differ
diff --git a/app/assets/images/emoji/zap.png b/public/-/emojis/1/zap.png
index 47e68e48e49..47e68e48e49 100644
--- a/app/assets/images/emoji/zap.png
+++ b/public/-/emojis/1/zap.png
Binary files differ
diff --git a/app/assets/images/emoji/zero.png b/public/-/emojis/1/zero.png
index 13aca83e018..13aca83e018 100644
--- a/app/assets/images/emoji/zero.png
+++ b/public/-/emojis/1/zero.png
Binary files differ
diff --git a/app/assets/images/emoji/zipper_mouth.png b/public/-/emojis/1/zipper_mouth.png
index f8ced2502a7..f8ced2502a7 100644
--- a/app/assets/images/emoji/zipper_mouth.png
+++ b/public/-/emojis/1/zipper_mouth.png
Binary files differ
diff --git a/app/assets/images/emoji/zzz.png b/public/-/emojis/1/zzz.png
index 9bc72b4469f..9bc72b4469f 100644
--- a/app/assets/images/emoji/zzz.png
+++ b/public/-/emojis/1/zzz.png
Binary files differ
diff --git a/qa/.gitignore b/qa/.gitignore
index 19ec17d0005..b0ae074ac07 100644
--- a/qa/.gitignore
+++ b/qa/.gitignore
@@ -1,2 +1,3 @@
tmp/
.ruby-version
+urls.yml
diff --git a/qa/Gemfile b/qa/Gemfile
index 873eac1013f..38e95ba2d65 100644
--- a/qa/Gemfile
+++ b/qa/Gemfile
@@ -8,3 +8,5 @@ gem 'rspec', '~> 3.7'
gem 'selenium-webdriver', '~> 3.12'
gem 'airborne', '~> 0.2.13'
gem 'nokogiri', '~> 1.10.1'
+gem 'rspec-retry', '~> 0.6.1'
+gem 'faker', '~> 1.6', '>= 1.6.6'
diff --git a/qa/Gemfile.lock b/qa/Gemfile.lock
index 9f84bdc3828..c9b0db6a272 100644
--- a/qa/Gemfile.lock
+++ b/qa/Gemfile.lock
@@ -32,6 +32,8 @@ GEM
diff-lcs (1.3)
domain_name (0.5.20170404)
unf (>= 0.0.5, < 1.0.0)
+ faker (1.9.3)
+ i18n (>= 0.7)
ffi (1.9.25)
http-cookie (1.0.3)
domain_name (~> 0.5)
@@ -76,6 +78,8 @@ GEM
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
+ rspec-retry (0.6.1)
+ rspec-core (> 3.3)
rspec-support (3.7.0)
rubyzip (1.2.2)
selenium-webdriver (3.141.0)
@@ -97,10 +101,12 @@ DEPENDENCIES
airborne (~> 0.2.13)
capybara (~> 2.16.1)
capybara-screenshot (~> 1.0.18)
+ faker (~> 1.6, >= 1.6.6)
nokogiri (~> 1.10.1)
pry-byebug (~> 3.5.1)
rake (~> 12.3.0)
rspec (~> 3.7)
+ rspec-retry (~> 0.6.1)
selenium-webdriver (~> 3.12)
BUNDLED WITH
diff --git a/qa/README.md b/qa/README.md
index 5e32496ea9f..735868e7640 100644
--- a/qa/README.md
+++ b/qa/README.md
@@ -1,21 +1,25 @@
-# GitLab QA - Integration tests for GitLab
+# GitLab QA - End-to-end tests for GitLab
-This directory contains integration tests for GitLab.
+This directory contains [end-to-end tests](doc/development/testing_guide/end_to_end_tests.md)
+for GitLab. It includes the test framework and the tests themselves.
+
+The tests can be found in `qa/specs/features` (not to be confused with the unit
+tests for the test framework, which are in `spec/`).
It is part of the [GitLab QA project](https://gitlab.com/gitlab-org/gitlab-qa).
## What is it?
-GitLab QA is an integration tests suite for GitLab.
+GitLab QA is an end-to-end tests suite for GitLab.
-These are black-box and entirely click-driven integration tests you can run
+These are black-box and entirely click-driven end-to-end tests you can run
against any existing instance.
## How does it work?
1. When we release a new version of GitLab, we build a Docker images for it.
1. Along with GitLab Docker Images we also build and publish GitLab QA images.
-1. GitLab QA project uses these images to execute integration tests.
+1. GitLab QA project uses these images to execute end-to-end tests.
## Validating GitLab views / partials / selectors in merge requests
@@ -38,6 +42,9 @@ following call would login to a local [GDK] instance and run all specs in
bin/qa Test::Instance::All http://localhost:3000
```
+Note: If you want to run tests requiring SSH against GDK, you
+will need to [modify your GDK setup](https://gitlab.com/gitlab-org/gitlab-qa/blob/master/docs/run_qa_against_gdk.md).
+
### Writing tests
1. [Using page objects](qa/page/README.md)
diff --git a/qa/Rakefile b/qa/Rakefile
index 9a7b9c6bb35..7ac018f7286 100644
--- a/qa/Rakefile
+++ b/qa/Rakefile
@@ -1,5 +1,6 @@
require_relative 'qa/tools/revoke_all_personal_access_tokens'
require_relative 'qa/tools/delete_subgroups'
+require_relative 'qa/tools/generate_perf_testdata'
desc "Revokes all personal access tokens"
task :revoke_personal_access_tokens do
@@ -10,3 +11,30 @@ desc "Deletes subgroups within a provided group"
task :delete_subgroups do
QA::Tools::DeleteSubgroups.new.run
end
+
+desc "Generate Performance Testdata"
+task :generate_perf_testdata do
+ QA::Tools::GeneratePerfTestdata.new.run
+end
+
+desc "Run artillery load tests"
+task :run_artillery_load_tests do
+ unless ENV['HOST_URL'] && ENV['LARGE_ISSUE_URL'] && ENV['LARGE_MR_URL']
+ urls_file = ENV['URLS_FILE_PATH'] || 'urls.yml'
+
+ unless File.exist?(urls_file)
+ raise "\n#{urls_file} file is missing. Please provide correct URLS_FILE_PATH or all of HOST_URL, LARGE_ISSUE_URL and LARGE_MR_URL\n\n"
+ end
+
+ urls = YAML.safe_load(File.read(urls_file))
+ ENV['HOST_URL'] = urls["host"]
+ ENV['LARGE_ISSUE_URL'] = urls["large_issue"]
+ ENV['LARGE_MR_URL'] = urls["large_mr"]
+ end
+
+ sh('artillery run load/artillery.yml -o report.json')
+ sh('artillery report report.json -o report.html && rm report.json')
+end
+
+desc "Generate data and run load tests"
+task generate_data_and_run_load_test: [:generate_perf_testdata, :run_artillery_load_tests]
diff --git a/qa/load/artillery.yml b/qa/load/artillery.yml
new file mode 100644
index 00000000000..17d253ec480
--- /dev/null
+++ b/qa/load/artillery.yml
@@ -0,0 +1,25 @@
+config:
+ target: "{{ $processEnvironment.HOST_URL }}"
+ http:
+ pool: 10 # All HTTP requests from all virtual users will be sent over the same <pool> connections.
+ # This also means that there is a limit on the number of requests sent per second.
+ phases:
+ - duration: 30
+ arrivalRate: 10
+ name: "Warm up"
+ - duration: 90
+ arrivalRate: 10
+ rampTo: 100
+ name: "Gradual ramp up"
+ - duration: 90
+ arrivalRate: 100
+ name: "Sustained max load"
+scenarios:
+ - name: "Visit large issue url"
+ flow:
+ - get:
+ url: "{{ $processEnvironment.LARGE_ISSUE_URL }}"
+ - name: "Visit large MR url"
+ flow:
+ - get:
+ url: "{{ $processEnvironment.LARGE_MR_URL }}"
diff --git a/qa/qa.rb b/qa/qa.rb
index d6dcfa3032b..ec8aef31e48 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -61,6 +61,7 @@ module QA
autoload :File, 'qa/resource/file'
autoload :Fork, 'qa/resource/fork'
autoload :SSHKey, 'qa/resource/ssh_key'
+ autoload :Snippet, 'qa/resource/snippet'
module Events
autoload :Base, 'qa/resource/events/base'
@@ -142,6 +143,12 @@ module QA
module Dashboard
autoload :Projects, 'qa/page/dashboard/projects'
autoload :Groups, 'qa/page/dashboard/groups'
+
+ module Snippet
+ autoload :New, 'qa/page/dashboard/snippet/new'
+ autoload :Index, 'qa/page/dashboard/snippet/index'
+ autoload :Show, 'qa/page/dashboard/snippet/show'
+ end
end
module Group
@@ -260,6 +267,10 @@ module QA
autoload :Sidebar, 'qa/page/issuable/sidebar'
end
+ module Alert
+ autoload :AutoDevopsAlert, 'qa/page/alert/auto_devops_alert'
+ end
+
module Layout
autoload :Banner, 'qa/page/layout/banner'
end
@@ -338,6 +349,10 @@ module QA
module Specs
autoload :Config, 'qa/specs/config'
autoload :Runner, 'qa/specs/runner'
+
+ module Helpers
+ autoload :Quarantine, 'qa/specs/helpers/quarantine'
+ end
end
##
diff --git a/qa/qa/git/repository.rb b/qa/qa/git/repository.rb
index 0aa94101098..b3bad40a90f 100644
--- a/qa/qa/git/repository.rb
+++ b/qa/qa/git/repository.rb
@@ -12,6 +12,7 @@ module QA
module Git
class Repository
include Scenario::Actable
+ RepositoryCommandError = Class.new(StandardError)
attr_writer :use_lfs
attr_accessor :env_vars
@@ -205,6 +206,10 @@ module QA
output.chomp!
Runtime::Logger.debug "Git: output=[#{output}], exitstatus=[#{status.exitstatus}]"
+ unless status.success?
+ raise RepositoryCommandError, "The command #{command} failed (#{status.exitstatus}) with the following output:\n#{output}"
+ end
+
Result.new(status.exitstatus == 0, output)
end
diff --git a/qa/qa/page/alert/auto_devops_alert.rb b/qa/qa/page/alert/auto_devops_alert.rb
new file mode 100644
index 00000000000..8f66c805b77
--- /dev/null
+++ b/qa/qa/page/alert/auto_devops_alert.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Alert
+ class AutoDevopsAlert < Page::Base
+ view 'app/views/shared/_auto_devops_implicitly_enabled_banner.html.haml' do
+ element :auto_devops_banner
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 05e38aba77d..9fabf83e2ce 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -77,8 +77,8 @@ module QA
page.evaluate_script('xhr.status') == 200
end
- def find_element(name, text_filter = nil, wait: Capybara.default_max_wait_time)
- find(element_selector_css(name), wait: wait, text: text_filter)
+ def find_element(name, text: nil, wait: Capybara.default_max_wait_time)
+ find(element_selector_css(name), wait: wait, text: text)
end
def all_elements(name)
@@ -109,8 +109,8 @@ module QA
element.select value.to_s.capitalize
end
- def has_element?(name, wait: Capybara.default_max_wait_time)
- has_css?(element_selector_css(name), wait: wait)
+ def has_element?(name, text: nil, wait: Capybara.default_max_wait_time)
+ has_css?(element_selector_css(name), wait: wait, text: text)
end
def has_no_element?(name, wait: Capybara.default_max_wait_time)
@@ -157,6 +157,10 @@ module QA
find('body').click
end
+ def visit_link_in_element(name)
+ visit find_element(name)['href']
+ end
+
def self.path
raise NotImplementedError
end
diff --git a/qa/qa/page/component/select2.rb b/qa/qa/page/component/select2.rb
index 98bcb96b92c..3c3461d6b20 100644
--- a/qa/qa/page/component/select2.rb
+++ b/qa/qa/page/component/select2.rb
@@ -3,7 +3,7 @@ module QA
module Component
module Select2
def select_item(item_text)
- find('.select2-result-label', text: item_text).click
+ find('.select2-result-label', text: item_text, match: :prefer_exact).click
end
def clear_current_selection_if_present
diff --git a/qa/qa/page/dashboard/projects.rb b/qa/qa/page/dashboard/projects.rb
index 0f434577b3b..271c5456efe 100644
--- a/qa/qa/page/dashboard/projects.rb
+++ b/qa/qa/page/dashboard/projects.rb
@@ -2,8 +2,6 @@ module QA
module Page
module Dashboard
class Projects < Page::Base
- view 'app/views/dashboard/projects/index.html.haml'
-
view 'app/views/shared/projects/_search_form.html.haml' do
element :form_filter_by_name, /form_tag.+id: 'project-filter-form'/ # rubocop:disable QA/ElementWithPattern
end
diff --git a/qa/qa/page/dashboard/snippet/index.rb b/qa/qa/page/dashboard/snippet/index.rb
new file mode 100644
index 00000000000..1f467fda9e1
--- /dev/null
+++ b/qa/qa/page/dashboard/snippet/index.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Dashboard
+ module Snippet
+ class Index < Page::Base
+ view 'app/views/layouts/header/_new_dropdown.haml' do
+ element :new_menu_toggle
+ element :global_new_snippet_link
+ end
+
+ def go_to_new_snippet_page
+ click_element :new_menu_toggle
+ click_element :global_new_snippet_link
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/dashboard/snippet/new.rb b/qa/qa/page/dashboard/snippet/new.rb
new file mode 100644
index 00000000000..a637b869d2f
--- /dev/null
+++ b/qa/qa/page/dashboard/snippet/new.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Dashboard
+ module Snippet
+ class New < Page::Base
+ view 'app/views/shared/form_elements/_description.html.haml' do
+ element :issuable_form_description
+ end
+
+ view 'app/views/shared/snippets/_form.html.haml' do
+ element :snippet_title
+ element :snippet_file_name
+ element :create_snippet_button
+ end
+
+ def fill_title(title)
+ fill_element :snippet_title, title
+ end
+
+ def fill_description(description)
+ fill_element :issuable_form_description, description
+ end
+
+ def set_visibility(visibility)
+ choose visibility
+ end
+
+ def fill_file_name(name)
+ finished_loading?
+ fill_element :snippet_file_name, name
+ end
+
+ def fill_file_content(content)
+ finished_loading?
+ text_area.set content
+ end
+
+ def create_snippet
+ click_element :create_snippet_button
+ end
+
+ private
+
+ def text_area
+ find('#editor>textarea', visible: false)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/dashboard/snippet/show.rb b/qa/qa/page/dashboard/snippet/show.rb
new file mode 100644
index 00000000000..a75ea63eca7
--- /dev/null
+++ b/qa/qa/page/dashboard/snippet/show.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+module QA
+ module Page
+ module Dashboard
+ module Snippet
+ class Show < Page::Base
+ view 'app/views/shared/snippets/_header.html.haml' do
+ element :snippet_title
+ element :snippet_description
+ element :embed_type
+ element :snippet_box
+ end
+
+ view 'app/views/projects/blob/_header_content.html.haml' do
+ element :file_title_name
+ end
+
+ view 'app/views/shared/_file_highlight.html.haml' do
+ element :file_content
+ end
+
+ def has_snippet_title?(snippet_title)
+ within_element(:snippet_title) do
+ has_text?(snippet_title)
+ end
+ end
+
+ def has_snippet_description?(snippet_description)
+ within_element(:snippet_description) do
+ has_text?(snippet_description)
+ end
+ end
+
+ def has_embed_type?(embed_type)
+ within_element(:embed_type) do
+ has_text?(embed_type)
+ end
+ end
+
+ def has_visibility_type?(visibility_type)
+ within_element(:snippet_box) do
+ has_text?(visibility_type)
+ end
+ end
+
+ def has_file_name?(file_name)
+ within_element(:file_title_name) do
+ has_text?(file_name)
+ end
+ end
+
+ def has_file_content?(file_content)
+ finished_loading?
+ within_element(:file_content) do
+ has_text?(file_content)
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index 9d6bd338027..41716326685 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -45,7 +45,7 @@ module QA
private
def select_kind(kind)
- retry_on_exception(sleep_interval: 1.0) do
+ QA::Support::Retrier.retry_on_exception(sleep_interval: 1.0) do
within_element(:new_project_or_subgroup_dropdown) do
# May need to click again because it is possible to click the button quicker than the JS is bound
wait(reload: false) do
diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb
index 55500e831c6..1b3445b0064 100644
--- a/qa/qa/page/main/menu.rb
+++ b/qa/qa/page/main/menu.rb
@@ -19,6 +19,7 @@ module QA
element :admin_area_link
element :projects_dropdown
element :groups_dropdown
+ element :snippets_link
end
view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do
@@ -66,6 +67,10 @@ module QA
end
end
+ def go_to_snippets
+ click_element :snippets_link
+ end
+
def has_personal_area?(wait: Capybara.default_max_wait_time)
has_element?(:user_avatar, wait: wait)
end
diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb
index 976e431186d..c0411db6505 100644
--- a/qa/qa/page/merge_request/show.rb
+++ b/qa/qa/page/merge_request/show.rb
@@ -27,6 +27,12 @@ module QA
element :squash_checkbox
end
+ view 'app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.vue' do
+ element :dropdown_toggle
+ element :download_email_patches
+ element :download_plain_diff
+ end
+
view 'app/views/projects/merge_requests/show.html.haml' do
element :notes_tab
element :diffs_tab
@@ -159,6 +165,16 @@ module QA
def edit!
click_element :edit_button
end
+
+ def view_email_patches
+ click_element :dropdown_toggle
+ visit_link_in_element(:download_email_patches)
+ end
+
+ def view_plain_diff
+ click_element :dropdown_toggle
+ visit_link_in_element(:download_plain_diff)
+ end
end
end
end
diff --git a/qa/qa/page/project/activity.rb b/qa/qa/page/project/activity.rb
index 56fbaa90790..afd4f49a844 100644
--- a/qa/qa/page/project/activity.rb
+++ b/qa/qa/page/project/activity.rb
@@ -6,7 +6,7 @@ module QA
element :push_events, "event_filter_link EventFilter::PUSH, _('Push events')" # rubocop:disable QA/ElementWithPattern
end
- def go_to_push_events
+ def click_push_events
click_on 'Push events'
end
end
diff --git a/qa/qa/page/project/import/github.rb b/qa/qa/page/project/import/github.rb
index 488157d9878..45c8d834a74 100644
--- a/qa/qa/page/project/import/github.rb
+++ b/qa/qa/page/project/import/github.rb
@@ -29,12 +29,19 @@ module QA
choose_test_namespace(full_path)
set_path(full_path, name)
import_project(full_path)
+ wait_for_success
end
private
def within_repo_path(full_path)
- page.within(%Q(tr[data-qa-repo-path="#{full_path}"])) do
+ wait(reload: false) do
+ has_element?(:project_import_row, text: full_path)
+ end
+
+ project_import_row = find_element(:project_import_row, text: full_path)
+
+ within(project_import_row) do
yield
end
end
@@ -44,18 +51,24 @@ module QA
click_element :project_namespace_select
end
- select_item(Runtime::Namespace.path)
+ search_and_select(Runtime::Namespace.path)
end
def set_path(full_path, name)
within_repo_path(full_path) do
- fill_in 'path', with: name
+ fill_element(:project_path_field, name)
end
end
def import_project(full_path)
within_repo_path(full_path) do
- click_button 'Import'
+ click_element(:import_button)
+ end
+ end
+
+ def wait_for_success
+ wait(max: 60, interval: 1.0, reload: false) do
+ page.has_content?('Done', wait: 1.0)
end
end
end
diff --git a/qa/qa/page/project/issue/show.rb b/qa/qa/page/project/issue/show.rb
index 1c25be5fd0c..9df3db1bba0 100644
--- a/qa/qa/page/project/issue/show.rb
+++ b/qa/qa/page/project/issue/show.rb
@@ -23,6 +23,10 @@ module QA
element :filter_options
end
+ view 'app/assets/javascripts/notes/components/noteable_note.vue' do
+ element :noteable_note_item
+ end
+
# Adds a comment to an issue
# attachment option should be an absolute path
def comment(text, attachment: nil)
@@ -36,6 +40,12 @@ module QA
click_element :comment_button
end
+ def has_comment?(comment_text)
+ wait(reload: false) do
+ has_element?(:noteable_note_item, text: comment_text)
+ end
+ end
+
def select_comments_only_filter
select_filter_with_text('Show comments only')
end
@@ -54,7 +64,7 @@ module QA
retry_on_exception do
click_body
click_element :discussion_filter
- find_element(:filter_options, text).click
+ find_element(:filter_options, text: text).click
end
end
end
diff --git a/qa/qa/page/project/job/show.rb b/qa/qa/page/project/job/show.rb
index 49c676c01f2..5853f487f0b 100644
--- a/qa/qa/page/project/job/show.rb
+++ b/qa/qa/page/project/job/show.rb
@@ -1,13 +1,11 @@
+# frozen_string_literal: true
+
module QA::Page
module Project::Job
class Show < QA::Page::Base
COMPLETED_STATUSES = %w[passed failed canceled blocked skipped manual].freeze # excludes created, pending, running
PASSED_STATUS = 'passed'.freeze
- view 'app/assets/javascripts/jobs/components/job_app.vue' do
- element :loading_animation
- end
-
view 'app/assets/javascripts/jobs/components/job_log.vue' do
element :build_trace
end
@@ -20,22 +18,13 @@ module QA::Page
element :pipeline_path
end
- def completed?
- COMPLETED_STATUSES.include?(status_badge)
- end
-
def successful?(timeout: 60)
- wait(reload: false, max: timeout) do
- completed? && !trace_loading?
- end
+ raise "Timed out waiting for the build trace to load" unless loaded?
+ raise "Timed out waiting for the status to be a valid completed state" unless completed?(timeout: timeout)
status_badge == PASSED_STATUS
end
- def trace_loading?
- has_element?(:loading_animation)
- end
-
# Reminder: You may wish to wait for a particular job status before checking output
def output
find_element(:build_trace).text
@@ -43,6 +32,18 @@ module QA::Page
private
+ def loaded?(wait: 60)
+ wait(reload: true, max: wait, interval: 1) do
+ has_element?(:build_trace, wait: 1)
+ end
+ end
+
+ def completed?(timeout: 60)
+ wait(reload: false, max: timeout) do
+ COMPLETED_STATUSES.include?(status_badge)
+ end
+ end
+
def status_badge
find_element(:status_badge).text
end
diff --git a/qa/qa/page/project/menu.rb b/qa/qa/page/project/menu.rb
index 46dfe87fe25..3fe048f752a 100644
--- a/qa/qa/page/project/menu.rb
+++ b/qa/qa/page/project/menu.rb
@@ -30,7 +30,7 @@ module QA
end
end
- def go_to_activity
+ def click_activity
within_sidebar do
click_element(:activity_link)
end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index 9f1867ef8a5..552b2293115 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -24,9 +24,15 @@ module QA
end
def choose_test_namespace
- click_element :project_namespace_select
+ choose_namespace(Runtime::Namespace.path)
+ end
- search_and_select(Runtime::Namespace.path)
+ def choose_namespace(namespace)
+ retry_on_exception do
+ click_body
+ click_element :project_namespace_select
+ search_and_select(namespace)
+ end
end
def go_to_import_project
diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb
index 98ac5c32d91..50a25718e96 100644
--- a/qa/qa/page/project/operations/kubernetes/show.rb
+++ b/qa/qa/page/project/operations/kubernetes/show.rb
@@ -11,7 +11,12 @@ module QA
end
view 'app/assets/javascripts/clusters/components/applications.vue' do
- element :ingress_ip_address, 'id="ingress-ip-address"' # rubocop:disable QA/ElementWithPattern
+ element :ingress_ip_address, 'id="ingress-endpoint"' # rubocop:disable QA/ElementWithPattern
+ end
+
+ view 'app/views/clusters/clusters/_form.html.haml' do
+ element :base_domain
+ element :save_domain
end
def install!(application_name)
@@ -30,7 +35,15 @@ module QA
def ingress_ip
# We need to wait longer since it can take some time before the
# ip address is assigned for the ingress controller
- page.find('#ingress-ip-address', wait: 1200).value
+ page.find('#ingress-endpoint', wait: 1200).value
+ end
+
+ def set_domain(domain)
+ fill_element :base_domain, domain
+ end
+
+ def save_domain
+ click_element :save_domain
end
end
end
diff --git a/qa/qa/page/project/pipeline/show.rb b/qa/qa/page/project/pipeline/show.rb
index f192f1fc64b..6f8a66bf527 100644
--- a/qa/qa/page/project/pipeline/show.rb
+++ b/qa/qa/page/project/pipeline/show.rb
@@ -18,6 +18,10 @@ module QA::Page
element :status_icon, 'ci-status-icon-${status}' # rubocop:disable QA/ElementWithPattern
end
+ view 'app/views/projects/pipelines/_info.html.haml' do
+ element :pipeline_badges
+ end
+
def running?
within('.ci-header-container') do
page.has_content?('running')
@@ -32,8 +36,14 @@ module QA::Page
end
end
+ def has_tag?(tag_name)
+ within_element(:pipeline_badges) do
+ has_selector?('.badge', text: tag_name)
+ end
+ end
+
def go_to_job(job_name)
- find_element(:job_link, job_name).click
+ find_element(:job_link, text: job_name).click
end
def go_to_first_job
diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb
index 5da8d352e74..e0f9e84096d 100644
--- a/qa/qa/page/project/settings/deploy_keys.rb
+++ b/qa/qa/page/project/settings/deploy_keys.rb
@@ -33,14 +33,14 @@ module QA
def find_fingerprint(title)
within_project_deploy_keys do
- find_element(:key, title)
+ find_element(:key, text: title)
.find(element_selector_css(:key_fingerprint)).text
end
end
def has_key?(title, fingerprint)
within_project_deploy_keys do
- find_element(:key, title)
+ find_element(:key, text: title)
.has_css?(element_selector_css(:key_fingerprint), text: fingerprint)
end
end
diff --git a/qa/qa/page/project/web_ide/edit.rb b/qa/qa/page/project/web_ide/edit.rb
index 2b6c01888d5..ff7cc04e352 100644
--- a/qa/qa/page/project/web_ide/edit.rb
+++ b/qa/qa/page/project/web_ide/edit.rb
@@ -43,11 +43,9 @@ module QA
def create_new_file_from_template(file_name, template)
click_element :new_file
within_element(:template_list) do
- begin
- click_on file_name
- rescue Capybara::ElementNotFound
- raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
- end
+ click_on file_name
+ rescue Capybara::ElementNotFound
+ raise ElementNotFound, %Q(Couldn't find file template named "#{file_name}". Please confirm that it is a valid option.)
end
wait(reload: false) do
diff --git a/qa/qa/resource/kubernetes_cluster.rb b/qa/qa/resource/kubernetes_cluster.rb
index 986b31da528..93a06be6818 100644
--- a/qa/qa/resource/kubernetes_cluster.rb
+++ b/qa/qa/resource/kubernetes_cluster.rb
@@ -12,10 +12,6 @@ module QA
Page::Project::Operations::Kubernetes::Show.perform(&:ingress_ip)
end
- attribute :domain do
- "#{ingress_ip}.nip.io"
- end
-
def fabricate!
@project.visit!
@@ -53,6 +49,12 @@ module QA
page.await_installed(:ingress) if @install_ingress
page.await_installed(:prometheus) if @install_prometheus
page.await_installed(:runner) if @install_runner
+
+ if @install_ingress
+ populate(:ingress_ip)
+ page.set_domain("#{ingress_ip}.nip.io")
+ page.save_domain
+ end
end
end
end
diff --git a/qa/qa/resource/project_imported_from_github.rb b/qa/qa/resource/project_imported_from_github.rb
index 3f02fe885a9..0d25e7dd842 100644
--- a/qa/qa/resource/project_imported_from_github.rb
+++ b/qa/qa/resource/project_imported_from_github.rb
@@ -4,7 +4,7 @@ require 'securerandom'
module QA
module Resource
- class ProjectImportedFromGithub < Project
+ class ProjectImportedFromGithub < Base
attr_accessor :name
attr_writer :personal_access_token, :github_repository_path
diff --git a/qa/qa/resource/snippet.rb b/qa/qa/resource/snippet.rb
new file mode 100644
index 00000000000..1478f197570
--- /dev/null
+++ b/qa/qa/resource/snippet.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+module QA
+ module Resource
+ class Snippet < Base
+ attr_accessor :title, :description, :file_content, :visibility, :file_name
+
+ def initialize
+ @title = 'New snippet title'
+ @description = 'The snippet description'
+ @visibility = 'Public'
+ @file_content = 'The snippet content'
+ @file_name = 'New snippet file name'
+ end
+
+ def fabricate!
+ Page::Dashboard::Snippet::Index.perform(&:go_to_new_snippet_page)
+
+ Page::Dashboard::Snippet::New.perform do |page|
+ page.fill_title(@title)
+ page.fill_description(@description)
+ page.set_visibility(@visibility)
+ page.fill_file_name(@file_name)
+ page.fill_file_content(@file_content)
+ page.create_snippet
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/namespace.rb b/qa/qa/runtime/namespace.rb
index 704c65467e0..9d7c1aea508 100644
--- a/qa/qa/runtime/namespace.rb
+++ b/qa/qa/runtime/namespace.rb
@@ -8,7 +8,9 @@ module QA
end
def name
- Runtime::Env.namespace_name || "qa-test-#{time.strftime('%Y-%m-%d-%H-%M-%S')}"
+ # If any changes are made to the name tag, following script has to be considered:
+ # https://ops.gitlab.net/gitlab-com/gl-infra/traffic-generator/blob/master/bin/janitor.bash
+ @name ||= Runtime::Env.namespace_name || "qa-test-#{time.strftime('%Y-%m-%d-%H-%M-%S')}-#{SecureRandom.hex(8)}"
end
def path
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index c5f12255d72..41ab702d8b2 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -9,7 +9,7 @@ module QA
attr_reader :api_url, :ca_certificate, :token, :rbac
- def initialize(rbac: false)
+ def initialize(rbac: true)
@rbac = rbac
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
index 4070a225260..d8609aa037a 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/add_project_member_spec.rb
@@ -19,7 +19,7 @@ module QA
page.add_member(user.username)
end
- expect(page).to have_content("#{user.name} @#{user.username} Given access")
+ expect(page).to have_content(/#{user.name} (. )?@#{user.username} Given access/)
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
index 3ce48de2c25..a9eafd61a91 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/import_github_repo_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
module QA
- context 'Manage', :orchestrated, :github do
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/58158
+ context 'Manage', :github, :quarantine do
describe 'Project import from GitHub' do
let(:imported_project) do
Resource::ProjectImportedFromGithub.fabricate! do |project|
@@ -48,20 +49,26 @@ module QA
end
def verify_issues_import
- Page::Project::Menu.act { click_issues }
- expect(page).to have_content('This is a sample issue')
+ QA::Support::Retrier.retry_on_exception do
+ Page::Project::Menu.act { click_issues }
+ expect(page).to have_content('This is a sample issue')
- click_link '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.')
+ 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.')
+ # Comments
+ comment_text = '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')
+ Page::Project::Issue::Show.perform do |issue_page|
+ expect(issue_page).to have_comment(comment_text)
+ end
+
+ 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
end
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
index e172206eb88..fe92fbd3ffe 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/view_project_activity_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/21
- context 'Manage', :quarantine do
+ context 'Manage' do
describe 'Project activity' do
it 'user creates an event in the activity page upon Git push' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
@@ -15,8 +14,8 @@ module QA
end
project_push.project.visit!
- Page::Project::Menu.perform(&:go_to_activity)
- Page::Project::Activity.perform(&:go_to_push_events)
+ Page::Project::Menu.perform(&:click_activity)
+ Page::Project::Activity.perform(&:click_push_events)
expect(page).to have_content('pushed new branch master')
end
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
index 7145b950b6c..358ab04eadc 100644
--- a/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/create_issue_spec.rb
@@ -5,15 +5,6 @@ module QA
describe 'Issue creation' do
let(:issue_title) { 'issue title' }
- def create_issue
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.act { sign_in_using_credentials }
-
- Resource::Issue.fabricate! do |issue|
- issue.title = issue_title
- end
- end
-
it 'user creates an issue' do
create_issue
@@ -46,6 +37,15 @@ module QA
end
end
end
+
+ def create_issue
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Resource::Issue.fabricate! do |issue|
+ issue.title = issue_title
+ end
+ end
end
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
index c06f13ee204..1eada4a6c28 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb
@@ -3,7 +3,29 @@
module QA
context 'Create' do
describe 'Merge request creation' do
- it 'user creates a new merge request' do
+ it 'user creates a new merge request', :smoke do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ current_project = Resource::Project.fabricate! do |project|
+ project.name = 'project-with-merge-request'
+ end
+
+ merge_request_title = 'This is a merge request'
+ merge_request_description = 'Great feature'
+
+ Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.title = merge_request_title
+ merge_request.description = merge_request_description
+ merge_request.project = current_project
+ end
+
+ expect(page).to have_content(merge_request_title)
+ expect(page).to have_content(merge_request_description)
+ expect(page).to have_content(/Opened [\w\s]+ ago/)
+ end
+
+ it 'user creates a new merge request with a milestone and label' do
gitlab_account_username = "@#{Runtime::User.username}"
Runtime::Browser.visit(:gitlab, Page::Main::Login)
@@ -24,9 +46,12 @@ module QA
label.description = 'Merge Request label'
end
+ merge_request_title = 'This is a merge request with a milestone and a label'
+ merge_request_description = 'Great feature with milestone'
+
Resource::MergeRequest.fabricate! do |merge_request|
- merge_request.title = 'This is a merge request with a milestone'
- merge_request.description = 'Great feature with milestone'
+ merge_request.title = merge_request_title
+ merge_request.description = merge_request_description
merge_request.project = current_project
merge_request.milestone = current_milestone
merge_request.assignee = 'me'
@@ -34,8 +59,8 @@ module QA
end
Page::MergeRequest::Show.perform do |merge_request|
- expect(merge_request).to have_content('This is a merge request with a milestone')
- expect(merge_request).to have_content('Great feature with milestone')
+ expect(merge_request).to have_content(merge_request_title)
+ expect(merge_request).to have_content(merge_request_description)
expect(merge_request).to have_content(/Opened [\w\s]+ ago/)
expect(merge_request).to have_assignee(gitlab_account_username)
expect(merge_request).to have_label(new_label.title)
@@ -47,25 +72,4 @@ module QA
end
end
end
-
- describe 'creates a merge request', :smoke 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 = Resource::Project.fabricate! do |project|
- project.name = 'project-with-merge-request'
- end
-
- Resource::MergeRequest.fabricate! do |merge_request|
- merge_request.title = 'This is a merge request'
- merge_request.description = 'Great feature'
- merge_request.project = current_project
- end
-
- expect(page).to have_content('This is a merge request')
- expect(page).to have_content('Great feature')
- expect(page).to have_content(/Opened [\w\s]+ ago/)
- end
- end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
index 60836a7b98d..f146636c49a 100644
--- a/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/squash_merge_request_spec.rb
@@ -2,7 +2,7 @@
module QA
# Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/31
- context 'Create', :quarantine do
+ context 'Create' do
describe 'Merge request squashing' do
it 'user squashes commits while merging' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb
new file mode 100644
index 00000000000..9e48ee7ca2a
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/view_merge_request_diff_patch_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create' do
+ describe 'Download merge request patch and diff' do
+ before(:context) do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+
+ @merge_request = Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.title = 'This is a merge request'
+ merge_request.description = 'For downloading patches and diffs'
+ end
+ end
+
+ it 'user views merge request email patches' do
+ @merge_request.visit!
+ Page::MergeRequest::Show.perform(&:view_email_patches)
+
+ expect(page.text).to start_with('From')
+ expect(page).to have_content('Subject: [PATCH] This is a test commit')
+ expect(page).to have_content('diff --git a/added_file.txt b/added_file.txt')
+ end
+
+ it 'user views merge request plain diff' do
+ @merge_request.visit!
+ Page::MergeRequest::Show.perform(&:view_plain_diff)
+
+ expect(page.text).to start_with('diff --git a/added_file.txt b/added_file.txt')
+ expect(page).to have_content('+File Added')
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
index d1535d6519d..01a367fceac 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_http_spec.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # Git protocol v2 is temporarily disabled
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/55769 (confidential)
+ context 'Create', :quarantine do
describe 'Push over HTTP using Git protocol version 2', :requires_git_protocol_v2 do
it 'user pushes to the repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
index 48800cc81e5..eb59b54e9ab 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/protocol_v2_push_ssh_spec.rb
@@ -1,7 +1,9 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # Git protocol v2 is temporarily disabled
+ # https://gitlab.com/gitlab-org/gitlab-ce/issues/55769 (confidential)
+ context 'Create', :quarantine do
describe 'Push over SSH using Git protocol version 2', :requires_git_protocol_v2 do
# Note: If you run this test against GDK make sure you've enabled sshd and
# enabled setting the Git protocol by adding `AcceptEnv GIT_PROTOCOL` to
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
index 2e4915b898d..99601e3d230 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_mirroring_over_http_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # https://gitlab.com/gitlab-org/quality/staging/issues/40
+ context 'Create', :quarantine do
describe 'Push mirror a repository over HTTP' do
it 'configures and syncs a (push) mirrored repository' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
index 7d96da32423..243f0b83b77 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_over_http_file_size_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/37
+ context 'Create', :quarantine do
describe 'push after setting the file size limit via admin/application_settings' do
before(:all) do
push = Resource::Repository::ProjectPush.fabricate! do |p|
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
index 4464fb812b7..6aebd04af03 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/push_protected_branch_spec.rb
@@ -37,12 +37,7 @@ module QA
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)
-
- expect(push.output)
- .to match(/remote\: GitLab\: You are not allowed to push code to protected branches on this project/)
- expect(push.output)
- .to match(/\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
+ expect { push_new_file(branch_name) }.to raise_error(QA::Git::Repository::RepositoryCommandError, /remote: GitLab: You are not allowed to push code to protected branches on this project\.([\s\S]+)\[remote rejected\] #{branch_name} -> #{branch_name} \(pre-receive hook declined\)/)
end
end
diff --git a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_raw_diff_patch_requests_spec.rb b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
index b862a7bd1ed..b862a7bd1ed 100644
--- a/qa/qa/specs/features/browser_ui/3_create/repository/user_views_raw_diff_patch_requests_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/repository/user_views_commit_diff_patch_spec.rb
diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb
new file mode 100644
index 00000000000..ab53dff464e
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_snippet_spec.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Create', :smoke do
+ describe 'Snippet creation' do
+ it 'User creates a snippet' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+
+ Page::Main::Menu.perform(&:go_to_snippets)
+
+ Resource::Snippet.fabricate_via_browser_ui! do |snippet|
+ snippet.title = 'Snippet title'
+ snippet.description = 'Snippet description'
+ snippet.visibility = 'Public'
+ snippet.file_name = 'New snippet file name'
+ snippet.file_content = 'Snippet file text'
+ end
+
+ Page::Dashboard::Snippet::Show.perform do |snippet|
+ expect(snippet).to have_snippet_title('Snippet title')
+ expect(snippet).to have_snippet_description('Snippet description')
+ expect(snippet).to have_embed_type('Embed')
+ expect(snippet).to have_visibility_type('Public')
+ expect(snippet).to have_file_name('New snippet file name')
+ expect(snippet).to have_file_content('Snippet file text')
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
index f176ec31abd..66cd712afb0 100644
--- a/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
+++ b/qa/qa/specs/features/browser_ui/3_create/web_ide/add_file_template_spec.rb
@@ -1,7 +1,8 @@
# frozen_string_literal: true
module QA
- context 'Create' do
+ # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/46
+ context 'Create', :quarantine do
describe 'Web IDE file templates' do
include Runtime::Fixtures
diff --git a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb
index e444bc7ef1b..0837b720df1 100644
--- a/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb
+++ b/qa/qa/specs/features/browser_ui/4_verify/ci_variable/add_ci_variable_spec.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
module QA
- # Failure issue: https://gitlab.com/gitlab-org/quality/staging/issues/30
- context 'Verify', :quarantine do
+ context 'Verify' do
describe 'CI variable support' do
it 'user adds a CI variable' do
Runtime::Browser.visit(:gitlab, Page::Main::Login)
diff --git a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
index 2375aa4ce91..3f65eabc756 100644
--- a/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
+++ b/qa/qa/specs/features/browser_ui/6_release/deploy_key/clone_using_deploy_key_spec.rb
@@ -95,7 +95,7 @@ module QA
Page::Project::Pipeline::Show.perform(&:go_to_first_job)
Page::Project::Job::Show.perform do |job|
- expect(job).to be_successful, "Job status did not become \"passed\"."
+ expect(job).to be_successful
expect(job.output).to include(sha1sum)
end
end
diff --git a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
index 8cd353fa250..ceb888bb4ef 100644
--- a/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
+++ b/qa/qa/specs/features/browser_ui/7_configure/auto_devops/create_project_with_auto_devops_spec.rb
@@ -3,176 +3,132 @@
require 'pathname'
module QA
- # Transient failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/68
- context 'Configure', :orchestrated, :kubernetes, :quarantine do
- describe 'Auto DevOps support' do
- def login
- Runtime::Browser.visit(:gitlab, Page::Main::Login)
- Page::Main::Login.perform(&:sign_in_using_credentials)
- end
-
- [true, false].each do |rbac|
- context "when rbac is #{rbac ? 'enabled' : 'disabled'}" do
- before(:all) do
- login
-
- @project = Resource::Project.fabricate! do |p|
- p.name = Runtime::Env.auto_devops_project_name || 'project-with-autodevops'
- p.description = 'Project with Auto DevOps'
- end
-
- # Disable code_quality check in Auto DevOps pipeline as it takes
- # too long and times out the test
- Resource::CiVariable.fabricate! do |resource|
- resource.project = @project
- resource.key = 'CODE_QUALITY_DISABLED'
- resource.value = '1'
- end
+ context 'Configure' do
+ def login
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+ end
- # Create Auto DevOps compatible repo
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = @project
- push.directory = Pathname
- .new(__dir__)
- .join('../../../../../fixtures/auto_devops_rack')
- push.commit_message = 'Create Auto DevOps compatible rack application'
- end
+ # Transient failure issue: https://gitlab.com/gitlab-org/quality/nightly/issues/68
+ describe 'Auto DevOps support', :orchestrated, :kubernetes, :quarantine do
+ context 'when rbac is enabled' do
+ before(:all) do
+ @cluster = Service::KubernetesCluster.new.create!
+ end
- # Create and connect K8s cluster
- @cluster = Service::KubernetesCluster.new(rbac: rbac).create!
- kubernetes_cluster = Resource::KubernetesCluster.fabricate! do |cluster|
- cluster.project = @project
- cluster.cluster = @cluster
- cluster.install_helm_tiller = true
- cluster.install_ingress = true
- cluster.install_prometheus = true
- cluster.install_runner = true
- end
+ after(:all) do
+ @cluster&.remove!
+ end
- kubernetes_cluster.populate(:ingress_ip)
- @project.visit!
- Page::Project::Menu.perform(&:click_ci_cd_settings)
- Page::Project::Settings::CICD.perform do |p|
- p.enable_auto_devops
- end
+ it 'runs auto devops' do
+ login
- kubernetes_cluster.populate(:domain)
+ @project = Resource::Project.fabricate! do |p|
+ p.name = Runtime::Env.auto_devops_project_name || 'project-with-autodevops'
+ p.description = 'Project with Auto DevOps'
end
- after(:all) do
- @cluster&.remove!
+ # Disable code_quality check in Auto DevOps pipeline as it takes
+ # too long and times out the test
+ Resource::CiVariable.fabricate! do |resource|
+ resource.project = @project
+ resource.key = 'CODE_QUALITY_DISABLED'
+ resource.value = '1'
end
- before do
- login
+ # Set an application secret CI variable (prefixed with K8S_SECRET_)
+ Resource::CiVariable.fabricate! do |resource|
+ resource.project = @project
+ resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE'
+ resource.value = 'You can see this application secret'
end
- it 'runs auto devops' do
- @project.visit!
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:go_to_latest_pipeline)
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.go_to_job('build')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 600), "Job did not pass"
-
- job.click_element(:pipeline_path)
- end
-
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.go_to_job('test')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 600), "Job did not pass"
-
- job.click_element(:pipeline_path)
- end
+ # Connect K8s cluster
+ Resource::KubernetesCluster.fabricate! do |cluster|
+ cluster.project = @project
+ cluster.cluster = @cluster
+ cluster.install_helm_tiller = true
+ cluster.install_ingress = true
+ cluster.install_prometheus = true
+ cluster.install_runner = true
+ end
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.go_to_job('production')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 1200), "Job did not pass"
+ # Create Auto DevOps compatible repo
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = @project
+ push.directory = Pathname
+ .new(__dir__)
+ .join('../../../../../fixtures/auto_devops_rack')
+ push.commit_message = 'Create Auto DevOps compatible rack application'
+ end
- job.click_element(:pipeline_path)
- end
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:go_to_latest_pipeline)
- Page::Project::Menu.perform(&:click_operations_environments)
- Page::Project::Operations::Environments::Index.perform do |index|
- index.go_to_environment('production')
- end
- Page::Project::Operations::Environments::Show.perform do |show|
- show.view_deployment do
- expect(page).to have_content('Hello World!')
- end
- end
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.go_to_job('build')
end
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 600)
- it 'user sets application secret variable and Auto DevOps passes it to container' do
- # Set an application secret CI variable (prefixed with K8S_SECRET_)
- Resource::CiVariable.fabricate! do |resource|
- resource.project = @project
- resource.key = 'K8S_SECRET_OPTIONAL_MESSAGE'
- resource.value = 'You can see this application secret'
- end
-
- # Our current Auto DevOps implementation won't update the production
- # app if we only update a CI variable with no code change.
- #
- # Workaround: push new code and use the resultant pipeline.
- Resource::Repository::ProjectPush.fabricate! do |push|
- push.project = @project
- push.commit_message = 'Force a Deployment change by pushing new code'
- push.file_name = 'new_file.txt'
- push.file_content = 'new file contents'
- end
+ job.click_element(:pipeline_path)
+ end
- @project.visit!
- Page::Project::Menu.perform(&:click_ci_cd_pipelines)
- Page::Project::Pipeline::Index.perform(&:go_to_latest_pipeline)
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.go_to_job('test')
+ end
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 600)
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.go_to_job('build')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 600), "Job did not pass"
+ job.click_element(:pipeline_path)
+ end
- job.click_element(:pipeline_path)
- end
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ pipeline.go_to_job('production')
+ end
+ Page::Project::Job::Show.perform do |job|
+ expect(job).to be_successful(timeout: 1200)
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.go_to_job('test')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 600), "Job did not pass"
+ job.click_element(:pipeline_path)
+ end
- job.click_element(:pipeline_path)
+ Page::Project::Menu.perform(&:click_operations_environments)
+ Page::Project::Operations::Environments::Index.perform do |index|
+ index.go_to_environment('production')
+ end
+ Page::Project::Operations::Environments::Show.perform do |show|
+ show.view_deployment do
+ expect(page).to have_content('Hello World!')
+ expect(page).to have_content('You can see this application secret')
end
+ end
+ end
+ end
+ end
- Page::Project::Pipeline::Show.perform do |pipeline|
- pipeline.go_to_job('production')
- end
- Page::Project::Job::Show.perform do |job|
- expect(job).to be_sucessful(timeout: 1200), "Job did not pass"
+ describe 'Auto DevOps', :smoke do
+ it 'enables AutoDevOps by default' do
+ login
- job.click_element(:pipeline_path)
- end
+ project = Resource::Project.fabricate! do |p|
+ p.name = Runtime::Env.auto_devops_project_name || 'project-with-autodevops'
+ p.description = 'Project with AutoDevOps'
+ end
- Page::Project::Menu.perform(&:click_operations_environments)
+ # Create AutoDevOps repo
+ Resource::Repository::ProjectPush.fabricate! do |push|
+ push.project = project
+ push.directory = Pathname
+ .new(__dir__)
+ .join('../../../../../fixtures/auto_devops_rack')
+ push.commit_message = 'Create AutoDevOps compatible Project'
+ end
- Page::Project::Operations::Environments::Index.perform do |index|
- index.go_to_environment('production')
- end
+ Page::Project::Menu.perform(&:click_ci_cd_pipelines)
+ Page::Project::Pipeline::Index.perform(&:go_to_latest_pipeline)
- Page::Project::Operations::Environments::Show.perform do |show|
- show.view_deployment do
- expect(page).to have_content('Hello World!')
- expect(page).to have_content('You can see this application secret')
- end
- end
- end
+ Page::Project::Pipeline::Show.perform do |pipeline|
+ expect(pipeline).to have_tag('Auto DevOps')
end
end
end
diff --git a/qa/qa/specs/helpers/quarantine.rb b/qa/qa/specs/helpers/quarantine.rb
new file mode 100644
index 00000000000..52cb05fcd13
--- /dev/null
+++ b/qa/qa/specs/helpers/quarantine.rb
@@ -0,0 +1,68 @@
+# frozen_string_literal: true
+
+require 'rspec/core'
+
+module QA::Specs::Helpers
+ module Quarantine
+ include RSpec::Core::Pending
+
+ extend self
+
+ def configure_rspec
+ RSpec.configure do |config|
+ config.before(:context, :quarantine) do
+ Quarantine.skip_or_run_quarantined_contexts(config.inclusion_filter.rules, self.class)
+ end
+
+ config.before do |example|
+ Quarantine.skip_or_run_quarantined_tests_or_contexts(config.inclusion_filter.rules, example)
+ end
+ end
+ end
+
+ # Skip tests in quarantine unless we explicitly focus on them.
+ def skip_or_run_quarantined_tests_or_contexts(filters, example)
+ if filters.key?(:quarantine)
+ included_filters = filters_other_than_quarantine(filters)
+
+ # If :quarantine is focused, skip the test/context unless its metadata
+ # includes quarantine and any other filters
+ # E.g., Suppose a test is tagged :smoke and :quarantine, and another is tagged
+ # :ldap and :quarantine. If we wanted to run just quarantined smoke tests
+ # using `--tag quarantine --tag smoke`, without this check we'd end up
+ # running that ldap test as well because of the :quarantine metadata.
+ # We could use an exclusion filter, but this way the test report will list
+ # the quarantined tests when they're not run so that we're aware of them
+ skip("Only running tests tagged with :quarantine and any of #{included_filters.keys}") if should_skip_when_focused?(example.metadata, included_filters)
+ else
+ skip('In quarantine') if example.metadata.key?(:quarantine)
+ end
+ end
+
+ # Skip the entire context if a context is quarantined. This avoids running
+ # before blocks unnecessarily.
+ def skip_or_run_quarantined_contexts(filters, example)
+ return unless example.metadata.key?(:quarantine)
+
+ skip_or_run_quarantined_tests_or_contexts(filters, example)
+ end
+
+ def filters_other_than_quarantine(filter)
+ filter.reject { |key, _| key == :quarantine }
+ end
+
+ # Checks if a test or context should be skipped.
+ #
+ # Returns true if
+ # - the metadata does not includes the :quarantine tag
+ # or if
+ # - the metadata includes the :quarantine tag
+ # - and the filter includes other tags that aren't in the metadata
+ def should_skip_when_focused?(metadata, included_filters)
+ return true unless metadata.key?(:quarantine)
+ return false if included_filters.empty?
+
+ (metadata.keys & included_filters.keys).empty?
+ end
+ end
+end
diff --git a/qa/qa/support/api.rb b/qa/qa/support/api.rb
index 8aa7d6812ac..229bfb44fa5 100644
--- a/qa/qa/support/api.rb
+++ b/qa/qa/support/api.rb
@@ -20,6 +20,16 @@ module QA
e.response
end
+ def put(url, payload)
+ RestClient::Request.execute(
+ method: :put,
+ url: url,
+ payload: payload,
+ verify_ssl: false)
+ rescue RestClient::ExceptionWithResponse => e
+ e.response
+ end
+
def delete(url)
RestClient::Request.execute(
method: :delete,
diff --git a/qa/qa/support/page/logging.rb b/qa/qa/support/page/logging.rb
index 5e97a92a6e3..69b6332ecce 100644
--- a/qa/qa/support/page/logging.rb
+++ b/qa/qa/support/page/logging.rb
@@ -33,9 +33,9 @@ module QA
exists
end
- def find_element(name, text_filter = nil, wait: Capybara.default_max_wait_time)
+ def find_element(name, text: nil, wait: Capybara.default_max_wait_time)
msg = ["finding :#{name}"]
- msg << %Q(with text_filter "#{text_filter}") if text_filter
+ msg << %Q(with text "#{text}") if text
msg << "(wait: #{wait})"
log(msg.compact.join(' '))
@@ -76,10 +76,15 @@ module QA
super
end
- def has_element?(name, wait: Capybara.default_max_wait_time)
+ def has_element?(name, text: nil, wait: Capybara.default_max_wait_time)
found = super
- log("has_element? :#{name} returned #{found}")
+ msg = ["has_element? :#{name}"]
+ msg << %Q(with text "#{text}") if text
+ msg << "(wait: #{wait})"
+ msg << "returned: #{found}"
+
+ log(msg.compact.join(' '))
found
end
diff --git a/qa/qa/tools/generate_perf_testdata.rb b/qa/qa/tools/generate_perf_testdata.rb
new file mode 100644
index 00000000000..4fda49c8e48
--- /dev/null
+++ b/qa/qa/tools/generate_perf_testdata.rb
@@ -0,0 +1,143 @@
+# frozen_string_literal: true
+
+require 'securerandom'
+require 'faker'
+require 'yaml'
+require_relative '../../qa'
+# This script generates testdata for Performance Testing.
+# Required environment variables: PERSONAL_ACCESS_TOKEN and GITLAB_ADDRESS
+# This job creates a urls.txt which contains a hash of all the URLs needed for Performance Testing
+# Run `rake generate_perf_testdata`
+
+module QA
+ module Tools
+ class GeneratePerfTestdata
+ include Support::Api
+
+ def initialize
+ raise ArgumentError, "Please provide GITLAB_ADDRESS" unless ENV['GITLAB_ADDRESS']
+ raise ArgumentError, "Please provide PERSONAL_ACCESS_TOKEN" unless ENV['PERSONAL_ACCESS_TOKEN']
+
+ @api_client = Runtime::API::Client.new(ENV['GITLAB_ADDRESS'], personal_access_token: ENV['PERSONAL_ACCESS_TOKEN'])
+ @group_name = "gitlab-qa-perf-sandbox-#{SecureRandom.hex(8)}"
+ @project_name = "my-test-project-#{SecureRandom.hex(8)}"
+ @visibility = "public"
+ @urls = { host: ENV['GITLAB_ADDRESS'] }
+ end
+
+ def run
+ STDOUT.puts 'Running...'
+ group_id = create_group
+ create_project(group_id)
+ create_branch
+ add_new_file
+ methods_arr = [
+ method(:create_issues),
+ method(:create_todos),
+ method(:create_merge_requests),
+ method(:create_issue_with_500_discussions),
+ method(:create_mr_with_large_files)
+ ]
+ threads_arr = []
+
+ methods_arr.each do |m|
+ threads_arr << Thread.new { m.call }
+ end
+
+ threads_arr.each(&:join)
+ STDOUT.puts "\nURLs: #{@urls}"
+ File.open("urls.yml", "w") { |file| file.puts @urls.stringify_keys.to_yaml }
+ STDOUT.puts "\nDone"
+ end
+
+ private
+
+ def create_group
+ group_search_response = post Runtime::API::Request.new(@api_client, "/groups").url, "name=#{@group_name}&path=#{@group_name}&visibility=#{@visibility}"
+ group = JSON.parse(group_search_response.body)
+ @urls[:group_page] = group["web_url"]
+ group["id"]
+ end
+
+ def create_project(group_id)
+ create_project_response = post Runtime::API::Request.new(@api_client, "/projects").url, "name=#{@project_name}&namespace_id=#{group_id}&visibility=#{@visibility}"
+ @urls[:project_page] = JSON.parse(create_project_response.body)["web_url"]
+ end
+
+ def create_issues
+ 30.times do |i|
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/issues").url, "title=issue#{i}&description=desc#{i}"
+ end
+ @urls[:issues_list_page] = @urls[:project_page] + "/issues"
+ STDOUT.puts "Created Issues"
+ end
+
+ def create_todos
+ 30.times do |i|
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/issues/#{i + 1}/todo").url, nil
+ end
+ @urls[:todos_page] = ENV['GITLAB_ADDRESS'] + "/dashboard/todos"
+ STDOUT.puts "Created todos"
+ end
+
+ def create_merge_requests
+ 30.times do |i|
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/merge_requests").url, "source_branch=branch#{i}&target_branch=master&title=MR#{i}"
+ end
+ @urls[:mr_list_page] = @urls[:project_page] + "/merge_requests"
+ STDOUT.puts "Created MRs"
+ end
+
+ def add_new_file
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/files/hello.txt").url, "branch=master&commit_message=\"hello\"&content=\"my new content\""
+ 30.times do |i|
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/files/hello#{i}.txt").url, "branch=branch#{i}&commit_message=\"hello\"&content=\"my new content\""
+ end
+ STDOUT.puts "Added Files"
+ end
+
+ def create_branch
+ 30.times do |i|
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/branches").url, "branch=branch#{i}&ref=master"
+ end
+ STDOUT.puts "Created branches"
+ end
+
+ def create_issue_with_500_discussions
+ issue_id = 1
+ 500.times do
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/issues/#{issue_id}/discussions").url, "body=\"Let us discuss\""
+ end
+ @urls[:large_issue] = @urls[:project_page] + "/issues/#{issue_id}"
+ STDOUT.puts "Created Issue with 500 Discussions"
+ end
+
+ def create_mr_with_large_files
+ content_arr = []
+ 20.times do |i|
+ faker_line_arr = Faker::Lorem.sentences(1500)
+ content = faker_line_arr.join("\n\r")
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/files/hello#{i}.txt").url, "branch=master&commit_message=\"Add hello#{i}.txt\"&content=#{content}"
+ content_arr[i] = faker_line_arr
+ end
+
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/branches").url, "branch=performance&ref=master"
+
+ 20.times do |i|
+ missed_line_array = content_arr[i].each_slice(2).map(&:first)
+ content = missed_line_array.join("\n\rIm new!:D \n\r ")
+ put Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/repository/files/hello#{i}.txt").url, "branch=performance&commit_message=\"Update hello#{i}.txt\"&content=#{content}"
+ end
+
+ create_mr_response = post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/merge_requests").url, "source_branch=performance&target_branch=master&title=Large_MR"
+
+ iid = JSON.parse(create_mr_response.body)["iid"]
+ 500.times do
+ post Runtime::API::Request.new(@api_client, "/projects/#{@group_name}%2F#{@project_name}/merge_requests/#{iid}/discussions").url, "body=\"Let us discuss\""
+ end
+ @urls[:large_mr] = JSON.parse(create_mr_response.body)["web_url"]
+ STDOUT.puts "Created MR with 500 Discussions and 20 Very Large Files"
+ end
+ end
+ end
+end
diff --git a/qa/spec/git/repository_spec.rb b/qa/spec/git/repository_spec.rb
index 62c81050bd9..0ded33a73a2 100644
--- a/qa/spec/git/repository_spec.rb
+++ b/qa/spec/git/repository_spec.rb
@@ -39,7 +39,7 @@ describe QA::Git::Repository do
describe '#clone' do
it 'is unable to resolve host' do
- expect(repository.clone).to include("fatal: unable to access 'http://root@foo/bar.git/'")
+ expect { repository.clone }.to raise_error(described_class::RepositoryCommandError, /The command .* failed \(128\) with the following output/)
end
end
@@ -49,7 +49,7 @@ describe QA::Git::Repository do
end
it 'fails to push changes' do
- expect(repository.push_changes).to include("error: failed to push some refs to 'http://root@foo/bar.git'")
+ expect { repository.push_changes }.to raise_error(described_class::RepositoryCommandError, /The command .* failed \(1\) with the following output/)
end
end
diff --git a/qa/spec/page/logging_spec.rb b/qa/spec/page/logging_spec.rb
index a6e9601cee4..707a7ff6d98 100644
--- a/qa/spec/page/logging_spec.rb
+++ b/qa/spec/page/logging_spec.rb
@@ -62,10 +62,10 @@ describe QA::Support::Page::Logging do
.to output(/found :element/).to_stdout_from_any_process
end
- it 'logs find_element with text_filter' do
- expect { subject.find_element(:element, 'foo') }
- .to output(/finding :element with text_filter "foo"/).to_stdout_from_any_process
- expect { subject.find_element(:element, 'foo') }
+ it 'logs find_element with text' do
+ expect { subject.find_element(:element, text: 'foo') }
+ .to output(/finding :element with text "foo"/).to_stdout_from_any_process
+ expect { subject.find_element(:element, text: 'foo') }
.to output(/found :element/).to_stdout_from_any_process
end
@@ -81,7 +81,12 @@ describe QA::Support::Page::Logging do
it 'logs has_element?' do
expect { subject.has_element?(:element) }
- .to output(/has_element\? :element returned true/).to_stdout_from_any_process
+ .to output(/has_element\? :element \(wait: 2\) returned: true/).to_stdout_from_any_process
+ end
+
+ it 'logs has_element? with text' do
+ expect { subject.has_element?(:element, text: "some text") }
+ .to output(/has_element\? :element with text \"some text\" \(wait: 2\) returned: true/).to_stdout_from_any_process
end
it 'logs has_no_element?' do
diff --git a/qa/spec/spec_helper.rb b/qa/spec/spec_helper.rb
index 20a153f3f63..be13c3fb683 100644
--- a/qa/spec/spec_helper.rb
+++ b/qa/spec/spec_helper.rb
@@ -1,20 +1,15 @@
require_relative '../qa'
+require 'rspec/retry'
%w[helpers shared_examples].each do |d|
Dir[::File.join(__dir__, d, '**', '*.rb')].each { |f| require f }
end
RSpec.configure do |config|
- config.before(:context) do
- if self.class.metadata.keys.include?(:quarantine)
- skip_or_run_quarantined_tests(self.class.metadata.keys, config.inclusion_filter.rules.keys)
- end
- end
+ QA::Specs::Helpers::Quarantine.configure_rspec
config.before do |example|
QA::Runtime::Logger.debug("Starting test: #{example.full_description}") if QA::Runtime::Env.debug?
-
- skip_or_run_quarantined_tests(example.metadata.keys, config.inclusion_filter.rules.keys)
end
config.expect_with :rspec do |expectations|
@@ -31,43 +26,15 @@ RSpec.configure do |config|
config.profile_examples = 10
config.order = :random
Kernel.srand config.seed
-end
-# Skip tests in quarantine unless we explicitly focus on them.
-# Skip the entire context if a context is tagged. This avoids running before
-# blocks unnecessarily.
-# If quarantine is focussed, skip tests/contexts that have other metadata
-# unless they're also focussed. This lets us run quarantined tests in a
-# particular category without running tests in other categories.
-# E.g., if a test is tagged 'smoke' and 'quarantine', and another is tagged
-# 'ldap' and 'quarantine', if we wanted to run just quarantined smoke tests
-# using `--tag quarantine --tag smoke`, without this check we'd end up
-# running that ldap test as well.
-# We could use an exclusion filter, but this way the test report will list
-# the quarantined tests when they're not run so that we're aware of them
-def skip_or_run_quarantined_tests(metadata_keys, filter_keys)
- included_filters = filters_other_than_quarantine(filter_keys)
+ # show retry status in spec process
+ config.verbose_retry = true
- if filter_keys.include?(:quarantine)
- skip("Only running tests tagged with :quarantine and any of #{included_filters}") unless quarantine_and_optional_other_tag?(metadata_keys, included_filters)
- else
- skip('In quarantine') if metadata_keys.include?(:quarantine)
- end
-end
+ # show exception that triggers a retry if verbose_retry is set to true
+ config.display_try_failure_messages = true
-def filters_other_than_quarantine(filter_keys)
- filter_keys.reject { |key| key == :quarantine }
-end
-
-# Checks if a test has the 'quarantine' tag and other tags in the inclusion filter.
-#
-# Returns true if
-# - the metadata includes the quarantine tag
-# - and the metadata and inclusion filter both have any other tag
-# - or no other tags are in the inclusion filter
-def quarantine_and_optional_other_tag?(metadata_keys, included_filters)
- return false unless metadata_keys.include? :quarantine
- return true if included_filters.empty?
-
- included_filters.any? { |key| metadata_keys.include? key }
+ config.around do |example|
+ retry_times = example.metadata.keys.include?(:quarantine) ? 1 : 3
+ example.run_with_retry retry: retry_times
+ end
end
diff --git a/qa/spec/spec_helper_spec.rb b/qa/spec/spec_helper_spec.rb
deleted file mode 100644
index 2427999e110..00000000000
--- a/qa/spec/spec_helper_spec.rb
+++ /dev/null
@@ -1,304 +0,0 @@
-# frozen_string_literal: true
-
-describe 'rspec config tests' do
- let(:group) do
- RSpec.describe do
- shared_examples 'passing tests' do
- example 'not in quarantine' do
- end
- example 'in quarantine', :quarantine do
- end
- end
-
- context 'default' do
- it_behaves_like 'passing tests'
- end
-
- context 'foo', :foo do
- it_behaves_like 'passing tests'
- end
-
- context 'quarantine', :quarantine do
- it_behaves_like 'passing tests'
- end
-
- context 'bar quarantine', :bar, :quarantine do
- it_behaves_like 'passing tests'
- end
- end
- end
-
- context 'with no tags focussed' do
- before do
- group.run
- end
-
- context 'in a context tagged :foo' do
- it 'skips tests in quarantine' do
- context = group.children.find { |c| c.description == "foo" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to eq(2)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- expect(ex.execution_result.pending_message).to eq('In quarantine')
- end
- end
-
- context 'in an untagged context' do
- it 'skips tests in quarantine' do
- context = group.children.find { |c| c.description == "default" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to eq(2)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- expect(ex.execution_result.pending_message).to eq('In quarantine')
- end
- end
-
- context 'in a context tagged :quarantine' do
- it 'skips all tests' do
- context = group.children.find { |c| c.description == "quarantine" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to eq(2)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- expect(ex.execution_result.pending_message).to eq('In quarantine')
- end
- end
- end
-
- context 'with :quarantine focussed' do
- before do
- RSpec.configure do |config|
- config.inclusion_filter = :quarantine
- end
-
- group.run
- end
- after do
- RSpec.configure do |config|
- config.inclusion_filter.clear
- end
- end
-
- context 'in an untagged context' do
- it 'only runs quarantined tests' do
- context = group.children.find { |c| c.description == "default" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(1)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
- end
- end
-
- context 'in a context tagged :foo' do
- it 'only runs quarantined tests' do
- context = group.children.find { |c| c.description == "foo" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(1)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
- end
- end
-
- context 'in a context tagged :quarantine' do
- it 'runs all tests' do
- context = group.children.find { |c| c.description == "quarantine" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(2)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
- end
- end
- end
-
- context 'with a non-quarantine tag (:foo) focussed' do
- before do
- RSpec.configure do |config|
- config.inclusion_filter = :foo
- end
-
- group.run
- end
- after do
- RSpec.configure do |config|
- config.inclusion_filter.clear
- end
- end
-
- context 'in an untagged context' do
- it 'runs no tests' do
- context = group.children.find { |c| c.description == "default" }
- expect(context.descendant_filtered_examples.count).to eq(0)
- end
- end
-
- context 'in a context tagged :foo' do
- it 'skips quarantined tests' do
- context = group.children.find { |c| c.description == "foo" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(2)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- expect(ex.execution_result.pending_message).to eq('In quarantine')
- end
- end
-
- context 'in a context tagged :quarantine' do
- it 'runs no tests' do
- context = group.children.find { |c| c.description == "quarantine" }
- expect(context.descendant_filtered_examples.count).to eq(0)
- end
- end
- end
-
- context 'with :quarantine and a non-quarantine tag (:foo) focussed' do
- before do
- RSpec.configure do |config|
- config.inclusion_filter = { quarantine: true, foo: true }
- end
-
- group.run
- end
- after do
- RSpec.configure do |config|
- config.inclusion_filter.clear
- end
- end
-
- context 'in an untagged context' do
- it 'ignores untagged tests and skips tests even if in quarantine' do
- context = group.children.find { |c| c.description == "default" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to eq(1)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- end
- end
-
- context 'in a context tagged :foo' do
- it 'only runs quarantined tests' do
- context = group.children.find { |c| c.description == "foo" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(2)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- end
- end
-
- context 'in a context tagged :quarantine' do
- it 'skips all tests' do
- context = group.children.find { |c| c.description == "quarantine" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(2)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- end
- end
-
- context 'in a context tagged :bar and :quarantine' do
- it 'skips all tests' do
- context = group.children.find { |c| c.description == "quarantine" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(2)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- end
- end
- end
-
- context 'with :quarantine and multiple non-quarantine tags focussed' do
- before do
- RSpec.configure do |config|
- config.inclusion_filter = { bar: true, foo: true, quarantine: true }
- end
-
- group.run
- end
- after do
- RSpec.configure do |config|
- config.inclusion_filter.clear
- end
- end
-
- context 'in a context tagged :foo' do
- it 'only runs quarantined tests' do
- context = group.children.find { |c| c.description == "foo" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(2)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- expect(ex.execution_result.pending_message).to eq('Only running tests tagged with :quarantine and any of [:bar, :foo]')
- end
- end
-
- context 'in a context tagged :quarantine' do
- it 'skips all tests' do
- context = group.children.find { |c| c.description == "quarantine" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(2)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- expect(ex.execution_result.pending_message).to eq('Only running tests tagged with :quarantine and any of [:bar, :foo]')
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:pending)
- expect(ex.execution_result.pending_message).to eq('Only running tests tagged with :quarantine and any of [:bar, :foo]')
- end
- end
-
- context 'in a context tagged :bar and :quarantine' do
- it 'runs all tests' do
- context = group.children.find { |c| c.description == "bar quarantine" }
- examples = context.descendant_filtered_examples
- expect(examples.count).to be(2)
-
- ex = examples.find { |e| e.description == "in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
-
- ex = examples.find { |e| e.description == "not in quarantine" }
- expect(ex.execution_result.status).to eq(:passed)
- end
- end
- end
-end
diff --git a/qa/spec/specs/helpers/quarantine_spec.rb b/qa/spec/specs/helpers/quarantine_spec.rb
new file mode 100644
index 00000000000..78beda39b5e
--- /dev/null
+++ b/qa/spec/specs/helpers/quarantine_spec.rb
@@ -0,0 +1,271 @@
+# frozen_string_literal: true
+
+require 'rspec/core/sandbox'
+
+# We need a reporter for internal tests that's different from the reporter for
+# external tests otherwise the results will be mixed up. We don't care about
+# most reporting, but we do want to know if a test fails
+class RaiseOnFailuresReporter < RSpec::Core::NullReporter
+ def self.example_failed(example)
+ raise example.exception
+ end
+end
+
+# We use an example group wrapper to prevent the state of internal tests
+# expanding into the global state
+# See: https://github.com/rspec/rspec-core/issues/2603
+def describe_successfully(*args, &describe_body)
+ example_group = RSpec.describe(*args, &describe_body)
+ ran_successfully = example_group.run RaiseOnFailuresReporter
+ expect(ran_successfully).to eq true
+ example_group
+end
+
+RSpec.configure do |c|
+ c.around do |ex|
+ RSpec::Core::Sandbox.sandboxed do |config|
+ # If there is an example-within-an-example, we want to make sure the inner example
+ # does not get a reference to the outer example (the real spec) if it calls
+ # something like `pending`
+ config.before(:context) { RSpec.current_example = nil }
+
+ config.color_mode = :off
+
+ # Load airborne again to avoid "undefined method `match_expected_default?'" errors
+ # that happen because a hook calls a method added via a custom RSpec setting
+ # that is removed when the RSpec configuration is sandboxed.
+ # If this needs to be changed (e.g., to load other libraries as well), see
+ # this discussion for alternative solutions:
+ # https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/25223#note_143392053
+ load 'airborne.rb'
+
+ ex.run
+ end
+ end
+end
+
+describe QA::Specs::Helpers::Quarantine do
+ describe '.skip_or_run_quarantined_contexts' do
+ context 'with no tag focused' do
+ before do
+ described_class.configure_rspec
+ end
+
+ it 'skips before hooks of quarantined contexts' do
+ executed_hooks = []
+
+ group = describe_successfully('quarantine', :quarantine) do
+ before(:all) do
+ executed_hooks << :before_all
+ end
+ before do
+ executed_hooks << :before
+ end
+ example {}
+ end
+
+ expect(executed_hooks).to eq []
+ expect(group.descendant_filtered_examples.first.execution_result.status).to eq(:pending)
+ expect(group.descendant_filtered_examples.first.execution_result.pending_message)
+ .to eq('In quarantine')
+ end
+
+ it 'executes before hooks of non-quarantined contexts' do
+ executed_hooks = []
+
+ group = describe_successfully do
+ before(:all) do
+ executed_hooks << :before_all
+ end
+ before do
+ executed_hooks << :before
+ end
+ example {}
+ end
+
+ expect(executed_hooks).to eq [:before_all, :before]
+ expect(group.descendant_filtered_examples.first.execution_result.status).to eq(:passed)
+ end
+ end
+
+ context 'with :quarantine focused' do
+ before do
+ described_class.configure_rspec
+ RSpec.configure do |c|
+ c.filter_run :quarantine
+ end
+ end
+
+ it 'executes before hooks of quarantined contexts' do
+ executed_hooks = []
+
+ group = describe_successfully('quarantine', :quarantine) do
+ before(:all) do
+ executed_hooks << :before_all
+ end
+ before do
+ executed_hooks << :before
+ end
+ example {}
+ end
+
+ expect(executed_hooks).to eq [:before_all, :before]
+ expect(group.descendant_filtered_examples.first.execution_result.status).to eq(:passed)
+ end
+
+ it 'skips before hooks of non-quarantined contexts' do
+ executed_hooks = []
+
+ group = describe_successfully do
+ before(:all) do
+ executed_hooks << :before_all
+ end
+ before do
+ executed_hooks << :before
+ end
+ example {}
+ end
+
+ expect(executed_hooks).to eq []
+ expect(group.descendant_filtered_examples.first).to be_nil
+ end
+ end
+ end
+
+ describe '.skip_or_run_quarantined_tests' do
+ context 'with no tag focused' do
+ before do
+ described_class.configure_rspec
+ end
+
+ it 'skips quarantined tests' do
+ group = describe_successfully do
+ it('is pending', :quarantine) {}
+ end
+
+ expect(group.examples.first.execution_result.status).to eq(:pending)
+ expect(group.examples.first.execution_result.pending_message)
+ .to eq('In quarantine')
+ end
+
+ it 'executes non-quarantined tests' do
+ group = describe_successfully do
+ example {}
+ end
+
+ expect(group.examples.first.execution_result.status).to eq(:passed)
+ end
+ end
+
+ context 'with :quarantine focused' do
+ before do
+ described_class.configure_rspec
+ RSpec.configure do |c|
+ c.filter_run :quarantine
+ end
+ end
+
+ it 'executes quarantined tests' do
+ group = describe_successfully do
+ it('passes', :quarantine) {}
+ end
+
+ expect(group.examples.first.execution_result.status).to eq(:passed)
+ end
+
+ it 'ignores non-quarantined tests' do
+ group = describe_successfully do
+ example {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be_nil
+ end
+ end
+
+ context 'with a non-quarantine tag focused' do
+ before do
+ described_class.configure_rspec
+ RSpec.configure do |c|
+ c.filter_run :foo
+ end
+ end
+
+ it 'ignores non-quarantined non-focused tests' do
+ group = describe_successfully do
+ example {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be_nil
+ end
+
+ it 'executes non-quarantined focused tests' do
+ group = describe_successfully do
+ it('passes', :foo) {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be(:passed)
+ end
+
+ it 'ignores quarantined tests' do
+ group = describe_successfully do
+ it('is ignored', :quarantine) {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be_nil
+ end
+
+ it 'skips quarantined focused tests' do
+ group = describe_successfully do
+ it('is pending', :quarantine, :foo) {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be(:pending)
+ expect(group.examples.first.execution_result.pending_message)
+ .to eq('In quarantine')
+ end
+ end
+
+ context 'with :quarantine and non-quarantine tags focused' do
+ before do
+ described_class.configure_rspec
+ RSpec.configure do |c|
+ c.filter_run :foo, :bar, :quarantine
+ end
+ end
+
+ it 'ignores non-quarantined non-focused tests' do
+ group = describe_successfully do
+ example {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be_nil
+ end
+
+ it 'skips non-quarantined focused tests' do
+ group = describe_successfully do
+ it('is pending', :foo) {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be(:pending)
+ expect(group.examples.first.execution_result.pending_message)
+ .to eq('Only running tests tagged with :quarantine and any of [:bar, :foo]')
+ end
+
+ it 'skips quarantined non-focused tests' do
+ group = describe_successfully do
+ it('is pending', :quarantine) {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be(:pending)
+ end
+
+ it 'executes quarantined focused tests' do
+ group = describe_successfully do
+ it('passes', :quarantine, :foo) {}
+ end
+
+ expect(group.examples.first.execution_result.status).to be(:passed)
+ end
+ end
+ end
+end
diff --git a/scripts/build_assets_image b/scripts/build_assets_image
index 9afada244c8..25b6060b6c4 100755
--- a/scripts/build_assets_image
+++ b/scripts/build_assets_image
@@ -7,14 +7,11 @@ then
fi
# Generate the image name based on the project this is being run in
-ASSETS_IMAGE_NAME=$(echo ${CI_PROJECT_NAME} |
- awk '{
- split($1, p, "-");
- interim = sprintf("%s-assets-%s", p[1], p[2]);
- sub(/-$/, "", interim);
- print interim
- }'
-)
+ASSETS_IMAGE_NAME="gitlab-assets-ce"
+if [[ "${CI_PROJECT_NAME}" == "gitlab-ee" ]]
+then
+ ASSETS_IMAGE_NAME="gitlab-assets-ee"
+fi
ASSETS_IMAGE_PATH=${CI_REGISTRY}/${CI_PROJECT_PATH}/${ASSETS_IMAGE_NAME}
@@ -27,3 +24,9 @@ docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
docker push ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG}
docker push ${ASSETS_IMAGE_PATH}:${CI_COMMIT_SHA}
+# Also tag the image with GitLab version, if running on a tag pipeline, so
+# other projects can simply use that instead of computing the slug.
+if [ -n "$CI_COMMIT_TAG" ]; then
+ docker tag ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_SLUG} ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}
+ docker push ${ASSETS_IMAGE_PATH}:${CI_COMMIT_REF_NAME}
+fi
diff --git a/scripts/frontend/postinstall.js b/scripts/frontend/postinstall.js
index 682039a41b3..94977e459e3 100644
--- a/scripts/frontend/postinstall.js
+++ b/scripts/frontend/postinstall.js
@@ -13,7 +13,7 @@ if (process.platform === 'darwin') {
ensure that it is supported by the fsevents library.
You can try installing again with \`${chalk.cyan('yarn install --force')}\`
- `)
+ `),
);
process.exit(1);
}
diff --git a/scripts/frontend/prettier.js b/scripts/frontend/prettier.js
index ffb09ea9779..bf0e98da139 100644
--- a/scripts/frontend/prettier.js
+++ b/scripts/frontend/prettier.js
@@ -32,7 +32,7 @@ let globDir = process.argv[3] || '';
if (globDir && globDir.charAt(globDir.length - 1) !== '/') globDir += '/';
console.log(
- `Loading all ${allFiles ? '' : 'staged '}files ${globDir ? `within ${globDir} ` : ''}...`
+ `Loading all ${allFiles ? '' : 'staged '}files ${globDir ? `within ${globDir} ` : ''}...`,
);
const globPatterns = matchExtensions.map(ext => `${globDir}**/*.${ext}`);
@@ -105,7 +105,7 @@ Promise.all(matchedFiles.map(checkFileWithPrettierConfig))
.then(() => {
const failAction = shouldSave ? 'fixed' : 'failed';
console.log(
- `\nSummary:\n ${matchedCount} files processed (${passedCount} passed, ${failedCount} ${failAction}, ${ignoredCount} ignored)\n`
+ `\nSummary:\n ${matchedCount} files processed (${passedCount} passed, ${failedCount} ${failAction}, ${ignoredCount} ignored)\n`,
);
if (didWarn) process.exit(1);
diff --git a/scripts/frontend/stylelint/stylelint-duplicate-selectors.js b/scripts/frontend/stylelint/stylelint-duplicate-selectors.js
new file mode 100644
index 00000000000..4b46ee21a7a
--- /dev/null
+++ b/scripts/frontend/stylelint/stylelint-duplicate-selectors.js
@@ -0,0 +1,23 @@
+const stylelint = require('stylelint');
+const utils = require('./stylelint-utils');
+const ruleName = 'stylelint-gitlab/duplicate-selectors';
+
+const messages = stylelint.utils.ruleMessages(ruleName, {
+ expected: (selector1, selector2) => {
+ return `"${selector1}" and "${selector2}" have the same properties.`;
+ },
+});
+
+module.exports = stylelint.createPlugin(ruleName, function(enabled) {
+ if (!enabled) {
+ return;
+ }
+
+ return function(root, result) {
+ const selectorGroups = {};
+ utils.createPropertiesHashmap(root, result, ruleName, messages, selectorGroups, true);
+ };
+});
+
+module.exports.ruleName = ruleName;
+module.exports.messages = messages;
diff --git a/scripts/frontend/stylelint/stylelint-utility-classes.js b/scripts/frontend/stylelint/stylelint-utility-classes.js
new file mode 100644
index 00000000000..8a1cfdbf302
--- /dev/null
+++ b/scripts/frontend/stylelint/stylelint-utility-classes.js
@@ -0,0 +1,24 @@
+const stylelint = require('stylelint');
+const utils = require('./stylelint-utils');
+const utilityClasses = require('./utility-classes-map.js');
+
+const ruleName = 'stylelint-gitlab/utility-classes';
+
+const messages = stylelint.utils.ruleMessages(ruleName, {
+ expected: (selector1, selector2) => {
+ return `"${selector1}" has the same properties as our BS4 utility class "${selector2}" so please use that instead.`;
+ },
+});
+
+module.exports = stylelint.createPlugin(ruleName, function(enabled) {
+ if (!enabled) {
+ return;
+ }
+
+ return function(root, result) {
+ utils.createPropertiesHashmap(root, result, ruleName, messages, utilityClasses, false);
+ };
+});
+
+module.exports.ruleName = ruleName;
+module.exports.messages = messages;
diff --git a/scripts/frontend/stylelint/stylelint-utility-map.js b/scripts/frontend/stylelint/stylelint-utility-map.js
new file mode 100644
index 00000000000..7e012b157b3
--- /dev/null
+++ b/scripts/frontend/stylelint/stylelint-utility-map.js
@@ -0,0 +1,54 @@
+const sass = require('node-sass');
+const postcss = require('postcss');
+const fs = require('fs');
+const path = require('path');
+const prettier = require('prettier');
+
+const utils = require('./stylelint-utils');
+const ROOT_PATH = path.resolve(__dirname, '../../..');
+const hashMapPath = path.resolve(__dirname, './utility-classes-map.js');
+
+//
+// This creates a JS based hash map (saved in utility-classes-map.js) of the different values in the utility classes
+//
+sass.render(
+ {
+ data: `
+ @import './functions';
+ @import './variables';
+ @import './mixins';
+ @import './utilities';
+ `,
+ includePaths: [path.resolve(ROOT_PATH, 'node_modules/bootstrap/scss')],
+ },
+ (err, result) => {
+ if (err) console.error('Error ', err);
+
+ const cssResult = result.css.toString();
+
+ // We just use postcss to create a CSS tree
+ postcss([])
+ .process(cssResult, {
+ // This supresses a postcss warning
+ from: undefined,
+ })
+ .then(result => {
+ const selectorGroups = {};
+ utils.createPropertiesHashmap(result.root, result, null, null, selectorGroups, true);
+
+ const prettierOptions = prettier.resolveConfig.sync(hashMapPath);
+ const prettyHashmap = prettier.format(
+ `module.exports = ${JSON.stringify(selectorGroups)};`,
+ prettierOptions,
+ );
+
+ fs.writeFile(hashMapPath, prettyHashmap, function(err) {
+ if (err) {
+ return console.log(err);
+ }
+
+ console.log('The file was saved!');
+ });
+ });
+ },
+);
diff --git a/scripts/frontend/stylelint/stylelint-utils.js b/scripts/frontend/stylelint/stylelint-utils.js
new file mode 100644
index 00000000000..2d931c1c4c2
--- /dev/null
+++ b/scripts/frontend/stylelint/stylelint-utils.js
@@ -0,0 +1,77 @@
+const stylelint = require('stylelint');
+const md5 = require('md5');
+
+module.exports.createPropertiesHashmap = (
+ ruleRoot,
+ result,
+ ruleName,
+ messages,
+ selectorGroups,
+ addSelectors,
+) => {
+ ruleRoot.walkRules(rule => {
+ const selector = rule.selector.replace(/(?:\r\n|\r|\n)/g, ' ');
+
+ if (
+ rule &&
+ rule.parent &&
+ rule.parent.type != 'atrule' &&
+ !(
+ selector.includes('-webkit-') ||
+ selector.includes('-moz-') ||
+ selector.includes('-o-') ||
+ selector.includes('-ms-') ||
+ selector.includes(':')
+ )
+ ) {
+ let cssArray = [];
+ rule.nodes.forEach(function(property) {
+ const { prop, value } = property;
+ if (property && value) {
+ const propval = `${prop}${value}${property.important ? '!important' : ''}`;
+ cssArray.push(propval);
+ }
+ });
+
+ cssArray = cssArray.sort();
+ const cssContent = cssArray.toString();
+
+ if (cssContent) {
+ const hashValue = md5(cssContent);
+ const selObj = selectorGroups[hashValue];
+
+ const selectorLine = `${selector} (${
+ rule.source.input.file ? rule.source.input.file + ' -' : ''
+ }${rule.source.start.line}:${rule.source.start.column})`;
+
+ if (selObj) {
+ if (selectorGroups[hashValue].selectors.indexOf(selector) == -1) {
+ let lastSelector =
+ selectorGroups[hashValue].selectors[selectorGroups[hashValue].selectors.length - 1];
+
+ // So we have nicer formatting if it is the same file, we remove the filename
+ lastSelector = lastSelector.replace(`${rule.source.input.file} - `, '');
+
+ if (messages) {
+ stylelint.utils.report({
+ result,
+ ruleName,
+ message: messages.expected(selector, lastSelector),
+ node: rule,
+ word: rule.node,
+ });
+ }
+
+ if (addSelectors) {
+ selectorGroups[hashValue].selectors.push(selectorLine);
+ }
+ }
+ } else if (addSelectors) {
+ selectorGroups[hashValue] = {
+ selectors: [selectorLine],
+ };
+ }
+ }
+ }
+ });
+};
diff --git a/scripts/frontend/stylelint/utility-classes-map.js b/scripts/frontend/stylelint/utility-classes-map.js
new file mode 100644
index 00000000000..1929f950f6c
--- /dev/null
+++ b/scripts/frontend/stylelint/utility-classes-map.js
@@ -0,0 +1,216 @@
+module.exports = {
+ '99097f29a9473b56eacdb9ff0681c366': { selectors: ['.align-baseline (1:1)'] },
+ d969b318bb994e104e8c965006d71cb7: { selectors: ['.align-top (4:1)'] },
+ '8cd54ab97b9cc43cb9d13d2ea7c601c7': { selectors: ['.align-middle (7:1)'] },
+ dd06eb6c49e979b7a9fdaa7119aa0a0b: { selectors: ['.align-bottom (10:1)'] },
+ '0af1e90cbc468615e299ec9f49e97c4a': { selectors: ['.align-text-bottom (13:1)'] },
+ '50af706df238cf59bdc634fc684ba0c9': { selectors: ['.align-text-top (16:1)'] },
+ c968922e6e47445362129a684b5913c0: { selectors: ['.bg-primary (19:1)'] },
+ '3c397f9786c24cff4779a11cf5b3d7e7': { selectors: ['.bg-secondary (27:1)'] },
+ '659677469a4477267fabc1788f7cad4e': { selectors: ['.bg-success (35:1)'] },
+ '56d246d5b6a708a4c6f78dbd2444106c': { selectors: ['.bg-info (43:1)'] },
+ '6bec0a33df3a6380c30103db5c273455': { selectors: ['.bg-warning (51:1)'] },
+ '0ce5d074c8667ce6c32360658f428d5d': { selectors: ['.bg-danger (59:1)'] },
+ '0d0269c62a01e97caa9039d227a25d12': { selectors: ['.bg-light (67:1)'] },
+ '3a56309ad8c5b46ebcc3b13fe1987ac1': { selectors: ['.bg-dark (75:1)'] },
+ '0e252f8dd392a33343d3d5efc1e3194a': { selectors: ['.bg-white (83:1)'] },
+ '3af6f52f0ed4f98e797d5f10a35ca6bc': { selectors: ['.bg-transparent (86:1)'] },
+ '16da7fdce74577ceab356509db565612': { selectors: ['.border (89:1)'] },
+ '929622517ca05efde3b51e5f1a57064e': { selectors: ['.border-top (92:1)'] },
+ '7283090353df54f1d515a6ceddfb9693': { selectors: ['.border-right (95:1)'] },
+ bd5670d71332c652b46db82949042e31: { selectors: ['.border-bottom (98:1)'] },
+ fa71e003d04734a898a85cc5285e3cbb: { selectors: ['.border-left (101:1)'] },
+ ed482cea071e316f29d78fd93c3f3644: { selectors: ['.border-0 (104:1)'] },
+ '90cb661baf21e10be6e479cb0544b1a7': { selectors: ['.border-top-0 (107:1)'] },
+ '8a32707eaa09fc998bf8cc915710b60c': { selectors: ['.border-right-0 (110:1)'] },
+ a6f01957e142a000e7742b31ac6c2331: { selectors: ['.border-bottom-0 (113:1)'] },
+ c740fe952cc1985ee14f7d1c7a359a29: { selectors: ['.border-left-0 (116:1)'] },
+ af9dd93e9780306ffa4bb25a6384902f: { selectors: ['.border-primary (119:1)'] },
+ afa290dfe58cca06be5924ceae1b019b: { selectors: ['.border-secondary (122:1)'] },
+ '9b1ac460bdddf1e0164d7bf988cc2da8': { selectors: ['.border-success (125:1)'] },
+ '091cbf41d6be12061382fa571ee1ce82': { selectors: ['.border-info (128:1)'] },
+ '3ada321d4a387901dad6d80e1b6be3fd': { selectors: ['.border-warning (131:1)'] },
+ '13b4713dd52c1e359d1b43dd658cb249': { selectors: ['.border-danger (134:1)'] },
+ '0048e110875ea22b04104d55e764a367': { selectors: ['.border-light (137:1)'] },
+ a900b6b567c9a911326cdd0e19f40f8e: { selectors: ['.border-dark (140:1)'] },
+ '78bcd867ac9677c743c2bc33b872f27b': { selectors: ['.border-white (143:1)'] },
+ e0fc10c49c7b7f4d1924336d21a4f64e: { selectors: ['.rounded (146:1)'] },
+ '1b74b9d0a7d6a59281b5b5cae43c859a': { selectors: ['.rounded-top (149:1)'] },
+ '20b75f55f39e662e038d51a6442c03df': { selectors: ['.rounded-right (153:1)'] },
+ '83ea6db794873239c21f44af25618677': { selectors: ['.rounded-bottom (157:1)'] },
+ '8464e9e8001e65dfc06397436a5eebd7': { selectors: ['.rounded-left (161:1)'] },
+ '59c2f788287fa43caf5891adfc5c796e': { selectors: ['.rounded-circle (165:1)'] },
+ '31a632ba94f8c41558bd6044458f1459': { selectors: ['.rounded-0 (168:1)'] },
+ '16aaf53ab29d6b248b0257f2fa413914': { selectors: ['.d-none (176:1)'] },
+ '4f42736ac9217039ed791b4306e60aeb': { selectors: ['.d-inline (179:1)'] },
+ '067efa04b76649e8afcdceb9f5f7e870': { selectors: ['.d-inline-block (182:1)'] },
+ de54f49149fb9b512aa79ad9ada838f2: { selectors: ['.d-block (185:1)'] },
+ '80fc32acbc0c28ee890a160c23529d26': { selectors: ['.d-table (188:1)'] },
+ '6a87b1db48298ca94cbe5dee79a6eed1': { selectors: ['.d-table-row (191:1)'] },
+ b9896f0d94760bf5920f47904e9f7512: { selectors: ['.d-table-cell (194:1)'] },
+ d25c51f38c4d057209b96c664de68c44: { selectors: ['.d-flex (197:1)'] },
+ e72d46b636d5b8e17e771daa95793f33: { selectors: ['.d-inline-flex (200:1)'] },
+ '2c433b7c14a5ae32cfa8ec7867ee8526': { selectors: ['.embed-responsive (303:1)'] },
+ '56b318b8d8eb845b769d60cefcd131bb': {
+ selectors: [
+ '.embed-responsive .embed-responsive-item, .embed-responsive iframe, .embed-responsive embed, .embed-responsive object, .embed-responsive video (312:3)',
+ ],
+ },
+ c5009af89633c4d2f71a0a9fa333630d: { selectors: ['.flex-row (337:1)'] },
+ '7b06a3d956579cd64b6f5b1a57255369': { selectors: ['.flex-column (340:1)'] },
+ '21744a8c4dc6ed1519903b4236b00af4': { selectors: ['.flex-row-reverse (343:1)'] },
+ '18d903735f9c71070b6d8166aa1112f1': { selectors: ['.flex-column-reverse (346:1)'] },
+ e2a57aa8196347d4da84f33a4f551325: { selectors: ['.flex-wrap (349:1)'] },
+ b6b29f75b174b5609f9e7d5eef457b70: { selectors: ['.flex-nowrap (352:1)'] },
+ '839230fc7c0abacb6418b49d8f10b27f': { selectors: ['.flex-wrap-reverse (355:1)'] },
+ '924d9b261944a8e8ff684d5b519062bb': { selectors: ['.flex-fill (358:1)'] },
+ '5ed396aeb08464b7df8fc37d29455319': { selectors: ['.flex-grow-0 (361:1)'] },
+ '94251dc4d012339a3e37df6196fc79bb': { selectors: ['.flex-grow-1 (364:1)'] },
+ '0eeed7dabca0452a46574776a4485e6e': { selectors: ['.flex-shrink-0 (367:1)'] },
+ c69e60d5e51a1b74d22b172ab98ef9d5: { selectors: ['.flex-shrink-1 (370:1)'] },
+ '03401c1a81eb6d4639f020f76dd60176': { selectors: ['.justify-content-start (373:1)'] },
+ '39e3d2c344e78869c98ef099249e717d': { selectors: ['.justify-content-end (376:1)'] },
+ '3b2d00c0bea857ab78a71b0872842980': { selectors: ['.justify-content-center (379:1)'] },
+ b1c3c9edd20ed7c08b43863d38ebee40: { selectors: ['.justify-content-between (382:1)'] },
+ a11b4b1d983c5fa75777f273e998f73e: { selectors: ['.justify-content-around (385:1)'] },
+ '50e33f29f65bfffa6a3591fcb6045ca9': { selectors: ['.align-items-start (388:1)'] },
+ e44b276e47415ec19b74cc16740ada1d: { selectors: ['.align-items-end (391:1)'] },
+ '4a9d2716bca651758564059dceed1271': { selectors: ['.align-items-center (394:1)'] },
+ fd970b017f7f558f30cb273bf71ede7d: { selectors: ['.align-items-baseline (397:1)'] },
+ '7204b6b00b69f8af1e4a24c9b6e7f7f9': { selectors: ['.align-items-stretch (400:1)'] },
+ '350fbb74eb70bd05f9438067c3990e9f': { selectors: ['.align-content-start (403:1)'] },
+ '74d61397b4fcbf608f3dba39ab3b2a1b': { selectors: ['.align-content-end (406:1)'] },
+ eed1ab4ee9e5b327a434512176741548: { selectors: ['.align-content-center (409:1)'] },
+ '19878ab832978ef7e1746ac2fe4084b2': { selectors: ['.align-content-between (412:1)'] },
+ dded333d0522692809517039f5a727c1: { selectors: ['.align-content-around (415:1)'] },
+ '5cbb83ea2af7e9db8ef13f4c7d6db875': { selectors: ['.align-content-stretch (418:1)'] },
+ dc3df46d3c023184d375a63a71916646: { selectors: ['.align-self-auto (421:1)'] },
+ '0c6d2d8c9732c571f9cf61a4b1d2877f': { selectors: ['.align-self-start (424:1)'] },
+ fe7c6071e3e17214df1bdd38850d9ff0: { selectors: ['.align-self-end (427:1)'] },
+ '9611bbad74d72d50cf238088576a5089': { selectors: ['.align-self-center (430:1)'] },
+ '4bc5edddf5981866946175bfedb7247f': { selectors: ['.align-self-baseline (433:1)'] },
+ '4fffdd27ec60120ec9ed16fd7feef801': { selectors: ['.align-self-stretch (436:1)'] },
+ '39e658501f5502b35919f02fa9591360': { selectors: ['.float-left (719:1)'] },
+ b51436c537ffc4269b1533e44d7c3467: { selectors: ['.float-right (722:1)'] },
+ c4597a87d2c793a6d696cfe06f6c95ce: { selectors: ['.float-none (725:1)'] },
+ aaf8dc6e0768e59f3098a98a5c144d66: { selectors: ['.position-static (760:1)'] },
+ '79592de2383045d15ab57d35aa1dab95': { selectors: ['.position-relative (763:1)'] },
+ a7c272f53d0368730bde4c2740ffb5c3: { selectors: ['.position-absolute (766:1)'] },
+ dad0bb92d53f17cf8affc10f77824b7f: { selectors: ['.position-fixed (769:1)'] },
+ '6da0f6a7354a75fe6c95b08a4cabc06f': { selectors: ['.position-sticky (772:1)'] },
+ '66602e21eea7b673883155c8f42b9590': { selectors: ['.fixed-top (775:1)'] },
+ '33ea70eb6db7f6ab3469680f182abb19': { selectors: ['.fixed-bottom (782:1)'] },
+ '405e9dd7c9919943af14658313fd31e4': { selectors: ['.sr-only (795:1)'] },
+ '9220ad156a70c2586b15fe2b9b6108b2': { selectors: ['.shadow-sm (813:1)'] },
+ b1b8c0ff70767ca2160811a026766982: { selectors: ['.shadow (816:1)'] },
+ da0792abe99964acb6692a03c61d6dd8: { selectors: ['.shadow-lg (819:1)'] },
+ bb2173057af1cf20e687469b2d9cbb3c: { selectors: ['.shadow-none (822:1)'] },
+ '6d8abb6519a186483b25429ab8b9765e': { selectors: ['.w-25 (825:1)'] },
+ a087c1ffdf8ead76cdd37445b414d63e: { selectors: ['.w-50 (828:1)'] },
+ '28180742013a90275be5633e6ec0dd51': { selectors: ['.w-75 (831:1)'] },
+ '195a03bc95a0af0ba6c8824db97a0b2f': { selectors: ['.w-100 (834:1)'] },
+ e67c74b650d6236b03be9dfc10c78e32: { selectors: ['.w-auto (837:1)'] },
+ c1b6262b3ee069addc1fbe46f64aac4e: { selectors: ['.h-25 (840:1)'] },
+ a520396ae349bef86145e0761aa0699e: { selectors: ['.h-50 (843:1)'] },
+ '7c53b57d54beb087fd7ab8b669c5fe60': { selectors: ['.h-75 (846:1)'] },
+ ad74f1972cb745b7a78b03e16a387f21: { selectors: ['.h-100 (849:1)'] },
+ '2cd49c3d63d260ba4f0b23c559ad05e0': { selectors: ['.h-auto (852:1)'] },
+ '0b43071a67efc45ee1735fdc2491313c': { selectors: ['.mw-100 (855:1)'] },
+ eac31a6f08e5c935e24b97df0fdad579: { selectors: ['.mh-100 (858:1)'] },
+ cfdb4f497b16074959bfd3deb7ea9c42: { selectors: ['.m-0 (861:1)'] },
+ '4d666c270ba50524312d97c4b937d153': { selectors: ['.mt-0, .my-0 (864:1)'] },
+ eccf47ccd76ceffb4b139cb6c080b5ac: { selectors: ['.mr-0, .mx-0 (868:1)'] },
+ '9bc513e73c0bdc6efdf170cb31de16d1': { selectors: ['.mb-0, .my-0 (872:1)'] },
+ e99cfc55b03f0e67f11628b19889ad7b: { selectors: ['.ml-0, .mx-0 (876:1)'] },
+ e1c39484d90d2acaa00973531f47f738: { selectors: ['.m-1 (880:1)'] },
+ '63791bc02eccfdfa2c01621a801e565f': { selectors: ['.mt-1, .my-1 (883:1)'] },
+ bcb27ab9d7dcfdd0d7cacad02709966c: { selectors: ['.mr-1, .mx-1 (887:1)'] },
+ cb5d1c4328e25b5bc93be9a252973690: { selectors: ['.mb-1, .my-1 (891:1)'] },
+ b80b1010c7dcfbb30bed9015c4f2e969: { selectors: ['.ml-1, .mx-1 (895:1)'] },
+ ecab1c9cdf8a562e3c0f70307aeafa89: { selectors: ['.m-2 (899:1)'] },
+ '6505fe17fbbd88b1884113a754aa82ab': { selectors: ['.mt-2, .my-2 (902:1)'] },
+ '6f0c7d09d1e729f332c4671ccc2b48c0': { selectors: ['.mr-2, .mx-2 (906:1)'] },
+ '70ef7b668b382b3c747b2d73e08cdbed': { selectors: ['.mb-2, .my-2 (910:1)'] },
+ '2d7f277cc78ed324a8fc1f71ab281e1f': { selectors: ['.ml-2, .mx-2 (914:1)'] },
+ '8ebcfe52fd4024861082ffb1735747a7': { selectors: ['.m-3 (918:1)'] },
+ '9965fb516bdb72b87023a533123a8035': { selectors: ['.mt-3, .my-3 (921:1)'] },
+ b1fcbbb1dc6226f6da6000830088e051: { selectors: ['.mr-3, .mx-3 (925:1)'] },
+ '02204826cfbe3da98535c0d802870940': { selectors: ['.mb-3, .my-3 (929:1)'] },
+ '0259859060250ae6b730218733e7a437': { selectors: ['.ml-3, .mx-3 (933:1)'] },
+ '8cf300dab2a4994a105eeddda826f2e6': { selectors: ['.m-4 (937:1)'] },
+ '1ba62fdddd3349f52a452050688905c7': { selectors: ['.mt-4, .my-4 (940:1)'] },
+ '66a104129fa13db5a0829567fba6ee41': { selectors: ['.mr-4, .mx-4 (944:1)'] },
+ eefcc4c10b79e70e8e8a5a66fb2b7aa1: { selectors: ['.mb-4, .my-4 (948:1)'] },
+ eb1503656dc920d15a31116956fdffa4: { selectors: ['.ml-4, .mx-4 (952:1)'] },
+ '79cbb6e5c9b73fd0be29d4fc5733a099': { selectors: ['.m-5 (956:1)'] },
+ '67d8671699df706a428e7da42a7141cb': { selectors: ['.mt-5, .my-5 (959:1)'] },
+ e9cb4a0a8a60ff018c87a0b7efa9de29: { selectors: ['.mr-5, .mx-5 (963:1)'] },
+ '93f579214354dbd8cb60209c068f0086': { selectors: ['.mb-5, .my-5 (967:1)'] },
+ '2a789d4af97d2b87fd0bf2b4626120cd': { selectors: ['.ml-5, .mx-5 (971:1)'] },
+ '64a89d28e8287c1a0ac153001082644c': { selectors: ['.p-0 (975:1)'] },
+ b03aa6db5ddf110bbdbefbbec43fda30: { selectors: ['.pt-0, .py-0 (978:1)'] },
+ e38192ca32a98888d4c4876880f4fece: { selectors: ['.pr-0, .px-0 (982:1)'] },
+ '70fe8ef50e999ddd29506f672c107069': { selectors: ['.pb-0, .py-0 (986:1)'] },
+ '9355e8cd9109049726475ba356661bcf': { selectors: ['.pl-0, .px-0 (990:1)'] },
+ '0d4c53468c2658c5324b9ec7a8ca6de2': { selectors: ['.p-1 (994:1)'] },
+ d74e430b2a56b3a4e20065c972b7fa3f: { selectors: ['.pt-1, .py-1 (997:1)'] },
+ '21e4644967aedd19888b6f4a700b629b': { selectors: ['.pr-1, .px-1 (1001:1)'] },
+ e315a7b9b7a1d0df3ea7d95af5203a0b: { selectors: ['.pb-1, .py-1 (1005:1)'] },
+ '14630ca122e1d9830a9ef5591c4097d0': { selectors: ['.pl-1, .px-1 (1009:1)'] },
+ '5b1c65e5139e86e5f4755824f8b77d13': { selectors: ['.p-2 (1013:1)'] },
+ '244af70950a1e200d3849f75ce51d707': { selectors: ['.pt-2, .py-2 (1016:1)'] },
+ b583832738cad724c7c23e5c14ac9bfb: { selectors: ['.pr-2, .px-2 (1020:1)'] },
+ e1e633c4f1375e8276154192d8899e39: { selectors: ['.pb-2, .py-2 (1024:1)'] },
+ '676b01e25f0dbb3f7d2f2529231cda08': { selectors: ['.pl-2, .px-2 (1028:1)'] },
+ '9b5165e3333b22801f2287f7983d7516': { selectors: ['.p-3 (1032:1)'] },
+ '5bcaa9df87a507f6cd14659ea176bdc5': { selectors: ['.pt-3, .py-3 (1035:1)'] },
+ f706637180776c5589385599705a2409: { selectors: ['.pr-3, .px-3 (1039:1)'] },
+ '41157cfbcf47990b383b5b0379386ab2': { selectors: ['.pb-3, .py-3 (1043:1)'] },
+ cac1e7a204bb6a1f42707b684ad46238: { selectors: ['.pl-3, .px-3 (1047:1)'] },
+ '43e0671cd41a4b7590284888b607a134': { selectors: ['.p-4 (1051:1)'] },
+ '116b0f95ebde1ff8907e488413a88854': { selectors: ['.pt-4, .py-4 (1054:1)'] },
+ ecb06765fe691d892df000eebbb23dcc: { selectors: ['.pr-4, .px-4 (1058:1)'] },
+ '1331503a48d36025c861e660bc615048': { selectors: ['.pb-4, .py-4 (1062:1)'] },
+ f8665f7e547e499abd7ac63813b274f5: { selectors: ['.pl-4, .px-4 (1066:1)'] },
+ '4160a315459f1b5a98255863f42136fe': { selectors: ['.p-5 (1070:1)'] },
+ f55a6b2de6a434ec7b4375f06f4fad75: { selectors: ['.pt-5, .py-5 (1073:1)'] },
+ '19391dc45c8d7730a86d521c28f52c3f': { selectors: ['.pr-5, .px-5 (1077:1)'] },
+ '15898bcb7ff74a60006f9931422b4ad3': { selectors: ['.pb-5, .py-5 (1081:1)'] },
+ '6290bdc6355aed1e9b27379003aa4828': { selectors: ['.pl-5, .px-5 (1085:1)'] },
+ e57ec4fe9e8ed36e38f1c50041fc9f47: { selectors: ['.m-auto (1089:1)'] },
+ f10380665932186d1effe0674a74ba12: { selectors: ['.mt-auto, .my-auto (1092:1)'] },
+ '2ce71a27023eb50a47c24a99399faa28': { selectors: ['.mr-auto, .mx-auto (1096:1)'] },
+ '196c77d357d314678cd3a99cfacbea96': { selectors: ['.mb-auto, .my-auto (1100:1)'] },
+ ca007ce268b463a6bf42145cf5ce3685: { selectors: ['.ml-auto, .mx-auto (1104:1)'] },
+ cb431b84034f2e710509c7656b3c6f16: { selectors: ['.text-monospace (1844:1)'] },
+ a8fc5ca823f51d72673577064387a029: { selectors: ['.text-justify (1847:1)'] },
+ '0bb94dfab7ca2c9892ebbd993b2baf0f': { selectors: ['.text-nowrap (1850:1)'] },
+ aea4958ce85ddc0cbffca1015c3a7eba: { selectors: ['.text-truncate (1853:1)'] },
+ '52b9443947b6b94a5c7e1b837da115e2': { selectors: ['.text-left (1858:1)'] },
+ baaf5136fc6e1c54ba29b6040f166d5f: { selectors: ['.text-right (1861:1)'] },
+ '282aa4319bee75af06cc2632b7124e26': { selectors: ['.text-center (1864:1)'] },
+ '1cb1c8ad9b560eca25ebcefe95c1b7fa': { selectors: ['.text-lowercase (1899:1)'] },
+ '45234533eac658ba2857e9c4d3bc78a5': { selectors: ['.text-uppercase (1902:1)'] },
+ f9e3f64237f2e81b6aed84223a0ceb1d: { selectors: ['.text-capitalize (1905:1)'] },
+ '09caca3d36aa9f3ef815e0da7e1a16b4': { selectors: ['.font-weight-light (1908:1)'] },
+ '25189f4fad18eaeef19e349c6680834c': { selectors: ['.font-weight-normal (1911:1)'] },
+ b2a9507678ec557603eb8ec077f0eb1f: { selectors: ['.font-weight-bold (1914:1)'] },
+ '7d2da06b621a98a8599e5ec82e39eac8': { selectors: ['.font-italic (1917:1)'] },
+ '0020d10e4fce033b418aace7c3143b82': { selectors: ['.text-white (1920:1)'] },
+ '34ad81e372a038e6f78ae4f22bd4813d': { selectors: ['.text-primary (1923:1)'] },
+ '9fde9a179d24755438ace2a874dda817': {
+ selectors: ['.text-secondary (1929:1)', '.text-muted (1974:1)'],
+ },
+ '9ffcb1532b3fb397c0e818850683da29': { selectors: ['.text-success (1935:1)'] },
+ f28fd089809bcd15d5684b158a0af98d: { selectors: ['.text-info (1941:1)'] },
+ '6cac1cb5ee5149e91e45d15d0bdae310': { selectors: ['.text-warning (1947:1)'] },
+ '2faab1e0abf22b20fdf05b9b01fff29b': { selectors: ['.text-danger (1953:1)'] },
+ '46b52fea531aaaf29b63c40be2356849': { selectors: ['.text-light (1959:1)'] },
+ '78f31d1ab6529decf28e0366a8ee81aa': { selectors: ['.text-dark (1965:1)'] },
+ '45330b41b77e8880ad7680c51e0f61c4': { selectors: ['.text-body (1971:1)'] },
+ '60d93588f62b5e85eb4f11dfd3461897': { selectors: ['.text-black-50 (1977:1)'] },
+ '7dea35658553032ff7b7cc0287613b7c': { selectors: ['.text-white-50 (1980:1)'] },
+ '61bf92980cac3d51d0cf1ba24c948fa1': { selectors: ['.text-hide (1983:1)'] },
+ '7dcad258820769677bc60871fafe9b93': { selectors: ['.visible (1990:1)'] },
+ '0f8833af4e2f4a6fc785bd7edc1e75b3': { selectors: ['.invisible (1993:1)'] },
+};
diff --git a/scripts/gitaly_test.rb b/scripts/gitaly_test.rb
index dee4c2eba7e..b5d3facd18a 100644
--- a/scripts/gitaly_test.rb
+++ b/scripts/gitaly_test.rb
@@ -79,15 +79,13 @@ module GitalyTest
socket = read_socket_path
Integer(timeout / delay).times do
- begin
- UNIXSocket.new(socket)
- puts ' OK'
-
- return
- rescue Errno::ENOENT, Errno::ECONNREFUSED
- print '.'
- sleep delay
- end
+ UNIXSocket.new(socket)
+ puts ' OK'
+
+ return
+ rescue Errno::ENOENT, Errno::ECONNREFUSED
+ print '.'
+ sleep delay
end
puts ' FAILED'
diff --git a/scripts/insert-rspec-profiling-data b/scripts/insert-rspec-profiling-data
new file mode 100755
index 00000000000..10e337b9972
--- /dev/null
+++ b/scripts/insert-rspec-profiling-data
@@ -0,0 +1,47 @@
+#!/usr/bin/env ruby
+
+require 'csv'
+require 'rspec_profiling'
+require 'postgres-copy'
+
+module RspecProfiling
+ module Collectors
+ class PSQL
+ def establish_connection
+ # This disables the automatic creation of the database and
+ # table. In the future, we may want a way to specify the host of
+ # the database to connect so that we can call #install.
+ Result.establish_connection(results_url)
+ end
+
+ def prepared?
+ connection.data_source_exists?(table)
+ end
+
+ def results_url
+ ENV['RSPEC_PROFILING_POSTGRES_URL']
+ end
+
+ class Result < ActiveRecord::Base
+ acts_as_copy_target
+ end
+ end
+ end
+end
+
+def insert_data(path)
+ puts "#{Time.now} Inserting CI stats..."
+
+ collector = RspecProfiling::Collectors::PSQL.new
+ collector.install
+
+ files = Dir[File.join(path, "*.csv")]
+
+ files.each do |filename|
+ puts "#{Time.now} Inserting #{filename}..."
+ result = RspecProfiling::Collectors::PSQL::Result.copy_from(filename)
+ puts "#{Time.now} Inserted #{result.cmd_tuples} lines in #{filename}, DB response: #{result.cmd_status}"
+ end
+end
+
+insert_data('rspec_profiling') if ENV['RSPEC_PROFILING_POSTGRES_URL'].present?
diff --git a/scripts/lint-doc.sh b/scripts/lint-doc.sh
index 848364b4a9b..bc73225c1bf 100755
--- a/scripts/lint-doc.sh
+++ b/scripts/lint-doc.sh
@@ -35,7 +35,7 @@ fi
# Do not use 'README.md', instead use 'index.md'
# Number of 'README.md's as of 2018-03-26
-NUMBER_READMES_CE=42
+NUMBER_READMES_CE=43
NUMBER_READMES_EE=46
FIND_READMES=$(find doc/ -name "README.md" | wc -l)
echo '=> Checking for new README.md files...'
diff --git a/scripts/lint-rugged b/scripts/lint-rugged
index 22e3e1f1505..9466c62a415 100755
--- a/scripts/lint-rugged
+++ b/scripts/lint-rugged
@@ -5,12 +5,18 @@ ALLOWED = [
'lib/gitlab/bare_repository_import/repository.rb',
# Needed to avoid using the git binary to validate a branch name
- 'lib/gitlab/git_ref_validator.rb'
+ 'lib/gitlab/git_ref_validator.rb',
+
+ # Reverted Rugged calls due to Gitaly atop NFS performance
+ # See https://docs.gitlab.com/ee/development/gitaly.html#legacy-rugged-code.
+ 'lib/gitlab/git/rugged_impl/',
+ 'lib/gitlab/gitaly_client/storage_settings.rb'
].freeze
rugged_lines = IO.popen(%w[git grep -i -n rugged -- app config lib], &:read).lines
rugged_lines = rugged_lines.select { |l| /^[^:]*\.rb:/ =~ l }
rugged_lines = rugged_lines.reject { |l| l.start_with?(*ALLOWED) }
+rugged_lines = rugged_lines.reject { |l| /(include|prepend) Gitlab::Git::RuggedImpl/ =~ l}
rugged_lines = rugged_lines.reject do |line|
code, _comment = line.split('# ', 2)
code !~ /rugged/i
diff --git a/scripts/merge-simplecov b/scripts/merge-simplecov
index 65f93f8830b..746be3317a7 100755
--- a/scripts/merge-simplecov
+++ b/scripts/merge-simplecov
@@ -12,11 +12,9 @@ module SimpleCov
def resultset_hashes
resultset_files.map do |path|
- begin
- JSON.parse(File.read(path))
- rescue
- {}
- end
+ JSON.parse(File.read(path))
+ rescue
+ {}
end
end
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index f610485a700..32fce946c17 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -4,6 +4,16 @@ export TILLER_NAMESPACE="$KUBE_NAMESPACE"
function echoerr() { printf "\033[0;31m%s\n\033[0m" "$*" >&2; }
function echoinfo() { printf "\033[0;33m%s\n\033[0m" "$*" >&2; }
+function perform_review_app_deployment() {
+ check_kube_domain
+ download_gitlab_chart
+ ensure_namespace
+ install_tiller
+ install_external_dns
+ time deploy
+ add_license
+}
+
function check_kube_domain() {
if [ -z ${REVIEW_APPS_DOMAIN+x} ]; then
echo "In order to deploy or use Review Apps, REVIEW_APPS_DOMAIN variable must be set"
diff --git a/scripts/trigger-build b/scripts/trigger-build
index 9dbafffddfc..9c5fc3c76a5 100755
--- a/scripts/trigger-build
+++ b/scripts/trigger-build
@@ -141,7 +141,7 @@ module Trigger
"GITLAB_#{edition}_VERSION" => ENV['CI_COMMIT_REF_NAME'],
"GITLAB_VERSION" => ENV['CI_COMMIT_REF_NAME'],
"GITLAB_TAG" => ENV['CI_COMMIT_TAG'],
- "GITLAB_ASSETS_TAG" => ENV['CI_COMMIT_REF_SLUG'],
+ "GITLAB_ASSETS_TAG" => ENV['CI_COMMIT_TAG'] ? ENV['CI_COMMIT_REF_NAME'] : ENV['CI_COMMIT_REF_SLUG'],
"#{edition}_PIPELINE" => 'true'
}
end
diff --git a/spec/controllers/admin/appearances_controller_spec.rb b/spec/controllers/admin/appearances_controller_spec.rb
new file mode 100644
index 00000000000..621aa148301
--- /dev/null
+++ b/spec/controllers/admin/appearances_controller_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Admin::AppearancesController do
+ let(:admin) { create(:admin) }
+ let(:header_message) { 'Header message' }
+ let(:footer_message) { 'Footer' }
+
+ describe 'POST #create' do
+ let(:create_params) do
+ {
+ title: 'Foo',
+ description: 'Bar',
+ header_message: header_message,
+ footer_message: footer_message
+ }
+ end
+
+ before do
+ sign_in(admin)
+ end
+
+ it 'creates appearance with footer and header message' do
+ post :create, params: { appearance: create_params }
+
+ expect(Appearance.current).to have_attributes(
+ header_message: header_message,
+ footer_message: footer_message,
+ email_header_and_footer_enabled: false,
+ message_background_color: '#E75E40',
+ message_font_color: '#FFFFFF'
+ )
+ end
+
+ context 'when enabling header and footer in email' do
+ it 'creates appearance with enabled flag' do
+ create_params[:email_header_and_footer_enabled] = true
+
+ post :create, params: { appearance: create_params }
+
+ expect(Appearance.current).to have_attributes(
+ header_message: header_message,
+ footer_message: footer_message,
+ email_header_and_footer_enabled: true
+ )
+ end
+ end
+ end
+
+ describe 'PUT #update' do
+ let(:update_params) do
+ {
+ header_message: header_message,
+ footer_message: footer_message
+ }
+ end
+
+ before do
+ create(:appearance)
+
+ sign_in(admin)
+ end
+
+ it 'updates appearance with footer and header message' do
+ put :update, params: { appearance: update_params }
+
+ expect(Appearance.current).to have_attributes(
+ header_message: header_message,
+ footer_message: footer_message,
+ email_header_and_footer_enabled: false,
+ message_background_color: '#E75E40',
+ message_font_color: '#FFFFFF'
+ )
+ end
+
+ context 'when enabling header and footer in email' do
+ it 'updates appearance with enabled flag' do
+ update_params[:email_header_and_footer_enabled] = true
+
+ post :update, params: { appearance: update_params }
+
+ expect(Appearance.current).to have_attributes(
+ header_message: header_message,
+ footer_message: footer_message,
+ email_header_and_footer_enabled: true
+ )
+ end
+ end
+ end
+end
diff --git a/spec/controllers/admin/projects_controller_spec.rb b/spec/controllers/admin/projects_controller_spec.rb
index 8166657f674..4caf8b46519 100644
--- a/spec/controllers/admin/projects_controller_spec.rb
+++ b/spec/controllers/admin/projects_controller_spec.rb
@@ -43,6 +43,16 @@ describe Admin::ProjectsController do
end
end
+ describe 'GET /projects.json' do
+ render_views
+
+ before do
+ get :index, format: :json
+ end
+
+ it { is_expected.to respond_with(:success) }
+ end
+
describe 'GET /projects/:id' do
render_views
diff --git a/spec/controllers/admin/runners_controller_spec.rb b/spec/controllers/admin/runners_controller_spec.rb
index 4cf14030ca1..82e24213408 100644
--- a/spec/controllers/admin/runners_controller_spec.rb
+++ b/spec/controllers/admin/runners_controller_spec.rb
@@ -1,18 +1,35 @@
require 'spec_helper'
describe Admin::RunnersController do
- let(:runner) { create(:ci_runner) }
+ let!(:runner) { create(:ci_runner) }
before do
sign_in(create(:admin))
end
describe '#index' do
+ render_views
+
it 'lists all runners' do
get :index
expect(response).to have_gitlab_http_status(200)
end
+
+ it 'avoids N+1 queries', :request_store do
+ get :index
+
+ control_count = ActiveRecord::QueryRecorder.new { get :index }.count
+
+ create(:ci_runner, :tagged_only)
+
+ # There is still an N+1 query for `runner.builds.count`
+ expect { get :index }.not_to exceed_query_limit(control_count + 1)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to have_content('tag1')
+ expect(response.body).to have_content('tag2')
+ end
end
describe '#show' do
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index c934db9e237..cb24a6ef142 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -8,6 +8,20 @@ describe Admin::UsersController do
sign_in(admin)
end
+ describe 'GET #index' do
+ it 'retrieves all users' do
+ get :index
+
+ expect(assigns(:users)).to match_array([user, admin])
+ end
+
+ it 'filters by admins' do
+ get :index, params: { filter: 'admins' }
+
+ expect(assigns(:users)).to eq([admin])
+ end
+ end
+
describe 'GET :id' do
it 'finds a user case-insensitively' do
user = create(:user, username: 'CaseSensitive')
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index c9e520317e8..dca74bd5f84 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -665,6 +665,14 @@ describe ApplicationController do
expect(response.headers['Cache-Control']).to eq 'max-age=0, private, must-revalidate, no-store'
end
+
+ it 'does not set the "no-store" header for XHR requests' do
+ sign_in(user)
+
+ get :index, xhr: true
+
+ expect(response.headers['Cache-Control']).to eq 'max-age=0, private, must-revalidate'
+ end
end
end
end
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index 4458a7223bf..d8b75c5151e 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -371,5 +371,36 @@ describe AutocompleteController do
expect(json_response[3]).to match('name' => 'thumbsdown')
end
end
+
+ context 'Get merge_request_target_branches' do
+ let(:user2) { create(:user) }
+ let!(:merge_request1) { create(:merge_request, source_project: project, target_branch: 'feature') }
+
+ context 'unauthorized user' do
+ it 'returns empty json' do
+ get :merge_request_target_branches
+
+ expect(json_response).to be_empty
+ end
+ end
+
+ context 'sign in as user without any accesible merge requests' do
+ it 'returns empty json' do
+ sign_in(user2)
+ get :merge_request_target_branches
+
+ expect(json_response).to be_empty
+ end
+ end
+
+ context 'sign in as user with a accesible merge request' do
+ it 'returns json' do
+ sign_in(user)
+ get :merge_request_target_branches
+
+ expect(json_response).to contain_exactly({ 'title' => 'feature' })
+ end
+ end
+ end
end
end
diff --git a/spec/controllers/concerns/issuable_collections_spec.rb b/spec/controllers/concerns/issuable_collections_spec.rb
index 307c5d60c57..8580900215c 100644
--- a/spec/controllers/concerns/issuable_collections_spec.rb
+++ b/spec/controllers/concerns/issuable_collections_spec.rb
@@ -112,7 +112,8 @@ describe IssuableCollections do
assignee_username: 'user1',
author_id: '2',
author_username: 'user2',
- authorized_only: 'true',
+ authorized_only: 'yes',
+ confidential: true,
due_date: '2017-01-01',
group_id: '3',
iids: '4',
@@ -140,6 +141,7 @@ describe IssuableCollections do
'assignee_username' => 'user1',
'author_id' => '2',
'author_username' => 'user2',
+ 'confidential' => true,
'label_name' => 'foo',
'milestone_title' => 'bar',
'my_reaction_emoji' => 'thumbsup',
diff --git a/spec/controllers/concerns/send_file_upload_spec.rb b/spec/controllers/concerns/send_file_upload_spec.rb
index cf3b24f50a3..aa71a247956 100644
--- a/spec/controllers/concerns/send_file_upload_spec.rb
+++ b/spec/controllers/concerns/send_file_upload_spec.rb
@@ -112,7 +112,7 @@ describe SendFileUpload do
it 'sends a file with a custom type' do
headers = double
- expected_headers = %r(response-content-disposition=attachment%3B%20filename%3D%22test.js%22%3B%20filename%2A%3DUTF-8%27%27test.js&response-content-type=application/ecmascript)
+ expected_headers = /response-content-disposition=attachment%3B%20filename%3D%22test.js%22%3B%20filename%2A%3DUTF-8%27%27test.js&response-content-type=application%2Fecmascript/
expect(Gitlab::Workhorse).to receive(:send_url).with(expected_headers).and_call_original
expect(headers).to receive(:store).with(Gitlab::Workhorse::SEND_DATA_HEADER, /^send-url:/)
diff --git a/spec/controllers/dashboard/milestones_controller_spec.rb b/spec/controllers/dashboard/milestones_controller_spec.rb
index 4b164d0aa6b..ab40b4eb178 100644
--- a/spec/controllers/dashboard/milestones_controller_spec.rb
+++ b/spec/controllers/dashboard/milestones_controller_spec.rb
@@ -13,7 +13,7 @@ describe Dashboard::MilestonesController do
)
end
let(:issue) { create(:issue, project: project, milestone: project_milestone) }
- let(:group_issue) { create(:issue, milestone: group_milestone) }
+ let(:group_issue) { create(:issue, milestone: group_milestone, project: create(:project, group: group)) }
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]) }
diff --git a/spec/controllers/dashboard/projects_controller_spec.rb b/spec/controllers/dashboard/projects_controller_spec.rb
index 2975205e09c..649441f4917 100644
--- a/spec/controllers/dashboard/projects_controller_spec.rb
+++ b/spec/controllers/dashboard/projects_controller_spec.rb
@@ -2,4 +2,30 @@ require 'spec_helper'
describe Dashboard::ProjectsController do
it_behaves_like 'authenticates sessionless user', :index, :atom
+
+ context 'json requests' do
+ render_views
+
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ describe 'GET /projects.json' do
+ before do
+ get :index, format: :json
+ end
+
+ it { is_expected.to respond_with(:success) }
+ end
+
+ describe 'GET /starred.json' do
+ before do
+ get :starred, format: :json
+ end
+
+ it { is_expected.to respond_with(:success) }
+ end
+ end
end
diff --git a/spec/controllers/explore/projects_controller_spec.rb b/spec/controllers/explore/projects_controller_spec.rb
index d57367e931e..7e20ddca249 100644
--- a/spec/controllers/explore/projects_controller_spec.rb
+++ b/spec/controllers/explore/projects_controller_spec.rb
@@ -1,6 +1,36 @@
require 'spec_helper'
describe Explore::ProjectsController do
+ describe 'GET #index.json' do
+ render_views
+
+ before do
+ get :index, format: :json
+ end
+
+ it { is_expected.to respond_with(:success) }
+ end
+
+ describe 'GET #trending.json' do
+ render_views
+
+ before do
+ get :trending, format: :json
+ end
+
+ it { is_expected.to respond_with(:success) }
+ end
+
+ describe 'GET #starred.json' do
+ render_views
+
+ before do
+ get :starred, format: :json
+ end
+
+ it { is_expected.to respond_with(:success) }
+ end
+
describe 'GET #trending' do
context 'sorting by update date' do
let(:project1) { create(:project, :public, updated_at: 3.days.ago) }
diff --git a/spec/controllers/google_api/authorizations_controller_spec.rb b/spec/controllers/google_api/authorizations_controller_spec.rb
index 1e8e82da4f3..d9ba85cf56a 100644
--- a/spec/controllers/google_api/authorizations_controller_spec.rb
+++ b/spec/controllers/google_api/authorizations_controller_spec.rb
@@ -6,7 +6,7 @@ describe GoogleApi::AuthorizationsController do
let(:token) { 'token' }
let(:expires_at) { 1.hour.since.strftime('%s') }
- subject { get :callback, params: { code: 'xxx', state: @state } }
+ subject { get :callback, params: { code: 'xxx', state: state } }
before do
sign_in(user)
@@ -15,35 +15,57 @@ describe GoogleApi::AuthorizationsController do
.to receive(:get_token).and_return([token, expires_at])
end
- it 'sets token and expires_at in session' do
- subject
+ shared_examples_for 'access denied' do
+ it 'returns a 404' do
+ subject
- expect(session[GoogleApi::CloudPlatform::Client.session_key_for_token])
- .to eq(token)
- expect(session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at])
- .to eq(expires_at)
+ expect(session[GoogleApi::CloudPlatform::Client.session_key_for_token]).to be_nil
+ expect(response).to have_http_status(:not_found)
+ end
end
- context 'when redirect uri key is stored in state' do
- set(:project) { create(:project) }
- let(:redirect_uri) { project_clusters_url(project).to_s }
+ context 'session key is present' do
+ let(:session_key) { 'session-key' }
+ let(:redirect_uri) { 'example.com' }
before do
- @state = GoogleApi::CloudPlatform::Client
- .new_session_key_for_redirect_uri do |key|
- session[key] = redirect_uri
+ session[GoogleApi::CloudPlatform::Client.session_key_for_redirect_uri(session_key)] = redirect_uri
+ end
+
+ context 'session key matches state param' do
+ let(:state) { session_key }
+
+ it 'sets token and expires_at in session' do
+ subject
+
+ expect(session[GoogleApi::CloudPlatform::Client.session_key_for_token])
+ .to eq(token)
+ expect(session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at])
+ .to eq(expires_at)
+ end
+
+ it 'redirects to the URL stored in state param' do
+ expect(subject).to redirect_to(redirect_uri)
end
end
- it 'redirects to the URL stored in state param' do
- expect(subject).to redirect_to(redirect_uri)
+ context 'session key does not match state param' do
+ let(:state) { 'bad-key' }
+
+ it_behaves_like 'access denied'
end
- end
- context 'when redirection url is not stored in state' do
- it 'redirects to root_path' do
- expect(subject).to redirect_to(root_path)
+ context 'state param is blank' do
+ let(:state) { '' }
+
+ it_behaves_like 'access denied'
end
end
+
+ context 'state param is present, but session key is blank' do
+ let(:state) { 'session-key' }
+
+ it_behaves_like 'access denied'
+ end
end
end
diff --git a/spec/controllers/graphql_controller_spec.rb b/spec/controllers/graphql_controller_spec.rb
index a0f40874db1..c19a752b07b 100644
--- a/spec/controllers/graphql_controller_spec.rb
+++ b/spec/controllers/graphql_controller_spec.rb
@@ -1,112 +1,45 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe GraphqlController do
- describe 'execute' do
- let(:user) { nil }
-
- before do
- sign_in(user) if user
-
- run_test_query!
- end
-
- subject { query_response }
+ before do
+ stub_feature_flags(graphql: true)
+ end
- context 'graphql is disabled by feature flag' do
- let(:user) { nil }
+ describe 'POST #execute' do
+ context 'when user is logged in' do
+ let(:user) { create(:user) }
before do
- stub_feature_flags(graphql: false)
- end
-
- it 'returns 404' do
- run_test_query!
-
- expect(response).to have_gitlab_http_status(404)
- end
- end
-
- context 'signed out' do
- let(:user) { nil }
-
- it 'runs the query with current_user: nil' do
- is_expected.to eq('echo' => 'nil says: test success')
- end
- end
-
- context 'signed in' do
- let(:user) { create(:user, username: 'Simon') }
-
- it 'runs the query with current_user set' do
- is_expected.to eq('echo' => '"Simon" says: test success')
- end
- end
-
- context 'invalid variables' do
- it 'returns an error' do
- run_test_query!(variables: "This is not JSON")
-
- expect(response).to have_gitlab_http_status(422)
- expect(json_response['errors'].first['message']).not_to be_nil
+ sign_in(user)
end
- end
- end
-
- context 'token authentication' do
- before do
- stub_authentication_activity_metrics(debug: false)
- end
-
- let(:user) { create(:user, username: 'Simon') }
- let(:personal_access_token) { create(:personal_access_token, user: user) }
-
- 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)
- run_test_query!(private_token: personal_access_token.token)
+ it 'returns 200 when user can access API' do
+ post :execute
expect(response).to have_gitlab_http_status(200)
- expect(query_response).to eq('echo' => '"Simon" says: test success')
end
- end
-
- context 'when the personal access token has no api scope' do
- it 'does not log the user in' do
- personal_access_token.update(scopes: [:read_user])
- run_test_query!(private_token: personal_access_token.token)
+ it 'returns access denied template when user cannot access API' do
+ # User cannot access API in a couple of cases
+ # * When user is internal(like ghost users)
+ # * When user is blocked
+ expect(Ability).to receive(:allowed?).with(user, :access_api, :global).and_return(false)
- expect(response).to have_gitlab_http_status(200)
+ post :execute
- expect(query_response).to eq('echo' => 'nil says: test success')
+ expect(response.status).to eq(403)
+ expect(response).to render_template('errors/access_denied')
end
end
- context 'without token' do
- it 'shows public data' do
- run_test_query!
+ context 'when user is not logged in' do
+ it 'returns 200' do
+ post :execute
- expect(query_response).to eq('echo' => 'nil says: test success')
+ expect(response).to have_gitlab_http_status(200)
end
end
end
-
- # Chosen to exercise all the moving parts in GraphqlController#execute
- def run_test_query!(variables: { 'text' => 'test success' }, private_token: nil)
- query = <<~QUERY
- query Echo($text: String) {
- echo(text: $text)
- }
- QUERY
-
- post :execute, params: { query: query, operationName: 'Echo', variables: variables, private_token: private_token }
- end
-
- def query_response
- json_response['data']
- end
end
diff --git a/spec/controllers/groups/boards_controller_spec.rb b/spec/controllers/groups/boards_controller_spec.rb
index 4228e727b52..27ee37b3817 100644
--- a/spec/controllers/groups/boards_controller_spec.rb
+++ b/spec/controllers/groups/boards_controller_spec.rb
@@ -22,28 +22,6 @@ describe Groups::BoardsController do
expect(response.content_type).to eq 'text/html'
end
- it 'redirects to latest visited board' do
- board = create(:board, group: group)
- create(:board_group_recent_visit, group: board.group, board: board, user: user)
-
- list_boards
-
- expect(response).to redirect_to(group_board_path(id: board.id))
- end
-
- it 'renders template if visited board is not found' do
- temporary_board = create(:board, group: group)
- visited = create(:board_group_recent_visit, group: temporary_board.group, board: temporary_board, user: user)
- temporary_board.delete
-
- allow_any_instance_of(Boards::Visits::LatestService).to receive(:execute).and_return(visited)
-
- list_boards
-
- expect(response).to render_template :index
- expect(response.content_type).to eq 'text/html'
- end
-
context 'with unauthorized user' do
before do
allow(Ability).to receive(:allowed?).with(user, :read_cross_project, :global).and_return(true)
diff --git a/spec/controllers/groups/clusters/applications_controller_spec.rb b/spec/controllers/groups/clusters/applications_controller_spec.rb
index dd5263b077c..16a63536ea6 100644
--- a/spec/controllers/groups/clusters/applications_controller_spec.rb
+++ b/spec/controllers/groups/clusters/applications_controller_spec.rb
@@ -9,9 +9,25 @@ describe Groups::Clusters::ApplicationsController do
Clusters::Cluster::APPLICATIONS[application]
end
+ shared_examples 'a secure endpoint' do
+ it { expect { subject }.to be_allowed_for(:admin) }
+ it { expect { subject }.to be_allowed_for(:owner).of(group) }
+ it { expect { subject }.to be_allowed_for(:maintainer).of(group) }
+ it { expect { subject }.to be_denied_for(:developer).of(group) }
+ it { expect { subject }.to be_denied_for(:reporter).of(group) }
+ it { expect { subject }.to be_denied_for(:guest).of(group) }
+ it { expect { subject }.to be_denied_for(:user) }
+ it { expect { subject }.to be_denied_for(:external) }
+ end
+
+ let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
+ let(:group) { cluster.group }
+
describe 'POST create' do
- let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
- let(:group) { cluster.group }
+ subject do
+ post :create, params: params.merge(group_id: group)
+ end
+
let(:application) { 'helm' }
let(:params) { { application: application, id: cluster.id } }
@@ -26,7 +42,7 @@ describe Groups::Clusters::ApplicationsController do
it 'schedule an application installation' do
expect(ClusterInstallAppWorker).to receive(:perform_async).with(application, anything).once
- expect { go }.to change { current_application.count }
+ expect { subject }.to change { current_application.count }
expect(response).to have_http_status(:no_content)
expect(cluster.application_helm).to be_scheduled
end
@@ -37,7 +53,7 @@ describe Groups::Clusters::ApplicationsController do
end
it 'return 404' do
- expect { go }.not_to change { current_application.count }
+ expect { subject }.not_to change { current_application.count }
expect(response).to have_http_status(:not_found)
end
end
@@ -46,9 +62,7 @@ describe Groups::Clusters::ApplicationsController do
let(:application) { 'unkwnown-app' }
it 'return 404' do
- go
-
- expect(response).to have_http_status(:not_found)
+ is_expected.to have_http_status(:not_found)
end
end
@@ -58,9 +72,7 @@ describe Groups::Clusters::ApplicationsController do
end
it 'returns 400' do
- go
-
- expect(response).to have_http_status(:bad_request)
+ is_expected.to have_http_status(:bad_request)
end
end
end
@@ -70,18 +82,66 @@ describe Groups::Clusters::ApplicationsController do
allow(ClusterInstallAppWorker).to receive(:perform_async)
end
- it { expect { go }.to be_allowed_for(:admin) }
- it { expect { go }.to be_allowed_for(:owner).of(group) }
- it { expect { go }.to be_allowed_for(:maintainer).of(group) }
- it { expect { go }.to be_denied_for(:developer).of(group) }
- it { expect { go }.to be_denied_for(:reporter).of(group) }
- it { expect { go }.to be_denied_for(:guest).of(group) }
- it { expect { go }.to be_denied_for(:user) }
- it { expect { go }.to be_denied_for(:external) }
+ it_behaves_like 'a secure endpoint'
end
+ end
- def go
- post :create, params: params.merge(group_id: group)
+ describe 'PATCH update' do
+ subject do
+ patch :update, params: params.merge(group_id: group)
+ end
+
+ let!(:application) { create(:clusters_applications_cert_managers, :installed, cluster: cluster) }
+ let(:application_name) { application.name }
+ let(:params) { { application: application_name, id: cluster.id, email: "new-email@example.com" } }
+
+ describe 'functionality' do
+ let(:user) { create(:user) }
+
+ before do
+ group.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context "when cluster and app exists" do
+ it "schedules an application update" do
+ expect(ClusterPatchAppWorker).to receive(:perform_async).with(application.name, anything).once
+
+ is_expected.to have_http_status(:no_content)
+
+ expect(cluster.application_cert_manager).to be_scheduled
+ end
+ end
+
+ context 'when cluster do not exists' do
+ before do
+ cluster.destroy!
+ end
+
+ it { is_expected.to have_http_status(:not_found) }
+ end
+
+ context 'when application is unknown' do
+ let(:application_name) { 'unkwnown-app' }
+
+ it { is_expected.to have_http_status(:not_found) }
+ end
+
+ context 'when application is already scheduled' do
+ before do
+ application.make_scheduled!
+ end
+
+ it { is_expected.to have_http_status(:bad_request) }
+ end
+ end
+
+ describe 'security' do
+ before do
+ allow(ClusterPatchAppWorker).to receive(:perform_async)
+ end
+
+ it_behaves_like 'a secure endpoint'
end
end
end
diff --git a/spec/controllers/groups/settings/ci_cd_controller_spec.rb b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
index 40673d10b91..15eb0a442a6 100644
--- a/spec/controllers/groups/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/groups/settings/ci_cd_controller_spec.rb
@@ -66,4 +66,77 @@ describe Groups::Settings::CiCdController do
end
end
end
+
+ describe 'PATCH #update_auto_devops' do
+ let(:auto_devops_param) { '1' }
+
+ subject do
+ patch :update_auto_devops, params: {
+ group_id: group,
+ group: { auto_devops_enabled: auto_devops_param }
+ }
+ end
+
+ context 'when user does not have enough permission' do
+ before do
+ group.add_maintainer(user)
+ end
+
+ it { is_expected.to have_gitlab_http_status(404) }
+ end
+
+ context 'when user has enough privileges' do
+ before do
+ group.add_owner(user)
+ end
+
+ it { is_expected.to redirect_to(group_settings_ci_cd_path) }
+
+ context 'when service execution went wrong' do
+ before do
+ allow_any_instance_of(Groups::AutoDevopsService).to receive(:execute).and_return(false)
+ allow_any_instance_of(Group).to receive_message_chain(:errors, :full_messages)
+ .and_return(['Error 1'])
+
+ subject
+ end
+
+ it 'returns a flash alert' do
+ expect(response).to set_flash[:alert]
+ .to eq("There was a problem updating Auto DevOps pipeline: [\"Error 1\"].")
+ end
+ end
+
+ context 'when service execution was successful' do
+ it 'returns a flash notice' do
+ subject
+
+ expect(response).to set_flash[:notice]
+ .to eq('Auto DevOps pipeline was updated for the group')
+ end
+ end
+
+ context 'when changing auto devops value' do
+ before do
+ subject
+
+ group.reload
+ end
+
+ context 'when explicitly enabling auto devops' do
+ it 'should update group attribute' do
+ expect(group.auto_devops_enabled).to eq(true)
+ end
+ end
+
+ context 'when explicitly disabling auto devops' do
+ let(:auto_devops_param) { '0' }
+
+ it 'should update group attribute' do
+ expect(group.auto_devops_enabled).to eq(false)
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/controllers/groups/shared_projects_controller_spec.rb b/spec/controllers/groups/shared_projects_controller_spec.rb
index dab7700cf64..b0c20fb5a90 100644
--- a/spec/controllers/groups/shared_projects_controller_spec.rb
+++ b/spec/controllers/groups/shared_projects_controller_spec.rb
@@ -6,6 +6,8 @@ describe Groups::SharedProjectsController do
end
def share_project(project)
+ group.add_developer(user)
+
Projects::GroupLinks::CreateService.new(
project,
user,
diff --git a/spec/controllers/groups_controller_spec.rb b/spec/controllers/groups_controller_spec.rb
index 7d87b33e503..b2e6df6060a 100644
--- a/spec/controllers/groups_controller_spec.rb
+++ b/spec/controllers/groups_controller_spec.rb
@@ -32,21 +32,46 @@ describe GroupsController do
end
end
+ shared_examples 'details view' do
+ it { is_expected.to render_template('groups/show') }
+
+ context 'as atom' do
+ let!(:event) { create(:event, project: project) }
+ let(:format) { :atom }
+
+ it { is_expected.to render_template('groups/show') }
+
+ it 'assigns events for all the projects in the group' do
+ subject
+ expect(assigns(:events)).to contain_exactly(event)
+ end
+ end
+ end
+
describe 'GET #show' do
before do
sign_in(user)
project
end
- context 'as atom' do
- it 'assigns events for all the projects in the group' do
- create(:event, project: project)
+ let(:format) { :html }
- get :show, params: { id: group.to_param }, format: :atom
+ subject { get :show, params: { id: group.to_param }, format: format }
- expect(assigns(:events)).not_to be_empty
- end
+ it_behaves_like 'details view'
+ end
+
+ describe 'GET #details' do
+ before do
+ sign_in(user)
+ project
end
+
+ let(:format) { :html }
+
+ subject { get :details, params: { id: group.to_param }, format: format }
+
+ it_behaves_like 'details view'
end
describe 'GET edit' do
@@ -227,9 +252,7 @@ describe GroupsController do
context 'searching' do
before do
- # Remove in https://gitlab.com/gitlab-org/gitlab-ce/issues/54643
- stub_feature_flags(use_cte_for_group_issues_search: false)
- stub_feature_flags(use_subquery_for_group_issues_search: true)
+ stub_feature_flags(attempt_group_search_optimizations: true)
end
it 'works with popularity sort' do
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 232a5e2793b..06c6f49f7cc 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -113,6 +113,33 @@ describe OmniauthCallbacksController, type: :controller do
expect(request.env['warden']).to be_authenticated
end
+ context 'when user has no linked provider' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in user
+ end
+
+ it 'links identity' do
+ expect do
+ post provider
+ user.reload
+ end.to change { user.identities.count }.by(1)
+ end
+
+ context 'and is not allowed to link the provider' do
+ before do
+ allow_any_instance_of(IdentityProviderPolicy).to receive(:can?).with(:link).and_return(false)
+ end
+
+ it 'returns 403' do
+ post provider
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+ end
+
shared_context 'sign_up' do
let(:user) { double(email: 'new@example.com') }
@@ -193,7 +220,7 @@ describe OmniauthCallbacksController, type: :controller do
before do
stub_omniauth_saml_config({ enabled: true, auto_link_saml_user: true, allow_single_sign_on: ['saml'],
providers: [saml_config] })
- mock_auth_hash('saml', 'my-uid', user.email, mock_saml_response)
+ mock_auth_hash_with_saml_xml('saml', 'my-uid', user.email, mock_saml_response)
request.env["devise.mapping"] = Devise.mappings[:user]
request.env['omniauth.auth'] = Rails.application.env_config['omniauth.auth']
post :saml, params: { SAMLResponse: mock_saml_response }
diff --git a/spec/controllers/profiles/preferences_controller_spec.rb b/spec/controllers/profiles/preferences_controller_spec.rb
index 760c0fab130..ee881f85233 100644
--- a/spec/controllers/profiles/preferences_controller_spec.rb
+++ b/spec/controllers/profiles/preferences_controller_spec.rb
@@ -43,7 +43,8 @@ describe Profiles::PreferencesController do
color_scheme_id: '1',
dashboard: 'stars',
theme_id: '2',
- first_day_of_week: '1'
+ first_day_of_week: '1',
+ preferred_language: 'jp'
}.with_indifferent_access
expect(user).to receive(:assign_attributes).with(ActionController::Parameters.new(prefs).permit!)
diff --git a/spec/controllers/projects/autocomplete_sources_controller_spec.rb b/spec/controllers/projects/autocomplete_sources_controller_spec.rb
new file mode 100644
index 00000000000..a9a058e7e17
--- /dev/null
+++ b/spec/controllers/projects/autocomplete_sources_controller_spec.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::AutocompleteSourcesController do
+ set(:group) { create(:group) }
+ set(:project) { create(:project, namespace: group) }
+ set(:issue) { create(:issue, project: project) }
+ set(:user) { create(:user) }
+
+ describe 'GET members' do
+ before do
+ group.add_owner(user)
+ sign_in(user)
+ end
+
+ it 'returns an array of member object' do
+ get :members, format: :json, params: { namespace_id: group.path, project_id: project.path, type: issue.class.name, type_id: issue.id }
+
+ all = json_response.find {|member| member["username"] == 'all'}
+ the_group = json_response.find {|member| member["username"] == group.full_path}
+ the_user = json_response.find {|member| member["username"] == user.username}
+
+ expect(all.symbolize_keys).to include(username: 'all',
+ name: 'All Project and Group Members',
+ count: 1)
+
+ expect(the_group.symbolize_keys).to include(type: group.class.name,
+ name: group.full_name,
+ avatar_url: group.avatar_url,
+ count: 1)
+
+ expect(the_user.symbolize_keys).to include(type: user.class.name,
+ name: user.name,
+ avatar_url: user.avatar_url)
+ end
+ end
+
+ describe 'GET milestones' do
+ let(:group) { create(:group, :public) }
+ let(:project) { create(:project, :public, namespace: group) }
+ let!(:project_milestone) { create(:milestone, project: project) }
+ let!(:group_milestone) { create(:milestone, group: group) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'lists milestones' do
+ group.add_owner(user)
+
+ get :milestones, format: :json, params: { namespace_id: group.path, project_id: project.path }
+
+ milestone_titles = json_response.map { |milestone| milestone["title"] }
+ expect(milestone_titles).to match_array([project_milestone.title, group_milestone.title])
+ end
+
+ context 'when user cannot read project issues and merge requests' do
+ it 'renders 404' do
+ project.project_feature.update!(issues_access_level: ProjectFeature::PRIVATE)
+ project.project_feature.update!(merge_requests_access_level: ProjectFeature::PRIVATE)
+
+ get :milestones, format: :json, params: { namespace_id: group.path, project_id: project.path }
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/blob_controller_spec.rb b/spec/controllers/projects/blob_controller_spec.rb
index 38957e96798..3801fca09dc 100644
--- a/spec/controllers/projects/blob_controller_spec.rb
+++ b/spec/controllers/projects/blob_controller_spec.rb
@@ -144,54 +144,34 @@ describe Projects::BlobController do
end
context 'when rendering for merge request' do
- it 'renders diff context lines Gitlab::Diff::Line array' do
- do_get(since: 1, to: 5, offset: 10, from_merge_request: true)
-
- lines = JSON.parse(response.body)
-
- expect(lines.first).to have_key('type')
- expect(lines.first).to have_key('rich_text')
- expect(lines.first).to have_key('rich_text')
+ let(:presenter) { double(:presenter, diff_lines: diff_lines) }
+ let(:diff_lines) do
+ Array.new(3, Gitlab::Diff::Line.new('plain', nil, nil, nil, nil, rich_text: 'rich'))
end
- context 'when rendering match lines' do
- it 'adds top match line when "since" is less than 1' do
- do_get(since: 5, to: 10, offset: 10, from_merge_request: true)
-
- match_line = JSON.parse(response.body).first
-
- expect(match_line['type']).to eq('match')
- expect(match_line['meta_data']).to have_key('old_pos')
- expect(match_line['meta_data']).to have_key('new_pos')
- end
-
- it 'does not add top match line when "since" is equal 1' do
- do_get(since: 1, to: 10, offset: 10, from_merge_request: true)
-
- match_line = JSON.parse(response.body).first
-
- expect(match_line['type']).to be_nil
- end
+ before do
+ allow(Blobs::UnfoldPresenter).to receive(:new).and_return(presenter)
+ end
- it 'adds bottom match line when "t"o is less than blob size' do
- do_get(since: 1, to: 5, offset: 10, from_merge_request: true, bottom: true)
+ it 'renders diff context lines Gitlab::Diff::Line array' do
+ do_get(since: 1, to: 2, offset: 0, from_merge_request: true)
- match_line = JSON.parse(response.body).last
+ lines = JSON.parse(response.body)
- expect(match_line['type']).to eq('match')
- expect(match_line['meta_data']).to have_key('old_pos')
- expect(match_line['meta_data']).to have_key('new_pos')
+ expect(lines.size).to eq(diff_lines.size)
+ lines.each do |line|
+ expect(line).to have_key('type')
+ expect(line['text']).to eq('plain')
+ expect(line['rich_text']).to eq('rich')
end
+ end
- it 'does not add bottom match line when "to" is less than blob size' do
- commit_id = project.repository.commit('master').id
- blob = project.repository.blob_at(commit_id, 'CHANGELOG')
- do_get(since: 1, to: blob.lines.count, offset: 10, from_merge_request: true, bottom: true)
+ it 'handles full being true' do
+ do_get(full: true, from_merge_request: true)
- match_line = JSON.parse(response.body).last
+ lines = JSON.parse(response.body)
- expect(match_line['type']).to be_nil
- end
+ expect(lines.size).to eq(diff_lines.size)
end
end
end
diff --git a/spec/controllers/projects/boards_controller_spec.rb b/spec/controllers/projects/boards_controller_spec.rb
index 09199067024..1eeded06459 100644
--- a/spec/controllers/projects/boards_controller_spec.rb
+++ b/spec/controllers/projects/boards_controller_spec.rb
@@ -28,28 +28,6 @@ describe Projects::BoardsController do
expect(response.content_type).to eq 'text/html'
end
- it 'redirects to latest visited board' do
- board = create(:board, project: project)
- create(:board_project_recent_visit, project: board.project, board: board, user: user)
-
- list_boards
-
- expect(response).to redirect_to(namespace_project_board_path(id: board.id))
- end
-
- it 'renders template if visited board is not found' do
- temporary_board = create(:board, project: project)
- visited = create(:board_project_recent_visit, project: temporary_board.project, board: temporary_board, user: user)
- temporary_board.delete
-
- allow_any_instance_of(Boards::Visits::LatestService).to receive(:execute).and_return(visited)
-
- list_boards
-
- expect(response).to render_template :index
- expect(response.content_type).to eq 'text/html'
- end
-
context 'with unauthorized user' do
before do
allow(Ability).to receive(:allowed?).with(user, :read_project, project).and_return(true)
diff --git a/spec/controllers/projects/clusters/applications_controller_spec.rb b/spec/controllers/projects/clusters/applications_controller_spec.rb
index cb558259225..cd1a01f8acc 100644
--- a/spec/controllers/projects/clusters/applications_controller_spec.rb
+++ b/spec/controllers/projects/clusters/applications_controller_spec.rb
@@ -9,7 +9,22 @@ describe Projects::Clusters::ApplicationsController do
Clusters::Cluster::APPLICATIONS[application]
end
+ shared_examples 'a secure endpoint' do
+ it { expect { subject }.to be_allowed_for(:admin) }
+ it { expect { subject }.to be_allowed_for(:owner).of(project) }
+ it { expect { subject }.to be_allowed_for(:maintainer).of(project) }
+ it { expect { subject }.to be_denied_for(:developer).of(project) }
+ it { expect { subject }.to be_denied_for(:reporter).of(project) }
+ it { expect { subject }.to be_denied_for(:guest).of(project) }
+ it { expect { subject }.to be_denied_for(:user) }
+ it { expect { subject }.to be_denied_for(:external) }
+ end
+
describe 'POST create' do
+ subject do
+ post :create, params: params.merge(namespace_id: project.namespace, project_id: project)
+ end
+
let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
let(:project) { cluster.project }
let(:application) { 'helm' }
@@ -26,7 +41,7 @@ describe Projects::Clusters::ApplicationsController do
it 'schedule an application installation' do
expect(ClusterInstallAppWorker).to receive(:perform_async).with(application, anything).once
- expect { go }.to change { current_application.count }
+ expect { subject }.to change { current_application.count }
expect(response).to have_http_status(:no_content)
expect(cluster.application_helm).to be_scheduled
end
@@ -37,7 +52,7 @@ describe Projects::Clusters::ApplicationsController do
end
it 'return 404' do
- expect { go }.not_to change { current_application.count }
+ expect { subject }.not_to change { current_application.count }
expect(response).to have_http_status(:not_found)
end
end
@@ -46,9 +61,7 @@ describe Projects::Clusters::ApplicationsController do
let(:application) { 'unkwnown-app' }
it 'return 404' do
- go
-
- expect(response).to have_http_status(:not_found)
+ is_expected.to have_http_status(:not_found)
end
end
@@ -58,9 +71,7 @@ describe Projects::Clusters::ApplicationsController do
end
it 'returns 400' do
- go
-
- expect(response).to have_http_status(:bad_request)
+ is_expected.to have_http_status(:bad_request)
end
end
end
@@ -70,18 +81,68 @@ describe Projects::Clusters::ApplicationsController do
allow(ClusterInstallAppWorker).to receive(:perform_async)
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) }
+ it_behaves_like 'a secure endpoint'
end
+ end
- def go
- post :create, params: params.merge(namespace_id: project.namespace, project_id: project)
+ describe 'PATCH update' do
+ subject do
+ patch :update, params: params.merge(namespace_id: project.namespace, project_id: project)
+ end
+
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+ let(:project) { cluster.project }
+ let!(:application) { create(:clusters_applications_knative, :installed, cluster: cluster) }
+ let(:application_name) { application.name }
+ let(:params) { { application: application_name, id: cluster.id, hostname: "new.example.com" } }
+
+ describe 'functionality' do
+ let(:user) { create(:user) }
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+ end
+
+ context "when cluster and app exists" do
+ it "schedules an application update" do
+ expect(ClusterPatchAppWorker).to receive(:perform_async).with(application.name, anything).once
+
+ is_expected.to have_http_status(:no_content)
+
+ expect(cluster.application_knative).to be_scheduled
+ end
+ end
+
+ context 'when cluster do not exists' do
+ before do
+ cluster.destroy!
+ end
+
+ it { is_expected.to have_http_status(:not_found) }
+ end
+
+ context 'when application is unknown' do
+ let(:application_name) { 'unkwnown-app' }
+
+ it { is_expected.to have_http_status(:not_found) }
+ end
+
+ context 'when application is already scheduled' do
+ before do
+ application.make_scheduled!
+ end
+
+ it { is_expected.to have_http_status(:bad_request) }
+ end
+ end
+
+ describe 'security' do
+ before do
+ allow(ClusterPatchAppWorker).to receive(:perform_async)
+ end
+
+ it_behaves_like 'a secure endpoint'
end
end
end
diff --git a/spec/controllers/projects/environments_controller_spec.rb b/spec/controllers/projects/environments_controller_spec.rb
index aa97a417a98..36ce1119100 100644
--- a/spec/controllers/projects/environments_controller_spec.rb
+++ b/spec/controllers/projects/environments_controller_spec.rb
@@ -54,9 +54,9 @@ describe Projects::EnvironmentsController do
it 'responds with a flat payload describing available environments' do
expect(environments.count).to eq 3
- expect(environments.first['name']).to eq 'production'
- expect(environments.second['name']).to eq 'staging/review-1'
- expect(environments.third['name']).to eq 'staging/review-2'
+ expect(environments.first).to include('name' => 'production', 'name_without_type' => 'production')
+ expect(environments.second).to include('name' => 'staging/review-1', 'name_without_type' => 'review-1')
+ expect(environments.third).to include('name' => 'staging/review-2', 'name_without_type' => 'review-2')
expect(json_response['available_count']).to eq 3
expect(json_response['stopped_count']).to eq 1
end
@@ -155,9 +155,9 @@ describe Projects::EnvironmentsController do
expect(response).to be_ok
expect(response).not_to render_template 'folder'
expect(json_response['environments'][0])
- .to include('name' => 'staging-1.0/review')
+ .to include('name' => 'staging-1.0/review', 'name_without_type' => 'review')
expect(json_response['environments'][1])
- .to include('name' => 'staging-1.0/zzz')
+ .to include('name' => 'staging-1.0/zzz', 'name_without_type' => 'zzz')
end
end
end
diff --git a/spec/controllers/projects/git_http_controller_spec.rb b/spec/controllers/projects/git_http_controller_spec.rb
new file mode 100644
index 00000000000..bf099e8deeb
--- /dev/null
+++ b/spec/controllers/projects/git_http_controller_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::GitHttpController do
+ describe 'HEAD #info_refs' do
+ it 'returns 403' do
+ project = create(:project, :public, :repository)
+
+ head :info_refs, params: { namespace_id: project.namespace.to_param, project_id: project.path + '.git' }
+
+ expect(response.status).to eq(403)
+ end
+ end
+end
diff --git a/spec/controllers/projects/graphs_controller_spec.rb b/spec/controllers/projects/graphs_controller_spec.rb
index 73fb7307e11..8decd8f1382 100644
--- a/spec/controllers/projects/graphs_controller_spec.rb
+++ b/spec/controllers/projects/graphs_controller_spec.rb
@@ -24,4 +24,20 @@ describe Projects::GraphsController do
expect(response).to redirect_to action: :charts
end
end
+
+ describe 'charts' do
+ context 'when languages were previously detected' do
+ let!(:repository_language) { create(:repository_language, project: project) }
+
+ it 'sets the languages properly' do
+ get(:charts, params: { namespace_id: project.namespace.path, project_id: project.path, id: 'master' })
+
+ expect(assigns[:languages]).to eq(
+ [value: repository_language.share,
+ label: repository_language.name,
+ color: repository_language.color,
+ highlight: repository_language.color])
+ end
+ end
+ end
end
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index 675eeff8d12..ce021b2f085 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -65,8 +65,24 @@ describe Projects::GroupLinksController do
end
end
+ context 'when user does not have access to the public group' do
+ let(:group) { create(:group, :public) }
+
+ include_context 'link project to group'
+
+ it 'renders 404' do
+ expect(response.status).to eq 404
+ end
+
+ it 'does not share project with that group' do
+ expect(group.shared_projects).not_to include project
+ end
+ end
+
context 'when project group id equal link group id' do
before do
+ group2.add_developer(user)
+
post(:create, params: {
namespace_id: project.namespace,
project_id: project,
@@ -102,5 +118,26 @@ describe Projects::GroupLinksController do
expect(flash[:alert]).to eq('Please select a group.')
end
end
+
+ context 'when link is not persisted in the database' do
+ before do
+ allow(::Projects::GroupLinks::CreateService).to receive_message_chain(:new, :execute)
+ .and_return({ status: :error, http_status: 409, message: 'error' })
+
+ post(:create, params: {
+ namespace_id: project.namespace,
+ project_id: project,
+ link_group_id: group.id,
+ link_group_access: ProjectGroupLink.default_access
+ })
+ end
+
+ it 'redirects to project group links page' do
+ expect(response).to redirect_to(
+ project_project_members_path(project)
+ )
+ expect(flash[:alert]).to eq('error')
+ end
+ end
end
end
diff --git a/spec/controllers/projects/notes_controller_spec.rb b/spec/controllers/projects/notes_controller_spec.rb
index 81892575889..deecb7fefe9 100644
--- a/spec/controllers/projects/notes_controller_spec.rb
+++ b/spec/controllers/projects/notes_controller_spec.rb
@@ -252,8 +252,8 @@ describe Projects::NotesController do
note: 'some note',
noteable_id: merge_request.id.to_s,
noteable_type: 'MergeRequest',
- merge_request_diff_head_sha: 'sha',
- in_reply_to_discussion_id: nil
+ commit_id: nil,
+ merge_request_diff_head_sha: 'sha'
}).permit!
expect(Notes::CreateService).to receive(:new).with(project, user, service_params).and_return(double(execute: true))
@@ -266,6 +266,22 @@ describe Projects::NotesController do
end
end
+ context 'when creating a comment on a commit with SHA1 starting with a large number' do
+ let(:commit) { create(:commit, project: project, id: '842616594688d2351480dfebd67b3d8d15571e6d') }
+
+ it 'creates a note successfully' do
+ expect do
+ post :create, params: {
+ note: { note: 'some note', commit_id: commit.id },
+ namespace_id: project.namespace,
+ project_id: project,
+ target_type: 'commit',
+ target_id: commit.id
+ }
+ end.to change { Note.count }.by(1)
+ end
+ end
+
context 'when creating a commit comment from an MR fork' do
let(:project) { create(:project, :repository) }
@@ -397,6 +413,37 @@ describe Projects::NotesController do
end
end
end
+
+ context 'when creating a note with quick actions' do
+ context 'with commands that return changes' do
+ let(:note_text) { "/award :thumbsup:\n/estimate 1d\n/spend 3h" }
+
+ it 'includes changes in commands_changes ' do
+ post :create, params: request_params.merge(note: { note: note_text }, format: :json)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['commands_changes']).to include('emoji_award', 'time_estimate', 'spend_time')
+ expect(json_response['commands_changes']).not_to include('target_project', 'title')
+ end
+ end
+
+ context 'with commands that do not return changes' do
+ let(:issue) { create(:issue, project: project) }
+ let(:other_project) { create(:project) }
+ let(:note_text) { "/move #{other_project.full_path}\n/title AAA" }
+
+ before do
+ other_project.add_developer(user)
+ end
+
+ it 'does not include changes in commands_changes' do
+ post :create, params: request_params.merge(note: { note: note_text }, target_type: 'issue', target_id: issue.id, format: :json)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['commands_changes']).not_to include('target_project', 'title')
+ end
+ end
+ end
end
describe 'PUT update' do
diff --git a/spec/controllers/projects/pages_controller_spec.rb b/spec/controllers/projects/pages_controller_spec.rb
index 4b742a5d427..d6eece47804 100644
--- a/spec/controllers/projects/pages_controller_spec.rb
+++ b/spec/controllers/projects/pages_controller_spec.rb
@@ -42,6 +42,18 @@ describe Projects::PagesController do
expect(response).to have_gitlab_http_status(302)
end
+
+ context 'when user is developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'returns 404 status' do
+ delete :destroy, params: request_params
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
end
context 'pages disabled' do
diff --git a/spec/controllers/projects/settings/operations_controller_spec.rb b/spec/controllers/projects/settings/operations_controller_spec.rb
index d989ec22481..02a392f23c2 100644
--- a/spec/controllers/projects/settings/operations_controller_spec.rb
+++ b/spec/controllers/projects/settings/operations_controller_spec.rb
@@ -74,38 +74,55 @@ describe Projects::Settings::OperationsController do
{
error_tracking_setting_attributes: {
enabled: '1',
- api_url: 'http://url',
- token: 'token'
+ api_host: 'http://url',
+ token: 'token',
+ project: {
+ slug: 'sentry-project',
+ name: 'Sentry Project',
+ organization_slug: 'sentry-org',
+ organization_name: 'Sentry Org'
+ }
}
}
end
+
let(:error_tracking_permitted) do
ActionController::Parameters.new(error_tracking_params).permit!
end
- context 'when update succeeds' do
- before do
- stub_operations_update_service_returning(status: :success)
- end
-
- it 'shows a notice' do
- patch :update, params: project_params(project, error_tracking_params)
-
- expect(response).to redirect_to(operations_url)
- expect(flash[:notice]).to eq _('Your changes have been saved')
- end
- end
-
- context 'when update fails' do
- before do
- stub_operations_update_service_returning(status: :error)
+ context 'format json' do
+ context 'when update succeeds' do
+ before do
+ stub_operations_update_service_returning(status: :success)
+ end
+
+ it 'returns success status' do
+ patch :update,
+ params: project_params(project, error_tracking_params),
+ format: :json
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq('status' => 'success')
+ expect(flash[:notice]).to eq('Your changes have been saved')
+ end
end
- it 'renders show page' do
- patch :update, params: project_params(project, error_tracking_params)
-
- expect(response).to have_gitlab_http_status(:ok)
- expect(response).to render_template(:show)
+ context 'when update fails' do
+ before do
+ stub_operations_update_service_returning(
+ status: :error,
+ message: 'error message'
+ )
+ end
+
+ it 'returns error' do
+ patch :update,
+ params: project_params(project, error_tracking_params),
+ format: :json
+
+ expect(response).to have_gitlab_http_status(:bad_request)
+ expect(json_response['message']).not_to be_nil
+ end
end
end
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
index fd151e8a298..c1baf88778d 100644
--- a/spec/controllers/registrations_controller_spec.rb
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -15,7 +15,7 @@ describe RegistrationsController do
context 'when send_user_confirmation_email is false' do
it 'signs the user in' do
- allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(false)
+ stub_application_setting(send_user_confirmation_email: false)
expect { post(:create, params: user_params) }.not_to change { ActionMailer::Base.deliveries.size }
expect(subject.current_user).not_to be_nil
@@ -24,7 +24,7 @@ describe RegistrationsController do
context 'when send_user_confirmation_email is true' do
it 'does not authenticate user and sends confirmation email' do
- allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true)
+ stub_application_setting(send_user_confirmation_email: true)
post(:create, params: user_params)
@@ -35,7 +35,7 @@ describe RegistrationsController do
context 'when signup_enabled? is false' do
it 'redirects to sign_in' do
- allow_any_instance_of(ApplicationSetting).to receive(:signup_enabled?).and_return(false)
+ stub_application_setting(signup_enabled: false)
expect { post(:create, params: user_params) }.not_to change(User, :count)
expect(response).to redirect_to(new_user_session_path)
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
index 5c6858dc7b2..77a94f26d8c 100644
--- a/spec/controllers/snippets_controller_spec.rb
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -205,6 +205,8 @@ describe SnippetsController do
end
context 'when the snippet description contains a file' do
+ include FileMoverHelpers
+
let(:picture_file) { '/-/system/temp/secret56/picture.jpg' }
let(:text_file) { '/-/system/temp/secret78/text.txt' }
let(:description) do
@@ -215,6 +217,8 @@ describe SnippetsController do
before do
allow(FileUtils).to receive(:mkdir_p)
allow(FileUtils).to receive(:move)
+ stub_file_mover(text_file)
+ stub_file_mover(picture_file)
end
subject { create_snippet({ description: description }, { files: [picture_file, text_file] }) }
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index af61026098b..4f6a6881193 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -185,13 +185,32 @@ describe UsersController do
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)
+ let(:issue) { create(:issue, project: project, author: user) }
+ let(:note) { create(:note, noteable: issue, author: user, project: project) }
+
+ render_views
+
+ before do
+ create_push_event
+ create_note_event
+ end
+ it 'renders calendar_activities' do
get :calendar_activities, params: { username: public_user.username }
+
expect(assigns[:events]).not_to be_empty
end
+
+ it 'avoids N+1 queries', :request_store do
+ get :calendar_activities, params: { username: public_user.username }
+
+ control = ActiveRecord::QueryRecorder.new { get :calendar_activities, params: { username: public_user.username } }
+
+ create_push_event
+ create_note_event
+
+ expect { get :calendar_activities, params: { username: public_user.username } }.not_to exceed_query_limit(control)
+ end
end
context 'with private profile' do
@@ -203,6 +222,15 @@ describe UsersController do
expect(response).to have_gitlab_http_status(:not_found)
end
end
+
+ def create_push_event
+ push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user)
+ EventCreateService.new.push(project, public_user, push_data)
+ end
+
+ def create_note_event
+ EventCreateService.new.leave_note(note, public_user)
+ end
end
end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 0b3e67b4987..067391c1179 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -75,6 +75,10 @@ FactoryBot.define do
status 'created'
end
+ trait :preparing do
+ status 'preparing'
+ end
+
trait :scheduled do
schedulable
status 'scheduled'
diff --git a/spec/factories/ci/group_variables.rb b/spec/factories/ci/group_variables.rb
index 64716842b12..9bf520a2c0a 100644
--- a/spec/factories/ci/group_variables.rb
+++ b/spec/factories/ci/group_variables.rb
@@ -2,6 +2,7 @@ FactoryBot.define do
factory :ci_group_variable, class: Ci::GroupVariable do
sequence(:key) { |n| "VARIABLE_#{n}" }
value 'VARIABLE_VALUE'
+ masked false
trait(:protected) do
protected true
diff --git a/spec/factories/ci/pipelines.rb b/spec/factories/ci/pipelines.rb
index 8a44ce52849..aa5ccbda6cd 100644
--- a/spec/factories/ci/pipelines.rb
+++ b/spec/factories/ci/pipelines.rb
@@ -50,6 +50,14 @@ FactoryBot.define do
failure_reason :config_error
end
+ trait :created do
+ status :created
+ end
+
+ trait :preparing do
+ status :preparing
+ end
+
trait :blocked do
status :manual
end
@@ -82,6 +90,12 @@ FactoryBot.define do
end
end
+ trait :with_job do
+ after(:build) do |pipeline, evaluator|
+ pipeline.builds << build(:ci_build, pipeline: pipeline, project: pipeline.project)
+ end
+ end
+
trait :auto_devops_source do
config_source { Ci::Pipeline.config_sources[:auto_devops_source] }
end
diff --git a/spec/factories/ci/variables.rb b/spec/factories/ci/variables.rb
index 3d014b9b54f..97a7c9ba252 100644
--- a/spec/factories/ci/variables.rb
+++ b/spec/factories/ci/variables.rb
@@ -2,6 +2,7 @@ FactoryBot.define do
factory :ci_variable, class: Ci::Variable do
sequence(:key) { |n| "VARIABLE_#{n}" }
value 'VARIABLE_VALUE'
+ masked false
trait(:protected) do
protected true
diff --git a/spec/factories/clusters/clusters.rb b/spec/factories/clusters/clusters.rb
index a2e5f4862db..1cc3c0e03d8 100644
--- a/spec/factories/clusters/clusters.rb
+++ b/spec/factories/clusters/clusters.rb
@@ -12,7 +12,7 @@ FactoryBot.define do
cluster_type { Clusters::Cluster.cluster_types[:project_type] }
before(:create) do |cluster, evaluator|
- cluster.projects << create(:project, :repository)
+ cluster.projects << create(:project, :repository) unless cluster.projects.present?
end
end
@@ -20,7 +20,7 @@ FactoryBot.define do
cluster_type { Clusters::Cluster.cluster_types[:group_type] }
before(:create) do |cluster, evalutor|
- cluster.groups << create(:group)
+ cluster.groups << create(:group) unless cluster.groups.present?
end
end
diff --git a/spec/factories/clusters/providers/gcp.rb b/spec/factories/clusters/providers/gcp.rb
index a002ab28519..186c7c8027c 100644
--- a/spec/factories/clusters/providers/gcp.rb
+++ b/spec/factories/clusters/providers/gcp.rb
@@ -28,5 +28,9 @@ FactoryBot.define do
gcp.make_errored('Something wrong')
end
end
+
+ trait :abac_enabled do
+ legacy_abac true
+ end
end
end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index 381bf07f6a0..848a31e96c1 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -33,6 +33,10 @@ FactoryBot.define do
status 'pending'
end
+ trait :preparing do
+ status 'preparing'
+ end
+
trait :created do
status 'created'
end
diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb
index 3b354c0d96b..dcef8571f41 100644
--- a/spec/factories/groups.rb
+++ b/spec/factories/groups.rb
@@ -36,5 +36,13 @@ FactoryBot.define do
trait :nested do
parent factory: :group
end
+
+ trait :auto_devops_enabled do
+ auto_devops_enabled true
+ end
+
+ trait :auto_devops_disabled do
+ auto_devops_enabled false
+ end
end
end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 2392bfc4a53..a73f330a7a9 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -101,6 +101,36 @@ FactoryBot.define do
end
end
+ trait :with_detached_merge_request_pipeline do
+ after(:build) do |merge_request|
+ merge_request.merge_request_pipelines << build(:ci_pipeline,
+ source: :merge_request_event,
+ merge_request: merge_request,
+ project: merge_request.source_project,
+ ref: merge_request.ref_path,
+ sha: merge_request.source_branch_sha)
+ end
+ end
+
+ trait :with_merge_request_pipeline do
+ transient do
+ merge_sha { 'test-merge-sha' }
+ source_sha { source_branch_sha }
+ target_sha { target_branch_sha }
+ end
+
+ after(:build) do |merge_request, evaluator|
+ merge_request.merge_request_pipelines << create(:ci_pipeline,
+ source: :merge_request_event,
+ merge_request: merge_request,
+ project: merge_request.source_project,
+ ref: merge_request.merge_ref_path,
+ sha: evaluator.merge_sha,
+ source_sha: evaluator.source_sha,
+ target_sha: evaluator.target_sha)
+ end
+ end
+
trait :deployed_review_app do
target_branch 'pages-deploy-target'
diff --git a/spec/factories/personal_access_tokens.rb b/spec/factories/personal_access_tokens.rb
index 1b12f84d7b8..e7fd22a96b2 100644
--- a/spec/factories/personal_access_tokens.rb
+++ b/spec/factories/personal_access_tokens.rb
@@ -1,13 +1,14 @@
FactoryBot.define do
factory :personal_access_token do
user
- token { SecureRandom.hex(50) }
sequence(:name) { |n| "PAT #{n}" }
revoked false
expires_at { 5.days.from_now }
scopes ['api']
impersonation false
+ after(:build) { |personal_access_token| personal_access_token.ensure_token }
+
trait :impersonation do
impersonation true
end
@@ -21,7 +22,7 @@ FactoryBot.define do
end
trait :invalid do
- token nil
+ token_digest nil
end
end
end
diff --git a/spec/factories/project_daily_statistics.rb b/spec/factories/project_daily_statistics.rb
new file mode 100644
index 00000000000..7e4142fa401
--- /dev/null
+++ b/spec/factories/project_daily_statistics.rb
@@ -0,0 +1,8 @@
+# frozen_string_literal: true
+
+FactoryBot.define do
+ factory :project_daily_statistic do
+ project
+ fetch_count 1
+ end
+end
diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb
index f7ef34d773b..ab185ab3972 100644
--- a/spec/factories/projects.rb
+++ b/spec/factories/projects.rb
@@ -271,6 +271,10 @@ FactoryBot.define do
trait :auto_devops do
association :auto_devops, factory: :project_auto_devops
end
+
+ trait :auto_devops_disabled do
+ association :auto_devops, factory: [:project_auto_devops, :disabled]
+ end
end
# Project with empty repository
@@ -313,6 +317,20 @@ FactoryBot.define do
end
end
+ factory :youtrack_project, parent: :project do
+ has_external_issue_tracker true
+
+ after :create do |project|
+ project.create_youtrack_service(
+ active: true,
+ properties: {
+ 'project_url' => 'http://youtrack/projects/project_guid_in_youtrack',
+ 'issues_url' => 'http://youtrack/issues/:id'
+ }
+ )
+ end
+ end
+
factory :jira_project, parent: :project do
has_external_issue_tracker true
jira_service
diff --git a/spec/factories/suggestions.rb b/spec/factories/suggestions.rb
index 307523cc061..b1427e0211f 100644
--- a/spec/factories/suggestions.rb
+++ b/spec/factories/suggestions.rb
@@ -16,5 +16,11 @@ FactoryBot.define do
applied true
commit_id { RepoHelpers.sample_commit.id }
end
+
+ trait :content_from_repo do
+ after(:build) do |suggestion, evaluator|
+ suggestion.from_content = suggestion.fetch_from_content
+ end
+ end
end
end
diff --git a/spec/features/admin/admin_appearance_spec.rb b/spec/features/admin/admin_appearance_spec.rb
index 57215c0d1e9..83cd686818c 100644
--- a/spec/features/admin/admin_appearance_spec.rb
+++ b/spec/features/admin/admin_appearance_spec.rb
@@ -39,6 +39,38 @@ describe 'Admin Appearance' do
expect_custom_new_project_appearance(appearance)
end
+ context 'Custom system header and footer' do
+ before do
+ sign_in(create(:admin))
+ end
+
+ context 'when system header and footer messages are empty' do
+ it 'shows custom system header and footer fields' do
+ visit admin_appearances_path
+
+ expect(page).to have_field('appearance_header_message', with: '')
+ expect(page).to have_field('appearance_footer_message', with: '')
+ expect(page).to have_field('appearance_message_background_color')
+ expect(page).to have_field('appearance_message_font_color')
+ end
+ end
+
+ context 'when system header and footer messages are not empty' do
+ before do
+ appearance.update(header_message: 'Foo', footer_message: 'Bar')
+ end
+
+ it 'shows custom system header and footer fields' do
+ visit admin_appearances_path
+
+ expect(page).to have_field('appearance_header_message', with: appearance.header_message)
+ expect(page).to have_field('appearance_footer_message', with: appearance.footer_message)
+ expect(page).to have_field('appearance_message_background_color')
+ expect(page).to have_field('appearance_message_font_color')
+ end
+ end
+ end
+
it 'Custom sign-in page' do
visit new_user_session_path
diff --git a/spec/features/admin/admin_runners_spec.rb b/spec/features/admin/admin_runners_spec.rb
index ed9c0ea9ac0..97b432a6751 100644
--- a/spec/features/admin/admin_runners_spec.rb
+++ b/spec/features/admin/admin_runners_spec.rb
@@ -141,6 +141,56 @@ describe "Admin Runners" do
end
end
+ describe 'filter by tag', :js do
+ it 'shows correct runner when tag matches' do
+ create :ci_runner, description: 'runner-blue', tag_list: ['blue']
+ create :ci_runner, description: 'runner-red', tag_list: ['red']
+
+ visit admin_runners_path
+
+ expect(page).to have_content 'runner-blue'
+ expect(page).to have_content 'runner-red'
+
+ input_filtered_search_keys('tag:blue')
+
+ expect(page).to have_content 'runner-blue'
+ expect(page).not_to have_content 'runner-red'
+ end
+
+ it 'shows no runner when tag does not match' do
+ create :ci_runner, description: 'runner-blue', tag_list: ['blue']
+ create :ci_runner, description: 'runner-red', tag_list: ['blue']
+
+ visit admin_runners_path
+
+ input_filtered_search_keys('tag:red')
+
+ expect(page).not_to have_content 'runner-blue'
+ expect(page).not_to have_content 'runner-blue'
+ expect(page).to have_text 'No runners found'
+ end
+
+ it 'shows correct runner when tag is selected and search term is entered' do
+ create :ci_runner, description: 'runner-a-1', tag_list: ['blue']
+ create :ci_runner, description: 'runner-a-2', tag_list: ['red']
+ create :ci_runner, description: 'runner-b-1', tag_list: ['blue']
+
+ visit admin_runners_path
+
+ input_filtered_search_keys('tag:blue')
+
+ expect(page).to have_content 'runner-a-1'
+ expect(page).to have_content 'runner-b-1'
+ expect(page).not_to have_content 'runner-a-2'
+
+ input_filtered_search_keys('tag:blue runner-a')
+
+ expect(page).to have_content 'runner-a-1'
+ expect(page).not_to have_content 'runner-b-1'
+ expect(page).not_to have_content 'runner-a-2'
+ end
+ end
+
it 'sorts by last contact date', :js do
create(:ci_runner, description: 'runner-1', created_at: '2018-07-12 15:37', contacted_at: '2018-07-12 15:37')
create(:ci_runner, description: 'runner-2', created_at: '2018-07-12 16:37', contacted_at: '2018-07-12 16:37')
diff --git a/spec/features/clusters/cluster_detail_page_spec.rb b/spec/features/clusters/cluster_detail_page_spec.rb
index 0a9c4bcaf12..b9fc52d0dce 100644
--- a/spec/features/clusters/cluster_detail_page_spec.rb
+++ b/spec/features/clusters/cluster_detail_page_spec.rb
@@ -4,6 +4,8 @@ require 'spec_helper'
describe 'Clusterable > Show page' do
let(:current_user) { create(:user) }
+ let(:cluster_ingress_help_text_selector) { '.js-ingress-domain-help-text' }
+ let(:hide_modifier_selector) { '.hide' }
before do
sign_in(current_user)
@@ -35,7 +37,7 @@ describe 'Clusterable > Show page' do
it 'shows help text with the domain as an alternative to custom domain' do
within '#cluster-integration' do
- expect(page).to have_content('Alternatively 192.168.1.100.nip.io can be used instead of a custom domain')
+ expect(find(cluster_ingress_help_text_selector)).not_to match_css(hide_modifier_selector)
end
end
end
@@ -45,7 +47,7 @@ describe 'Clusterable > Show page' do
visit cluster_path
within '#cluster-integration' do
- expect(page).not_to have_content('can be used instead of a custom domain.')
+ expect(find(cluster_ingress_help_text_selector)).to match_css(hide_modifier_selector)
end
end
end
diff --git a/spec/features/cycle_analytics_spec.rb b/spec/features/cycle_analytics_spec.rb
index f4b2b9033ab..48edc764a8e 100644
--- a/spec/features/cycle_analytics_spec.rb
+++ b/spec/features/cycle_analytics_spec.rb
@@ -90,25 +90,6 @@ describe 'Cycle Analytics', :js do
end
end
end
-
- context "when my preferred language is Spanish" do
- before do
- user.update_attribute(:preferred_language, 'es')
-
- project.add_maintainer(user)
- sign_in(user)
- visit project_cycle_analytics_path(project)
- wait_for_requests
- end
-
- it 'shows the content in Spanish' do
- expect(page).to have_content('Estado del Pipeline')
- end
-
- it 'resets the language to English' do
- expect(I18n.locale).to eq(:en)
- end
- end
end
context "as a guest" do
diff --git a/spec/features/dashboard/activity_spec.rb b/spec/features/dashboard/activity_spec.rb
index bf91dc121d8..c55dc4523f7 100644
--- a/spec/features/dashboard/activity_spec.rb
+++ b/spec/features/dashboard/activity_spec.rb
@@ -39,6 +39,8 @@ describe 'Dashboard > Activity' do
event
end
+ let(:issue) { create(:issue, project: project) }
+
let!(:merged_event) do
create(:event, :merged, project: project, target: merge_request, author: user)
end
@@ -59,6 +61,10 @@ describe 'Dashboard > Activity' do
create(:event, :closed, project: project, target: milestone, author: user)
end
+ let!(:issue_event) do
+ create(:event, :created, project: project, target: issue, author: user)
+ end
+
before do
project.add_maintainer(user)
@@ -74,6 +80,7 @@ describe 'Dashboard > Activity' do
expect(page).to have_content('closed')
expect(page).to have_content('commented on')
expect(page).to have_content('closed milestone')
+ expect(page).to have_content('opened issue')
end
end
@@ -87,6 +94,7 @@ describe 'Dashboard > Activity' do
expect(page).not_to have_content('accepted')
expect(page).not_to have_content('closed')
expect(page).not_to have_content('commented on')
+ expect(page).not_to have_content('opened issue')
end
end
@@ -100,6 +108,7 @@ describe 'Dashboard > Activity' do
expect(page).to have_content('accepted')
expect(page).not_to have_content('closed')
expect(page).not_to have_content('commented on')
+ expect(page).not_to have_content('opened issue')
end
end
@@ -111,9 +120,10 @@ describe 'Dashboard > Activity' do
expect(page).not_to have_content('pushed new branch')
expect(page).not_to have_content('joined')
expect(page).not_to have_content('accepted')
- expect(page).to have_content('closed')
+ expect(page).not_to have_content('closed')
expect(page).not_to have_content('commented on')
- expect(page).to have_content('closed milestone')
+ expect(page).not_to have_content('closed milestone')
+ expect(page).to have_content('opened issue')
end
end
@@ -127,6 +137,7 @@ describe 'Dashboard > Activity' do
expect(page).not_to have_content('accepted')
expect(page).not_to have_content('closed')
expect(page).to have_content('commented on')
+ expect(page).not_to have_content('opened issue')
end
end
@@ -140,6 +151,7 @@ describe 'Dashboard > Activity' do
expect(page).not_to have_content('accepted')
expect(page).not_to have_content('closed')
expect(page).not_to have_content('commented on')
+ expect(page).not_to have_content('opened issue')
end
end
@@ -155,6 +167,7 @@ describe 'Dashboard > Activity' do
expect(page).not_to have_content('accepted')
expect(page).not_to have_content('closed')
expect(page).not_to have_content('commented on')
+ expect(page).not_to have_content('opened issue')
end
end
end
diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb
index 6c4b04ab76b..9d1c1e3acc7 100644
--- a/spec/features/dashboard/projects_spec.rb
+++ b/spec/features/dashboard/projects_spec.rb
@@ -114,7 +114,16 @@ describe 'Dashboard Projects' do
end
end
- context 'when on Starred projects tab' do
+ context 'when on Starred projects tab', :js do
+ it 'shows the empty state when there are no starred projects' do
+ visit(starred_dashboard_projects_path)
+
+ element = page.find('.row.empty-state')
+
+ expect(element).to have_content("You don't have starred projects yet.")
+ expect(element.find('.svg-content img')['src']).to have_content('illustrations/starred_empty')
+ end
+
it 'shows only starred projects' do
user.toggle_star(project2)
diff --git a/spec/features/dashboard/shortcuts_spec.rb b/spec/features/dashboard/shortcuts_spec.rb
index cbddf117462..55f5ff04d01 100644
--- a/spec/features/dashboard/shortcuts_spec.rb
+++ b/spec/features/dashboard/shortcuts_spec.rb
@@ -44,7 +44,7 @@ describe 'Dashboard shortcuts', :js do
find('body').send_keys([:shift, 'S'])
find('.nothing-here-block')
- expect(page).to have_selector('.snippets-list-holder')
+ expect(page).to have_content('No snippets found')
find('body').send_keys([:shift, 'P'])
diff --git a/spec/features/dashboard/snippets_spec.rb b/spec/features/dashboard/snippets_spec.rb
index fb4263d74c4..0e248c8732d 100644
--- a/spec/features/dashboard/snippets_spec.rb
+++ b/spec/features/dashboard/snippets_spec.rb
@@ -13,6 +13,21 @@ describe 'Dashboard snippets' do
it_behaves_like 'paginated snippets'
end
+ context 'when there are no project snippets', :js do
+ let(:project) { create(:project, :public) }
+ before do
+ sign_in(project.owner)
+ visit dashboard_snippets_path
+ end
+
+ it 'shows the empty state when there are no snippets' do
+ element = page.find('.row.empty-state')
+
+ expect(element).to have_content("Snippets are small pieces of code or notes that you want to keep.")
+ expect(element.find('.svg-content img')['src']).to have_content('illustrations/snippets_empty')
+ end
+ end
+
context 'filtering by visibility' do
let(:user) { create(:user) }
let!(:snippets) do
diff --git a/spec/features/display_system_header_and_footer_bar_spec.rb b/spec/features/display_system_header_and_footer_bar_spec.rb
new file mode 100644
index 00000000000..af9d9a5834f
--- /dev/null
+++ b/spec/features/display_system_header_and_footer_bar_spec.rb
@@ -0,0 +1,137 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'Display system header and footer bar' do
+ let(:header_message) { "Foo" }
+ let(:footer_message) { "Bar" }
+
+ shared_examples 'system header is configured' do
+ it 'shows system header' do
+ expect(page).to have_css('.header-message')
+ end
+
+ it 'shows the correct content' do
+ page.within('.header-message') do
+ expect(page).to have_content(header_message)
+ end
+ end
+ end
+
+ shared_examples 'system footer is configured' do
+ it 'shows system footer' do
+ expect(page).to have_css('.footer-message')
+ end
+
+ it 'shows the correct content' do
+ page.within('.footer-message') do
+ expect(page).to have_content(footer_message)
+ end
+ end
+ end
+
+ shared_examples 'system header is not configured' do
+ it 'does not show system header' do
+ expect(page).not_to have_css('.header-message')
+ end
+ end
+
+ shared_examples 'system footer is not configured' do
+ it 'does not show system footer' do
+ expect(page).not_to have_css('.footer-message')
+ end
+ end
+
+ context 'when authenticated' do
+ context 'when system header and footer are not configured' do
+ before do
+ sign_in(create(:user))
+
+ visit root_path
+ end
+
+ it_behaves_like 'system header is not configured'
+ it_behaves_like 'system footer is not configured'
+ end
+
+ context 'when only system header is defined' do
+ before do
+ create(:appearance, header_message: header_message)
+
+ sign_in(create(:user))
+ visit root_path
+ end
+
+ it_behaves_like 'system header is configured'
+ it_behaves_like 'system footer is not configured'
+ end
+
+ context 'when only system footer is defined' do
+ before do
+ create(:appearance, footer_message: footer_message)
+
+ sign_in(create(:user))
+ visit root_path
+ end
+
+ it_behaves_like 'system header is not configured'
+ it_behaves_like 'system footer is configured'
+ end
+
+ context 'when system header and footer are defined' do
+ before do
+ create(:appearance, header_message: header_message, footer_message: footer_message)
+
+ sign_in(create(:user))
+ visit root_path
+ end
+
+ it_behaves_like 'system header is configured'
+ it_behaves_like 'system footer is configured'
+ end
+ end
+
+ context 'when not authenticated' do
+ context 'when system header and footer are not configured' do
+ before do
+ visit root_path
+ end
+
+ it_behaves_like 'system header is not configured'
+ it_behaves_like 'system footer is not configured'
+ end
+
+ context 'when only system header is defined' do
+ before do
+ create(:appearance, header_message: header_message)
+
+ visit root_path
+ end
+
+ it_behaves_like 'system header is configured'
+ it_behaves_like 'system footer is not configured'
+ end
+
+ context 'when only system footer is defined' do
+ before do
+ create(:appearance, footer_message: footer_message)
+
+ visit root_path
+ end
+
+ it_behaves_like 'system header is not configured'
+ it_behaves_like 'system footer is configured'
+ end
+
+ context 'when system header and footer are defined' do
+ before do
+ create(:appearance, header_message: header_message, footer_message: footer_message)
+
+ visit root_path
+ end
+
+ it_behaves_like 'system header is configured'
+ it_behaves_like 'system footer is configured'
+ end
+ end
+end
diff --git a/spec/features/group_variables_spec.rb b/spec/features/group_variables_spec.rb
index 57e3ddfb39c..1a53e7c9512 100644
--- a/spec/features/group_variables_spec.rb
+++ b/spec/features/group_variables_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
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!(:variable) { create(:ci_group_variable, key: 'test_key', value: 'test_value', group: group) }
let(:page_path) { group_settings_ci_cd_path(group) }
before do
diff --git a/spec/features/groups/settings/ci_cd_spec.rb b/spec/features/groups/settings/ci_cd_spec.rb
index d422fd18346..0f793dbab6e 100644
--- a/spec/features/groups/settings/ci_cd_spec.rb
+++ b/spec/features/groups/settings/ci_cd_spec.rb
@@ -5,8 +5,8 @@ require 'spec_helper'
describe 'Group CI/CD settings' do
include WaitForRequests
- let(:user) {create(:user)}
- let(:group) {create(:group)}
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
before do
group.add_owner(user)
@@ -36,4 +36,45 @@ describe 'Group CI/CD settings' do
end
end
end
+
+ describe 'Auto DevOps form' do
+ before do
+ stub_application_setting(auto_devops_enabled: true)
+ end
+
+ context 'as owner first visiting group settings' do
+ it 'should see instance enabled badge' do
+ visit group_settings_ci_cd_path(group)
+
+ page.within '#auto-devops-settings' do
+ expect(page).to have_content('instance enabled')
+ end
+ end
+ end
+
+ context 'when Auto DevOps group has been enabled' do
+ it 'should see group enabled badge' do
+ group.update!(auto_devops_enabled: true)
+
+ visit group_settings_ci_cd_path(group)
+
+ page.within '#auto-devops-settings' do
+ expect(page).to have_content('group enabled')
+ end
+ end
+ end
+
+ context 'when Auto DevOps group has been disabled' do
+ it 'should not see a badge' do
+ group.update!(auto_devops_enabled: false)
+
+ visit group_settings_ci_cd_path(group)
+
+ page.within '#auto-devops-settings' do
+ expect(page).not_to have_content('instance enabled')
+ expect(page).not_to have_content('group enabled')
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/issuables/issuable_list_spec.rb b/spec/features/issuables/issuable_list_spec.rb
index 2f45ef856a5..7b6e9cd66b2 100644
--- a/spec/features/issuables/issuable_list_spec.rb
+++ b/spec/features/issuables/issuable_list_spec.rb
@@ -28,6 +28,22 @@ describe 'issuable list' do
expect(first('.fa-thumbs-down').find(:xpath, '..')).to have_content(1)
expect(first('.fa-comments').find(:xpath, '..')).to have_content(2)
end
+
+ it 'sorts labels alphabetically' do
+ label1 = create(:label, project: project, title: 'a')
+ label2 = create(:label, project: project, title: 'z')
+ label3 = create(:label, project: project, title: 'X')
+ label4 = create(:label, project: project, title: 'B')
+ issuable = create_issuable(issuable_type)
+ issuable.labels << [label1, label2, label3, label4]
+
+ visit_issuable_list(issuable_type)
+
+ expect(all('.label-link')[0].text).to have_content('B')
+ expect(all('.label-link')[1].text).to have_content('X')
+ expect(all('.label-link')[2].text).to have_content('a')
+ expect(all('.label-link')[3].text).to have_content('z')
+ end
end
it "counts merge requests closing issues icons for each issue" do
@@ -45,6 +61,14 @@ describe 'issuable list' do
end
end
+ def create_issuable(issuable_type)
+ if issuable_type == :issue
+ create(:issue, project: project)
+ else
+ create(:merge_request, source_project: project)
+ end
+ end
+
def create_issuables(issuable_type)
3.times do |n|
issuable =
diff --git a/spec/features/issues/filtered_search/dropdown_hint_spec.rb b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
index 0e296ab2109..096756f19cc 100644
--- a/spec/features/issues/filtered_search/dropdown_hint_spec.rb
+++ b/spec/features/issues/filtered_search/dropdown_hint_spec.rb
@@ -66,7 +66,7 @@ describe 'Dropdown hint', :js do
it 'filters with text' do
filtered_search.set('a')
- expect(find(js_dropdown_hint)).to have_selector('.filter-dropdown .filter-dropdown-item', count: 4)
+ expect(find(js_dropdown_hint)).to have_selector('.filter-dropdown .filter-dropdown-item', count: 5)
end
end
@@ -119,6 +119,15 @@ describe 'Dropdown hint', :js do
expect_tokens([{ name: 'my-reaction' }])
expect_filtered_search_input_empty
end
+
+ it 'opens the yes-no dropdown when you click on confidential' do
+ click_hint('confidential')
+
+ expect(page).to have_css(js_dropdown_hint, visible: false)
+ expect(page).to have_css('#js-dropdown-confidential', visible: true)
+ expect_tokens([{ name: 'confidential' }])
+ expect_filtered_search_input_empty
+ end
end
describe 'selecting from dropdown with some input' do
diff --git a/spec/features/issues/filtered_search/search_bar_spec.rb b/spec/features/issues/filtered_search/search_bar_spec.rb
index 8abab3f35d6..da23aea1fc9 100644
--- a/spec/features/issues/filtered_search/search_bar_spec.rb
+++ b/spec/features/issues/filtered_search/search_bar_spec.rb
@@ -86,7 +86,7 @@ describe 'Search bar', :js do
expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: original_size)
end
- it 'resets the dropdown filters' do
+ it 'resets the dropdown filters', :quarantine do
filtered_search.click
hint_offset = get_left_style(find('#js-dropdown-hint')['style'])
@@ -100,7 +100,7 @@ describe 'Search bar', :js do
find('.filtered-search-box .clear-search').click
filtered_search.click
- expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 5)
+ expect(find('#js-dropdown-hint')).to have_selector('.filter-dropdown .filter-dropdown-item', count: 6)
expect(get_left_style(find('#js-dropdown-hint')['style'])).to eq(hint_offset)
end
end
diff --git a/spec/features/issues/filtered_search/visual_tokens_spec.rb b/spec/features/issues/filtered_search/visual_tokens_spec.rb
index a4c34ce85f0..9fd661d80ae 100644
--- a/spec/features/issues/filtered_search/visual_tokens_spec.rb
+++ b/spec/features/issues/filtered_search/visual_tokens_spec.rb
@@ -59,13 +59,6 @@ describe 'Visual tokens', :js do
expect(page).to have_css('#js-dropdown-author', visible: false)
end
- it 'ends editing mode when scroll container is clicked' do
- find('.scroll-container').click
-
- expect_filtered_search_input_empty
- expect(page).to have_css('#js-dropdown-author', visible: false)
- end
-
describe 'selecting different author from dropdown' do
before do
filter_author_dropdown.find('.filter-dropdown-item .dropdown-light-content', text: "@#{user_rock.username}").click
@@ -109,13 +102,6 @@ describe 'Visual tokens', :js do
expect(page).to have_css('#js-dropdown-assignee', visible: false)
end
- it 'ends editing mode when scroll container is clicked' do
- find('.scroll-container').click
-
- expect_filtered_search_input_empty
- expect(page).to have_css('#js-dropdown-assignee', visible: false)
- end
-
describe 'selecting static option from dropdown' do
before do
find("#js-dropdown-assignee").find('.filter-dropdown-item', text: 'None').click
@@ -167,13 +153,6 @@ describe 'Visual tokens', :js do
expect_filtered_search_input_empty
expect(page).to have_css('#js-dropdown-milestone', visible: false)
end
-
- it 'ends editing mode when scroll container is clicked' do
- find('.scroll-container').click
-
- expect_filtered_search_input_empty
- expect(page).to have_css('#js-dropdown-milestone', visible: false)
- end
end
describe 'editing label token' do
diff --git a/spec/features/issues/gfm_autocomplete_spec.rb b/spec/features/issues/gfm_autocomplete_spec.rb
index c22ad0d20ef..8eb413bdd8d 100644
--- a/spec/features/issues/gfm_autocomplete_spec.rb
+++ b/spec/features/issues/gfm_autocomplete_spec.rb
@@ -278,12 +278,7 @@ describe 'GFM autocomplete', :js do
end
end
- # This context has jsut one example in each contexts in order to improve spec performance.
- context 'labels', :quarantine do
- let!(:backend) { create(:label, project: project, title: 'backend') }
- let!(:bug) { create(:label, project: project, title: 'bug') }
- let!(:feature_proposal) { create(:label, project: project, title: 'feature proposal') }
-
+ context 'labels' do
it 'opens autocomplete menu for Labels when field starts with text with item escaping HTML characters' do
create(:label, project: project, title: label_xss_title)
@@ -298,83 +293,6 @@ describe 'GFM autocomplete', :js do
expect(find('.atwho-view-ul').text).to have_content('alert label')
end
end
-
- context 'when no labels are assigned' do
- it 'shows labels' do
- note = find('#note-body')
-
- # It should show all the labels on "~".
- type(note, '~')
- wait_for_requests
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show all the labels on "/label ~".
- type(note, '/label ~')
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show all the labels on "/relabel ~".
- type(note, '/relabel ~')
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show no labels on "/unlabel ~".
- type(note, '/unlabel ~')
- expect_labels(not_shown: [backend, bug, feature_proposal])
- end
- end
-
- context 'when some labels are assigned' do
- before do
- issue.labels << [backend]
- end
-
- it 'shows labels' do
- note = find('#note-body')
-
- # It should show all the labels on "~".
- type(note, '~')
- wait_for_requests
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show only unset labels on "/label ~".
- type(note, '/label ~')
- expect_labels(shown: [bug, feature_proposal], not_shown: [backend])
-
- # It should show all the labels on "/relabel ~".
- type(note, '/relabel ~')
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show only set labels on "/unlabel ~".
- type(note, '/unlabel ~')
- expect_labels(shown: [backend], not_shown: [bug, feature_proposal])
- end
- end
-
- context 'when all labels are assigned' do
- before do
- issue.labels << [backend, bug, feature_proposal]
- end
-
- it 'shows labels' do
- note = find('#note-body')
-
- # It should show all the labels on "~".
- type(note, '~')
- wait_for_requests
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show no labels on "/label ~".
- type(note, '/label ~')
- expect_labels(not_shown: [backend, bug, feature_proposal])
-
- # It should show all the labels on "/relabel ~".
- type(note, '/relabel ~')
- expect_labels(shown: [backend, bug, feature_proposal])
-
- # It should show all the labels on "/unlabel ~".
- type(note, '/unlabel ~')
- expect_labels(shown: [backend, bug, feature_proposal])
- end
- end
end
shared_examples 'autocomplete suggestions' do
diff --git a/spec/features/issues/user_creates_issue_spec.rb b/spec/features/issues/user_creates_issue_spec.rb
index e60486f6dcb..0f604db870f 100644
--- a/spec/features/issues/user_creates_issue_spec.rb
+++ b/spec/features/issues/user_creates_issue_spec.rb
@@ -93,4 +93,22 @@ describe "User creates issue" do
end
end
end
+
+ context "when signed in as user with special characters in their name" do
+ let(:user_special) { create(:user, name: "Jon O'Shea") }
+
+ before do
+ project.add_developer(user_special)
+ sign_in(user_special)
+
+ visit(new_project_issue_path(project))
+ end
+
+ it "will correctly escape user names with an apostrophe when clicking 'Assign to me'", :js do
+ first('.assign-to-me-link').click
+
+ expect(page).to have_content(user_special.name)
+ expect(page.find('input[name="issue[assignee_ids][]"]', visible: false)['data-meta']).to eq(user_special.name)
+ end
+ end
end
diff --git a/spec/features/issues/user_uses_quick_actions_spec.rb b/spec/features/issues/user_uses_quick_actions_spec.rb
index 27cffdc5f8b..b5e7c3954e2 100644
--- a/spec/features/issues/user_uses_quick_actions_spec.rb
+++ b/spec/features/issues/user_uses_quick_actions_spec.rb
@@ -243,7 +243,9 @@ describe 'Issues > User uses quick actions', :js do
it 'does not move the issue' do
add_note("/move not/valid")
- expect(page).not_to have_content 'Commands applied'
+ wait_for_requests
+
+ expect(page).to have_content 'Commands applied'
expect(issue.reload).to be_open
end
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index 406e80e91aa..51508b78649 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -233,8 +233,8 @@ describe 'Issues' do
created_at: Time.now - (index * 60))
end
end
- let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') }
- let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') }
+ let(:newer_due_milestone) { create(:milestone, project: project, due_date: '2013-12-11') }
+ let(:later_due_milestone) { create(:milestone, project: project, due_date: '2013-12-12') }
it 'sorts by newest' do
visit project_issues_path(project, sort: sort_value_created_date)
@@ -497,12 +497,21 @@ describe 'Issues' do
it 'allows user to unselect themselves', :js do
issue2 = create(:issue, project: project, author: user)
+
visit project_issue_path(project, issue2)
+ def close_dropdown_menu_if_visible
+ find('.dropdown-menu-toggle', visible: :all).tap do |toggle|
+ toggle.click if toggle.visible?
+ end
+ end
+
page.within '.assignee' do
click_link 'Edit'
click_link user.name
+ close_dropdown_menu_if_visible
+
page.within '.value .author' do
expect(page).to have_content user.name
end
@@ -510,6 +519,8 @@ describe 'Issues' do
click_link 'Edit'
click_link user.name
+ close_dropdown_menu_if_visible
+
page.within '.value .assign-yourself' do
expect(page).to have_content "No assignee"
end
diff --git a/spec/features/markdown/copy_as_gfm_spec.rb b/spec/features/markdown/copy_as_gfm_spec.rb
index 60ddb02da2c..c30ac9c4ae2 100644
--- a/spec/features/markdown/copy_as_gfm_spec.rb
+++ b/spec/features/markdown/copy_as_gfm_spec.rb
@@ -55,15 +55,10 @@ describe 'Copy as GFM', :js do
To see how GitLab looks please see the [features page on our website](https://about.gitlab.com/features/).
* Manage Git repositories with fine grained access controls that keep your code secure
-
* Perform code reviews and enhance collaboration with merge requests
-
* Complete continuous integration (CI) and CD pipelines to builds, test, and deploy your applications
-
* Each project can also have an issue tracker, issue board, and a wiki
-
* Used by more than 100,000 organizations, GitLab is the most popular solution to manage Git repositories on-premises
-
* Completely free and open source (MIT Expat license)
GFM
)
@@ -116,13 +111,11 @@ describe 'Copy as GFM', :js do
<<~GFM,
* [ ] Unchecked task
-
* [x] Checked task
GFM
<<~GFM
1. [ ] Unchecked ordered task
-
1. [x] Checked ordered task
GFM
)
@@ -551,7 +544,6 @@ describe 'Copy as GFM', :js do
<<~GFM,
* List item
-
* List item 2
GFM
@@ -565,7 +557,6 @@ describe 'Copy as GFM', :js do
# nested lists
<<~GFM,
* Nested
-
* Lists
GFM
@@ -578,7 +569,6 @@ describe 'Copy as GFM', :js do
<<~GFM,
1. Ordered list item
-
1. Ordered list item 2
GFM
@@ -592,7 +582,6 @@ describe 'Copy as GFM', :js do
# nested ordered list
<<~GFM,
1. Nested
-
1. Ordered lists
GFM
diff --git a/spec/features/merge_request/maintainer_edits_fork_spec.rb b/spec/features/merge_request/maintainer_edits_fork_spec.rb
index 7839b97122c..b35f985126c 100644
--- a/spec/features/merge_request/maintainer_edits_fork_spec.rb
+++ b/spec/features/merge_request/maintainer_edits_fork_spec.rb
@@ -18,13 +18,15 @@ describe 'a maintainer edits files on a source-branch of an MR from a fork', :js
end
before do
+ stub_feature_flags(web_ide_default: false)
+
target_project.add_maintainer(user)
sign_in(user)
visit project_merge_request_path(target_project, merge_request)
click_link 'Changes'
wait_for_requests
- first('.js-file-title').click_link 'Edit'
+ first('.js-file-title').find('.js-edit-blob').click
wait_for_requests
end
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 51b78d3e7d1..19edce1b562 100644
--- a/spec/features/merge_request/user_posts_diff_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_diff_notes_spec.rb
@@ -178,7 +178,7 @@ describe 'Merge request > User posts diff notes', :js do
end
end
- describe 'with muliple note forms' do
+ describe 'with multiple note forms' do
before do
visit diffs_project_merge_request_path(project, merge_request, view: 'inline')
click_diff_line(find('[id="2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"]'))
diff --git a/spec/features/merge_request/user_posts_notes_spec.rb b/spec/features/merge_request/user_posts_notes_spec.rb
index 1bbcf455ac7..dc0862be6fc 100644
--- a/spec/features/merge_request/user_posts_notes_spec.rb
+++ b/spec/features/merge_request/user_posts_notes_spec.rb
@@ -67,7 +67,7 @@ describe 'Merge request > User posts notes', :js do
end
end
- describe 'when reply_to_individual_notes feature flag is not set' do
+ describe 'when reply_to_individual_notes feature flag is disabled' do
before do
stub_feature_flags(reply_to_individual_notes: false)
visit project_merge_request_path(project, merge_request)
@@ -78,9 +78,8 @@ describe 'Merge request > User posts notes', :js do
end
end
- describe 'when reply_to_individual_notes feature flag is set' do
+ describe 'when reply_to_individual_notes feature flag is not set' do
before do
- stub_feature_flags(reply_to_individual_notes: true)
visit project_merge_request_path(project, merge_request)
end
diff --git a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb
index 8c2599615cb..2f7d359575e 100644
--- a/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb
+++ b/spec/features/merge_request/user_scrolls_to_note_on_load_spec.rb
@@ -5,9 +5,7 @@ describe 'Merge request > User scrolls to note on load', :js do
let(:user) { project.creator }
let(:merge_request) { create(:merge_request, source_project: project, author: user) }
let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: project) }
- let(:resolved_note) { create(:diff_note_on_merge_request, :resolved, noteable: merge_request, project: project) }
let(:fragment_id) { "#note_#{note.id}" }
- let(:collapsed_fragment_id) { "#note_#{resolved_note.id}" }
before do
sign_in(user)
@@ -45,13 +43,35 @@ describe 'Merge request > User scrolls to note on load', :js do
end
end
- # TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
- xit 'expands collapsed notes' do
- visit "#{project_merge_request_path(project, merge_request)}#{collapsed_fragment_id}"
- note_element = find(collapsed_fragment_id)
- note_container = note_element.ancestor('.timeline-content')
+ context 'resolved notes' do
+ let(:collapsed_fragment_id) { "#note_#{resolved_note.id}" }
- expect(note_element.visible?).to eq true
- expect(note_container.find('.line_content.noteable_line.old', match: :first).visible?).to eq true
+ context 'when diff note' do
+ let(:resolved_note) { create(:diff_note_on_merge_request, :resolved, noteable: merge_request, project: project) }
+
+ it 'expands collapsed notes' do
+ visit "#{project_merge_request_path(project, merge_request)}#{collapsed_fragment_id}"
+
+ note_element = find(collapsed_fragment_id)
+ diff_container = note_element.ancestor('.diff-content')
+
+ expect(note_element.visible?).to eq(true)
+ expect(diff_container.visible?).to eq(true)
+ end
+ end
+
+ context 'when non-diff note' do
+ let(:non_diff_discussion) { create(:discussion_note_on_merge_request, :resolved, noteable: merge_request, project: project) }
+ let(:resolved_note) { create(:discussion_note_on_merge_request, :resolved, noteable: merge_request, project: project, in_reply_to: non_diff_discussion) }
+
+ it 'expands collapsed replies' do
+ visit "#{project_merge_request_path(project, merge_request)}#{collapsed_fragment_id}"
+
+ note_element = find(collapsed_fragment_id)
+
+ expect(note_element.visible?).to eq(true)
+ expect(note_element.sibling('.replies-toggle')[:class]).to include('expanded')
+ end
+ end
end
end
diff --git a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
index 7b473faa884..28f88718ec1 100644
--- a/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_request_pipelines_spec.rb
@@ -2,7 +2,7 @@
require 'rails_helper'
-describe 'Merge request > User sees merge request pipelines', :js do
+describe 'Merge request > User sees pipelines triggered by merge request', :js do
include ProjectForksHelper
include TestReportsHelper
@@ -47,9 +47,9 @@ describe 'Merge request > User sees merge request pipelines', :js do
.execute(:push)
end
- let!(:merge_request_pipeline) do
+ let!(:detached_merge_request_pipeline) do
Ci::CreatePipelineService.new(project, user, ref: 'feature')
- .execute(:merge_request, merge_request: merge_request)
+ .execute(:merge_request_event, merge_request: merge_request)
end
before do
@@ -60,16 +60,16 @@ describe 'Merge request > User sees merge request pipelines', :js do
end
end
- it 'sees branch pipelines and merge request pipelines in correct order' do
+ it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 2)
- expect(first('.js-pipeline-url-link')).to have_content("##{merge_request_pipeline.id}")
+ expect(first('.js-pipeline-url-link')).to have_content("##{detached_merge_request_pipeline.id}")
end
end
- it 'sees the latest merge request pipeline as the head pipeline' do
+ it 'sees the latest detached merge request pipeline as the head pipeline' do
page.within('.ci-widget-content') do
- expect(page).to have_content("##{merge_request_pipeline.id}")
+ expect(page).to have_content("##{detached_merge_request_pipeline.id}")
end
end
@@ -79,9 +79,9 @@ describe 'Merge request > User sees merge request pipelines', :js do
.execute(:push)
end
- let!(:merge_request_pipeline_2) do
+ let!(:detached_merge_request_pipeline_2) do
Ci::CreatePipelineService.new(project, user, ref: 'feature')
- .execute(:merge_request, merge_request: merge_request)
+ .execute(:merge_request_event, merge_request: merge_request)
end
before do
@@ -92,15 +92,15 @@ describe 'Merge request > User sees merge request pipelines', :js do
end
end
- it 'sees branch pipelines and merge request pipelines in correct order' do
+ it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 4)
expect(all('.js-pipeline-url-link')[0])
- .to have_content("##{merge_request_pipeline_2.id}")
+ .to have_content("##{detached_merge_request_pipeline_2.id}")
expect(all('.js-pipeline-url-link')[1])
- .to have_content("##{merge_request_pipeline.id}")
+ .to have_content("##{detached_merge_request_pipeline.id}")
expect(all('.js-pipeline-url-link')[2])
.to have_content("##{push_pipeline_2.id}")
@@ -110,25 +110,25 @@ describe 'Merge request > User sees merge request pipelines', :js do
end
end
- it 'sees merge request tag for merge request pipelines' do
+ it 'sees detached tag for detached merge request pipelines' do
page.within('.ci-table') do
expect(all('.pipeline-tags')[0])
- .to have_content("merge request")
+ .to have_content("detached")
expect(all('.pipeline-tags')[1])
- .to have_content("merge request")
+ .to have_content("detached")
expect(all('.pipeline-tags')[2])
- .not_to have_content("merge request")
+ .not_to have_content("detached")
expect(all('.pipeline-tags')[3])
- .not_to have_content("merge request")
+ .not_to have_content("detached")
end
end
- it 'sees the latest merge request pipeline as the head pipeline' do
+ it 'sees the latest detached merge request pipeline as the head pipeline' do
page.within('.ci-widget-content') do
- expect(page).to have_content("##{merge_request_pipeline_2.id}")
+ expect(page).to have_content("##{detached_merge_request_pipeline_2.id}")
end
end
end
@@ -140,16 +140,16 @@ describe 'Merge request > User sees merge request pipelines', :js do
wait_for_requests
end
- context 'when merge request pipeline is pending' do
+ context 'when detached merge request pipeline is pending' do
it 'waits the head pipeline' do
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
expect(page).to have_link('Cancel automatic merge')
end
end
- context 'when merge request pipeline succeeds' do
+ context 'when detached merge request pipeline succeeds' do
before do
- merge_request_pipeline.succeed!
+ detached_merge_request_pipeline.succeed!
wait_for_requests
end
@@ -218,9 +218,9 @@ describe 'Merge request > User sees merge request pipelines', :js do
.execute(:push)
end
- let!(:merge_request_pipeline) do
+ let!(:detached_merge_request_pipeline) do
Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature')
- .execute(:merge_request, merge_request: merge_request)
+ .execute(:merge_request_event, merge_request: merge_request)
end
let(:forked_project) { fork_project(project, user2, repository: true) }
@@ -236,16 +236,16 @@ describe 'Merge request > User sees merge request pipelines', :js do
end
end
- it 'sees branch pipelines and merge request pipelines in correct order' do
+ it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 2)
- expect(first('.js-pipeline-url-link')).to have_content("##{merge_request_pipeline.id}")
+ expect(first('.js-pipeline-url-link')).to have_content("##{detached_merge_request_pipeline.id}")
end
end
- it 'sees the latest merge request pipeline as the head pipeline' do
+ it 'sees the latest detached merge request pipeline as the head pipeline' do
page.within('.ci-widget-content') do
- expect(page).to have_content("##{merge_request_pipeline.id}")
+ expect(page).to have_content("##{detached_merge_request_pipeline.id}")
end
end
@@ -261,9 +261,9 @@ describe 'Merge request > User sees merge request pipelines', :js do
.execute(:push)
end
- let!(:merge_request_pipeline_2) do
+ let!(:detached_merge_request_pipeline_2) do
Ci::CreatePipelineService.new(forked_project, user2, ref: 'feature')
- .execute(:merge_request, merge_request: merge_request)
+ .execute(:merge_request_event, merge_request: merge_request)
end
before do
@@ -274,15 +274,15 @@ describe 'Merge request > User sees merge request pipelines', :js do
end
end
- it 'sees branch pipelines and merge request pipelines in correct order' do
+ it 'sees branch pipelines and detached merge request pipelines in correct order' do
page.within('.ci-table') do
expect(page).to have_selector('.ci-pending', count: 4)
expect(all('.js-pipeline-url-link')[0])
- .to have_content("##{merge_request_pipeline_2.id}")
+ .to have_content("##{detached_merge_request_pipeline_2.id}")
expect(all('.js-pipeline-url-link')[1])
- .to have_content("##{merge_request_pipeline.id}")
+ .to have_content("##{detached_merge_request_pipeline.id}")
expect(all('.js-pipeline-url-link')[2])
.to have_content("##{push_pipeline_2.id}")
@@ -292,25 +292,25 @@ describe 'Merge request > User sees merge request pipelines', :js do
end
end
- it 'sees merge request tag for merge request pipelines' do
+ it 'sees detached tag for detached merge request pipelines' do
page.within('.ci-table') do
expect(all('.pipeline-tags')[0])
- .to have_content("merge request")
+ .to have_content("detached")
expect(all('.pipeline-tags')[1])
- .to have_content("merge request")
+ .to have_content("detached")
expect(all('.pipeline-tags')[2])
- .not_to have_content("merge request")
+ .not_to have_content("detached")
expect(all('.pipeline-tags')[3])
- .not_to have_content("merge request")
+ .not_to have_content("detached")
end
end
- it 'sees the latest merge request pipeline as the head pipeline' do
+ it 'sees the latest detached merge request pipeline as the head pipeline' do
page.within('.ci-widget-content') do
- expect(page).to have_content("##{merge_request_pipeline_2.id}")
+ expect(page).to have_content("##{detached_merge_request_pipeline_2.id}")
end
end
@@ -328,16 +328,16 @@ describe 'Merge request > User sees merge request pipelines', :js do
wait_for_requests
end
- context 'when merge request pipeline is pending' do
+ context 'when detached merge request pipeline is pending' do
it 'waits the head pipeline' do
expect(page).to have_content('to be merged automatically when the pipeline succeeds')
expect(page).to have_link('Cancel automatic merge')
end
end
- context 'when merge request pipeline succeeds' do
+ context 'when detached merge request pipeline succeeds' do
before do
- merge_request_pipeline.succeed!
+ detached_merge_request_pipeline.succeed!
wait_for_requests
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 afb978d7c45..2609546990d 100644
--- a/spec/features/merge_request/user_sees_merge_widget_spec.rb
+++ b/spec/features/merge_request/user_sees_merge_widget_spec.rb
@@ -145,6 +145,119 @@ describe 'Merge request > User sees merge widget', :js do
end
end
+ context 'when merge request has a branch pipeline as the head pipeline' do
+ let!(:pipeline) do
+ create(:ci_pipeline,
+ ref: merge_request.source_branch,
+ sha: merge_request.source_branch_sha,
+ project: merge_request.source_project)
+ end
+
+ before do
+ merge_request.update_head_pipeline
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'shows head pipeline information' do
+ within '.ci-widget-content' do
+ expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
+ "for #{pipeline.short_sha} " \
+ "on #{pipeline.ref}")
+ end
+ end
+ end
+
+ context 'when merge request has a detached merge request pipeline as the head pipeline' do
+ let(:merge_request) do
+ create(:merge_request,
+ :with_detached_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project)
+ end
+
+ let!(:pipeline) do
+ merge_request.all_pipelines.last
+ end
+
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ before do
+ merge_request.update_head_pipeline
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'shows head pipeline information' do
+ within '.ci-widget-content' do
+ expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
+ "for #{pipeline.short_sha} " \
+ "on #{merge_request.to_reference} " \
+ "with #{merge_request.source_branch}")
+ end
+ end
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ it 'shows head pipeline information' do
+ within '.ci-widget-content' do
+ expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
+ "for #{pipeline.short_sha} " \
+ "on #{merge_request.to_reference} " \
+ "with #{merge_request.source_branch}")
+ end
+ end
+ end
+ end
+
+ context 'when merge request has a merge request pipeline as the head pipeline' do
+ let(:merge_request) do
+ create(:merge_request,
+ :with_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project,
+ merge_sha: merge_sha)
+ end
+
+ let!(:pipeline) do
+ merge_request.all_pipelines.last
+ end
+
+ let(:source_project) { project }
+ let(:target_project) { project }
+ let(:merge_sha) { project.commit.sha }
+
+ before do
+ merge_request.update_head_pipeline
+ visit project_merge_request_path(project, merge_request)
+ end
+
+ it 'shows head pipeline information' do
+ within '.ci-widget-content' do
+ expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
+ "for #{pipeline.short_sha} " \
+ "on #{merge_request.to_reference} " \
+ "with #{merge_request.source_branch} " \
+ "into #{merge_request.target_branch}")
+ end
+ end
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+ let(:merge_sha) { source_project.commit.sha }
+
+ it 'shows head pipeline information' do
+ within '.ci-widget-content' do
+ expect(page).to have_content("Pipeline ##{pipeline.id} pending " \
+ "for #{pipeline.short_sha} " \
+ "on #{merge_request.to_reference} " \
+ "with #{merge_request.source_branch} " \
+ "into #{merge_request.target_branch}")
+ end
+ end
+ end
+ end
+
context 'view merge request with MWBS button' do
before do
commit_status = create(:commit_status, project: project, status: 'pending')
diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb
index aa91ade46ca..5c45e363997 100644
--- a/spec/features/merge_request/user_sees_versions_spec.rb
+++ b/spec/features/merge_request/user_sees_versions_spec.rb
@@ -1,7 +1,11 @@
require 'rails_helper'
describe 'Merge request > User sees versions', :js do
- let(:merge_request) { create(:merge_request, importing: true) }
+ let(:merge_request) do
+ create(:merge_request).tap do |mr|
+ mr.merge_request_diff.destroy
+ end
+ end
let(:project) { merge_request.source_project }
let(:user) { project.creator }
let!(:merge_request_diff1) { merge_request.merge_request_diffs.create(head_commit_sha: '6f6d7e7ed97bb5f0054f2b1df789b39ca89b6ff9') }
diff --git a/spec/features/merge_request/user_views_diffs_spec.rb b/spec/features/merge_request/user_views_diffs_spec.rb
index 0434db04113..74342b16cb2 100644
--- a/spec/features/merge_request/user_views_diffs_spec.rb
+++ b/spec/features/merge_request/user_views_diffs_spec.rb
@@ -34,6 +34,16 @@ describe 'User views diffs', :js do
expect(page).not_to have_selector('.mr-loading-status .loading', visible: true)
end
+ it 'expands all diffs' do
+ first('#a5cc2925ca8258af241be7e5b0381edf30266302 .js-file-title').click
+
+ expect(page).to have_button('Expand all')
+
+ click_button 'Expand all'
+
+ expect(page).not_to have_button('Expand all')
+ end
+
context 'when in the inline view' do
include_examples 'unfold diffs'
end
diff --git a/spec/features/merge_request/user_views_open_merge_request_spec.rb b/spec/features/merge_request/user_views_open_merge_request_spec.rb
index 71022c6bb08..849fab62fc6 100644
--- a/spec/features/merge_request/user_views_open_merge_request_spec.rb
+++ b/spec/features/merge_request/user_views_open_merge_request_spec.rb
@@ -13,7 +13,7 @@ describe 'User views an open merge request' do
end
it 'renders both the title and the description' do
- node = find('.wiki h1 a#user-content-description-header')
+ node = find('.md h1 a#user-content-description-header')
expect(node[:href]).to end_with('#description-header')
# Work around a weird Capybara behavior where calling `parent` on a node
diff --git a/spec/features/merge_requests/user_filters_by_target_branch_spec.rb b/spec/features/merge_requests/user_filters_by_target_branch_spec.rb
new file mode 100644
index 00000000000..ffbdacc68f6
--- /dev/null
+++ b/spec/features/merge_requests/user_filters_by_target_branch_spec.rb
@@ -0,0 +1,45 @@
+require 'rails_helper'
+
+describe 'Merge Requests > User filters by target branch', :js do
+ include FilteredSearchHelpers
+
+ let!(:project) { create(:project, :public, :repository) }
+ let!(:user) { project.creator }
+ let!(:mr1) { create(:merge_request, source_project: project, target_project: project, source_branch: 'feature', target_branch: 'master') }
+ let!(:mr2) { create(:merge_request, source_project: project, target_project: project, source_branch: 'feature', target_branch: 'merged-target') }
+
+ before do
+ sign_in(user)
+ visit project_merge_requests_path(project)
+ end
+
+ context 'filtering by target-branch:master' do
+ it 'applies the filter' do
+ input_filtered_search('target-branch:master')
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).to have_content mr1.title
+ expect(page).not_to have_content mr2.title
+ end
+ end
+
+ context 'filtering by target-branch:merged-target' do
+ it 'applies the filter' do
+ input_filtered_search('target-branch:merged-target')
+
+ expect(page).to have_issuable_counts(open: 1, closed: 0, all: 1)
+ expect(page).not_to have_content mr1.title
+ expect(page).to have_content mr2.title
+ end
+ end
+
+ context 'filtering by target-branch:feature' do
+ it 'applies the filter' do
+ input_filtered_search('target-branch:feature')
+
+ expect(page).to have_issuable_counts(open: 0, closed: 0, all: 0)
+ expect(page).not_to have_content mr1.title
+ expect(page).not_to have_content mr2.title
+ end
+ end
+end
diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
index ef7ae490b0f..c691011b9ca 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -13,7 +13,7 @@ describe 'Merge requests > User lists merge requests' do
source_project: project,
source_branch: 'fix',
assignee: user,
- milestone: create(:milestone, due_date: '2013-12-11'),
+ milestone: create(:milestone, project: project, due_date: '2013-12-11'),
created_at: 1.minute.ago,
updated_at: 1.minute.ago)
create(:merge_request,
@@ -21,7 +21,7 @@ describe 'Merge requests > User lists merge requests' do
source_project: project,
source_branch: 'markdown',
assignee: user,
- milestone: create(:milestone, due_date: '2013-12-12'),
+ milestone: create(:milestone, project: project, due_date: '2013-12-12'),
created_at: 2.minutes.ago,
updated_at: 2.minutes.ago)
create(:merge_request,
diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb
index 6e349395017..adac59b89ef 100644
--- a/spec/features/milestone_spec.rb
+++ b/spec/features/milestone_spec.rb
@@ -122,4 +122,32 @@ describe 'Milestone' do
expect(page).to have_selector('.popover')
end
end
+
+ describe 'reopen closed milestones' do
+ before do
+ create(:milestone, :closed, project: project)
+ end
+
+ describe 'group milestones page' do
+ it 'reopens the milestone' do
+ visit group_milestones_path(group, { state: 'closed' })
+
+ click_link 'Reopen Milestone'
+
+ expect(page).not_to have_selector('.status-box-closed')
+ expect(page).to have_selector('.status-box-open')
+ end
+ end
+
+ describe 'project milestones page' do
+ it 'reopens the milestone' do
+ visit project_milestones_path(project, { state: 'closed' })
+
+ click_link 'Reopen Milestone'
+
+ expect(page).not_to have_selector('.status-box-closed')
+ expect(page).to have_selector('.status-box-open')
+ end
+ end
+ end
end
diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb
index d3050760c06..2aa0177af5d 100644
--- a/spec/features/profiles/active_sessions_spec.rb
+++ b/spec/features/profiles/active_sessions_spec.rb
@@ -7,6 +7,8 @@ describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
end
end
+ let(:admin) { create(:admin) }
+
around do |example|
Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do
example.run
@@ -16,6 +18,7 @@ describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
it 'User sees their active sessions' do
Capybara::Session.new(:session1)
Capybara::Session.new(:session2)
+ Capybara::Session.new(:session3)
# note: headers can only be set on the non-js (aka. rack-test) driver
using_session :session1 do
@@ -37,9 +40,27 @@ describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
gitlab_sign_in(user)
end
+ # set an admin session impersonating the user
+ using_session :session3 do
+ Capybara.page.driver.header(
+ 'User-Agent',
+ 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36'
+ )
+
+ gitlab_sign_in(admin)
+
+ visit admin_user_path(user)
+
+ click_link 'Impersonate'
+ end
+
using_session :session1 do
visit profile_active_sessions_path
+ expect(page).to(
+ have_selector('ul.list-group li.list-group-item', { text: 'Signed in on',
+ count: 2 }))
+
expect(page).to have_content(
'127.0.0.1 ' \
'This is your current session ' \
@@ -57,33 +78,8 @@ describe 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
)
expect(page).to have_selector '[title="Smartphone"]', count: 1
- end
- end
-
- it 'User can revoke a session', :js, :redis_session_store do
- Capybara::Session.new(:session1)
- Capybara::Session.new(:session2)
-
- # set an additional session in another browser
- using_session :session2 do
- gitlab_sign_in(user)
- end
-
- using_session :session1 do
- gitlab_sign_in(user)
- visit profile_active_sessions_path
-
- expect(page).to have_link('Revoke', count: 1)
-
- accept_confirm { click_on 'Revoke' }
-
- expect(page).not_to have_link('Revoke')
- end
-
- using_session :session2 do
- visit profile_active_sessions_path
- expect(page).to have_content('You need to sign in or sign up before continuing.')
+ expect(page).not_to have_content('Chrome on Windows')
end
end
end
diff --git a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
index 90d6841af0e..9909bfb5904 100644
--- a/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
+++ b/spec/features/profiles/user_visits_profile_preferences_page_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe 'User visits the profile preferences page' do
+ include Select2Helper
+
let(:user) { create(:user) }
before do
@@ -60,6 +62,28 @@ describe 'User visits the profile preferences page' do
end
end
+ describe 'User changes their language', :js do
+ it 'creates a flash message' do
+ select2('en', from: '#user_preferred_language')
+ click_button 'Save'
+
+ wait_for_requests
+
+ expect_preferences_saved_message
+ end
+
+ it 'updates their preference' do
+ wait_for_requests
+ select2('eo', from: '#user_preferred_language')
+ click_button 'Save'
+
+ wait_for_requests
+ refresh
+
+ expect(page).to have_css('html[lang="eo"]')
+ end
+ end
+
def expect_preferences_saved_message
page.within('.flash-container') do
expect(page).to have_content('Preferences saved.')
diff --git a/spec/features/project_variables_spec.rb b/spec/features/project_variables_spec.rb
index a93df3696d2..6bdf5df1036 100644
--- a/spec/features/project_variables_spec.rb
+++ b/spec/features/project_variables_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe 'Project variables', :js do
let(:user) { create(:user) }
let(:project) { create(:project) }
- let(:variable) { create(:ci_variable, key: 'test_key', value: 'test value') }
+ let(:variable) { create(:ci_variable, key: 'test_key', value: 'test_value') }
let(:page_path) { project_settings_ci_cd_path(project) }
before do
diff --git a/spec/features/projects/artifacts/user_browses_artifacts_spec.rb b/spec/features/projects/artifacts/user_browses_artifacts_spec.rb
index 5f630c9ffa4..a1fcd4024c0 100644
--- a/spec/features/projects/artifacts/user_browses_artifacts_spec.rb
+++ b/spec/features/projects/artifacts/user_browses_artifacts_spec.rb
@@ -19,6 +19,12 @@ describe "User browses artifacts" do
visit(browse_project_job_artifacts_path(project, job))
end
+ it "renders a link to the job in the breadcrumbs" do
+ page.within('.js-breadcrumbs-list') do
+ expect(page).to have_link("##{job.id}", href: project_job_path(project, job))
+ end
+ end
+
it "shows artifacts" do
expect(page).not_to have_selector(".build-sidebar")
diff --git a/spec/features/projects/badges/pipeline_badge_spec.rb b/spec/features/projects/badges/pipeline_badge_spec.rb
index dee81898928..4ac4e8f0fcb 100644
--- a/spec/features/projects/badges/pipeline_badge_spec.rb
+++ b/spec/features/projects/badges/pipeline_badge_spec.rb
@@ -41,6 +41,25 @@ describe 'Pipeline Badge' do
end
end
+ context 'when the pipeline is preparing' do
+ let!(:job) { create(:ci_build, status: 'created', pipeline: pipeline) }
+
+ before do
+ # Prevent skipping directly to 'pending'
+ allow(Ci::BuildPrepareWorker).to receive(:perform_async)
+ allow(job).to receive(:prerequisites).and_return([double])
+ end
+
+ it 'displays the preparing badge' do
+ job.enqueue
+
+ visit pipeline_project_badges_path(project, ref: ref, format: :svg)
+
+ expect(page.status_code).to eq(200)
+ expect_badge('preparing')
+ end
+ end
+
context 'when the pipeline is running' do
it 'shows displays so on the badge' do
create(:ci_build, pipeline: pipeline, name: 'second build', status_event: 'run')
diff --git a/spec/features/projects/blobs/blob_show_spec.rb b/spec/features/projects/blobs/blob_show_spec.rb
index 3edcc7ac2cd..a7aa63018fd 100644
--- a/spec/features/projects/blobs/blob_show_spec.rb
+++ b/spec/features/projects/blobs/blob_show_spec.rb
@@ -548,10 +548,7 @@ describe 'File blob', :js do
it 'displays an auxiliary viewer' do
aggregate_failures do
# shows names of dependency manager and package
- expect(page).to have_content('This project manages its dependencies using RubyGems and defines a gem named activerecord.')
-
- # shows a link to the gem
- expect(page).to have_link('activerecord', href: 'https://rubygems.org/gems/activerecord')
+ expect(page).to have_content('This project manages its dependencies using RubyGems.')
# shows a learn more link
expect(page).to have_link('Learn more', href: 'https://rubygems.org/')
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 6e6c299ee2e..57d21f3e182 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -9,6 +9,10 @@ describe 'Editing file blob', :js do
let(:file_path) { project.repository.ls_files(project.repository.root_ref)[1] }
let(:readme_file_path) { 'README.md' }
+ before do
+ stub_feature_flags(web_ide_default: false)
+ end
+
context 'as a developer' do
let(:user) { create(:user) }
let(:role) { :developer }
@@ -45,6 +49,15 @@ describe 'Editing file blob', :js do
end
end
+ it 'updates the content of file with a number as file path' do
+ project.repository.create_file(user, '1', 'test', message: 'testing', branch_name: branch)
+ visit project_blob_path(project, tree_join(branch, '1'))
+
+ edit_and_commit
+
+ expect(page).to have_content 'NextFeature'
+ end
+
context 'from blob file path' do
before do
visit project_blob_path(project, tree_join(branch, file_path))
@@ -77,7 +90,7 @@ describe 'Editing file blob', :js do
click_link 'Preview'
wait_for_requests
- # the above generates two seperate lists (not embedded) in CommonMark
+ # the above generates two separate lists (not embedded) in CommonMark
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
diff --git a/spec/features/projects/branches_spec.rb b/spec/features/projects/branches_spec.rb
index 97757e8da92..ee71c843b80 100644
--- a/spec/features/projects/branches_spec.rb
+++ b/spec/features/projects/branches_spec.rb
@@ -229,6 +229,38 @@ describe 'Branches' do
end
end
+ describe 'comparing branches' do
+ before do
+ sign_in(user)
+ project.add_developer(user)
+ end
+
+ shared_examples 'compares branches' do
+ it 'compares branches' do
+ visit project_branches_path(project)
+
+ page.within first('.all-branches li') do
+ click_link 'Compare'
+ end
+
+ expect(page).to have_content 'Commits'
+ expect(page).to have_link 'Create merge request'
+ end
+ end
+
+ context 'on a read-only instance' do
+ before do
+ allow(Gitlab::Database).to receive(:read_only?).and_return(true)
+ end
+
+ it_behaves_like 'compares branches'
+ end
+
+ context 'on a read-write instance' do
+ it_behaves_like 'compares branches'
+ end
+ end
+
def sorted_branches(repository, count:, sort_by:, state: nil)
branches = repository.branches_sorted_by(sort_by)
branches = branches.select { |b| state == 'active' ? b.active? : b.stale? } if state
diff --git a/spec/features/projects/clusters/applications_spec.rb b/spec/features/projects/clusters/applications_spec.rb
index 2c8d014c36d..aa1c3902f0f 100644
--- a/spec/features/projects/clusters/applications_spec.rb
+++ b/spec/features/projects/clusters/applications_spec.rb
@@ -17,7 +17,7 @@ describe 'Clusters Applications', :js do
end
context 'when cluster is being created' do
- let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project])}
+ let(:cluster) { create(:cluster, :providing_by_gcp, projects: [project]) }
it 'user is unable to install applications' do
page.within('.js-cluster-application-row-helm') do
@@ -28,9 +28,11 @@ describe 'Clusters Applications', :js do
end
context 'when cluster is created' do
- let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project])}
+ let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
it 'user can install applications' do
+ wait_for_requests
+
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')
@@ -44,6 +46,8 @@ describe 'Clusters Applications', :js do
page.within('.js-cluster-application-row-helm') do
page.find(:css, '.js-cluster-application-install-button').click
end
+
+ wait_for_requests
end
it 'they see status transition' do
@@ -52,8 +56,6 @@ describe 'Clusters Applications', :js 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: 'Installing')
- wait_until_helm_created!
-
Clusters::Cluster.last.application_helm.make_installing!
# FE starts polling and update the buttons to "Installing"
@@ -76,25 +78,79 @@ describe 'Clusters Applications', :js do
end
context 'on an abac cluster' do
- let(:cluster) { create(:cluster, :provided_by_gcp, :rbac_disabled, projects: [project])}
+ let(:cluster) { create(:cluster, :provided_by_gcp, :rbac_disabled, projects: [project]) }
it 'should show info block and not be installable' do
page.within('.js-cluster-application-row-knative') do
- expect(page).to have_css('.bs-callout-info')
+ expect(page).to have_css('.rbac-notice')
expect(page.find(:css, '.js-cluster-application-install-button')['disabled']).to eq('true')
end
end
end
context 'on an rbac cluster' do
- let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project])}
+ let(:cluster) { create(:cluster, :provided_by_gcp, projects: [project]) }
it 'should not show callout block and be installable' do
page.within('.js-cluster-application-row-knative') do
- expect(page).not_to have_css('.bs-callout-info')
+ expect(page).not_to have_css('.rbac-notice')
expect(page).to have_css('.js-cluster-application-install-button:not([disabled])')
end
end
+
+ describe 'when user clicks install button' do
+ def domainname_form_value
+ page.find('.js-knative-domainname').value
+ end
+
+ before do
+ allow(ClusterInstallAppWorker).to receive(:perform_async)
+ allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_in)
+ allow(ClusterWaitForIngressIpAddressWorker).to receive(:perform_async)
+
+ page.within('.js-cluster-application-row-knative') do
+ expect(page).to have_css('.js-cluster-application-install-button:not([disabled])')
+
+ page.find('.js-knative-domainname').set("domain.example.org")
+
+ click_button 'Install'
+
+ wait_for_requests
+
+ expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installing')
+
+ Clusters::Cluster.last.application_knative.make_installing!
+ Clusters::Cluster.last.application_knative.make_installed!
+ Clusters::Cluster.last.application_knative.update_attribute(:external_ip, '127.0.0.1')
+ end
+ end
+
+ it 'shows status transition' do
+ page.within('.js-cluster-application-row-knative') do
+ expect(domainname_form_value).to eq('domain.example.org')
+ expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installed')
+ end
+
+ expect(page).to have_content('Knative was successfully installed on your Kubernetes cluster')
+ expect(page).to have_css('.js-knative-save-domain-button'), exact_text: 'Save changes'
+ end
+
+ it 'can then update the domain' do
+ page.within('.js-cluster-application-row-knative') do
+ expect(ClusterPatchAppWorker).to receive(:perform_async)
+
+ expect(domainname_form_value).to eq('domain.example.org')
+
+ page.find('.js-knative-domainname').set("new.domain.example.org")
+
+ click_button 'Save changes'
+
+ wait_for_requests
+
+ expect(domainname_form_value).to eq('new.domain.example.org')
+ end
+ end
+ end
end
end
@@ -148,6 +204,8 @@ describe 'Clusters Applications', :js do
page.within('.js-cluster-application-row-ingress') do
expect(page).to have_css('.js-cluster-application-install-button:not([disabled])')
page.find(:css, '.js-cluster-application-install-button').click
+
+ wait_for_requests
end
end
@@ -168,14 +226,14 @@ describe 'Clusters Applications', :js do
expect(page).to have_css('.js-cluster-application-install-button', exact_text: 'Installed')
expect(page).to have_css('.js-cluster-application-install-button[disabled]')
- expect(page).to have_selector('.js-no-ip-message')
- expect(page.find('.js-ip-address').value).to eq('?')
+ expect(page).to have_selector('.js-no-endpoint-message')
+ expect(page).to have_selector('.js-ingress-ip-loading-icon')
# We receive the external IP address and display
Clusters::Cluster.last.application_ingress.update!(external_ip: '192.168.1.100')
- expect(page).not_to have_selector('.js-no-ip-message')
- expect(page.find('.js-ip-address').value).to eq('192.168.1.100')
+ expect(page).not_to have_selector('.js-no-endpoint-message')
+ expect(page.find('.js-endpoint').value).to eq('192.168.1.100')
end
expect(page).to have_content('Ingress was successfully installed on your Kubernetes cluster')
@@ -184,14 +242,4 @@ describe '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/environments/environment_spec.rb b/spec/features/projects/environments/environment_spec.rb
index a8a3b6910fb..fe71cb7661a 100644
--- a/spec/features/projects/environments/environment_spec.rb
+++ b/spec/features/projects/environments/environment_spec.rb
@@ -120,7 +120,7 @@ describe 'Environment' do
end
it 'does show a play button' do
- expect(page).to have_link(action.name.humanize)
+ expect(page).to have_link(action.name)
end
it 'does allow to play manual action', :js do
@@ -128,7 +128,7 @@ describe 'Environment' do
find('button.dropdown').click
- expect { click_link(action.name.humanize) }
+ expect { click_link(action.name) }
.not_to change { Ci::Pipeline.count }
wait_for_all_requests
@@ -140,7 +140,7 @@ describe 'Environment' do
context 'when user has no ability to trigger a deployment' do
it 'does not show a play button' do
- expect(page).not_to have_link(action.name.humanize)
+ expect(page).not_to have_link(action.name)
end
end
@@ -319,7 +319,7 @@ describe 'Environment' do
yield
- GitPushService.new(project, user, params).execute
+ Git::BranchPushService.new(project, user, params).execute
end
end
diff --git a/spec/features/projects/environments/environments_spec.rb b/spec/features/projects/environments/environments_spec.rb
index 0c517d5f490..b2a435e554d 100644
--- a/spec/features/projects/environments/environments_spec.rb
+++ b/spec/features/projects/environments/environments_spec.rb
@@ -38,6 +38,23 @@ describe 'Environments page', :js do
end
end
+ describe 'with environments spanning multiple pages', :js do
+ before do
+ allow(Kaminari.config).to receive(:default_per_page).and_return(3)
+ create_list(:environment, 4, project: project, state: :available)
+ end
+
+ it 'should render second page of pipelines' do
+ visit_environments(project, scope: 'available')
+
+ find('.js-next-button').click
+ wait_for_requests
+
+ expect(page).to have_selector('.gl-pagination .page', count: 2)
+ expect(find('.gl-pagination .page-item.active .page-link').text).to eq("2")
+ end
+ end
+
describe 'in stopped tab page' do
it 'should show no environments' do
visit_environments(project, scope: 'stopped')
@@ -166,14 +183,14 @@ describe 'Environments page', :js do
it 'shows a play button' do
find('.js-environment-actions-dropdown').click
- expect(page).to have_content(action.name.humanize)
+ expect(page).to have_content(action.name)
end
it 'allows to play a manual action', :js do
expect(action).to be_manual
find('.js-environment-actions-dropdown').click
- expect(page).to have_content(action.name.humanize)
+ expect(page).to have_content(action.name)
expect { find('.js-manual-action-link').click }
.not_to change { Ci::Pipeline.count }
@@ -294,7 +311,7 @@ describe 'Environments page', :js do
it "has link to the delayed job's action" do
find('.js-environment-actions-dropdown').click
- expect(page).to have_button('Delayed job')
+ expect(page).to have_button('delayed job')
expect(page).to have_content(/\d{2}:\d{2}:\d{2}/)
end
@@ -316,7 +333,7 @@ describe 'Environments page', :js do
context 'when user played a delayed job immediately' do
before do
find('.js-environment-actions-dropdown').click
- page.accept_confirm { click_button('Delayed job') }
+ page.accept_confirm { click_button('delayed job') }
wait_for_requests
end
diff --git a/spec/features/projects/files/user_creates_files_spec.rb b/spec/features/projects/files/user_creates_files_spec.rb
index a4f94b7a76d..dd2964c2186 100644
--- a/spec/features/projects/files/user_creates_files_spec.rb
+++ b/spec/features/projects/files/user_creates_files_spec.rb
@@ -12,6 +12,8 @@ describe 'Projects > Files > User creates files' do
let(:user) { create(:user) }
before do
+ stub_feature_flags(web_ide_default: false)
+
project.add_maintainer(user)
sign_in(user)
end
diff --git a/spec/features/projects/files/user_edits_files_spec.rb b/spec/features/projects/files/user_edits_files_spec.rb
index 9eb65ec159c..ec3930c26db 100644
--- a/spec/features/projects/files/user_edits_files_spec.rb
+++ b/spec/features/projects/files/user_edits_files_spec.rb
@@ -9,6 +9,8 @@ describe 'Projects > Files > User edits files', :js do
let(:user) { create(:user) }
before do
+ stub_feature_flags(web_ide_default: false)
+
sign_in(user)
end
diff --git a/spec/features/projects/jobs_spec.rb b/spec/features/projects/jobs_spec.rb
index 65ce872363f..224375daf71 100644
--- a/spec/features/projects/jobs_spec.rb
+++ b/spec/features/projects/jobs_spec.rb
@@ -2,6 +2,9 @@ require 'spec_helper'
require 'tempfile'
describe 'Jobs', :clean_gitlab_redis_shared_state do
+ include Gitlab::Routing
+ include ProjectForksHelper
+
let(:user) { create(:user) }
let(:user_access_level) { :developer }
let(:project) { create(:project, :repository) }
@@ -121,6 +124,112 @@ describe 'Jobs', :clean_gitlab_redis_shared_state do
end
end
+ context 'pipeline info block', :js do
+ it 'shows pipeline id and source branch' do
+ visit project_job_path(project, job)
+
+ within '.js-pipeline-info' do
+ expect(page).to have_content("Pipeline ##{pipeline.id} for #{pipeline.ref}")
+ end
+ end
+
+ context 'when pipeline is detached merge request pipeline' do
+ let(:merge_request) do
+ create(:merge_request,
+ :with_detached_merge_request_pipeline,
+ target_project: target_project,
+ source_project: source_project)
+ end
+
+ let(:source_project) { project }
+ let(:target_project) { project }
+ let(:pipeline) { merge_request.all_pipelines.last }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'shows merge request iid and source branch' do
+ visit project_job_path(project, job)
+
+ within '.js-pipeline-info' do
+ expect(page).to have_content("for !#{pipeline.merge_request.iid} " \
+ "with #{pipeline.merge_request.source_branch}")
+ expect(page).to have_link("!#{pipeline.merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(pipeline.merge_request.source_branch,
+ href: project_commits_path(project, merge_request.source_branch))
+ end
+ end
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+ let(:target_project) { project }
+
+ it 'shows merge request iid and source branch' do
+ visit project_job_path(source_project, job)
+
+ within '.js-pipeline-info' do
+ expect(page).to have_content("for !#{pipeline.merge_request.iid} " \
+ "with #{pipeline.merge_request.source_branch}")
+ expect(page).to have_link("!#{pipeline.merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(pipeline.merge_request.source_branch,
+ href: project_commits_path(source_project, merge_request.source_branch))
+ end
+ end
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let(:merge_request) do
+ create(:merge_request,
+ :with_merge_request_pipeline,
+ target_project: target_project,
+ source_project: source_project)
+ end
+
+ let(:source_project) { project }
+ let(:target_project) { project }
+ let(:pipeline) { merge_request.all_pipelines.last }
+ let(:job) { create(:ci_build, pipeline: pipeline) }
+
+ it 'shows merge request iid and source branch' do
+ visit project_job_path(project, job)
+
+ within '.js-pipeline-info' do
+ expect(page).to have_content("for !#{pipeline.merge_request.iid} " \
+ "with #{pipeline.merge_request.source_branch} " \
+ "into #{pipeline.merge_request.target_branch}")
+ expect(page).to have_link("!#{pipeline.merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(pipeline.merge_request.source_branch,
+ href: project_commits_path(project, merge_request.source_branch))
+ expect(page).to have_link(pipeline.merge_request.target_branch,
+ href: project_commits_path(project, merge_request.target_branch))
+ end
+ end
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+ let(:target_project) { project }
+
+ it 'shows merge request iid and source branch' do
+ visit project_job_path(source_project, job)
+
+ within '.js-pipeline-info' do
+ expect(page).to have_content("for !#{pipeline.merge_request.iid} " \
+ "with #{pipeline.merge_request.source_branch} " \
+ "into #{pipeline.merge_request.target_branch}")
+ expect(page).to have_link("!#{pipeline.merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(pipeline.merge_request.source_branch,
+ href: project_commits_path(source_project, merge_request.source_branch))
+ expect(page).to have_link(pipeline.merge_request.target_branch,
+ href: project_commits_path(project, merge_request.target_branch))
+ end
+ end
+ end
+ end
+ end
+
context 'sidebar', :js do
let(:job) { create(:ci_build, :success, :trace_live, pipeline: pipeline, name: '<img src=x onerror=alert(document.domain)>') }
diff --git a/spec/features/projects/members/invite_group_spec.rb b/spec/features/projects/members/invite_group_spec.rb
index fceead0b45e..b2d2dba55f1 100644
--- a/spec/features/projects/members/invite_group_spec.rb
+++ b/spec/features/projects/members/invite_group_spec.rb
@@ -27,6 +27,7 @@ describe 'Project > Members > Invite group', :js do
before do
project.add_maintainer(maintainer)
+ group_to_share_with.add_guest(maintainer)
sign_in(maintainer)
end
@@ -112,6 +113,7 @@ describe 'Project > Members > Invite group', :js do
before do
project.add_maintainer(maintainer)
+ group.add_guest(maintainer)
sign_in(maintainer)
visit project_settings_members_path(project)
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 435fb229b69..f564ae34f11 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -13,16 +13,6 @@ describe 'Pages' do
sign_in(user)
end
- shared_examples 'no pages deployed' do
- it 'does not see anything to destroy' do
- visit project_pages_path(project)
-
- expect(page).to have_content('Configure pages')
- expect(page).not_to have_link('Remove pages')
- expect(page).not_to have_text('Only the project owner can remove pages')
- end
- end
-
context 'when user is the owner' do
before do
project.namespace.update(owner: user)
@@ -181,7 +171,12 @@ describe 'Pages' do
end
end
- it_behaves_like 'no pages deployed'
+ it 'does not see anything to destroy' do
+ visit project_pages_path(project)
+
+ expect(page).to have_content('Configure pages')
+ expect(page).not_to have_link('Remove pages')
+ end
describe 'project settings page' do
it 'renders "Pages" tab' do
@@ -208,22 +203,6 @@ describe 'Pages' do
end
end
- context 'when the user is not the owner' do
- context 'when pages deployed' do
- before do
- allow_any_instance_of(Project).to receive(:pages_deployed?) { true }
- end
-
- 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')
- end
- end
-
- it_behaves_like 'no pages deployed'
- end
-
describe 'HTTPS settings', :js, :https_pages_enabled do
before do
project.namespace.update(owner: user)
@@ -233,7 +212,7 @@ describe 'Pages' 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")
+ expect(page).to have_content("Force HTTPS (requires valid certificates)")
uncheck :project_pages_https_only
@@ -282,58 +261,52 @@ describe 'Pages' do
visit project_pages_path(project)
expect(page).not_to have_field(:project_pages_https_only)
- expect(page).not_to have_content('Force domains with SSL certificates to use HTTPS')
+ expect(page).not_to have_content('Force HTTPS (requires valid certificates)')
expect(page).not_to have_button('Save')
end
end
end
describe 'Remove page' do
- context 'when user is the owner' do
- let(:project) { create :project, :repository }
-
- before do
- project.namespace.update(owner: user)
+ let(:project) { create :project, :repository }
+
+ context 'when pages are deployed' do
+ let(:pipeline) do
+ commit_sha = project.commit('HEAD').sha
+
+ project.ci_pipelines.create(
+ ref: 'HEAD',
+ sha: commit_sha,
+ source: :push,
+ protected: false
+ )
end
- context 'when pages are deployed' do
- let(:pipeline) do
- commit_sha = project.commit('HEAD').sha
-
- project.ci_pipelines.create(
- ref: 'HEAD',
- sha: commit_sha,
- source: :push,
- protected: false
- )
- end
-
- let(:ci_build) do
- create(
- :ci_build,
- project: project,
- pipeline: pipeline,
- ref: 'HEAD',
- legacy_artifacts_file: fixture_file_upload(File.join('spec/fixtures/pages.zip')),
- legacy_artifacts_metadata: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta'))
- )
- end
+ let(:ci_build) do
+ create(
+ :ci_build,
+ project: project,
+ pipeline: pipeline,
+ ref: 'HEAD',
+ legacy_artifacts_file: fixture_file_upload(File.join('spec/fixtures/pages.zip')),
+ legacy_artifacts_metadata: fixture_file_upload(File.join('spec/fixtures/pages.zip.meta'))
+ )
+ end
- before do
- result = Projects::UpdatePagesService.new(project, ci_build).execute
- expect(result[:status]).to eq(:success)
- expect(project).to be_pages_deployed
- end
+ before do
+ result = Projects::UpdatePagesService.new(project, ci_build).execute
+ expect(result[:status]).to eq(:success)
+ expect(project).to be_pages_deployed
+ end
- it 'removes the pages' do
- visit project_pages_path(project)
+ it 'removes the pages' do
+ visit project_pages_path(project)
- expect(page).to have_link('Remove pages')
+ expect(page).to have_link('Remove pages')
- click_link 'Remove pages'
+ click_link 'Remove pages'
- expect(project.pages_deployed?).to be_falsey
- end
+ expect(project.pages_deployed?).to be_falsey
end
end
end
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 72ef460d315..9fdf78baa1e 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -1,6 +1,9 @@
require 'spec_helper'
describe 'Pipeline', :js do
+ include RoutesHelpers
+ include ProjectForksHelper
+
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:role) { :developer }
@@ -21,6 +24,11 @@ describe 'Pipeline', :js do
pipeline: pipeline, stage: 'test', name: 'test')
end
+ let!(:build_preparing) do
+ create(:ci_build, :preparing,
+ pipeline: pipeline, stage: 'deploy', name: 'prepare')
+ end
+
let!(:build_running) do
create(:ci_build, :running,
pipeline: pipeline, stage: 'deploy', name: 'deploy')
@@ -72,6 +80,15 @@ describe 'Pipeline', :js do
expect(page).to have_link(pipeline.ref)
end
+ it 'shows the pipeline information' do
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for #{pipeline.ref} ")
+ expect(page).to have_link(pipeline.ref,
+ href: project_commits_path(pipeline.project, pipeline.ref))
+ end
+ end
+
it_behaves_like 'showing user status' do
let(:user_with_status) { pipeline.user }
@@ -97,6 +114,24 @@ describe 'Pipeline', :js do
end
end
+ context 'when pipeline has preparing builds' do
+ it 'shows a preparing icon and a cancel action' do
+ page.within('#ci-badge-prepare') do
+ expect(page).to have_selector('.js-ci-status-icon-preparing')
+ expect(page).to have_selector('.js-icon-cancel')
+ expect(page).to have_content('prepare')
+ end
+ end
+
+ it 'cancels the preparing build and shows retry button' do
+ find('#ci-badge-deploy .ci-action-icon-container').click
+
+ page.within('#ci-badge-deploy') do
+ expect(page).to have_css('.js-icon-retry')
+ end
+ end
+ end
+
context 'when pipeline has successful builds' do
it 'shows the success icon and a retry action for the successful build' do
page.within('#ci-badge-build') do
@@ -254,6 +289,113 @@ describe 'Pipeline', :js do
expect(page).to have_content(pipeline.ref)
end
end
+
+ context 'when pipeline is detached merge request pipeline' do
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ let(:merge_request) do
+ create(:merge_request,
+ :with_detached_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project)
+ end
+
+ let(:pipeline) do
+ merge_request.all_pipelines.last
+ end
+
+ it 'shows the pipeline information' do
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for !#{merge_request.iid} " \
+ "with #{merge_request.source_branch}")
+ expect(page).to have_link("!#{merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(merge_request.source_branch,
+ href: project_commits_path(merge_request.source_project, merge_request.source_branch))
+ end
+ end
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ before do
+ visit project_pipeline_path(source_project, pipeline)
+ end
+
+ it 'shows the pipeline information' do
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for !#{merge_request.iid} " \
+ "with #{merge_request.source_branch}")
+ expect(page).to have_link("!#{merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(merge_request.source_branch,
+ href: project_commits_path(merge_request.source_project, merge_request.source_branch))
+ end
+ end
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ let(:merge_request) do
+ create(:merge_request,
+ :with_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project,
+ merge_sha: project.commit.id)
+ end
+
+ let(:pipeline) do
+ merge_request.all_pipelines.last
+ end
+
+ before do
+ pipeline.update(user: user)
+ end
+
+ it 'shows the pipeline information' do
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for !#{merge_request.iid} " \
+ "with #{merge_request.source_branch} " \
+ "into #{merge_request.target_branch}")
+ expect(page).to have_link("!#{merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(merge_request.source_branch,
+ href: project_commits_path(merge_request.source_project, merge_request.source_branch))
+ expect(page).to have_link(merge_request.target_branch,
+ href: project_commits_path(merge_request.target_project, merge_request.target_branch))
+ end
+ end
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ before do
+ visit project_pipeline_path(source_project, pipeline)
+ end
+
+ it 'shows the pipeline information' do
+ within '.pipeline-info' do
+ expect(page).to have_content("#{pipeline.statuses.count} jobs " \
+ "for !#{merge_request.iid} " \
+ "with #{merge_request.source_branch} " \
+ "into #{merge_request.target_branch}")
+ expect(page).to have_link("!#{merge_request.iid}",
+ href: project_merge_request_path(project, merge_request))
+ expect(page).to have_link(merge_request.source_branch,
+ href: project_commits_path(merge_request.source_project, merge_request.source_branch))
+ expect(page).to have_link(merge_request.target_branch,
+ href: project_commits_path(merge_request.target_project, merge_request.target_branch))
+ end
+ end
+ end
+ end
end
context 'when user does not have access to read jobs' do
@@ -666,7 +808,7 @@ describe 'Pipeline', :js do
let(:pipeline) do
create(:ci_pipeline,
- source: :merge_request,
+ source: :merge_request_event,
project: merge_request.source_project,
ref: 'feature',
sha: merge_request.diff_head_sha,
@@ -686,9 +828,9 @@ describe 'Pipeline', :js do
visit project_pipeline_path(project, pipeline)
end
- it 'contains badge that indicates merge request pipeline' do
+ it 'contains badge that indicates detached merge request pipeline' do
page.within(all('.well-segment')[1]) do
- expect(page).to have_content 'merge request'
+ expect(page).to have_content 'detached'
end
end
end
diff --git a/spec/features/projects/pipelines/pipelines_spec.rb b/spec/features/projects/pipelines/pipelines_spec.rb
index ffa165c5440..7ca3b3d8edd 100644
--- a/spec/features/projects/pipelines/pipelines_spec.rb
+++ b/spec/features/projects/pipelines/pipelines_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe 'Pipelines', :js do
+ include ProjectForksHelper
+
let(:project) { create(:project) }
context 'when user is logged in' do
@@ -165,6 +167,99 @@ describe 'Pipelines', :js do
end
end
+ context 'when pipeline is detached merge request pipeline' do
+ let(:merge_request) do
+ create(:merge_request,
+ :with_detached_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project)
+ end
+
+ let!(:pipeline) { merge_request.all_pipelines.first }
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ before do
+ visit project_pipelines_path(source_project)
+ end
+
+ shared_examples_for 'showing detached merge request pipeline information' do
+ it 'shows detached tag for the pipeline' do
+ within '.pipeline-tags' do
+ expect(page).to have_content('detached')
+ end
+ end
+
+ it 'shows the link of the merge request' do
+ within '.branch-commit' do
+ expect(page).to have_link(merge_request.iid,
+ href: project_merge_request_path(project, merge_request))
+ end
+ end
+
+ it 'does not show the ref of the pipeline' do
+ within '.branch-commit' do
+ expect(page).not_to have_link(pipeline.ref)
+ end
+ end
+ end
+
+ it_behaves_like 'showing detached merge request pipeline information'
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ it_behaves_like 'showing detached merge request pipeline information'
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let(:merge_request) do
+ create(:merge_request,
+ :with_merge_request_pipeline,
+ source_project: source_project,
+ target_project: target_project,
+ merge_sha: target_project.commit.sha)
+ end
+
+ let!(:pipeline) { merge_request.all_pipelines.first }
+ let(:source_project) { project }
+ let(:target_project) { project }
+
+ before do
+ visit project_pipelines_path(source_project)
+ end
+
+ shared_examples_for 'Correct merge request pipeline information' do
+ it 'does not show detached tag for the pipeline' do
+ within '.pipeline-tags' do
+ expect(page).not_to have_content('detached')
+ end
+ end
+
+ it 'shows the link of the merge request' do
+ within '.branch-commit' do
+ expect(page).to have_link(merge_request.iid,
+ href: project_merge_request_path(project, merge_request))
+ end
+ end
+
+ it 'does not show the ref of the pipeline' do
+ within '.branch-commit' do
+ expect(page).not_to have_link(pipeline.ref)
+ end
+ end
+ end
+
+ it_behaves_like 'Correct merge request pipeline information'
+
+ context 'when source project is a forked project' do
+ let(:source_project) { fork_project(project, user, repository: true) }
+
+ it_behaves_like 'Correct merge request pipeline information'
+ end
+ end
+
context 'when pipeline has configuration errors' do
let(:pipeline) do
create(:ci_pipeline, :invalid, project: project)
@@ -282,6 +377,30 @@ describe 'Pipelines', :js do
end
context 'for generic statuses' do
+ context 'when preparing' do
+ let!(:pipeline) do
+ create(:ci_empty_pipeline,
+ status: 'preparing', project: project)
+ end
+
+ let!(:status) do
+ create(:generic_commit_status,
+ :preparing, pipeline: pipeline)
+ end
+
+ before do
+ visit_project_pipelines
+ end
+
+ it 'is cancelable' do
+ expect(page).to have_selector('.js-pipelines-cancel-button')
+ end
+
+ it 'shows the pipeline as preparing' do
+ expect(page).to have_selector('.ci-preparing')
+ end
+ end
+
context 'when running' do
let!(:running) do
create(:generic_commit_status,
@@ -485,7 +604,7 @@ describe 'Pipelines', :js do
it 'should show updated content' do
visit project_pipelines_path(project)
wait_for_requests
- page.find('.js-next-button a').click
+ page.find('.js-next-button .page-link').click
expect(page).to have_selector('.gl-pagination .page', count: 2)
end
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 7cd5b12802b..74b9a2b20cd 100644
--- a/spec/features/projects/services/user_activates_issue_tracker_spec.rb
+++ b/spec/features/projects/services/user_activates_issue_tracker_spec.rb
@@ -6,11 +6,17 @@ describe 'User activates issue tracker', :js do
let(:url) { 'http://tracker.example.com' }
- def fill_form(active = true)
+ def fill_short_form(active = true)
check 'Active' if active
fill_in 'service_project_url', with: url
fill_in 'service_issues_url', with: "#{url}/:id"
+ end
+
+ def fill_full_form(active = true)
+ fill_short_form(active)
+ check 'Active' if active
+
fill_in 'service_new_issue_url', with: url
end
@@ -21,14 +27,20 @@ describe 'User activates issue tracker', :js do
visit project_settings_integrations_path(project)
end
- shared_examples 'external issue tracker activation' do |tracker:|
+ shared_examples 'external issue tracker activation' do |tracker:, skip_new_issue_url: false|
describe 'user sets and activates the Service' do
context 'when the connection test succeeds' do
before do
stub_request(:head, url).to_return(headers: { 'Content-Type' => 'application/json' })
click_link(tracker)
- fill_form
+
+ if skip_new_issue_url
+ fill_short_form
+ else
+ fill_full_form
+ end
+
click_button('Test settings and save changes')
wait_for_requests
end
@@ -50,7 +62,13 @@ describe 'User activates issue tracker', :js do
stub_request(:head, url).to_raise(HTTParty::Error)
click_link(tracker)
- fill_form
+
+ if skip_new_issue_url
+ fill_short_form
+ else
+ fill_full_form
+ end
+
click_button('Test settings and save changes')
wait_for_requests
@@ -69,7 +87,13 @@ describe 'User activates issue tracker', :js do
describe 'user sets the service but keeps it disabled' do
before do
click_link(tracker)
- fill_form(false)
+
+ if skip_new_issue_url
+ fill_short_form(false)
+ else
+ fill_full_form(false)
+ end
+
click_button('Save changes')
end
@@ -87,6 +111,7 @@ describe 'User activates issue tracker', :js do
end
it_behaves_like 'external issue tracker activation', tracker: 'Redmine'
+ it_behaves_like 'external issue tracker activation', tracker: 'YouTrack', skip_new_issue_url: true
it_behaves_like 'external issue tracker activation', tracker: 'Bugzilla'
it_behaves_like 'external issue tracker activation', tracker: 'Custom Issue Tracker'
end
diff --git a/spec/features/projects/services/user_activates_youtrack_spec.rb b/spec/features/projects/services/user_activates_youtrack_spec.rb
new file mode 100644
index 00000000000..bb6a030c1cf
--- /dev/null
+++ b/spec/features/projects/services/user_activates_youtrack_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe 'User activates issue tracker', :js do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+
+ let(:url) { 'http://tracker.example.com' }
+
+ def fill_form(active = true)
+ check 'Active' if active
+
+ fill_in 'service_project_url', with: url
+ fill_in 'service_issues_url', with: "#{url}/:id"
+ end
+
+ before do
+ project.add_maintainer(user)
+ sign_in(user)
+
+ visit project_settings_integrations_path(project)
+ end
+
+ shared_examples 'external issue tracker activation' do |tracker:|
+ describe 'user sets and activates the Service' do
+ context 'when the connection test succeeds' do
+ before do
+ stub_request(:head, url).to_return(headers: { 'Content-Type' => 'application/json' })
+
+ click_link(tracker)
+ fill_form
+ click_button('Test settings and save changes')
+ wait_for_requests
+ end
+
+ it 'activates the service' do
+ expect(page).to have_content("#{tracker} activated.")
+ expect(current_path).to eq(project_settings_integrations_path(project))
+ end
+
+ it 'shows the link in the menu' do
+ page.within('.nav-sidebar') do
+ expect(page).to have_link(tracker, href: url)
+ end
+ end
+ end
+
+ context 'when the connection test fails' do
+ it 'activates the service' do
+ stub_request(:head, url).to_raise(HTTParty::Error)
+
+ click_link(tracker)
+ fill_form
+ click_button('Test settings and save changes')
+ wait_for_requests
+
+ expect(find('.flash-container-page')).to have_content 'Test failed.'
+ expect(find('.flash-container-page')).to have_content 'Save anyway'
+
+ find('.flash-alert .flash-action').click
+ wait_for_requests
+
+ expect(page).to have_content("#{tracker} activated.")
+ expect(current_path).to eq(project_settings_integrations_path(project))
+ end
+ end
+ end
+
+ describe 'user sets the service but keeps it disabled' do
+ before do
+ click_link(tracker)
+ fill_form(false)
+ click_button('Save changes')
+ end
+
+ it 'saves but does not activate the service' do
+ expect(page).to have_content("#{tracker} settings saved, but not activated.")
+ expect(current_path).to eq(project_settings_integrations_path(project))
+ end
+
+ it 'does not show the external tracker link in the menu' do
+ page.within('.nav-sidebar') do
+ expect(page).not_to have_link(tracker, href: url)
+ end
+ end
+ end
+ end
+
+ it_behaves_like 'external issue tracker activation', tracker: 'YouTrack'
+end
diff --git a/spec/features/projects/settings/operations_settings_spec.rb b/spec/features/projects/settings/operations_settings_spec.rb
index 06290c67c70..af56cb0d4ee 100644
--- a/spec/features/projects/settings/operations_settings_spec.rb
+++ b/spec/features/projects/settings/operations_settings_spec.rb
@@ -20,4 +20,81 @@ describe 'Projects > Settings > For a forked project', :js do
expect(page).to have_selector('a[title="Operations"]', visible: false)
end
end
+
+ describe 'Settings > Operations' do
+ context 'error tracking settings form' do
+ let(:sentry_list_projects_url) { 'http://sentry.example.com/api/0/projects/' }
+
+ context 'success path' do
+ let(:projects_sample_response) do
+ Gitlab::Utils.deep_indifferent_access(
+ JSON.parse(fixture_file('sentry/list_projects_sample_response.json'))
+ )
+ end
+
+ before do
+ WebMock.stub_request(:get, sentry_list_projects_url)
+ .to_return(
+ status: 200,
+ headers: { 'Content-Type' => 'application/json' },
+ body: projects_sample_response.to_json
+ )
+ end
+
+ it 'successfully fills and submits the form' do
+ visit project_settings_operations_path(project)
+
+ wait_for_requests
+
+ expect(page).to have_content('Sentry API URL')
+ expect(page.body).to include('Error Tracking')
+ expect(page).to have_button('Connect')
+
+ check('Active')
+ fill_in('error-tracking-api-host', with: 'http://sentry.example.com')
+ fill_in('error-tracking-token', with: 'token')
+
+ click_button('Connect')
+
+ within('div#project-dropdown') do
+ click_button('Select project')
+ click_button('Sentry | Internal')
+ end
+
+ click_button('Save changes')
+
+ wait_for_requests
+
+ assert_text('Your changes have been saved')
+ end
+ end
+
+ context 'project dropdown fails to load' do
+ before do
+ WebMock.stub_request(:get, sentry_list_projects_url)
+ .to_return(
+ status: 400,
+ headers: { 'Content-Type' => 'application/json' },
+ body: {
+ message: 'Sentry response code: 401'
+ }.to_json
+ )
+ end
+
+ it 'displays error message' do
+ visit project_settings_operations_path(project)
+
+ wait_for_requests
+
+ check('Active')
+ fill_in('error-tracking-api-host', with: 'http://sentry.example.com')
+ fill_in('error-tracking-token', with: 'token')
+
+ click_button('Connect')
+
+ assert_text('Connection has failed. Re-check Auth Token and try again.')
+ end
+ end
+ end
+ end
end
diff --git a/spec/features/projects/settings/pipelines_settings_spec.rb b/spec/features/projects/settings/pipelines_settings_spec.rb
index 4c85abe9971..bf0c0de89b2 100644
--- a/spec/features/projects/settings/pipelines_settings_spec.rb
+++ b/spec/features/projects/settings/pipelines_settings_spec.rb
@@ -110,6 +110,37 @@ describe "Projects > Settings > Pipelines settings" do
expect(page).not_to have_content('instance enabled')
end
end
+
+ context 'when auto devops is turned on group level' do
+ before do
+ project.update!(namespace: create(:group, :auto_devops_enabled))
+ end
+
+ it 'renders group enabled badge' do
+ visit project_settings_ci_cd_path(project)
+
+ page.within '#autodevops-settings' do
+ expect(page).to have_content('group enabled')
+ expect(find_field('project_auto_devops_attributes_enabled')).to be_checked
+ end
+ end
+ end
+
+ context 'when auto devops is turned on group parent level', :nested_groups do
+ before do
+ group = create(:group, parent: create(:group, :auto_devops_enabled))
+ project.update!(namespace: group)
+ end
+
+ it 'renders group enabled badge' do
+ visit project_settings_ci_cd_path(project)
+
+ page.within '#autodevops-settings' do
+ expect(page).to have_content('group enabled')
+ expect(find_field('project_auto_devops_attributes_enabled')).to be_checked
+ end
+ end
+ end
end
end
diff --git a/spec/features/projects/settings/project_settings_spec.rb b/spec/features/projects/settings/project_settings_spec.rb
new file mode 100644
index 00000000000..7afddc0e712
--- /dev/null
+++ b/spec/features/projects/settings/project_settings_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Projects settings' do
+ set(:project) { create(:project) }
+ let(:user) { project.owner }
+ let(:panel) { find('.general-settings', match: :first) }
+ let(:button) { panel.find('.btn.js-settings-toggle') }
+ let(:title) { panel.find('.settings-title') }
+
+ before do
+ sign_in(user)
+ visit edit_project_path(project)
+ end
+
+ it 'can toggle sections by clicking the title or button', :js do
+ expect_toggle_state(:expanded)
+
+ button.click
+
+ expect_toggle_state(:collapsed)
+
+ button.click
+
+ expect_toggle_state(:expanded)
+
+ title.click
+
+ expect_toggle_state(:collapsed)
+
+ title.click
+
+ expect_toggle_state(:expanded)
+ end
+
+ def expect_toggle_state(state)
+ is_collapsed = state == :collapsed
+
+ expect(button).to have_content(is_collapsed ? 'Expand' : 'Collapse')
+ expect(panel[:class]).send(is_collapsed ? 'not_to' : 'to', include('expanded'))
+ end
+end
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 676659b90c3..e5a58c44e41 100644
--- a/spec/features/projects/settings/user_manages_group_links_spec.rb
+++ b/spec/features/projects/settings/user_manages_group_links_spec.rb
@@ -10,6 +10,7 @@ describe 'Projects > Settings > User manages group links' do
before do
project.add_maintainer(user)
+ group_market.add_guest(user)
sign_in(user)
share_link = project.project_group_links.new(group_access: Gitlab::Access::MAINTAINER)
diff --git a/spec/features/projects/show/user_sees_git_instructions_spec.rb b/spec/features/projects/show/user_sees_git_instructions_spec.rb
index ffa80235083..0d59ef4a727 100644
--- a/spec/features/projects/show/user_sees_git_instructions_spec.rb
+++ b/spec/features/projects/show/user_sees_git_instructions_spec.rb
@@ -13,7 +13,7 @@ describe 'Projects > Show > User sees Git instructions' do
it 'shows Git command line instructions' do
click_link 'Create empty repository'
- page.within '.empty_wrapper' do
+ page.within '.empty-wrapper' do
expect(page).to have_content('Command line instructions')
end
end
diff --git a/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb b/spec/features/projects/show/user_sees_setup_shortcut_buttons_spec.rb
index dcca1d388c7..58bd20d7551 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
@@ -20,18 +20,18 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
visit project_path(project)
end
- it 'no Auto DevOps button if can not manage pipelines' do
- page.within('.project-buttons') do
- expect(page).not_to have_link('Enable Auto DevOps')
- expect(page).not_to have_link('Auto DevOps enabled')
- end
- end
-
- it '"Auto DevOps enabled" button not linked' do
+ it 'Project buttons are not visible' do
visit project_path(project)
page.within('.project-buttons') do
- expect(page).to have_text('Auto DevOps enabled')
+ expect(page).not_to have_link('New file')
+ expect(page).not_to have_link('Add README')
+ expect(page).not_to have_link('Add CHANGELOG')
+ expect(page).not_to have_link('Add CONTRIBUTING')
+ expect(page).not_to have_link('Enable Auto DevOps')
+ expect(page).not_to have_link('Auto DevOps enabled')
+ expect(page).not_to have_link('Add Kubernetes cluster')
+ expect(page).not_to have_link('Kubernetes configured')
end
end
end
@@ -61,46 +61,6 @@ describe 'Projects > Show > User sees setup shortcut buttons' do
expect(page).to have_link('Add license', href: presenter.add_license_path)
end
end
-
- describe 'Auto DevOps button' do
- context 'when Auto DevOps is enabled' do
- it '"Auto DevOps enabled" anchor linked to settings page' do
- visit project_path(project)
-
- page.within('.project-buttons') do
- expect(page).to have_link('Auto DevOps enabled', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
- end
- end
- end
-
- context 'when Auto DevOps is not enabled' do
- let(:project) { create(:project, :public, :empty_repo, auto_devops_attributes: { enabled: false }) }
-
- it '"Enable Auto DevOps" button linked to settings page' do
- page.within('.project-buttons') do
- expect(page).to have_link('Enable Auto DevOps', href: project_settings_ci_cd_path(project, anchor: 'autodevops-settings'))
- end
- end
- end
- end
-
- describe 'Kubernetes cluster button' do
- it '"Add Kubernetes cluster" button linked to clusters page' do
- page.within('.project-buttons') do
- expect(page).to have_link('Add Kubernetes cluster', href: new_project_cluster_path(project))
- end
- end
-
- it '"Kubernetes cluster" anchor linked to cluster page' do
- cluster = create(:cluster, :provided_by_gcp, projects: [project])
-
- visit project_path(project)
-
- page.within('.project-buttons') do
- expect(page).to have_link('Kubernetes configured', href: project_cluster_path(project, cluster))
- end
- end
- end
end
end
diff --git a/spec/features/projects/user_sees_sidebar_spec.rb b/spec/features/projects/user_sees_sidebar_spec.rb
index ee5734a9bf1..383e8824b7b 100644
--- a/spec/features/projects/user_sees_sidebar_spec.rb
+++ b/spec/features/projects/user_sees_sidebar_spec.rb
@@ -4,6 +4,108 @@ describe 'Projects > User sees sidebar' do
let(:user) { create(:user) }
let(:project) { create(:project, :private, public_builds: false, namespace: user.namespace) }
+ # NOTE: See documented behaviour https://design.gitlab.com/regions/navigation#contextual-navigation
+ context 'on different viewports', :js do
+ include MobileHelpers
+
+ before do
+ sign_in(user)
+ end
+
+ shared_examples 'has a expanded nav sidebar' do
+ it 'has a expanded desktop nav-sidebar on load' do
+ expect(page).to have_content('Collapse sidebar')
+ expect(page).not_to have_selector('.sidebar-collapsed-desktop')
+ expect(page).not_to have_selector('.sidebar-expanded-mobile')
+ end
+
+ it 'can collapse the nav-sidebar' do
+ page.find('.nav-sidebar .js-toggle-sidebar').click
+ expect(page).to have_selector('.sidebar-collapsed-desktop')
+ expect(page).not_to have_content('Collapse sidebar')
+ expect(page).not_to have_selector('.sidebar-expanded-mobile')
+ end
+ end
+
+ shared_examples 'has a collapsed nav sidebar' do
+ it 'has a collapsed desktop nav-sidebar on load' do
+ expect(page).not_to have_content('Collapse sidebar')
+ expect(page).not_to have_selector('.sidebar-expanded-mobile')
+ end
+
+ it 'can expand the nav-sidebar' do
+ page.find('.nav-sidebar .js-toggle-sidebar').click
+ expect(page).to have_selector('.sidebar-expanded-mobile')
+ expect(page).to have_content('Collapse sidebar')
+ end
+ end
+
+ shared_examples 'has a mobile nav-sidebar' do
+ it 'has a hidden nav-sidebar on load' do
+ expect(page).not_to have_content('.mobile-nav-open')
+ expect(page).not_to have_selector('.sidebar-expanded-mobile')
+ end
+
+ it 'can expand the nav-sidebar' do
+ page.find('.toggle-mobile-nav').click
+ expect(page).to have_selector('.mobile-nav-open')
+ expect(page).to have_selector('.sidebar-expanded-mobile')
+ end
+ end
+
+ context 'with a extra small viewport' do
+ before do
+ resize_screen_xs
+ visit project_path(project)
+ expect(page).to have_selector('.nav-sidebar')
+ expect(page).to have_selector('.toggle-mobile-nav')
+ end
+
+ it_behaves_like 'has a mobile nav-sidebar'
+ end
+
+ context 'with a small size viewport' do
+ before do
+ resize_screen_sm
+ visit project_path(project)
+ expect(page).to have_selector('.nav-sidebar')
+ expect(page).to have_selector('.toggle-mobile-nav')
+ end
+
+ it_behaves_like 'has a mobile nav-sidebar'
+ end
+
+ context 'with medium size viewport' do
+ before do
+ resize_window(768, 800)
+ visit project_path(project)
+ expect(page).to have_selector('.nav-sidebar')
+ end
+
+ it_behaves_like 'has a collapsed nav sidebar'
+ end
+
+ context 'with viewport size 1199px' do
+ before do
+ resize_window(1199, 800)
+ visit project_path(project)
+ expect(page).to have_selector('.nav-sidebar')
+ end
+
+ it_behaves_like 'has a collapsed nav sidebar'
+ end
+
+ context 'with a extra large viewport' do
+ before do
+ resize_window(1200, 800)
+ visit project_path(project)
+ expect(page).to have_selector('.nav-sidebar')
+ end
+
+ it_behaves_like 'has a expanded nav sidebar'
+ end
+ end
+
context 'as owner' do
before do
sign_in(user)
diff --git a/spec/features/projects/wiki/markdown_preview_spec.rb b/spec/features/projects/wiki/markdown_preview_spec.rb
index 49244c53a91..49058d1372a 100644
--- a/spec/features/projects/wiki/markdown_preview_spec.rb
+++ b/spec/features/projects/wiki/markdown_preview_spec.rb
@@ -170,7 +170,7 @@ describe 'Projects > Wiki > User previews markdown changes', :js do
fill_in :wiki_content, with: "1. one\n - sublist\n"
click_on "Preview"
- # the above generates two seperate lists (not embedded) in CommonMark
+ # the above generates two separate lists (not embedded) in CommonMark
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
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 b1a7f167977..efb7b01f5ad 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -136,7 +136,7 @@ describe "User creates wiki page" do
click_button("Create page")
end
- page.within ".wiki" do
+ page.within ".md" do
expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4")
end
end
diff --git a/spec/features/search/user_searches_for_users_spec.rb b/spec/features/search/user_searches_for_users_spec.rb
new file mode 100644
index 00000000000..3725143291d
--- /dev/null
+++ b/spec/features/search/user_searches_for_users_spec.rb
@@ -0,0 +1,83 @@
+require 'spec_helper'
+
+describe 'User searches for users' do
+ context 'when on the dashboard' do
+ it 'finds the user' do
+ create(:user, username: 'gob_bluth', name: 'Gob Bluth')
+
+ sign_in(create(:user))
+
+ visit dashboard_projects_path
+
+ fill_in 'search', with: 'gob'
+ click_button 'Go'
+
+ expect(page).to have_content('Users 1')
+
+ click_on('Users 1')
+
+ expect(page).to have_content('Gob Bluth')
+ expect(page).to have_content('@gob_bluth')
+ end
+ end
+
+ context 'when on the project page' do
+ it 'finds the user belonging to the project' do
+ project = create(:project)
+
+ user1 = create(:user, username: 'gob_bluth', name: 'Gob Bluth')
+ create(:project_member, :developer, user: user1, project: project)
+
+ user2 = create(:user, username: 'michael_bluth', name: 'Michael Bluth')
+ create(:project_member, :developer, user: user2, project: project)
+
+ create(:user, username: 'gob_2018', name: 'George Oscar Bluth')
+
+ sign_in(user1)
+
+ visit projects_path(project)
+
+ fill_in 'search', with: 'gob'
+ click_button 'Go'
+
+ expect(page).to have_content('Gob Bluth')
+ expect(page).to have_content('@gob_bluth')
+
+ expect(page).not_to have_content('Michael Bluth')
+ expect(page).not_to have_content('@michael_bluth')
+
+ expect(page).not_to have_content('George Oscar Bluth')
+ expect(page).not_to have_content('@gob_2018')
+ end
+ end
+
+ context 'when on the group page' do
+ it 'finds the user belonging to the group' do
+ group = create(:group)
+
+ user1 = create(:user, username: 'gob_bluth', name: 'Gob Bluth')
+ create(:group_member, :developer, user: user1, group: group)
+
+ user2 = create(:user, username: 'michael_bluth', name: 'Michael Bluth')
+ create(:group_member, :developer, user: user2, group: group)
+
+ create(:user, username: 'gob_2018', name: 'George Oscar Bluth')
+
+ sign_in(user1)
+
+ visit group_path(group)
+
+ fill_in 'search', with: 'gob'
+ click_button 'Go'
+
+ expect(page).to have_content('Gob Bluth')
+ expect(page).to have_content('@gob_bluth')
+
+ expect(page).not_to have_content('Michael Bluth')
+ expect(page).not_to have_content('@michael_bluth')
+
+ expect(page).not_to have_content('George Oscar Bluth')
+ expect(page).not_to have_content('@gob_2018')
+ end
+ end
+end
diff --git a/spec/features/security/group/private_access_spec.rb b/spec/features/security/group/private_access_spec.rb
index 4705cd12d23..a776169a8e5 100644
--- a/spec/features/security/group/private_access_spec.rb
+++ b/spec/features/security/group/private_access_spec.rb
@@ -93,4 +93,25 @@ describe 'Private Group access' do
it { is_expected.to be_denied_for(:visitor) }
it { is_expected.to be_denied_for(:external) }
end
+
+ describe 'GET /groups/:path for shared projects' do
+ let(:project) { create(:project, :public) }
+
+ before do
+ create(:project_group_link, project: project, group: group)
+ end
+
+ subject { group_path(group) }
+
+ 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(: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) }
+ it { is_expected.to be_denied_for(project_guest) }
+ it { is_expected.to be_denied_for(:user) }
+ it { is_expected.to be_denied_for(:external) }
+ it { is_expected.to be_denied_for(:visitor) }
+ end
end
diff --git a/spec/features/snippets/notes_on_personal_snippets_spec.rb b/spec/features/snippets/notes_on_personal_snippets_spec.rb
index eeacaf5f72a..fc6726985ae 100644
--- a/spec/features/snippets/notes_on_personal_snippets_spec.rb
+++ b/spec/features/snippets/notes_on_personal_snippets_spec.rb
@@ -70,7 +70,7 @@ describe 'Comments on personal snippets', :js do
fill_in 'note[note]', with: 'This is **awesome**!'
find('.js-md-preview-button').click
- page.within('.new-note .md-preview') do
+ page.within('.new-note .md-preview-holder') do
expect(page).to have_content('This is awesome!')
expect(page).to have_selector('strong')
end
diff --git a/spec/features/snippets/user_creates_snippet_spec.rb b/spec/features/snippets/user_creates_snippet_spec.rb
index 879c46d7c4e..1c97d5ec5b4 100644
--- a/spec/features/snippets/user_creates_snippet_spec.rb
+++ b/spec/features/snippets/user_creates_snippet_spec.rb
@@ -37,7 +37,7 @@ describe 'User creates snippet', :js do
dropzone_file Rails.root.join('spec', 'fixtures', 'banana_sample.gif')
find('.js-md-preview-button').click
- page.within('#new_personal_snippet .md-preview') do
+ page.within('#new_personal_snippet .md-preview-holder') do
expect(page).to have_content('My Snippet')
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb
index 8d567e925ef..bdbbe645779 100644
--- a/spec/features/tags/master_deletes_tag_spec.rb
+++ b/spec/features/tags/master_deletes_tag_spec.rb
@@ -37,7 +37,7 @@ describe 'Maintainer deletes tag' do
context 'when pre-receive hook fails', :js do
before do
allow_any_instance_of(Gitlab::GitalyClient::OperationService).to receive(:rm_tag)
- .and_raise(Gitlab::Git::PreReceiveError, 'Do not delete tags')
+ .and_raise(Gitlab::Git::PreReceiveError, 'GitLab: Do not delete tags')
end
it 'shows the error message' do
diff --git a/spec/features/task_lists_spec.rb b/spec/features/task_lists_spec.rb
index 6fe840dccf6..33d9c10f5e8 100644
--- a/spec/features/task_lists_spec.rb
+++ b/spec/features/task_lists_spec.rb
@@ -79,7 +79,7 @@ describe 'Task Lists' do
visit_issue(project, issue)
wait_for_requests
- expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
+ expect(page).to have_selector(".md .task-list .task-list-item .task-list-item-checkbox")
expect(page).to have_selector('a.btn-close')
end
@@ -87,14 +87,14 @@ describe 'Task Lists' do
visit_issue(project, issue)
wait_for_requests
- expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
+ expect(page).to have_selector(".md .task-list .task-list-item .task-list-item-checkbox")
logout(:user)
login_as(user2)
visit current_path
wait_for_requests
- expect(page).to have_selector(".wiki .task-list .task-list-item .task-list-item-checkbox")
+ expect(page).to have_selector(".md .task-list .task-list-item .task-list-item-checkbox")
end
it 'provides a summary on Issues#index' do
@@ -231,7 +231,7 @@ describe 'Task Lists' do
container = '.detail-page-description .description.js-task-list-container'
expect(page).to have_selector(container)
- expect(page).to have_selector("#{container} .wiki .task-list .task-list-item .task-list-item-checkbox")
+ expect(page).to have_selector("#{container} .md .task-list .task-list-item .task-list-item-checkbox")
expect(page).to have_selector("#{container} .js-task-list-field")
expect(page).to have_selector('form.js-issuable-update')
expect(page).to have_selector('a.btn-close')
diff --git a/spec/features/user_opens_link_to_comment.rb b/spec/features/user_opens_link_to_comment.rb
new file mode 100644
index 00000000000..f1e07e55799
--- /dev/null
+++ b/spec/features/user_opens_link_to_comment.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'User opens link to comment', :js do
+ let(:project) { create(:project, :public) }
+ let(:note) { create(:note_on_issue, project: project) }
+
+ context 'authenticated user' do
+ let(:user) { create(:user) }
+
+ before do
+ sign_in(user)
+ end
+
+ it 'switches to all activity and does not show error message' do
+ create(:user_preference, user: user, issue_notes_filter: UserPreference::NOTES_FILTERS[:only_activity])
+
+ visit Gitlab::UrlBuilder.build(note)
+
+ expect(page.find('#discussion-filter-dropdown')).to have_content('Show all activity')
+ expect(page).not_to have_content('Something went wrong while fetching comments')
+ end
+ end
+
+ context 'anonymous user' do
+ it 'does not show error message' do
+ visit Gitlab::UrlBuilder.build(note)
+
+ expect(page).not_to have_content('Something went wrong while fetching comments')
+ end
+ end
+end
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index ad856bd062e..368a814874f 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -434,16 +434,22 @@ describe 'Login' do
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 ' \
- 'Two-Factor Authentication for your account. You need to do this ' \
- 'before ')
+ Timecop.freeze 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 '\
+ 'Two-Factor Authentication for your account. '\
+ 'You can leave Group 1 and leave Group 2. '\
+ 'You need to do this '\
+ 'before '\
+ "#{(Time.zone.now + 2.days).strftime("%a, %-d %b %Y %H:%M:%S %z")}"
+ )
+ end
end
it 'allows skipping two-factor configuration', :js do
@@ -500,7 +506,8 @@ describe 'Login' do
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 ' \
- 'Two-Factor Authentication for your account.'
+ 'Two-Factor Authentication for your account. '\
+ 'You can leave Group 1 and leave Group 2.'
)
end
end
diff --git a/spec/finders/admin/runners_finder_spec.rb b/spec/finders/admin/runners_finder_spec.rb
index 0b2325cc7ca..94ccb398801 100644
--- a/spec/finders/admin/runners_finder_spec.rb
+++ b/spec/finders/admin/runners_finder_spec.rb
@@ -37,6 +37,14 @@ describe Admin::RunnersFinder do
end
end
+ context 'filter by tag_name' do
+ it 'calls the corresponding scope on Ci::Runner' do
+ expect(Ci::Runner).to receive(:tagged_with).with(%w[tag1 tag2]).and_call_original
+
+ described_class.new(params: { tag_name: %w[tag1 tag2] }).execute
+ end
+ end
+
context 'sort' do
context 'without sort param' do
it 'sorts by created_at' do
diff --git a/spec/finders/autocomplete/acts_as_taggable_on/tags_finder_spec.rb b/spec/finders/autocomplete/acts_as_taggable_on/tags_finder_spec.rb
new file mode 100644
index 00000000000..79d2f9cdb45
--- /dev/null
+++ b/spec/finders/autocomplete/acts_as_taggable_on/tags_finder_spec.rb
@@ -0,0 +1,66 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Autocomplete::ActsAsTaggableOn::TagsFinder do
+ describe '#execute' do
+ context 'with empty params' do
+ it 'returns all tags' do
+ tag1 = ActsAsTaggableOn::Tag.create!(name: 'tag1')
+ tag2 = ActsAsTaggableOn::Tag.create!(name: 'tag2')
+
+ tags = described_class.new(params: {}).execute
+
+ expect(tags).to match_array [tag1, tag2]
+ end
+ end
+
+ context 'filter by search' do
+ context 'with an empty search term' do
+ it 'returns an empty collection' do
+ ActsAsTaggableOn::Tag.create!(name: 'tag1')
+ ActsAsTaggableOn::Tag.create!(name: 'tag2')
+
+ tags = described_class.new(params: { search: '' }).execute
+
+ expect(tags).to be_empty
+ end
+ end
+
+ context 'with a search containing 2 characters' do
+ it 'returns the tag that strictly matches the search term' do
+ tag1 = ActsAsTaggableOn::Tag.create!(name: 't1')
+ ActsAsTaggableOn::Tag.create!(name: 't11')
+
+ tags = described_class.new(params: { search: 't1' }).execute
+
+ expect(tags).to match_array [tag1]
+ end
+ end
+
+ context 'with a search containing 3 characters' do
+ it 'returns the tag that partially matches the search term' do
+ tag1 = ActsAsTaggableOn::Tag.create!(name: 'tag1')
+ tag2 = ActsAsTaggableOn::Tag.create!(name: 'tag11')
+
+ tags = described_class.new(params: { search: 'ag1' }).execute
+
+ expect(tags).to match_array [tag1, tag2]
+ end
+ end
+ end
+
+ context 'limit' do
+ it 'limits the result set by the limit constant' do
+ stub_const("#{described_class}::LIMIT", 1)
+
+ ActsAsTaggableOn::Tag.create!(name: 'tag1')
+ ActsAsTaggableOn::Tag.create!(name: 'tag2')
+
+ tags = described_class.new(params: { search: 'tag' }).execute
+
+ expect(tags.count).to eq 1
+ end
+ end
+ end
+end
diff --git a/spec/finders/concerns/finder_methods_spec.rb b/spec/finders/concerns/finder_methods_spec.rb
index a4ad331f613..e074e53c2c5 100644
--- a/spec/finders/concerns/finder_methods_spec.rb
+++ b/spec/finders/concerns/finder_methods_spec.rb
@@ -12,7 +12,7 @@ describe FinderMethods do
end
def execute
- Project.all
+ Project.all.order(id: :desc)
end
end
end
@@ -38,6 +38,16 @@ describe FinderMethods do
it 'raises not found the user does not have access' do
expect { finder.find_by!(id: unauthorized_project.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
+
+ it 'ignores ordering' do
+ # Memoise the finder result so we can add message expectations to it
+ relation = finder.execute
+ allow(finder).to receive(:execute).and_return(relation)
+
+ expect(relation).to receive(:reorder).with(nil).and_call_original
+
+ finder.find_by!(id: authorized_project.id)
+ end
end
describe '#find' do
@@ -66,5 +76,15 @@ describe FinderMethods do
it 'returns nil when the user does not have access' do
expect(finder.find_by(id: unauthorized_project.id)).to be_nil
end
+
+ it 'ignores ordering' do
+ # Memoise the finder result so we can add message expectations to it
+ relation = finder.execute
+ allow(finder).to receive(:execute).and_return(relation)
+
+ expect(relation).to receive(:reorder).with(nil).and_call_original
+
+ finder.find_by(id: authorized_project.id)
+ end
end
end
diff --git a/spec/finders/group_projects_finder_spec.rb b/spec/finders/group_projects_finder_spec.rb
index d6d95906f5e..f8fcc2d0e40 100644
--- a/spec/finders/group_projects_finder_spec.rb
+++ b/spec/finders/group_projects_finder_spec.rb
@@ -1,26 +1,7 @@
require 'spec_helper'
describe GroupProjectsFinder do
- let(:group) { create(:group) }
- let(:subgroup) { create(:group, parent: group) }
- let(:current_user) { create(:user) }
- let(:options) { {} }
-
- let(:finder) { described_class.new(group: group, current_user: current_user, options: options) }
-
- let!(:public_project) { create(:project, :public, group: group, path: '1') }
- let!(:private_project) { create(:project, :private, group: group, path: '2') }
- let!(:shared_project_1) { create(:project, :public, path: '3') }
- let!(:shared_project_2) { create(:project, :private, path: '4') }
- let!(:shared_project_3) { create(:project, :internal, path: '5') }
- let!(:subgroup_project) { create(:project, :public, path: '6', group: subgroup) }
- let!(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup) }
-
- before do
- 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
+ include_context 'GroupProjectsFinder context'
subject { finder.execute }
@@ -144,6 +125,24 @@ describe GroupProjectsFinder do
end
end
+ describe 'with an admin current user' do
+ let(:current_user) { create(:admin) }
+
+ context "only shared" do
+ let(:options) { { only_shared: true } }
+ it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1]) }
+ end
+
+ context "only owned" do
+ let(:options) { { only_owned: true } }
+ it { is_expected.to eq([private_project, public_project]) }
+ end
+
+ context "all" do
+ it { is_expected.to eq([shared_project_3, shared_project_2, shared_project_1, private_project, public_project]) }
+ end
+ end
+
describe "no user" do
context "only shared" do
let(:options) { { only_shared: true } }
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index 34cb09942be..00b6cad1a66 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -1,45 +1,10 @@
require 'spec_helper'
describe IssuesFinder do
- set(:user) { create(:user) }
- set(:user2) { create(:user) }
- set(:group) { create(:group) }
- set(:subgroup) { create(:group, parent: group) }
- set(:project1) { create(:project, group: group) }
- set(:project2) { create(:project) }
- set(:project3) { create(:project, group: subgroup) }
- set(:milestone) { create(:milestone, project: project1) }
- set(:label) { create(:label, project: project2) }
- set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab', created_at: 1.week.ago, updated_at: 1.week.ago) }
- set(:issue2) { create(:issue, author: user, assignees: [user], project: project2, description: 'gitlab', created_at: 1.week.from_now, updated_at: 1.week.from_now) }
- set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki', created_at: 2.weeks.from_now, updated_at: 2.weeks.from_now) }
- set(:issue4) { create(:issue, project: project3) }
- set(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue1) }
- set(:award_emoji2) { create(:award_emoji, name: 'thumbsup', user: user2, awardable: issue2) }
- set(:award_emoji3) { create(:award_emoji, name: 'thumbsdown', user: user, awardable: issue3) }
+ include_context 'IssuesFinder context'
describe '#execute' do
- let!(:closed_issue) { create(:issue, author: user2, assignees: [user2], project: project2, state: 'closed') }
- let!(:label_link) { create(:label_link, label: label, target: issue2) }
- let(:search_user) { user }
- let(:params) { {} }
- let(:issues) { described_class.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute }
-
- before(:context) do
- project1.add_maintainer(user)
- project2.add_developer(user)
- project2.add_developer(user2)
- project3.add_developer(user)
-
- issue1
- issue2
- issue3
- issue4
-
- award_emoji1
- award_emoji2
- award_emoji3
- end
+ include_context 'IssuesFinder#execute context'
context 'scope: all' do
let(:scope) { 'all' }
@@ -56,6 +21,21 @@ describe IssuesFinder do
end
end
+ context 'filtering by assignee usernames' do
+ set(:user3) { create(:user) }
+ let(:params) { { assignee_username: [user2.username, user3.username] } }
+
+ before do
+ project2.add_developer(user3)
+
+ issue3.assignees = [user2, user3]
+ end
+
+ it 'returns issues assigned to those users' do
+ expect(issues).to contain_exactly(issue3)
+ end
+ end
+
context 'filtering by no assignee' do
let(:params) { { assignee_id: 'None' } }
@@ -220,6 +200,7 @@ describe IssuesFinder do
let(:yesterday) { Date.today - 1.day }
let(:tomorrow) { Date.today + 1.day }
let(:two_days_ago) { Date.today - 2.days }
+ let(:three_days_ago) { Date.today - 3.days }
let(:milestones) do
[
@@ -227,6 +208,8 @@ describe IssuesFinder do
create(:milestone, project: project_started_1_and_2, title: '1.0', start_date: two_days_ago),
create(:milestone, project: project_started_1_and_2, title: '2.0', start_date: yesterday),
create(:milestone, project: project_started_1_and_2, title: '3.0', start_date: tomorrow),
+ create(:milestone, :closed, project: project_started_1_and_2, title: '4.0', start_date: three_days_ago),
+ create(:milestone, :closed, project: project_started_8, title: '6.0', start_date: three_days_ago),
create(:milestone, project: project_started_8, title: '7.0'),
create(:milestone, project: project_started_8, title: '8.0', start_date: yesterday),
create(:milestone, project: project_started_8, title: '9.0', start_date: tomorrow)
@@ -416,6 +399,36 @@ describe IssuesFinder do
end
end
+ context 'filtering by closed_at' do
+ let!(:closed_issue1) { create(:issue, project: project1, state: :closed, closed_at: 1.week.ago) }
+ let!(:closed_issue2) { create(:issue, project: project2, state: :closed, closed_at: 1.week.from_now) }
+ let!(:closed_issue3) { create(:issue, project: project2, state: :closed, closed_at: 2.weeks.from_now) }
+
+ context 'through closed_after' do
+ let(:params) { { state: :closed, closed_after: closed_issue3.closed_at } }
+
+ it 'returns issues closed on or after the given date' do
+ expect(issues).to contain_exactly(closed_issue3)
+ end
+ end
+
+ context 'through closed_before' do
+ let(:params) { { state: :closed, closed_before: closed_issue1.closed_at } }
+
+ it 'returns issues closed on or before the given date' do
+ expect(issues).to contain_exactly(closed_issue1)
+ end
+ end
+
+ context 'through closed_after and closed_before' do
+ let(:params) { { state: :closed, closed_after: closed_issue2.closed_at, closed_before: closed_issue3.closed_at } }
+
+ it 'returns issues closed between the given dates' do
+ expect(issues).to contain_exactly(closed_issue2, closed_issue3)
+ end
+ end
+ end
+
context 'filtering by reaction name' do
context 'user searches by no reaction' do
let(:params) { { my_reaction_emoji: 'None' } }
@@ -460,6 +473,32 @@ describe IssuesFinder do
end
end
+ context 'filtering by confidential' do
+ set(:confidential_issue) { create(:issue, project: project1, confidential: true) }
+
+ context 'no filtering' do
+ it 'returns all issues' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3, issue4, confidential_issue)
+ end
+ end
+
+ context 'user filters confidential issues' do
+ let(:params) { { confidential: true } }
+
+ it 'returns only confdential issues' do
+ expect(issues).to contain_exactly(confidential_issue)
+ end
+ end
+
+ context 'user filters only public issues' do
+ let(:params) { { confidential: false } }
+
+ it 'returns only confdential issues' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3, issue4)
+ end
+ end
+ end
+
context 'when the user is unauthorized' do
let(:search_user) { nil }
@@ -526,7 +565,7 @@ describe IssuesFinder 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(5)
end
it 'returns the number of rows for a given state' do
@@ -584,6 +623,16 @@ describe IssuesFinder do
expect(subject).to include(public_issue, confidential_issue)
end
end
+
+ context 'for an admin' do
+ let(:admin_user) { create(:user, :admin) }
+
+ subject { described_class.new(admin_user, params).with_confidentiality_access_check }
+
+ it 'returns all issues' do
+ expect(subject).to include(public_issue, confidential_issue)
+ end
+ end
end
context 'when searching within a specific project' do
@@ -651,6 +700,22 @@ describe IssuesFinder do
subject
end
end
+
+ context 'for an admin' do
+ let(:admin_user) { create(:user, :admin) }
+
+ subject { described_class.new(admin_user, params).with_confidentiality_access_check }
+
+ it 'returns all issues' do
+ expect(subject).to include(public_issue, confidential_issue)
+ end
+
+ it 'does not filter by confidentiality' do
+ expect(Issue).not_to receive(:where).with(a_string_matching('confidential'), anything)
+
+ subject
+ end
+ end
end
end
@@ -659,7 +724,7 @@ describe IssuesFinder do
before do
allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
- stub_feature_flags(use_subquery_for_group_issues_search: true)
+ stub_feature_flags(attempt_group_search_optimizations: true)
end
context 'when there is no search param' do
@@ -690,11 +755,11 @@ describe IssuesFinder do
end
end
- context 'when the use_subquery_for_group_issues_search flag is disabled' do
+ context 'when the attempt_group_search_optimizations flag is disabled' do
let(:params) { { search: 'foo', attempt_group_search_optimizations: true } }
before do
- stub_feature_flags(use_subquery_for_group_issues_search: false)
+ stub_feature_flags(attempt_group_search_optimizations: false)
end
it 'returns false' do
@@ -702,6 +767,14 @@ describe IssuesFinder do
end
end
+ context 'when force_cte? is true' do
+ let(:params) { { search: 'foo', attempt_group_search_optimizations: true, force_cte: true } }
+
+ it 'returns false' do
+ expect(finder.use_subquery_for_search?).to be_falsey
+ end
+ end
+
context 'when all conditions are met' do
let(:params) { { search: 'foo', attempt_group_search_optimizations: true } }
@@ -711,72 +784,59 @@ describe IssuesFinder do
end
end
- describe '#use_cte_for_search?' do
+ describe '#use_cte_for_count?' do
let(:finder) { described_class.new(nil, params) }
before do
allow(Gitlab::Database).to receive(:postgresql?).and_return(true)
- stub_feature_flags(use_cte_for_group_issues_search: true)
- stub_feature_flags(use_subquery_for_group_issues_search: false)
+ stub_feature_flags(attempt_group_search_optimizations: true)
end
context 'when there is no search param' do
- let(:params) { { attempt_group_search_optimizations: true } }
+ let(:params) { { attempt_group_search_optimizations: true, force_cte: true } }
it 'returns false' do
- expect(finder.use_cte_for_search?).to be_falsey
+ expect(finder.use_cte_for_count?).to be_falsey
end
end
context 'when the database is not Postgres' do
- let(:params) { { search: 'foo', attempt_group_search_optimizations: true } }
+ let(:params) { { search: 'foo', force_cte: true, attempt_group_search_optimizations: true } }
before do
allow(Gitlab::Database).to receive(:postgresql?).and_return(false)
end
it 'returns false' do
- expect(finder.use_cte_for_search?).to be_falsey
+ expect(finder.use_cte_for_count?).to be_falsey
end
end
- context 'when the attempt_group_search_optimizations param is falsey' do
+ context 'when the force_cte param is falsey' do
let(:params) { { search: 'foo' } }
it 'returns false' do
- expect(finder.use_cte_for_search?).to be_falsey
+ expect(finder.use_cte_for_count?).to be_falsey
end
end
- context 'when the use_cte_for_group_issues_search flag is disabled' do
- let(:params) { { search: 'foo', attempt_group_search_optimizations: true } }
+ context 'when the attempt_group_search_optimizations flag is disabled' do
+ let(:params) { { search: 'foo', force_cte: true, attempt_group_search_optimizations: true } }
before do
- stub_feature_flags(use_cte_for_group_issues_search: false)
+ stub_feature_flags(attempt_group_search_optimizations: false)
end
it 'returns false' do
- expect(finder.use_cte_for_search?).to be_falsey
- end
- end
-
- context 'when use_subquery_for_search? is true' do
- let(:params) { { search: 'foo', attempt_group_search_optimizations: true } }
-
- before do
- stub_feature_flags(use_subquery_for_group_issues_search: true)
- end
-
- it 'returns false' do
- expect(finder.use_cte_for_search?).to be_falsey
+ expect(finder.use_cte_for_count?).to be_falsey
end
end
context 'when all conditions are met' do
- let(:params) { { search: 'foo', attempt_group_search_optimizations: true } }
+ let(:params) { { search: 'foo', force_cte: true, attempt_group_search_optimizations: true } }
it 'returns true' do
- expect(finder.use_cte_for_search?).to be_truthy
+ expect(finder.use_cte_for_count?).to be_truthy
end
end
end
diff --git a/spec/finders/labels_finder_spec.rb b/spec/finders/labels_finder_spec.rb
index 9abc52aa664..3f060ba0553 100644
--- a/spec/finders/labels_finder_spec.rb
+++ b/spec/finders/labels_finder_spec.rb
@@ -209,6 +209,12 @@ describe LabelsFinder do
expect(finder.execute).to eq [project_label_1]
end
+
+ it 'returns labels matching a single character' do
+ finder = described_class.new(user, search: '(')
+
+ expect(finder.execute).to eq [group_label_1]
+ end
end
context 'filter by subscription' do
diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb
index 107da08a0a9..56136eb84bc 100644
--- a/spec/finders/merge_requests_finder_spec.rb
+++ b/spec/finders/merge_requests_finder_spec.rb
@@ -1,287 +1,372 @@
require 'spec_helper'
describe MergeRequestsFinder do
- include ProjectForksHelper
-
- # We need to explicitly permit Gitaly N+1s because of the specs that use
- # :request_store. Gitaly N+1 detection is only enabled when :request_store is,
- # but we don't care about potential N+1s when we're just creating several
- # projects in the setup phase.
- def create_project_without_n_plus_1(*args)
- Gitlab::GitalyClient.allow_n_plus_1_calls do
- create(:project, :public, *args)
- end
- end
-
- let(:user) { create :user }
- let(:user2) { create :user }
+ context "multiple projects with merge requests" do
+ include_context 'MergeRequestsFinder multiple projects with merge requests context'
- let(:group) { create(:group) }
- let(:subgroup) { create(:group, parent: group) }
- let(:project1) { create_project_without_n_plus_1(group: group) }
- let(:project2) do
- Gitlab::GitalyClient.allow_n_plus_1_calls do
- fork_project(project1, user)
- end
- end
- let(:project3) do
- Gitlab::GitalyClient.allow_n_plus_1_calls do
- p = fork_project(project1, user)
- p.update!(archived: true)
- p
- end
- end
- let(:project4) { create_project_without_n_plus_1(group: subgroup) }
- let(:project5) { create_project_without_n_plus_1(group: subgroup) }
- let(:project6) { create_project_without_n_plus_1(group: subgroup) }
-
- 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, state: 'locked', title: 'thing WIP thing') }
- let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3, title: 'WIP thing') }
- let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4, title: '[WIP]') }
- let!(:merge_request6) { create(:merge_request, :simple, author: user, source_project: project5, target_project: project5, title: 'WIP: thing') }
- let!(:merge_request7) { create(:merge_request, :simple, author: user, source_project: project6, target_project: project6, title: 'wip thing') }
- let!(:merge_request8) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project1, title: '[wip] thing') }
- let!(:merge_request9) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2, title: 'wip: thing') }
-
- before do
- project1.add_maintainer(user)
- project2.add_developer(user)
- project3.add_developer(user)
- project2.add_developer(user2)
- project4.add_developer(user)
- project5.add_developer(user)
- project6.add_developer(user)
- end
+ describe '#execute' do
+ it 'filters by scope' do
+ params = { scope: 'authored', state: 'opened' }
- describe "#execute" 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(7)
- end
+ merge_requests = described_class.new(user, params).execute
- it 'filters by project' do
- params = { project_id: project1.id, scope: 'authored', state: 'opened' }
- merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(2)
- end
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request4, merge_request5)
+ end
- context 'filtering by group' do
- it 'includes all merge requests when user has access' do
- params = { group_id: group.id }
+ it 'filters by project' do
+ params = { project_id: project1.id, scope: 'authored', state: 'opened' }
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(3)
+ expect(merge_requests).to contain_exactly(merge_request1)
+ end
+
+ it 'filters by commit sha' do
+ merge_requests = described_class.new(
+ user,
+ commit_sha: merge_request5.merge_request_diff.last_commit_sha
+ ).execute
+
+ expect(merge_requests).to contain_exactly(merge_request5)
end
- it 'excludes merge requests from projects the user does not have access to' do
- private_project = create_project_without_n_plus_1(:private, group: group)
- private_mr = create(:merge_request, :simple, author: user, source_project: private_project, target_project: private_project)
- params = { group_id: group.id }
+ context 'filtering by group' do
+ it 'includes all merge requests when user has access exceluding merge requests from projects the user does not have access to' do
+ private_project = allow_gitaly_n_plus_1 { create(:project, :private, group: group) }
+ private_project.add_guest(user)
+ create(:merge_request, :simple, author: user, source_project: private_project, target_project: private_project)
+ params = { group_id: group.id }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2)
+ end
+
+ it 'filters by group including subgroups', :nested_groups do
+ params = { group_id: group.id, include_subgroups: true }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request5)
+ end
+ end
+
+ it 'filters by non_archived' do
+ params = { non_archived: true }
- private_project.add_guest(user)
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(3)
- expect(merge_requests).not_to include(private_mr)
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3, merge_request5)
end
- it 'filters by group including subgroups', :nested_groups do
- params = { group_id: group.id, include_subgroups: true }
+ it 'filters by iid' do
+ params = { project_id: project1.id, iids: merge_request1.iid }
merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(6)
+ expect(merge_requests).to contain_exactly(merge_request1)
end
- end
- it 'filters by non_archived' do
- params = { non_archived: true }
- merge_requests = described_class.new(user, params).execute
- expect(merge_requests.size).to eq(8)
- end
+ it 'filters by source branch' do
+ params = { source_branch: merge_request2.source_branch }
- it 'filters by iid' do
- params = { project_id: project1.id, iids: merge_request1.iid }
+ merge_requests = described_class.new(user, params).execute
- merge_requests = described_class.new(user, params).execute
+ expect(merge_requests).to contain_exactly(merge_request2)
+ end
- expect(merge_requests).to contain_exactly(merge_request1)
- end
+ it 'filters by target branch' do
+ params = { target_branch: merge_request2.target_branch }
- it 'filters by source branch' do
- params = { source_branch: merge_request2.source_branch }
+ merge_requests = described_class.new(user, params).execute
- merge_requests = described_class.new(user, params).execute
+ expect(merge_requests).to contain_exactly(merge_request2)
+ end
- expect(merge_requests).to contain_exactly(merge_request2)
- end
+ it 'filters by state' do
+ params = { state: 'locked' }
- it 'filters by target branch' do
- params = { target_branch: merge_request2.target_branch }
+ merge_requests = described_class.new(user, params).execute
- merge_requests = described_class.new(user, params).execute
+ expect(merge_requests).to contain_exactly(merge_request3)
+ end
- expect(merge_requests).to contain_exactly(merge_request2)
- end
+ describe 'WIP state' do
+ let!(:wip_merge_request1) { create(:merge_request, :simple, author: user, source_project: project5, target_project: project5, title: 'WIP: thing') }
+ let!(:wip_merge_request2) { create(:merge_request, :simple, author: user, source_project: project6, target_project: project6, title: 'wip thing') }
+ let!(:wip_merge_request3) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project1, title: '[wip] thing') }
+ let!(:wip_merge_request4) { create(:merge_request, :simple, author: user, source_project: project1, target_project: project2, title: 'wip: thing') }
- it 'filters by state' do
- params = { state: 'locked' }
+ it 'filters by wip' do
+ params = { wip: 'yes' }
- merge_requests = described_class.new(user, params).execute
+ merge_requests = described_class.new(user, params).execute
- expect(merge_requests).to contain_exactly(merge_request3)
- end
+ expect(merge_requests).to contain_exactly(merge_request4, merge_request5, wip_merge_request1, wip_merge_request2, wip_merge_request3, wip_merge_request4)
+ end
- it 'filters by wip' do
- params = { wip: 'yes' }
+ it 'filters by not wip' do
+ params = { wip: 'no' }
- merge_requests = described_class.new(user, params).execute
+ merge_requests = described_class.new(user, params).execute
- expect(merge_requests).to contain_exactly(merge_request4, merge_request5, merge_request6, merge_request7, merge_request8, merge_request9)
- end
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3)
+ end
- it 'filters by not wip' do
- params = { wip: 'no' }
+ it 'returns all items if no valid wip param exists' do
+ params = { wip: '' }
- merge_requests = described_class.new(user, params).execute
+ merge_requests = described_class.new(user, params).execute
- expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3)
- end
+ expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3, merge_request4, merge_request5, wip_merge_request1, wip_merge_request2, wip_merge_request3, wip_merge_request4)
+ end
- it 'returns all items if no valid wip param exists' do
- params = { wip: '' }
+ it 'adds wip to scalar params' do
+ scalar_params = described_class.scalar_params
- merge_requests = described_class.new(user, params).execute
+ expect(scalar_params).to include(:wip, :assignee_id)
+ end
+ end
- expect(merge_requests).to contain_exactly(merge_request1, merge_request2, merge_request3, merge_request4, merge_request5, merge_request6, merge_request7, merge_request8, merge_request9)
- end
+ context 'filtering by group milestone' do
+ let(:group_milestone) { create(:milestone, group: group) }
- it 'adds wip to scalar params' do
- scalar_params = described_class.scalar_params
+ before do
+ project2.update(namespace: group)
+ merge_request2.update(milestone: group_milestone)
+ merge_request3.update(milestone: group_milestone)
+ end
- expect(scalar_params).to include(:wip, :assignee_id)
- end
+ it 'returns merge requests assigned to that group milestone' do
+ params = { milestone_title: group_milestone.title }
- context 'filtering by group milestone' do
- let!(:group) { create(:group, :public) }
- let(:group_milestone) { create(:milestone, group: group) }
- let!(:group_member) { create(:group_member, group: group, user: user) }
- let(:params) { { milestone_title: group_milestone.title } }
+ merge_requests = described_class.new(user, params).execute
- before do
- project2.update(namespace: group)
- merge_request2.update(milestone: group_milestone)
- merge_request3.update(milestone: group_milestone)
+ expect(merge_requests).to contain_exactly(merge_request2, merge_request3)
+ end
end
- it 'returns issues assigned to that group milestone' do
- merge_requests = described_class.new(user, params).execute
+ context 'filtering by created_at/updated_at' do
+ let(:new_project) { create(:project, forked_from_project: project1) }
- expect(merge_requests).to contain_exactly(merge_request2, merge_request3)
- end
- end
+ let!(:new_merge_request) do
+ create(:merge_request,
+ :simple,
+ author: user,
+ created_at: 1.week.from_now,
+ updated_at: 1.week.from_now,
+ source_project: new_project,
+ target_project: new_project)
+ end
- context 'filtering by created_at/updated_at' do
- let(:new_project) { create(:project, forked_from_project: project1) }
-
- let!(:new_merge_request) do
- create(:merge_request,
- :simple,
- author: user,
- created_at: 1.week.from_now,
- updated_at: 1.week.from_now,
- source_project: new_project,
- target_project: new_project)
- end
+ let!(:old_merge_request) do
+ create(:merge_request,
+ :simple,
+ author: user,
+ source_branch: 'feature_1',
+ created_at: 1.week.ago,
+ updated_at: 1.week.ago,
+ source_project: new_project,
+ target_project: new_project)
+ end
- let!(:old_merge_request) do
- create(:merge_request,
- :simple,
- author: user,
- source_branch: 'feature_1',
- created_at: 1.week.ago,
- updated_at: 1.week.ago,
- source_project: new_project,
- target_project: new_project)
- end
+ before do
+ new_project.add_maintainer(user)
+ end
- before do
- new_project.add_maintainer(user)
- end
+ it 'filters by created_after' do
+ params = { project_id: new_project.id, created_after: new_merge_request.created_at }
- it 'filters by created_after' do
- params = { project_id: new_project.id, created_after: new_merge_request.created_at }
+ merge_requests = described_class.new(user, params).execute
- merge_requests = described_class.new(user, params).execute
+ expect(merge_requests).to contain_exactly(new_merge_request)
+ end
- expect(merge_requests).to contain_exactly(new_merge_request)
- end
+ it 'filters by created_before' do
+ params = { project_id: new_project.id, created_before: old_merge_request.created_at }
- it 'filters by created_before' do
- params = { project_id: new_project.id, created_before: old_merge_request.created_at }
+ merge_requests = described_class.new(user, params).execute
- merge_requests = described_class.new(user, params).execute
+ expect(merge_requests).to contain_exactly(old_merge_request)
+ end
- expect(merge_requests).to contain_exactly(old_merge_request)
- end
+ it 'filters by created_after and created_before' do
+ params = {
+ project_id: new_project.id,
+ created_after: old_merge_request.created_at,
+ created_before: new_merge_request.created_at
+ }
- it 'filters by created_after and created_before' do
- params = {
- project_id: new_project.id,
- created_after: old_merge_request.created_at,
- created_before: new_merge_request.created_at
- }
+ merge_requests = described_class.new(user, params).execute
- merge_requests = described_class.new(user, params).execute
+ expect(merge_requests).to contain_exactly(old_merge_request, new_merge_request)
+ end
+
+ it 'filters by updated_after' do
+ params = { project_id: new_project.id, updated_after: new_merge_request.updated_at }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(new_merge_request)
+ end
+
+ it 'filters by updated_before' do
+ params = { project_id: new_project.id, updated_before: old_merge_request.updated_at }
- expect(merge_requests).to contain_exactly(old_merge_request, new_merge_request)
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(old_merge_request)
+ end
+
+ it 'filters by updated_after and updated_before' do
+ params = {
+ project_id: new_project.id,
+ updated_after: old_merge_request.updated_at,
+ updated_before: new_merge_request.updated_at
+ }
+
+ merge_requests = described_class.new(user, params).execute
+
+ expect(merge_requests).to contain_exactly(old_merge_request, new_merge_request)
+ end
end
+ end
- it 'filters by updated_after' do
- params = { project_id: new_project.id, updated_after: new_merge_request.updated_at }
+ describe '#row_count', :request_store do
+ it 'returns the number of rows for the default state' do
+ finder = described_class.new(user)
- merge_requests = described_class.new(user, params).execute
+ expect(finder.row_count).to eq(3)
+ end
+
+ it 'returns the number of rows for a given state' do
+ finder = described_class.new(user, state: 'closed')
- expect(merge_requests).to contain_exactly(new_merge_request)
+ expect(finder.row_count).to eq(1)
end
+ end
+ end
- it 'filters by updated_before' do
- params = { project_id: new_project.id, updated_before: old_merge_request.updated_at }
+ context 'when projects require different access levels for merge requests' do
+ let(:user) { create(:user) }
- merge_requests = described_class.new(user, params).execute
+ let(:public_project) { create(:project, :public) }
+ let(:internal) { create(:project, :internal) }
+ let(:private_project) { create(:project, :private) }
+ let(:public_with_private_repo) { create(:project, :public, :repository, :repository_private) }
+ let(:internal_with_private_repo) { create(:project, :internal, :repository, :repository_private) }
+
+ let(:merge_requests) { described_class.new(user, {}).execute }
+
+ let!(:mr_public) { create(:merge_request, source_project: public_project) }
+ let!(:mr_private) { create(:merge_request, source_project: private_project) }
+ let!(:mr_internal) { create(:merge_request, source_project: internal) }
+ let!(:mr_private_repo_access) { create(:merge_request, source_project: public_with_private_repo) }
+ let!(:mr_internal_private_repo_access) { create(:merge_request, source_project: internal_with_private_repo) }
- expect(merge_requests).to contain_exactly(old_merge_request)
+ context 'with admin user' do
+ let(:user) { create(:user, :admin) }
+
+ it 'returns all merge requests' do
+ expect(merge_requests).to eq(
+ [mr_internal_private_repo_access, mr_private_repo_access, mr_internal, mr_private, mr_public]
+ )
end
+ end
- it 'filters by updated_after and updated_before' do
- params = {
- project_id: new_project.id,
- updated_after: old_merge_request.updated_at,
- updated_before: new_merge_request.updated_at
- }
+ context 'when project restricts merge requests' do
+ let(:non_member) { create(:user) }
+ let(:project) { create(:project, :repository, :public, :merge_requests_private) }
+ let!(:merge_request) { create(:merge_request, source_project: project) }
- merge_requests = described_class.new(user, params).execute
+ it "returns nothing to to non members" do
+ merge_requests = described_class.new(
+ non_member,
+ project_id: project.id
+ ).execute
- expect(merge_requests).to contain_exactly(old_merge_request, new_merge_request)
+ expect(merge_requests).to be_empty
end
end
- end
- describe '#row_count', :request_store do
- it 'returns the number of rows for the default state' do
- finder = described_class.new(user)
+ context 'with external user' do
+ let(:user) { create(:user, :external) }
- expect(finder.row_count).to eq(7)
+ it 'returns only public merge requests' do
+ expect(merge_requests).to eq([mr_public])
+ end
end
- it 'returns the number of rows for a given state' do
- finder = described_class.new(user, state: 'closed')
+ context 'with authenticated user' do
+ it 'returns public and internal merge requests' do
+ expect(merge_requests).to eq([mr_internal, mr_public])
+ end
- expect(finder.row_count).to eq(1)
+ context 'being added to the private project' do
+ context 'as a guest' do
+ before do
+ private_project.add_guest(user)
+ end
+
+ it 'does not return merge requests from the private project' do
+ expect(merge_requests).to eq([mr_internal, mr_public])
+ end
+ end
+
+ context 'as a developer' do
+ before do
+ private_project.add_developer(user)
+ end
+
+ it 'returns merge requests from the private project' do
+ expect(merge_requests).to eq([mr_internal, mr_private, mr_public])
+ end
+ end
+ end
+
+ context 'being added to the public project with private repo access' do
+ context 'as a guest' do
+ before do
+ public_with_private_repo.add_guest(user)
+ end
+
+ it 'returns merge requests from the project' do
+ expect(merge_requests).to eq([mr_internal, mr_public])
+ end
+ end
+
+ context 'as a reporter' do
+ before do
+ public_with_private_repo.add_reporter(user)
+ end
+
+ it 'returns merge requests from the project' do
+ expect(merge_requests).to eq([mr_private_repo_access, mr_internal, mr_public])
+ end
+ end
+ end
+
+ context 'being added to the internal project with private repo access' do
+ context 'as a guest' do
+ before do
+ internal_with_private_repo.add_guest(user)
+ end
+
+ it 'returns merge requests from the project' do
+ expect(merge_requests).to eq([mr_internal, mr_public])
+ end
+ end
+
+ context 'as a reporter' do
+ before do
+ internal_with_private_repo.add_reporter(user)
+ end
+
+ it 'returns merge requests from the project' do
+ expect(merge_requests).to eq([mr_internal_private_repo_access, mr_internal, mr_public])
+ end
+ end
+ end
end
end
end
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index 134fb5f2c04..93287f3e9b8 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -2,7 +2,6 @@ require 'spec_helper'
describe SnippetsFinder do
include Gitlab::Allowable
- using RSpec::Parameterized::TableSyntax
describe '#initialize' do
it 'raises ArgumentError when a project and author are given' do
@@ -14,174 +13,142 @@ describe SnippetsFinder do
end
end
- context 'filter by scope' do
- let(:user) { create :user }
- let!(:snippet1) { create(:personal_snippet, :private, author: user) }
- let!(:snippet2) { create(:personal_snippet, :internal, author: user) }
- let!(:snippet3) { create(:personal_snippet, :public, author: user) }
-
- it "returns all snippets for 'all' scope" do
- snippets = described_class.new(user, scope: :all).execute
-
- expect(snippets).to include(snippet1, snippet2, snippet3)
- end
-
- it "returns all snippets for 'are_private' scope" do
- snippets = described_class.new(user, scope: :are_private).execute
+ describe '#execute' do
+ set(:user) { create(:user) }
+ set(:private_personal_snippet) { create(:personal_snippet, :private, author: user) }
+ set(:internal_personal_snippet) { create(:personal_snippet, :internal, author: user) }
+ set(:public_personal_snippet) { create(:personal_snippet, :public, author: user) }
- expect(snippets).to include(snippet1)
- expect(snippets).not_to include(snippet2, snippet3)
- end
+ context 'filter by scope' do
+ it "returns all snippets for 'all' scope" do
+ snippets = described_class.new(user, scope: :all).execute
- it "returns all snippets for 'are_internal' scope" do
- snippets = described_class.new(user, scope: :are_internal).execute
+ expect(snippets).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
+ end
- expect(snippets).to include(snippet2)
- expect(snippets).not_to include(snippet1, snippet3)
- end
+ it "returns all snippets for 'are_private' scope" do
+ snippets = described_class.new(user, scope: :are_private).execute
- it "returns all snippets for 'are_private' scope" do
- snippets = described_class.new(user, scope: :are_public).execute
+ expect(snippets).to contain_exactly(private_personal_snippet)
+ end
- expect(snippets).to include(snippet3)
- expect(snippets).not_to include(snippet1, snippet2)
- end
- end
+ it "returns all snippets for 'are_internal' scope" do
+ snippets = described_class.new(user, scope: :are_internal).execute
- context 'filter by author' do
- let(:user) { create :user }
- let(:user1) { create :user }
- let!(:snippet1) { create(:personal_snippet, :private, author: user) }
- let!(:snippet2) { create(:personal_snippet, :internal, author: user) }
- let!(:snippet3) { create(:personal_snippet, :public, author: user) }
+ expect(snippets).to contain_exactly(internal_personal_snippet)
+ end
- it "returns all public and internal snippets" do
- snippets = described_class.new(user1, author: user).execute
+ it "returns all snippets for 'are_private' scope" do
+ snippets = described_class.new(user, scope: :are_public).execute
- expect(snippets).to include(snippet2, snippet3)
- expect(snippets).not_to include(snippet1)
+ expect(snippets).to contain_exactly(public_personal_snippet)
+ end
end
- it "returns internal snippets" do
- snippets = described_class.new(user, author: user, scope: :are_internal).execute
+ context 'filter by author' do
+ it 'returns all public and internal snippets' do
+ snippets = described_class.new(create(:user), author: user).execute
- expect(snippets).to include(snippet2)
- expect(snippets).not_to include(snippet1, snippet3)
- end
+ expect(snippets).to contain_exactly(internal_personal_snippet, public_personal_snippet)
+ end
- it "returns private snippets" do
- snippets = described_class.new(user, author: user, scope: :are_private).execute
+ it 'returns internal snippets' do
+ snippets = described_class.new(user, author: user, scope: :are_internal).execute
- expect(snippets).to include(snippet1)
- expect(snippets).not_to include(snippet2, snippet3)
- end
+ expect(snippets).to contain_exactly(internal_personal_snippet)
+ end
- it "returns public snippets" do
- snippets = described_class.new(user, author: user, scope: :are_public).execute
+ it 'returns private snippets' do
+ snippets = described_class.new(user, author: user, scope: :are_private).execute
- expect(snippets).to include(snippet3)
- expect(snippets).not_to include(snippet1, snippet2)
- end
+ expect(snippets).to contain_exactly(private_personal_snippet)
+ end
- it "returns all snippets" do
- snippets = described_class.new(user, author: user).execute
+ it 'returns public snippets' do
+ snippets = described_class.new(user, author: user, scope: :are_public).execute
- expect(snippets).to include(snippet1, snippet2, snippet3)
- end
+ expect(snippets).to contain_exactly(public_personal_snippet)
+ end
- it "returns only public snippets if unauthenticated user" do
- snippets = described_class.new(nil, author: user).execute
+ it 'returns all snippets' do
+ snippets = described_class.new(user, author: user).execute
- expect(snippets).to include(snippet3)
- expect(snippets).not_to include(snippet2, snippet1)
- end
+ expect(snippets).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
+ end
- it 'returns all snippets for an admin' do
- admin = create(:user, :admin)
- snippets = described_class.new(admin, author: user).execute
+ it 'returns only public snippets if unauthenticated user' do
+ snippets = described_class.new(nil, author: user).execute
- expect(snippets).to include(snippet1, snippet2, snippet3)
- end
- end
+ expect(snippets).to contain_exactly(public_personal_snippet)
+ end
- context 'filter by project' do
- let(:user) { create :user }
- let(:group) { create :group, :public }
- let(:project1) { create(:project, :public, group: group) }
+ it 'returns all snippets for an admin' do
+ admin = create(:user, :admin)
+ snippets = described_class.new(admin, author: user).execute
- before do
- @snippet1 = create(:project_snippet, :private, project: project1)
- @snippet2 = create(:project_snippet, :internal, project: project1)
- @snippet3 = create(:project_snippet, :public, project: project1)
+ expect(snippets).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
+ end
end
- it "returns public snippets for unauthorized user" do
- snippets = described_class.new(nil, project: project1).execute
+ context 'project snippets' do
+ let(:group) { create(:group, :public) }
+ let(:project) { create(:project, :public, group: group) }
+ let!(:private_project_snippet) { create(:project_snippet, :private, project: project) }
+ let!(:internal_project_snippet) { create(:project_snippet, :internal, project: project) }
+ let!(:public_project_snippet) { create(:project_snippet, :public, project: project) }
- expect(snippets).to include(@snippet3)
- expect(snippets).not_to include(@snippet1, @snippet2)
- end
+ it 'returns public personal and project snippets for unauthorized user' do
+ snippets = described_class.new(nil, project: project).execute
- it "returns public and internal snippets for non project members" do
- snippets = described_class.new(user, project: project1).execute
+ expect(snippets).to contain_exactly(public_project_snippet)
+ end
- expect(snippets).to include(@snippet2, @snippet3)
- expect(snippets).not_to include(@snippet1)
- end
+ it 'returns public and internal snippets for non project members' do
+ snippets = described_class.new(user, project: project).execute
- it "returns public snippets for non project members" do
- snippets = described_class.new(user, project: project1, scope: :are_public).execute
+ expect(snippets).to contain_exactly(internal_project_snippet, public_project_snippet)
+ end
- expect(snippets).to include(@snippet3)
- expect(snippets).not_to include(@snippet1, @snippet2)
- end
+ it 'returns public snippets for non project members' do
+ snippets = described_class.new(user, project: project, scope: :are_public).execute
- it "returns internal snippets for non project members" do
- snippets = described_class.new(user, project: project1, scope: :are_internal).execute
+ expect(snippets).to contain_exactly(public_project_snippet)
+ end
- expect(snippets).to include(@snippet2)
- expect(snippets).not_to include(@snippet1, @snippet3)
- end
+ it 'returns internal snippets for non project members' do
+ snippets = described_class.new(user, project: project, scope: :are_internal).execute
- it "does not return private snippets for non project members" do
- snippets = described_class.new(user, project: project1, scope: :are_private).execute
+ expect(snippets).to contain_exactly(internal_project_snippet)
+ end
- expect(snippets).not_to include(@snippet1, @snippet2, @snippet3)
- end
+ it 'does not return private snippets for non project members' do
+ snippets = described_class.new(user, project: project, scope: :are_private).execute
- it "returns all snippets for project members" do
- project1.add_developer(user)
+ expect(snippets).to be_empty
+ end
- snippets = described_class.new(user, project: project1).execute
+ it 'returns all snippets for project members' do
+ project.add_developer(user)
- expect(snippets).to include(@snippet1, @snippet2, @snippet3)
- end
+ snippets = described_class.new(user, project: project).execute
- it "returns private snippets for project members" do
- project1.add_developer(user)
+ expect(snippets).to contain_exactly(private_project_snippet, internal_project_snippet, public_project_snippet)
+ end
- snippets = described_class.new(user, project: project1, scope: :are_private).execute
+ it 'returns private snippets for project members' do
+ project.add_developer(user)
- expect(snippets).to include(@snippet1)
- end
+ snippets = described_class.new(user, project: project, scope: :are_private).execute
- it 'returns all snippets for an admin' do
- admin = create(:user, :admin)
- snippets = described_class.new(admin, project: project1).execute
+ expect(snippets).to contain_exactly(private_project_snippet)
+ end
- expect(snippets).to include(@snippet1, @snippet2, @snippet3)
- end
- end
+ it 'returns all snippets for an admin' do
+ admin = create(:user, :admin)
+ snippets = described_class.new(admin, project: project).execute
- describe '#execute' do
- let(:project) { create(:project, :public) }
- let!(:project_snippet) { create(:project_snippet, :public, project: project) }
- let!(:personal_snippet) { create(:personal_snippet, :public) }
- let(:user) { create(:user) }
- subject(:finder) { described_class.new(user) }
-
- it 'returns project- and personal snippets' do
- expect(finder.execute).to contain_exactly(project_snippet, personal_snippet)
+ expect(snippets).to contain_exactly(private_project_snippet, internal_project_snippet, public_project_snippet)
+ end
end
context 'when the user cannot read cross project' do
@@ -191,7 +158,7 @@ describe SnippetsFinder do
end
it 'returns only personal snippets when the user cannot read cross project' do
- expect(finder.execute).to contain_exactly(personal_snippet)
+ expect(described_class.new(user).execute).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
end
end
end
diff --git a/spec/finders/users_finder_spec.rb b/spec/finders/users_finder_spec.rb
index fecf97dc641..d71d3c99272 100644
--- a/spec/finders/users_finder_spec.rb
+++ b/spec/finders/users_finder_spec.rb
@@ -2,10 +2,7 @@ require 'spec_helper'
describe UsersFinder do
describe '#execute' do
- let!(:user1) { create(:user, username: 'johndoe') }
- let!(:user2) { create(:user, :blocked, username: 'notsorandom') }
- let!(:external_user) { create(:user, :external) }
- let!(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
+ include_context 'UsersFinder#execute filter by project context'
context 'with a normal user' do
let(:user) { create(:user) }
@@ -13,43 +10,43 @@ describe UsersFinder do
it 'returns all users' do
users = described_class.new(user).execute
- expect(users).to contain_exactly(user, user1, user2, omniauth_user)
+ expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user)
end
it 'filters by username' do
users = described_class.new(user, username: 'johndoe').execute
- expect(users).to contain_exactly(user1)
+ expect(users).to contain_exactly(normal_user)
end
it 'filters by username (case insensitive)' do
users = described_class.new(user, username: 'joHNdoE').execute
- expect(users).to contain_exactly(user1)
+ expect(users).to contain_exactly(normal_user)
end
it 'filters by search' do
users = described_class.new(user, search: 'orando').execute
- expect(users).to contain_exactly(user2)
+ expect(users).to contain_exactly(blocked_user)
end
it 'filters by blocked users' do
users = described_class.new(user, blocked: true).execute
- expect(users).to contain_exactly(user2)
+ expect(users).to contain_exactly(blocked_user)
end
it 'filters by active users' do
users = described_class.new(user, active: true).execute
- expect(users).to contain_exactly(user, user1, omniauth_user)
+ expect(users).to contain_exactly(user, normal_user, omniauth_user)
end
it 'returns no external users' do
users = described_class.new(user, external: true).execute
- expect(users).to contain_exactly(user, user1, user2, omniauth_user)
+ expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user)
end
it 'filters by created_at' do
@@ -69,7 +66,7 @@ describe UsersFinder do
custom_attributes: { foo: 'bar' }
).execute
- expect(users).to contain_exactly(user, user1, user2, omniauth_user)
+ expect(users).to contain_exactly(user, normal_user, blocked_user, omniauth_user)
end
end
@@ -85,20 +82,20 @@ describe UsersFinder do
it 'returns all users' do
users = described_class.new(admin).execute
- expect(users).to contain_exactly(admin, user1, user2, external_user, omniauth_user)
+ expect(users).to contain_exactly(admin, normal_user, blocked_user, external_user, omniauth_user)
end
it 'filters by custom attributes' do
- create :user_custom_attribute, user: user1, key: 'foo', value: 'foo'
- create :user_custom_attribute, user: user1, key: 'bar', value: 'bar'
- create :user_custom_attribute, user: user2, key: 'foo', value: 'foo'
+ create :user_custom_attribute, user: normal_user, key: 'foo', value: 'foo'
+ create :user_custom_attribute, user: normal_user, key: 'bar', value: 'bar'
+ create :user_custom_attribute, user: blocked_user, key: 'foo', value: 'foo'
users = described_class.new(
admin,
custom_attributes: { foo: 'foo', bar: 'bar' }
).execute
- expect(users).to contain_exactly(user1)
+ expect(users).to contain_exactly(normal_user)
end
end
end
diff --git a/spec/fixtures/api/schemas/board.json b/spec/fixtures/api/schemas/board.json
index 03aca4a3cc0..7c146647948 100644
--- a/spec/fixtures/api/schemas/board.json
+++ b/spec/fixtures/api/schemas/board.json
@@ -6,6 +6,5 @@
"properties" : {
"id": { "type": "integer" },
"name": { "type": "string" }
- },
- "additionalProperties": false
+ }
}
diff --git a/spec/fixtures/api/schemas/cluster_status.json b/spec/fixtures/api/schemas/cluster_status.json
index 5ebc09a96dc..9da07a0b253 100644
--- a/spec/fixtures/api/schemas/cluster_status.json
+++ b/spec/fixtures/api/schemas/cluster_status.json
@@ -33,6 +33,7 @@
"version": { "type": "string" },
"status_reason": { "type": ["string", "null"] },
"external_ip": { "type": ["string", "null"] },
+ "external_hostname": { "type": ["string", "null"] },
"hostname": { "type": ["string", "null"] },
"email": { "type": ["string", "null"] },
"update_available": { "type": ["boolean", "null"] }
diff --git a/spec/fixtures/api/schemas/entities/issue.json b/spec/fixtures/api/schemas/entities/issue.json
index 00abe73ec8a..162fb9c8108 100644
--- a/spec/fixtures/api/schemas/entities/issue.json
+++ b/spec/fixtures/api/schemas/entities/issue.json
@@ -38,6 +38,5 @@
"items": { "$ref": "label.json" }
},
"assignees": { "type": ["array", "null"] }
- },
- "additionalProperties": false
+ }
}
diff --git a/spec/fixtures/api/schemas/entities/issue_boards.json b/spec/fixtures/api/schemas/entities/issue_boards.json
index 0ac1d9468c8..742f7be5485 100644
--- a/spec/fixtures/api/schemas/entities/issue_boards.json
+++ b/spec/fixtures/api/schemas/entities/issue_boards.json
@@ -10,6 +10,5 @@
"items": { "$ref": "issue_board.json" }
},
"size": { "type": "integer" }
- },
- "additionalProperties": false
+ }
}
diff --git a/spec/fixtures/api/schemas/entities/merge_request_widget.json b/spec/fixtures/api/schemas/entities/merge_request_widget.json
index 67c209f3fc3..6b1cd60c25d 100644
--- a/spec/fixtures/api/schemas/entities/merge_request_widget.json
+++ b/spec/fixtures/api/schemas/entities/merge_request_widget.json
@@ -52,6 +52,7 @@
"mergeable_discussions_state": { "type": "boolean" },
"conflicts_can_be_resolved_in_ui": { "type": "boolean" },
"branch_missing": { "type": "boolean" },
+ "commits_count": { "type": ["integer", "null"] },
"has_conflicts": { "type": "boolean" },
"can_be_merged": { "type": "boolean" },
"mergeable": { "type": "boolean" },
@@ -125,6 +126,5 @@
"can_receive_suggestion": { "type": "boolean" },
"source_branch_protected": { "type": "boolean" },
"conflicts_docs_path": { "type": ["string", "null"] }
- },
- "additionalProperties": false
+ }
}
diff --git a/spec/fixtures/api/schemas/environment.json b/spec/fixtures/api/schemas/environment.json
index f1d33e3ce7b..9a10ab18c30 100644
--- a/spec/fixtures/api/schemas/environment.json
+++ b/spec/fixtures/api/schemas/environment.json
@@ -20,6 +20,7 @@
"state": { "type": "string" },
"external_url": { "$ref": "types/nullable_string.json" },
"environment_type": { "$ref": "types/nullable_string.json" },
+ "name_without_type": { "type": "string" },
"has_stop_action": { "type": "boolean" },
"environment_path": { "type": "string" },
"stop_path": { "type": "string" },
diff --git a/spec/fixtures/api/schemas/issue.json b/spec/fixtures/api/schemas/issue.json
index a83ec55cede..77de9ae4f9f 100644
--- a/spec/fixtures/api/schemas/issue.json
+++ b/spec/fixtures/api/schemas/issue.json
@@ -28,7 +28,7 @@
"items": { "$ref": "entities/label.json" }
},
"assignee": {
- "id": { "type": "integet" },
+ "id": { "type": "integer" },
"name": { "type": "string" },
"username": { "type": "string" },
"avatar_url": { "type": "uri" }
@@ -52,6 +52,5 @@
}
},
"subscribed": { "type": ["boolean", "null"] }
- },
- "additionalProperties": false
+ }
}
diff --git a/spec/fixtures/api/schemas/issues.json b/spec/fixtures/api/schemas/issues.json
index 70771b21c96..fbcd9eea389 100644
--- a/spec/fixtures/api/schemas/issues.json
+++ b/spec/fixtures/api/schemas/issues.json
@@ -10,6 +10,5 @@
"items": { "$ref": "issue.json" }
},
"size": { "type": "integer" }
- },
- "additionalProperties": false
+ }
}
diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_request.json b/spec/fixtures/api/schemas/public_api/v4/merge_request.json
new file mode 100644
index 00000000000..cd50be00418
--- /dev/null
+++ b/spec/fixtures/api/schemas/public_api/v4/merge_request.json
@@ -0,0 +1,124 @@
+{
+ "type": "object",
+ "properties" : {
+ "properties" : {
+ "id": { "type": "integer" },
+ "iid": { "type": "integer" },
+ "project_id": { "type": "integer" },
+ "title": { "type": "string" },
+ "description": { "type": ["string", "null"] },
+ "state": { "type": "string" },
+ "merged_by": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
+ },
+ "additionalProperties": false
+ },
+ "merged_at": { "type": ["date", "null"] },
+ "closed_by": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
+ },
+ "additionalProperties": false
+ },
+ "closed_at": { "type": ["date", "null"] },
+ "created_at": { "type": "date" },
+ "updated_at": { "type": "date" },
+ "target_branch": { "type": "string" },
+ "source_branch": { "type": "string" },
+ "upvotes": { "type": "integer" },
+ "downvotes": { "type": "integer" },
+ "author": {
+ "type": "object",
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
+ },
+ "additionalProperties": false
+ },
+ "assignee": {
+ "type": ["object", "null"],
+ "properties": {
+ "name": { "type": "string" },
+ "username": { "type": "string" },
+ "id": { "type": "integer" },
+ "state": { "type": "string" },
+ "avatar_url": { "type": "uri" },
+ "web_url": { "type": "uri" }
+ },
+ "additionalProperties": false
+ },
+ "source_project_id": { "type": "integer" },
+ "target_project_id": { "type": "integer" },
+ "labels": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "work_in_progress": { "type": "boolean" },
+ "milestone": {
+ "type": ["object", "null"],
+ "properties": {
+ "id": { "type": "integer" },
+ "iid": { "type": "integer" },
+ "project_id": { "type": ["integer", "null"] },
+ "group_id": { "type": ["integer", "null"] },
+ "title": { "type": "string" },
+ "description": { "type": ["string", "null"] },
+ "state": { "type": "string" },
+ "created_at": { "type": "date" },
+ "updated_at": { "type": "date" },
+ "due_date": { "type": "date" },
+ "start_date": { "type": "date" }
+ },
+ "additionalProperties": false
+ },
+ "merge_when_pipeline_succeeds": { "type": "boolean" },
+ "merge_status": { "type": "string" },
+ "sha": { "type": "string" },
+ "merge_commit_sha": { "type": ["string", "null"] },
+ "user_notes_count": { "type": "integer" },
+ "changes_count": { "type": "string" },
+ "should_remove_source_branch": { "type": ["boolean", "null"] },
+ "force_remove_source_branch": { "type": ["boolean", "null"] },
+ "discussion_locked": { "type": ["boolean", "null"] },
+ "web_url": { "type": "uri" },
+ "squash": { "type": "boolean" },
+ "time_stats": {
+ "time_estimate": { "type": "integer" },
+ "total_time_spent": { "type": "integer" },
+ "human_time_estimate": { "type": ["string", "null"] },
+ "human_total_time_spent": { "type": ["string", "null"] }
+ },
+ "allow_collaboration": { "type": ["boolean", "null"] },
+ "allow_maintainer_to_push": { "type": ["boolean", "null"] }
+ },
+ "required": [
+ "id", "iid", "project_id", "title", "description",
+ "state", "created_at", "updated_at", "target_branch",
+ "source_branch", "upvotes", "downvotes", "author",
+ "assignee", "source_project_id", "target_project_id",
+ "labels", "work_in_progress", "milestone", "merge_when_pipeline_succeeds",
+ "merge_status", "sha", "merge_commit_sha", "user_notes_count",
+ "should_remove_source_branch", "force_remove_source_branch",
+ "web_url", "squash"
+ ]
+ }
+}
diff --git a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
index 6df27bf32b9..b35c83950e8 100644
--- a/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
+++ b/spec/fixtures/api/schemas/public_api/v4/merge_requests.json
@@ -1,126 +1,6 @@
{
"type": "array",
"items": {
- "type": "object",
- "properties" : {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": "integer" },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "merged_by": {
- "type": ["object", "null"],
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
- },
- "merged_at": { "type": ["date", "null"] },
- "closed_by": {
- "type": ["object", "null"],
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
- },
- "closed_at": { "type": ["date", "null"] },
- "created_at": { "type": "date" },
- "updated_at": { "type": "date" },
- "target_branch": { "type": "string" },
- "source_branch": { "type": "string" },
- "upvotes": { "type": "integer" },
- "downvotes": { "type": "integer" },
- "author": {
- "type": "object",
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
- },
- "assignee": {
- "type": ["object", "null"],
- "properties": {
- "name": { "type": "string" },
- "username": { "type": "string" },
- "id": { "type": "integer" },
- "state": { "type": "string" },
- "avatar_url": { "type": "uri" },
- "web_url": { "type": "uri" }
- },
- "additionalProperties": false
- },
- "source_project_id": { "type": "integer" },
- "target_project_id": { "type": "integer" },
- "labels": {
- "type": "array",
- "items": {
- "type": "string"
- }
- },
- "work_in_progress": { "type": "boolean" },
- "milestone": {
- "type": ["object", "null"],
- "properties": {
- "id": { "type": "integer" },
- "iid": { "type": "integer" },
- "project_id": { "type": ["integer", "null"] },
- "group_id": { "type": ["integer", "null"] },
- "title": { "type": "string" },
- "description": { "type": ["string", "null"] },
- "state": { "type": "string" },
- "created_at": { "type": "date" },
- "updated_at": { "type": "date" },
- "due_date": { "type": "date" },
- "start_date": { "type": "date" }
- },
- "additionalProperties": false
- },
- "merge_when_pipeline_succeeds": { "type": "boolean" },
- "merge_status": { "type": "string" },
- "sha": { "type": "string" },
- "merge_commit_sha": { "type": ["string", "null"] },
- "user_notes_count": { "type": "integer" },
- "changes_count": { "type": "string" },
- "should_remove_source_branch": { "type": ["boolean", "null"] },
- "force_remove_source_branch": { "type": ["boolean", "null"] },
- "discussion_locked": { "type": ["boolean", "null"] },
- "web_url": { "type": "uri" },
- "squash": { "type": "boolean" },
- "time_stats": {
- "time_estimate": { "type": "integer" },
- "total_time_spent": { "type": "integer" },
- "human_time_estimate": { "type": ["string", "null"] },
- "human_total_time_spent": { "type": ["string", "null"] }
- },
- "allow_collaboration": { "type": ["boolean", "null"] },
- "allow_maintainer_to_push": { "type": ["boolean", "null"] }
- },
- "required": [
- "id", "iid", "project_id", "title", "description",
- "state", "created_at", "updated_at", "target_branch",
- "source_branch", "upvotes", "downvotes", "author",
- "assignee", "source_project_id", "target_project_id",
- "labels", "work_in_progress", "milestone", "merge_when_pipeline_succeeds",
- "merge_status", "sha", "merge_commit_sha", "user_notes_count",
- "should_remove_source_branch", "force_remove_source_branch",
- "web_url", "squash"
- ],
- "additionalProperties": false
+ "$ref": "./merge_request.json"
}
}
diff --git a/spec/fixtures/security-reports/remediations/gl-dependency-scanning-report.json b/spec/fixtures/security-reports/remediations/gl-dependency-scanning-report.json
new file mode 100644
index 00000000000..c96e831b027
--- /dev/null
+++ b/spec/fixtures/security-reports/remediations/gl-dependency-scanning-report.json
@@ -0,0 +1,104 @@
+{
+ "version": "2.0",
+ "vulnerabilities": [
+ {
+ "category": "dependency_scanning",
+ "name": "Regular Expression Denial of Service",
+ "message": "Regular Expression Denial of Service in debug",
+ "description": "The debug module is vulnerable to regular expression denial of service when untrusted user input is passed into the `o` formatter. It takes around 50k characters to block for 2 seconds making this a low severity issue.",
+ "cve": "yarn.lock:debug:gemnasium:37283ed4-0380-40d7-ada7-2d994afcc62a",
+ "severity": "Unknown",
+ "solution": "Upgrade to latest versions.",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "yarn.lock",
+ "dependency": {
+ "package": {
+ "name": "debug"
+ },
+ "version": "1.0.5"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-37283ed4-0380-40d7-ada7-2d994afcc62a",
+ "value": "37283ed4-0380-40d7-ada7-2d994afcc62a",
+ "url": "https://deps.sec.gitlab.com/packages/npm/debug/versions/1.0.5/advisories"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://nodesecurity.io/advisories/534"
+ },
+ {
+ "url": "https://github.com/visionmedia/debug/issues/501"
+ },
+ {
+ "url": "https://github.com/visionmedia/debug/pull/504"
+ }
+ ]
+ },
+ {
+ "category": "dependency_scanning",
+ "name": "Authentication bypass via incorrect DOM traversal and canonicalization",
+ "message": "Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js",
+ "description": "Some XML DOM traversal and canonicalization APIs may be inconsistent in handling of comments within XML nodes. Incorrect use of these APIs by some SAML libraries results in incorrect parsing of the inner text of XML nodes such that any inner text after the comment is lost prior to cryptographically signing the SAML message. Text after the comment therefore has no impact on the signature on the SAML message.\r\n\r\nA remote attacker can modify SAML content for a SAML service provider without invalidating the cryptographic signature, which may allow attackers to bypass primary authentication for the affected SAML service provider.",
+ "cve": "yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98",
+ "severity": "Unknown",
+ "solution": "Upgrade to fixed version.\r\n",
+ "scanner": {
+ "id": "gemnasium",
+ "name": "Gemnasium"
+ },
+ "location": {
+ "file": "yarn.lock",
+ "dependency": {
+ "package": {
+ "name": "saml2-js"
+ },
+ "version": "1.5.0"
+ }
+ },
+ "identifiers": [
+ {
+ "type": "gemnasium",
+ "name": "Gemnasium-9952e574-7b5b-46fa-a270-aeb694198a98",
+ "value": "9952e574-7b5b-46fa-a270-aeb694198a98",
+ "url": "https://deps.sec.gitlab.com/packages/npm/saml2-js/versions/1.5.0/advisories"
+ },
+ {
+ "type": "cve",
+ "name": "CVE-2017-11429",
+ "value": "CVE-2017-11429",
+ "url": "https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-11429"
+ }
+ ],
+ "links": [
+ {
+ "url": "https://github.com/Clever/saml2/commit/3546cb61fd541f219abda364c5b919633609ef3d#diff-af730f9f738de1c9ad87596df3f6de84R279"
+ },
+ {
+ "url": "https://github.com/Clever/saml2/issues/127"
+ },
+ {
+ "url": "https://www.kb.cert.org/vuls/id/475445"
+ }
+ ]
+ }
+ ],
+ "remediations": [
+ {
+ "fixes": [
+ {
+ "cve": "yarn.lock:saml2-js:gemnasium:9952e574-7b5b-46fa-a270-aeb694198a98"
+ }
+ ],
+ "summary": "Upgrade saml2-js",
+ "diff": "ZGlmZiAtLWdpdCBhL3lhcm4ubG9jayBiL3lhcm4ubG9jawppbmRleCAwZWNjOTJmLi43ZmE0NTU0IDEwMDY0NAotLS0gYS95YXJuLmxvY2sKKysrIGIveWFybi5sb2NrCkBAIC0yLDEwMyArMiwxMjQgQEAKICMgeWFybiBsb2NrZmlsZSB2MQogCiAKLWFzeW5jQH4wLjIuNzoKLSAgdmVyc2lvbiAiMC4yLjEwIgotICByZXNvbHZlZCAiaHR0cDovL3JlZ2lzdHJ5Lm5wbWpzLm9yZy9hc3luYy8tL2FzeW5jLTAuMi4xMC50Z3ojYjZiYmUwYjA2NzRiOWQ3MTk3MDhjYTM4ZGU4YzIzN2NiNTI2YzNkMSIKLQotYXN5bmNAfjEuNS4yOgotICB2ZXJzaW9uICIxLjUuMiIKLSAgcmVzb2x2ZWQgImh0dHA6Ly9yZWdpc3RyeS5ucG1qcy5vcmcvYXN5bmMvLS9hc3luYy0xLjUuMi50Z3ojZWM2YTYxYWU1NjQ4MGMwYzNjYjI0MWM5NTYxOGUyMDg5MmY5NjcyYSIKK2FzeW5jQF4yLjEuNSwgYXN5bmNAXjIuNS4wOgorICB2ZXJzaW9uICIyLjYuMSIKKyAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20vYXN5bmMvLS9hc3luYy0yLjYuMS50Z3ojYjI0NWEyM2NhNzE5MzAwNDRlYzUzZmE0NmFhMDBhM2U4N2M2YTYxMCIKKyAgaW50ZWdyaXR5IHNoYTUxMi1mTkVpTDIrQVp0NkFsQXcvMjlDcjBVRGU0c1JBSENwRUhoNTRXTXorQmI3UWZOY0Z3NGgzbG9vZnlKcExlUXM0WXg3eXVxdS8yZExnTTVoS09zNkhsUT09CisgIGRlcGVuZGVuY2llczoKKyAgICBsb2Rhc2ggIl40LjE3LjEwIgogCi1kZWJ1Z0BeMS4wLjQ6Ci0gIHZlcnNpb24gIjEuMC41IgotICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS9kZWJ1Zy8tL2RlYnVnLTEuMC41LnRneiNmNzI0MTIxNzQzMGY5OWRlYzRjMmI0NzNlYWI5MjIyOGU4NzRjMmFjIgorZGVidWdAXjIuNi4wOgorICB2ZXJzaW9uICIyLjYuOSIKKyAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20vZGVidWcvLS9kZWJ1Zy0yLjYuOS50Z3ojNWQxMjg1MTVkZjEzNGZmMzI3ZTkwYTRjOTNmNGUwNzdhNTM2MzQxZiIKKyAgaW50ZWdyaXR5IHNoYTUxMi1iQzdFbHJkSmFKblBiQVArMUVvdFl2cVpzYjNlY2w1d2k2QmZpNkJKVFVjTm93cDZjdnNwZzBqWHpuUlRLRGptL0U3QWRnRkJWZUFQVk1OY0tHc0hNQT09CiAgIGRlcGVuZGVuY2llczoKICAgICBtcyAiMi4wLjAiCiAKLWVqc0B+MC44LjM6Ci0gIHZlcnNpb24gIjAuOC44IgotICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS9lanMvLS9lanMtMC44LjgudGd6I2ZmZGM1NmRjYzM1ZDAyOTI2ZGQ1MGFkMTM0MzliYmM1NDA2MWQ1OTgiCitlanNAXjIuNS42OgorICB2ZXJzaW9uICIyLjYuMSIKKyAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20vZWpzLy0vZWpzLTIuNi4xLnRneiM0OThlYzBkNDk1NjU1YWJjNmYyM2NkNjE4NjhkOTI2NDY0MDcxYWEwIgorICBpbnRlZ3JpdHkgc2hhNTEyLTB4eTRBL3R3ZnJSQ25raGZrOEVyRGk1RHFkQXNBcWVHeGh0NHhrQ1Vyc3ZoaGJRTnM3RSs0alYwQ043K05LSVkwYUhFNzIrWHZxdEJJWHpEMzFaYlhRPT0KKworbG9kYXNoLW5vZGVAfjIuNC4xOgorICB2ZXJzaW9uICIyLjQuMSIKKyAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20vbG9kYXNoLW5vZGUvLS9sb2Rhc2gtbm9kZS0yLjQuMS50Z3ojZWE4MmY3YjEwMGM3MzNkMWE0MmFmNzY4MDFlNTA2MTA1ZTJhODBlYyIKKyAgaW50ZWdyaXR5IHNoYTEtNm9MM3NRREhNOUdrS3Zkb0FlVUdFRjRxZ093PQorCitsb2Rhc2hAXjQuMTcuMTA6CisgIHZlcnNpb24gIjQuMTcuMTEiCisgIHJlc29sdmVkICJodHRwczovL3JlZ2lzdHJ5Lnlhcm5wa2cuY29tL2xvZGFzaC8tL2xvZGFzaC00LjE3LjExLnRneiNiMzllYTYyMjllZjYwN2VjZDg5ZTJjOGRmMTI1MzY4OTFjYWM5YjhkIgorICBpbnRlZ3JpdHkgc2hhNTEyLWNRS2g4aWdvNVFVaFo3bGczOERZV0F4TXZqU0FLRzBBOHdHU1ZpbVAwN1NJVUVLMlVPK2FyU1JLYlJaV3RlbE10TjVWMEhrd2g1cnlPdG8vU3NoWUlnPT0KIAogbXNAMi4wLjA6CiAgIHZlcnNpb24gIjIuMC4wIgogICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS9tcy8tL21zLTIuMC4wLnRneiM1NjA4YWVhZGZjMDBiZTZjMjkwMWRmNWY5ODYxNzg4ZGUwZDU5N2M4IgorICBpbnRlZ3JpdHkgc2hhMS1WZ2l1cmZ3QXZtd3BBZDlmbUdGNGplRFZsOGc9CiAKLW5vZGUtZm9yZ2VAMC4yLjI0OgotICB2ZXJzaW9uICIwLjIuMjQiCi0gIHJlc29sdmVkICJodHRwczovL3JlZ2lzdHJ5Lnlhcm5wa2cuY29tL25vZGUtZm9yZ2UvLS9ub2RlLWZvcmdlLTAuMi4yNC50Z3ojZmE2Zjg0NmY0MmZhOTNmNjNhMGEzMGM5ZmJmZjdiNGUxMzBlMDg1OCIKK25vZGUtZm9yZ2VAXjAuNy4wOgorICB2ZXJzaW9uICIwLjcuNiIKKyAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20vbm9kZS1mb3JnZS8tL25vZGUtZm9yZ2UtMC43LjYudGd6I2ZkZjNiNDE4YWVlMWY5NGYwZWY2NDJjZDYzNDg2Yzc3Y2E5NzI0YWMiCisgIGludGVncml0eSBzaGE1MTItc29sMzBMVXB6MWpRRkJqT0t3Ymp4aWppRTNiNnBqZDc0WXdmRDBmSk9LUGpGK2ZPTktiMllnOHJZZ1M2K2JLNlZEbCsvd2ZyNElZcEM3akR6TFVJZnc9PQogCiBzYW1sMi1qc0BeMS41LjA6Ci0gIHZlcnNpb24gIjEuNS4wIgotICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS9zYW1sMi1qcy8tL3NhbWwyLWpzLTEuNS4wLnRneiNjMGQyMjY4YTE3OWU3MzI5ZDI5ZWIyNWFhODJkZjU1MDM3NzRiMGQ5IgorICB2ZXJzaW9uICIxLjEyLjQiCisgIHJlc29sdmVkICJodHRwczovL3JlZ2lzdHJ5Lnlhcm5wa2cuY29tL3NhbWwyLWpzLy0vc2FtbDItanMtMS4xMi40LnRneiNjMjg4ZjIwYmRhNmQyYjkxMDczYjE2Yzk0ZWE3MmYyMjM0OWFjM2IzIgorICBpbnRlZ3JpdHkgc2hhMS13b2p5QzlwdEs1RUhPeGJKVHFjdklqU2F3N009CiAgIGRlcGVuZGVuY2llczoKLSAgICBhc3luYyAifjEuNS4yIgotICAgIGRlYnVnICJeMS4wLjQiCi0gICAgdW5kZXJzY29yZSAifjEuNi4wIgotICAgIHhtbC1jcnlwdG8gIl4wLjguMSIKLSAgICB4bWwtZW5jcnlwdGlvbiAifjAuNy40IgotICAgIHhtbDJqcyAifjAuNC4xIgotICAgIHhtbGJ1aWxkZXIgIn4yLjEuMCIKLSAgICB4bWxkb20gIn4wLjEuMTkiCisgICAgYXN5bmMgIl4yLjUuMCIKKyAgICBkZWJ1ZyAiXjIuNi4wIgorICAgIHVuZGVyc2NvcmUgIl4xLjguMCIKKyAgICB4bWwtY3J5cHRvICJeMC4xMC4wIgorICAgIHhtbC1lbmNyeXB0aW9uICJeMC4xMS4wIgorICAgIHhtbDJqcyAiXjAuNC4wIgorICAgIHhtbGJ1aWxkZXIgIn4yLjIuMCIKKyAgICB4bWxkb20gIl4wLjEuMCIKIAogc2F4QD49MC42LjA6CiAgIHZlcnNpb24gIjEuMi40IgogICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS9zYXgvLS9zYXgtMS4yLjQudGd6IzI4MTYyMzRlMjM3OGJkZGM0ZTUzNTRmYWI1Y2FhODk1ZGY3MTAwZDkiCisgIGludGVncml0eSBzaGE1MTItTnFWRHY5VHBBTlVqRm0wTjh1TTVHeEwzNlVnS2k5L2F0WncreDdZRm5ROGNrd0ZHS3JsNHhYNHlXdHJleTNVSm01blAxa1VibllnTG9wcVdOU1JoV3c9PQogCi11bmRlcnNjb3JlQD49MS41Lng6Cit1bmRlcnNjb3JlQF4xLjguMDoKICAgdmVyc2lvbiAiMS45LjEiCiAgIHJlc29sdmVkICJodHRwczovL3JlZ2lzdHJ5Lnlhcm5wa2cuY29tL3VuZGVyc2NvcmUvLS91bmRlcnNjb3JlLTEuOS4xLnRneiMwNmRjZTM0YTBlNjhhN2JhYmMyOWIzNjViOGU3NGI4OTI1MjAzOTYxIgorICBpbnRlZ3JpdHkgc2hhNTEyLTUvNGV0bkNrZDljOGd3Z293aTUvb20vbVlPNWFqQ2FPZ2R6ai9vVyswZVFWOVd4S0JEWnc1K3ljbUttZWFUWGpJblMvVzBCenBHTG8yeFIyYUJ3WmRnPT0KIAotdW5kZXJzY29yZUB+MS42LjA6Ci0gIHZlcnNpb24gIjEuNi4wIgotICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS91bmRlcnNjb3JlLy0vdW5kZXJzY29yZS0xLjYuMC50Z3ojOGIzOGIxMGNhY2RlZjYzMzM3YjhiMjRlNGZmODZkNDVhZWE1MjlhOCIKLQoteG1sLWNyeXB0b0BeMC44LjE6Ci0gIHZlcnNpb24gIjAuOC41IgotICByZXNvbHZlZCAiaHR0cDovL3JlZ2lzdHJ5Lm5wbWpzLm9yZy94bWwtY3J5cHRvLy0veG1sLWNyeXB0by0wLjguNS50Z3ojMmJiY2ZiM2ViMzNmM2E4MmEyMThiODIyYmY2NzJiNmIxYzIwZTUzOCIKK3htbC1jcnlwdG9AXjAuMTAuMDoKKyAgdmVyc2lvbiAiMC4xMC4xIgorICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS94bWwtY3J5cHRvLy0veG1sLWNyeXB0by0wLjEwLjEudGd6I2Y4MzJmNzRjY2Y1NmYyNGFmY2FlMTE2M2ExZmNhYjQ0ZDk2Nzc0YTgiCisgIGludGVncml0eSBzaGExLStETDNUTTlXOGtyOHJoRmpvZnlyUk5sbmRLZz0KICAgZGVwZW5kZW5jaWVzOgogICAgIHhtbGRvbSAiPTAuMS4xOSIKICAgICB4cGF0aC5qcyAiPj0wLjAuMyIKIAoteG1sLWVuY3J5cHRpb25AfjAuNy40OgotICB2ZXJzaW9uICIwLjcuNCIKLSAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20veG1sLWVuY3J5cHRpb24vLS94bWwtZW5jcnlwdGlvbi0wLjcuNC50Z3ojNDI3OTFlYzY0ZDU1NmQyNDU1ZGNiOWRhMGE1NDEyMzY2NWFjNjVjNyIKK3htbC1lbmNyeXB0aW9uQF4wLjExLjA6CisgIHZlcnNpb24gIjAuMTEuMiIKKyAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20veG1sLWVuY3J5cHRpb24vLS94bWwtZW5jcnlwdGlvbi0wLjExLjIudGd6I2MyMTdmNTUwOTU0N2UzNGI1MDBiODI5ZjJjMGJjYTg1Y2NhNzNhMjEiCisgIGludGVncml0eSBzaGE1MTItalZ2RVM3aTVvdmRPN04rTmpnbmNBMzI2eFlLamhxZUFubnZJZ1JuWTdST0xDZkZxRURMd1AwU3hwLzMwU0hHMEFYUVYxMDQ4VDV5aW5PRnl2d0dGemc9PQogICBkZXBlbmRlbmNpZXM6Ci0gICAgYXN5bmMgIn4wLjIuNyIKLSAgICBlanMgIn4wLjguMyIKLSAgICBub2RlLWZvcmdlICIwLjIuMjQiCisgICAgYXN5bmMgIl4yLjEuNSIKKyAgICBlanMgIl4yLjUuNiIKKyAgICBub2RlLWZvcmdlICJeMC43LjAiCiAgICAgeG1sZG9tICJ+MC4xLjE1IgotICAgIHhwYXRoICIwLjAuNSIKKyAgICB4cGF0aCAiMC4wLjI3IgogCi14bWwyanNAfjAuNC4xOgoreG1sMmpzQF4wLjQuMDoKICAgdmVyc2lvbiAiMC40LjE5IgogICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS94bWwyanMvLS94bWwyanMtMC40LjE5LnRneiM2ODZjMjBmMjEzMjA5ZTk0YWJmMGQxYmNmMWVmYWEyOTFjNzgyN2E3IgorICBpbnRlZ3JpdHkgc2hhNTEyLWVzWm5KWkpPaUpSOXdXS015dXZTRTF5NkRxNUxDdUphbnFoeHNsSDJieE02ZHVhaE5aK0hNcENMaEJRR1prYlg2eFJmOHgxWTJlSmxndDJxM3FvNDlRPT0KICAgZGVwZW5kZW5jaWVzOgogICAgIHNheCAiPj0wLjYuMCIKICAgICB4bWxidWlsZGVyICJ+OS4wLjEiCiAKLXhtbGJ1aWxkZXJAfjIuMS4wOgotICB2ZXJzaW9uICIyLjEuMCIKLSAgcmVzb2x2ZWQgImh0dHA6Ly9yZWdpc3RyeS5ucG1qcy5vcmcveG1sYnVpbGRlci8tL3htbGJ1aWxkZXItMi4xLjAudGd6IzZkZGFlMzE2ODNiNmRmMTIxMDBiMjlmYzhhMGQ0ZjQ2MzQ5YWJiZWQiCit4bWxidWlsZGVyQH4yLjIuMDoKKyAgdmVyc2lvbiAiMi4yLjEiCisgIHJlc29sdmVkICJodHRwczovL3JlZ2lzdHJ5Lnlhcm5wa2cuY29tL3htbGJ1aWxkZXIvLS94bWxidWlsZGVyLTIuMi4xLnRneiM5MzI2NDMwZjEzMGQ4NzQzNWQ0YzQwODY2NDNhYTI5MjZlMTA1YTMyIgorICBpbnRlZ3JpdHkgc2hhMS1reVpERHhNTmgwTmRURUNHWkRxaWttNFFXakk9CiAgIGRlcGVuZGVuY2llczoKLSAgICB1bmRlcnNjb3JlICI+PTEuNS54IgorICAgIGxvZGFzaC1ub2RlICJ+Mi40LjEiCiAKIHhtbGJ1aWxkZXJAfjkuMC4xOgogICB2ZXJzaW9uICI5LjAuNyIKLSAgcmVzb2x2ZWQgImh0dHA6Ly9yZWdpc3RyeS5ucG1qcy5vcmcveG1sYnVpbGRlci8tL3htbGJ1aWxkZXItOS4wLjcudGd6IzEzMmVlNjNkMmVjNTU2NWM1NTdlMjBmNGMyMmRmOWFjYTY4NmIxMGQiCisgIHJlc29sdmVkICJodHRwczovL3JlZ2lzdHJ5Lnlhcm5wa2cuY29tL3htbGJ1aWxkZXIvLS94bWxidWlsZGVyLTkuMC43LnRneiMxMzJlZTYzZDJlYzU1NjVjNTU3ZTIwZjRjMjJkZjlhY2E2ODZiMTBkIgorICBpbnRlZ3JpdHkgc2hhMS1FeTdtUFM3RlZseFZmaUQwd2kzNXJLYUdzUTA9CiAKIHhtbGRvbUA9MC4xLjE5OgogICB2ZXJzaW9uICIwLjEuMTkiCiAgIHJlc29sdmVkICJodHRwczovL3JlZ2lzdHJ5Lnlhcm5wa2cuY29tL3htbGRvbS8tL3htbGRvbS0wLjEuMTkudGd6IzYzMWZjMDc3NzZlZmQ4NDExOGJmMjUxNzFiMzdlZDRkMDc1YTBhYmMiCisgIGludGVncml0eSBzaGExLVl4L0FkM2J2MkVFWXZ5VVhHemZ0VFFkYUNydz0KIAoteG1sZG9tQH4wLjEuMTUsIHhtbGRvbUB+MC4xLjE5OgoreG1sZG9tQF4wLjEuMCwgeG1sZG9tQH4wLjEuMTU6CiAgIHZlcnNpb24gIjAuMS4yNyIKICAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20veG1sZG9tLy0veG1sZG9tLTAuMS4yNy50Z3ojZDUwMWY5N2IzYmRiNDAzYWY4ZWY5ZWNjMjA1NzMxODdhYWRhYzBlOSIKKyAgaW50ZWdyaXR5IHNoYTEtMVFINWV6dmJRRHI0NzU3TUlGY3hoNnJhd09rPQogCiB4cGF0aC5qc0A+PTAuMC4zOgogICB2ZXJzaW9uICIxLjEuMCIKICAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20veHBhdGguanMvLS94cGF0aC5qcy0xLjEuMC50Z3ojMzgxNmE0NGVkNGJiMzUyMDkxMDgzZDAwMmEzODNkZDUxMDRhNWZmMSIKKyAgaW50ZWdyaXR5IHNoYTUxMi1qZytxa2ZTNEs4RTc5NjVzcWFVbDhtUm5nWGlLYjNXWkdmT05nRTE4cHIwM0ZVUWl1U1Y2RytFajR0UzU1QitySVFTRkVJdzNwaGRWQVE0cFBxTldmUT09CiAKLXhwYXRoQDAuMC41OgotICB2ZXJzaW9uICIwLjAuNSIKLSAgcmVzb2x2ZWQgImh0dHBzOi8vcmVnaXN0cnkueWFybnBrZy5jb20veHBhdGgvLS94cGF0aC0wLjAuNS50Z3ojNDU0MDM2ZjZlZjBmM2RmNWFmNWQ0YmE0YTExOWZiNzU2NzRiM2U2YyIKK3hwYXRoQDAuMC4yNzoKKyAgdmVyc2lvbiAiMC4wLjI3IgorICByZXNvbHZlZCAiaHR0cHM6Ly9yZWdpc3RyeS55YXJucGtnLmNvbS94cGF0aC8tL3hwYXRoLTAuMC4yNy50Z3ojZGQzNDIxZmJkY2M1NjQ2YWMzMmM0ODUzMWI0ZDdlOWQwYzJjZmE5MiIKKyAgaW50ZWdyaXR5IHNoYTUxMi1mZzAzV1J4dGtDVjZvaENsZVBOQUVDWXNtcEtLVHY1TDh5L1gzRG4xaFFyZWMzUE94MmpIWi8wUDJxUTZIdnNyVTFCbWVxWGNvZjNOR0d1ZUc2THh3UT09Cg=="
+ }
+ ]
+}
diff --git a/spec/fixtures/security-reports/remediations/remediation.patch b/spec/fixtures/security-reports/remediations/remediation.patch
new file mode 100644
index 00000000000..bbfb6874627
--- /dev/null
+++ b/spec/fixtures/security-reports/remediations/remediation.patch
@@ -0,0 +1,180 @@
+diff --git a/yarn.lock b/yarn.lock
+index 0ecc92f..7fa4554 100644
+--- a/yarn.lock
++++ b/yarn.lock
+@@ -2,103 +2,124 @@
+ # yarn lockfile v1
+
+
+-async@~0.2.7:
+- version "0.2.10"
+- resolved "http://registry.npmjs.org/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
+-
+-async@~1.5.2:
+- version "1.5.2"
+- resolved "http://registry.npmjs.org/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
++async@^2.1.5, async@^2.5.0:
++ version "2.6.1"
++ resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
++ integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==
++ dependencies:
++ lodash "^4.17.10"
+
+-debug@^1.0.4:
+- version "1.0.5"
+- resolved "https://registry.yarnpkg.com/debug/-/debug-1.0.5.tgz#f7241217430f99dec4c2b473eab92228e874c2ac"
++debug@^2.6.0:
++ version "2.6.9"
++ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
++ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+-ejs@~0.8.3:
+- version "0.8.8"
+- resolved "https://registry.yarnpkg.com/ejs/-/ejs-0.8.8.tgz#ffdc56dcc35d02926dd50ad13439bbc54061d598"
++ejs@^2.5.6:
++ version "2.6.1"
++ resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.6.1.tgz#498ec0d495655abc6f23cd61868d926464071aa0"
++ integrity sha512-0xy4A/twfrRCnkhfk8ErDi5DqdAsAqeGxht4xkCUrsvhhbQNs7E+4jV0CN7+NKIY0aHE72+XvqtBIXzD31ZbXQ==
++
++lodash-node@~2.4.1:
++ version "2.4.1"
++ resolved "https://registry.yarnpkg.com/lodash-node/-/lodash-node-2.4.1.tgz#ea82f7b100c733d1a42af76801e506105e2a80ec"
++ integrity sha1-6oL3sQDHM9GkKvdoAeUGEF4qgOw=
++
++lodash@^4.17.10:
++ version "4.17.11"
++ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
++ integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
+
+ ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
++ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+-node-forge@0.2.24:
+- version "0.2.24"
+- resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.2.24.tgz#fa6f846f42fa93f63a0a30c9fbff7b4e130e0858"
++node-forge@^0.7.0:
++ version "0.7.6"
++ resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac"
++ integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw==
+
+ saml2-js@^1.5.0:
+- version "1.5.0"
+- resolved "https://registry.yarnpkg.com/saml2-js/-/saml2-js-1.5.0.tgz#c0d2268a179e7329d29eb25aa82df5503774b0d9"
++ version "1.12.4"
++ resolved "https://registry.yarnpkg.com/saml2-js/-/saml2-js-1.12.4.tgz#c288f20bda6d2b91073b16c94ea72f22349ac3b3"
++ integrity sha1-wojyC9ptK5EHOxbJTqcvIjSaw7M=
+ dependencies:
+- async "~1.5.2"
+- debug "^1.0.4"
+- underscore "~1.6.0"
+- xml-crypto "^0.8.1"
+- xml-encryption "~0.7.4"
+- xml2js "~0.4.1"
+- xmlbuilder "~2.1.0"
+- xmldom "~0.1.19"
++ async "^2.5.0"
++ debug "^2.6.0"
++ underscore "^1.8.0"
++ xml-crypto "^0.10.0"
++ xml-encryption "^0.11.0"
++ xml2js "^0.4.0"
++ xmlbuilder "~2.2.0"
++ xmldom "^0.1.0"
+
+ sax@>=0.6.0:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
++ integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
+
+-underscore@>=1.5.x:
++underscore@^1.8.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
++ integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==
+
+-underscore@~1.6.0:
+- version "1.6.0"
+- resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
+-
+-xml-crypto@^0.8.1:
+- version "0.8.5"
+- resolved "http://registry.npmjs.org/xml-crypto/-/xml-crypto-0.8.5.tgz#2bbcfb3eb33f3a82a218b822bf672b6b1c20e538"
++xml-crypto@^0.10.0:
++ version "0.10.1"
++ resolved "https://registry.yarnpkg.com/xml-crypto/-/xml-crypto-0.10.1.tgz#f832f74ccf56f24afcae1163a1fcab44d96774a8"
++ integrity sha1-+DL3TM9W8kr8rhFjofyrRNlndKg=
+ dependencies:
+ xmldom "=0.1.19"
+ xpath.js ">=0.0.3"
+
+-xml-encryption@~0.7.4:
+- version "0.7.4"
+- resolved "https://registry.yarnpkg.com/xml-encryption/-/xml-encryption-0.7.4.tgz#42791ec64d556d2455dcb9da0a54123665ac65c7"
++xml-encryption@^0.11.0:
++ version "0.11.2"
++ resolved "https://registry.yarnpkg.com/xml-encryption/-/xml-encryption-0.11.2.tgz#c217f5509547e34b500b829f2c0bca85cca73a21"
++ integrity sha512-jVvES7i5ovdO7N+NjgncA326xYKjhqeAnnvIgRnY7ROLCfFqEDLwP0Sxp/30SHG0AXQV1048T5yinOFyvwGFzg==
+ dependencies:
+- async "~0.2.7"
+- ejs "~0.8.3"
+- node-forge "0.2.24"
++ async "^2.1.5"
++ ejs "^2.5.6"
++ node-forge "^0.7.0"
+ xmldom "~0.1.15"
+- xpath "0.0.5"
++ xpath "0.0.27"
+
+-xml2js@~0.4.1:
++xml2js@^0.4.0:
+ version "0.4.19"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
++ integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==
+ dependencies:
+ sax ">=0.6.0"
+ xmlbuilder "~9.0.1"
+
+-xmlbuilder@~2.1.0:
+- version "2.1.0"
+- resolved "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.1.0.tgz#6ddae31683b6df12100b29fc8a0d4f46349abbed"
++xmlbuilder@~2.2.0:
++ version "2.2.1"
++ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-2.2.1.tgz#9326430f130d87435d4c4086643aa2926e105a32"
++ integrity sha1-kyZDDxMNh0NdTECGZDqikm4QWjI=
+ dependencies:
+- underscore ">=1.5.x"
++ lodash-node "~2.4.1"
+
+ xmlbuilder@~9.0.1:
+ version "9.0.7"
+- resolved "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
++ resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
++ integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=
+
+ xmldom@=0.1.19:
+ version "0.1.19"
+ resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.19.tgz#631fc07776efd84118bf25171b37ed4d075a0abc"
++ integrity sha1-Yx/Ad3bv2EEYvyUXGzftTQdaCrw=
+
+-xmldom@~0.1.15, xmldom@~0.1.19:
++xmldom@^0.1.0, xmldom@~0.1.15:
+ version "0.1.27"
+ resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9"
++ integrity sha1-1QH5ezvbQDr4757MIFcxh6rawOk=
+
+ xpath.js@>=0.0.3:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/xpath.js/-/xpath.js-1.1.0.tgz#3816a44ed4bb352091083d002a383dd5104a5ff1"
++ integrity sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==
+
+-xpath@0.0.5:
+- version "0.0.5"
+- resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.5.tgz#454036f6ef0f3df5af5d4ba4a119fb75674b3e6c"
++xpath@0.0.27:
++ version "0.0.27"
++ resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.27.tgz#dd3421fbdcc5646ac32c48531b4d7e9d0c2cfa92"
++ integrity sha512-fg03WRxtkCV6ohClePNAECYsmpKKTv5L8y/X3Dn1hQrec3POx2jHZ/0P2qQ6HvsrU1BmeqXcof3NGGueG6LxwQ==
diff --git a/spec/fixtures/security-reports/remediations/yarn.lock b/spec/fixtures/security-reports/remediations/yarn.lock
new file mode 100644
index 00000000000..0ecc92fb711
--- /dev/null
+++ b/spec/fixtures/security-reports/remediations/yarn.lock
@@ -0,0 +1,104 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+async@~0.2.7:
+ version "0.2.10"
+ resolved "http://registry.npmjs.org/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1"
+
+async@~1.5.2:
+ version "1.5.2"
+ resolved "http://registry.npmjs.org/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
+
+debug@^1.0.4:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-1.0.5.tgz#f7241217430f99dec4c2b473eab92228e874c2ac"
+ dependencies:
+ ms "2.0.0"
+
+ejs@~0.8.3:
+ version "0.8.8"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-0.8.8.tgz#ffdc56dcc35d02926dd50ad13439bbc54061d598"
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+
+node-forge@0.2.24:
+ version "0.2.24"
+ resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.2.24.tgz#fa6f846f42fa93f63a0a30c9fbff7b4e130e0858"
+
+saml2-js@^1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/saml2-js/-/saml2-js-1.5.0.tgz#c0d2268a179e7329d29eb25aa82df5503774b0d9"
+ dependencies:
+ async "~1.5.2"
+ debug "^1.0.4"
+ underscore "~1.6.0"
+ xml-crypto "^0.8.1"
+ xml-encryption "~0.7.4"
+ xml2js "~0.4.1"
+ xmlbuilder "~2.1.0"
+ xmldom "~0.1.19"
+
+sax@>=0.6.0:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
+
+underscore@>=1.5.x:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961"
+
+underscore@~1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.6.0.tgz#8b38b10cacdef63337b8b24e4ff86d45aea529a8"
+
+xml-crypto@^0.8.1:
+ version "0.8.5"
+ resolved "http://registry.npmjs.org/xml-crypto/-/xml-crypto-0.8.5.tgz#2bbcfb3eb33f3a82a218b822bf672b6b1c20e538"
+ dependencies:
+ xmldom "=0.1.19"
+ xpath.js ">=0.0.3"
+
+xml-encryption@~0.7.4:
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/xml-encryption/-/xml-encryption-0.7.4.tgz#42791ec64d556d2455dcb9da0a54123665ac65c7"
+ dependencies:
+ async "~0.2.7"
+ ejs "~0.8.3"
+ node-forge "0.2.24"
+ xmldom "~0.1.15"
+ xpath "0.0.5"
+
+xml2js@~0.4.1:
+ version "0.4.19"
+ resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7"
+ dependencies:
+ sax ">=0.6.0"
+ xmlbuilder "~9.0.1"
+
+xmlbuilder@~2.1.0:
+ version "2.1.0"
+ resolved "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-2.1.0.tgz#6ddae31683b6df12100b29fc8a0d4f46349abbed"
+ dependencies:
+ underscore ">=1.5.x"
+
+xmlbuilder@~9.0.1:
+ version "9.0.7"
+ resolved "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d"
+
+xmldom@=0.1.19:
+ version "0.1.19"
+ resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.19.tgz#631fc07776efd84118bf25171b37ed4d075a0abc"
+
+xmldom@~0.1.15, xmldom@~0.1.19:
+ version "0.1.27"
+ resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9"
+
+xpath.js@>=0.0.3:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/xpath.js/-/xpath.js-1.1.0.tgz#3816a44ed4bb352091083d002a383dd5104a5ff1"
+
+xpath@0.0.5:
+ version "0.0.5"
+ resolved "https://registry.yarnpkg.com/xpath/-/xpath-0.0.5.tgz#454036f6ef0f3df5af5d4ba4a119fb75674b3e6c"
diff --git a/spec/fixtures/trace/sample_trace b/spec/fixtures/trace/sample_trace
index 3d8beb0dec2..8f9747f8143 100644
--- a/spec/fixtures/trace/sample_trace
+++ b/spec/fixtures/trace/sample_trace
@@ -1795,7 +1795,7 @@ GroupsController
when requesting a redirected path
returns not found
PUT transfer
- when transfering to a subgroup goes right
+ when transferring to a subgroup goes right
should return a notice (PENDING: around hook at ./spec/spec_helper.rb:190 did not execute the example)
should redirect to the new path (PENDING: around hook at ./spec/spec_helper.rb:190 did not execute the example)
when converting to a root group goes right
@@ -2299,7 +2299,7 @@ Groups::TransferService
should update subgroups path (PENDING: around hook at ./spec/spec_helper.rb:190 did not execute the example)
should update projects path (PENDING: around hook at ./spec/spec_helper.rb:190 did not execute the example)
should create redirect for the subgroups and projects (PENDING: around hook at ./spec/spec_helper.rb:190 did not execute the example)
- when transfering a group with nested groups and projects
+ when transferring a group with nested groups and projects
should update subgroups path (PENDING: around hook at ./spec/spec_helper.rb:190 did not execute the example)
should update projects path (PENDING: around hook at ./spec/spec_helper.rb:190 did not execute the example)
should create redirect for the subgroups and projects (PENDING: around hook at ./spec/spec_helper.rb:190 did not execute the example)
@@ -2426,9 +2426,9 @@ Groups::MilestonesController
lists legacy group milestones and group milestones
#show
when there is a title parameter
- searchs for a legacy group milestone
+ searches for a legacy group milestone
when there is not a title parameter
- searchs for a group milestone
+ searches for a group milestone
behaves like milestone tabs
#merge_requests
as html
@@ -3109,11 +3109,11 @@ 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:129
- 15) GroupsController PUT transfer when transfering to a subgroup goes right should return a notice
+ 15) GroupsController PUT transfer when transferring to a subgroup goes right should return a notice
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:516
- 16) GroupsController PUT transfer when transfering to a subgroup goes right should redirect to the new path
+ 16) GroupsController PUT transfer when transferring to a subgroup goes right should redirect to the new path
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/controllers/groups_controller_spec.rb:520
@@ -3301,15 +3301,15 @@ 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:341
- 63) Groups::TransferService#execute when transferring a subgroup into another group when transfering a group with nested groups and projects should update subgroups path
+ 63) Groups::TransferService#execute when transferring a subgroup into another group when transferring a group with nested groups and projects should update subgroups path
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/services/groups/transfer_service_spec.rb:363
- 64) Groups::TransferService#execute when transferring a subgroup into another group when transfering a group with nested groups and projects should update projects path
+ 64) Groups::TransferService#execute when transferring a subgroup into another group when transferring a group with nested groups and projects should update projects path
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/services/groups/transfer_service_spec.rb:375
- 65) Groups::TransferService#execute when transferring a subgroup into another group when transfering a group with nested groups and projects should create redirect for the subgroups and projects
+ 65) Groups::TransferService#execute when transferring a subgroup into another group when transferring a group with nested groups and projects should create redirect for the subgroups and projects
# around hook at ./spec/spec_helper.rb:190 did not execute the example
# ./spec/services/groups/transfer_service_spec.rb:383
diff --git a/spec/frontend/__mocks__/file_mock.js b/spec/frontend/__mocks__/file_mock.js
new file mode 100644
index 00000000000..08d725cd4e4
--- /dev/null
+++ b/spec/frontend/__mocks__/file_mock.js
@@ -0,0 +1 @@
+export default '';
diff --git a/spec/javascripts/behaviors/secret_values_spec.js b/spec/frontend/behaviors/secret_values_spec.js
index 5aaab093c0c..5aaab093c0c 100644
--- a/spec/javascripts/behaviors/secret_values_spec.js
+++ b/spec/frontend/behaviors/secret_values_spec.js
diff --git a/spec/javascripts/blob/blob_fork_suggestion_spec.js b/spec/frontend/blob/blob_fork_suggestion_spec.js
index 9b81b7e6f92..9b81b7e6f92 100644
--- a/spec/javascripts/blob/blob_fork_suggestion_spec.js
+++ b/spec/frontend/blob/blob_fork_suggestion_spec.js
diff --git a/spec/javascripts/boards/modal_store_spec.js b/spec/frontend/boards/modal_store_spec.js
index 3257a3fb8a3..3257a3fb8a3 100644
--- a/spec/javascripts/boards/modal_store_spec.js
+++ b/spec/frontend/boards/modal_store_spec.js
diff --git a/spec/javascripts/cycle_analytics/limit_warning_component_spec.js b/spec/frontend/cycle_analytics/limit_warning_component_spec.js
index 13e9fe00a00..13e9fe00a00 100644
--- a/spec/javascripts/cycle_analytics/limit_warning_component_spec.js
+++ b/spec/frontend/cycle_analytics/limit_warning_component_spec.js
diff --git a/spec/javascripts/diffs/components/diff_stats_spec.js b/spec/frontend/diffs/components/diff_stats_spec.js
index 984b3026209..984b3026209 100644
--- a/spec/javascripts/diffs/components/diff_stats_spec.js
+++ b/spec/frontend/diffs/components/diff_stats_spec.js
diff --git a/spec/frontend/diffs/components/edit_button_spec.js b/spec/frontend/diffs/components/edit_button_spec.js
new file mode 100644
index 00000000000..ccdae4cb312
--- /dev/null
+++ b/spec/frontend/diffs/components/edit_button_spec.js
@@ -0,0 +1,61 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import EditButton from '~/diffs/components/edit_button.vue';
+
+const localVue = createLocalVue();
+const editPath = 'test-path';
+
+describe('EditButton', () => {
+ let wrapper;
+
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(EditButton, {
+ localVue,
+ sync: false,
+ propsData: { ...props },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('has correct href attribute', () => {
+ createComponent({
+ editPath,
+ canCurrentUserFork: false,
+ });
+
+ expect(wrapper.attributes('href')).toBe(editPath);
+ });
+
+ it('emits a show fork message event if current user can fork', () => {
+ createComponent({
+ editPath,
+ canCurrentUserFork: true,
+ });
+ wrapper.trigger('click');
+
+ expect(wrapper.emitted('showForkMessage')).toBeTruthy();
+ });
+
+ it('doesnt emit a show fork message event if current user cannot fork', () => {
+ createComponent({
+ editPath,
+ canCurrentUserFork: false,
+ });
+ wrapper.trigger('click');
+
+ expect(wrapper.emitted('showForkMessage')).toBeFalsy();
+ });
+
+ it('doesnt emit a show fork message event if current user can modify blob', () => {
+ createComponent({
+ editPath,
+ canCurrentUserFork: true,
+ canModifyBlob: true,
+ });
+ wrapper.trigger('click');
+
+ expect(wrapper.emitted('showForkMessage')).toBeFalsy();
+ });
+});
diff --git a/spec/frontend/diffs/components/hidden_files_warning_spec.js b/spec/frontend/diffs/components/hidden_files_warning_spec.js
new file mode 100644
index 00000000000..5bf5ddd27bd
--- /dev/null
+++ b/spec/frontend/diffs/components/hidden_files_warning_spec.js
@@ -0,0 +1,48 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
+
+const localVue = createLocalVue();
+const propsData = {
+ total: '10',
+ visible: 5,
+ plainDiffPath: 'plain-diff-path',
+ emailPatchPath: 'email-patch-path',
+};
+
+describe('HiddenFilesWarning', () => {
+ let wrapper;
+
+ const createComponent = () => {
+ wrapper = shallowMount(HiddenFilesWarning, {
+ localVue,
+ sync: false,
+ propsData,
+ });
+ };
+
+ beforeEach(() => {
+ createComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('has a correct plain diff URL', () => {
+ const plainDiffLink = wrapper.findAll('a').wrappers.filter(x => x.text() === 'Plain diff')[0];
+
+ expect(plainDiffLink.attributes('href')).toBe(propsData.plainDiffPath);
+ });
+
+ it('has a correct email patch URL', () => {
+ const emailPatchLink = wrapper.findAll('a').wrappers.filter(x => x.text() === 'Email patch')[0];
+
+ expect(emailPatchLink.attributes('href')).toBe(propsData.emailPatchPath);
+ });
+
+ it('has a correct visible/total files text', () => {
+ const filesText = wrapper.find('strong');
+
+ expect(filesText.text()).toBe('5 of 10');
+ });
+});
diff --git a/spec/javascripts/diffs/components/no_changes_spec.js b/spec/frontend/diffs/components/no_changes_spec.js
index e45d34bf9d5..e45d34bf9d5 100644
--- a/spec/javascripts/diffs/components/no_changes_spec.js
+++ b/spec/frontend/diffs/components/no_changes_spec.js
diff --git a/spec/frontend/environment.js b/spec/frontend/environment.js
new file mode 100644
index 00000000000..cb128c7d880
--- /dev/null
+++ b/spec/frontend/environment.js
@@ -0,0 +1,27 @@
+/* eslint-disable import/no-commonjs */
+
+const { ErrorWithStack } = require('jest-util');
+const JSDOMEnvironment = require('jest-environment-jsdom');
+
+class CustomEnvironment extends JSDOMEnvironment {
+ constructor(config, context) {
+ super(config, context);
+ Object.assign(context.console, {
+ error(...args) {
+ throw new ErrorWithStack(
+ `Unexpected call of console.error() with:\n\n${args.join(', ')}`,
+ this.error,
+ );
+ },
+
+ warn(...args) {
+ throw new ErrorWithStack(
+ `Unexpected call of console.warn() with:\n\n${args.join(', ')}`,
+ this.warn,
+ );
+ },
+ });
+ }
+}
+
+module.exports = CustomEnvironment;
diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
new file mode 100644
index 00000000000..503af3920a8
--- /dev/null
+++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js
@@ -0,0 +1,118 @@
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import Vuex from 'vuex';
+import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
+import { GlButton, GlEmptyState, GlLoadingIcon, GlTable, GlLink } from '@gitlab/ui';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('ErrorTrackingList', () => {
+ let store;
+ let wrapper;
+ let actions;
+
+ function mountComponent({ errorTrackingEnabled = true } = {}) {
+ wrapper = shallowMount(ErrorTrackingList, {
+ localVue,
+ store,
+ propsData: {
+ indexPath: '/path',
+ enableErrorTrackingLink: '/link',
+ errorTrackingEnabled,
+ illustrationPath: 'illustration/path',
+ },
+ stubs: {
+ 'gl-link': GlLink,
+ },
+ });
+ }
+
+ beforeEach(() => {
+ actions = {
+ getErrorList: () => {},
+ startPolling: () => {},
+ restartPolling: jasmine.createSpy('restartPolling'),
+ };
+
+ const state = {
+ errors: [],
+ loading: true,
+ };
+
+ store = new Vuex.Store({
+ actions,
+ state,
+ });
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ describe('loading', () => {
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ it('shows spinner', () => {
+ expect(wrapper.find(GlLoadingIcon).exists()).toBeTruthy();
+ expect(wrapper.find(GlTable).exists()).toBeFalsy();
+ expect(wrapper.find(GlButton).exists()).toBeFalsy();
+ });
+ });
+
+ describe('results', () => {
+ beforeEach(() => {
+ store.state.loading = false;
+
+ mountComponent();
+ });
+
+ it('shows table', () => {
+ expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy();
+ expect(wrapper.find(GlTable).exists()).toBeTruthy();
+ expect(wrapper.find(GlButton).exists()).toBeTruthy();
+ });
+ });
+
+ describe('no results', () => {
+ beforeEach(() => {
+ store.state.loading = false;
+
+ mountComponent();
+ });
+
+ it('shows empty table', () => {
+ expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy();
+ expect(wrapper.find(GlTable).exists()).toBeTruthy();
+ expect(wrapper.find(GlButton).exists()).toBeTruthy();
+ });
+
+ it('shows a message prompting to refresh', () => {
+ const refreshLink = wrapper.vm.$refs.empty.querySelector('a');
+
+ expect(refreshLink.textContent.trim()).toContain('Check again');
+ });
+
+ it('restarts polling', () => {
+ wrapper.find('.js-try-again').trigger('click');
+
+ expect(actions.restartPolling).toHaveBeenCalled();
+ });
+ });
+
+ describe('error tracking feature disabled', () => {
+ beforeEach(() => {
+ mountComponent({ errorTrackingEnabled: false });
+ });
+
+ it('shows empty state', () => {
+ expect(wrapper.find(GlEmptyState).exists()).toBeTruthy();
+ expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy();
+ expect(wrapper.find(GlTable).exists()).toBeFalsy();
+ expect(wrapper.find(GlButton).exists()).toBeFalsy();
+ });
+ });
+});
diff --git a/spec/javascripts/error_tracking/store/mutation_spec.js b/spec/frontend/error_tracking/store/mutation_spec.js
index 8117104bdbc..8117104bdbc 100644
--- a/spec/javascripts/error_tracking/store/mutation_spec.js
+++ b/spec/frontend/error_tracking/store/mutation_spec.js
diff --git a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js b/spec/frontend/filtered_search/filtered_search_token_keys_spec.js
index d1fea18dea8..d1fea18dea8 100644
--- a/spec/javascripts/filtered_search/filtered_search_token_keys_spec.js
+++ b/spec/frontend/filtered_search/filtered_search_token_keys_spec.js
diff --git a/spec/javascripts/filtered_search/services/recent_searches_service_error_spec.js b/spec/frontend/filtered_search/services/recent_searches_service_error_spec.js
index ea7c146fa4f..ea7c146fa4f 100644
--- a/spec/javascripts/filtered_search/services/recent_searches_service_error_spec.js
+++ b/spec/frontend/filtered_search/services/recent_searches_service_error_spec.js
diff --git a/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js b/spec/frontend/filtered_search/stores/recent_searches_store_spec.js
index 56bb82ae941..56bb82ae941 100644
--- a/spec/javascripts/filtered_search/stores/recent_searches_store_spec.js
+++ b/spec/frontend/filtered_search/stores/recent_searches_store_spec.js
diff --git a/spec/javascripts/frequent_items/store/getters_spec.js b/spec/frontend/frequent_items/store/getters_spec.js
index 1cd12eb6832..1cd12eb6832 100644
--- a/spec/javascripts/frequent_items/store/getters_spec.js
+++ b/spec/frontend/frequent_items/store/getters_spec.js
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
new file mode 100644
index 00000000000..ed12af925f1
--- /dev/null
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -0,0 +1,348 @@
+/* eslint no-param-reassign: "off" */
+
+import $ from 'jquery';
+import GfmAutoComplete from '~/gfm_auto_complete';
+
+import 'jquery.caret';
+import 'at.js';
+
+import { TEST_HOST } from 'helpers/test_constants';
+import { setTestTimeout } from 'helpers/timeout';
+import { getJSONFixture } from 'helpers/fixtures';
+
+setTestTimeout(500);
+
+const labelsFixture = getJSONFixture('autocomplete_sources/labels.json');
+
+describe('GfmAutoComplete', () => {
+ const gfmAutoCompleteCallbacks = GfmAutoComplete.prototype.getDefaultCallbacks.call({
+ fetchData: () => {},
+ });
+
+ let atwhoInstance;
+ let sorterValue;
+
+ describe('DefaultOptions.sorter', () => {
+ describe('assets loading', () => {
+ let items;
+
+ beforeEach(() => {
+ jest.spyOn(GfmAutoComplete, 'isLoading').mockReturnValue(true);
+
+ atwhoInstance = { setting: {} };
+ items = [];
+
+ sorterValue = gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, '', items);
+ });
+
+ it('should disable highlightFirst', () => {
+ expect(atwhoInstance.setting.highlightFirst).toBe(false);
+ });
+
+ it('should return the passed unfiltered items', () => {
+ expect(sorterValue).toEqual(items);
+ });
+ });
+
+ describe('assets finished loading', () => {
+ beforeEach(() => {
+ jest.spyOn(GfmAutoComplete, 'isLoading').mockReturnValue(false);
+ jest.spyOn($.fn.atwho.default.callbacks, 'sorter').mockImplementation(() => {});
+ });
+
+ it('should enable highlightFirst if alwaysHighlightFirst is set', () => {
+ atwhoInstance = { setting: { alwaysHighlightFirst: true } };
+
+ gfmAutoCompleteCallbacks.sorter.call(atwhoInstance);
+
+ expect(atwhoInstance.setting.highlightFirst).toBe(true);
+ });
+
+ it('should enable highlightFirst if a query is present', () => {
+ atwhoInstance = { setting: {} };
+
+ gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, 'query');
+
+ expect(atwhoInstance.setting.highlightFirst).toBe(true);
+ });
+
+ it('should call the default atwho sorter', () => {
+ atwhoInstance = { setting: {} };
+
+ const query = 'query';
+ const items = [];
+ const searchKey = 'searchKey';
+
+ gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, query, items, searchKey);
+
+ expect($.fn.atwho.default.callbacks.sorter).toHaveBeenCalledWith(query, items, searchKey);
+ });
+ });
+ });
+
+ describe('DefaultOptions.beforeInsert', () => {
+ const beforeInsert = (context, value) =>
+ gfmAutoCompleteCallbacks.beforeInsert.call(context, value);
+
+ beforeEach(() => {
+ atwhoInstance = { setting: { skipSpecialCharacterTest: false } };
+ });
+
+ it('should not quote if value only contains alphanumeric charecters', () => {
+ expect(beforeInsert(atwhoInstance, '@user1')).toBe('@user1');
+ expect(beforeInsert(atwhoInstance, '~label1')).toBe('~label1');
+ });
+
+ it('should quote if value contains any non-alphanumeric characters', () => {
+ expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label\\-20"');
+ expect(beforeInsert(atwhoInstance, '~label 20')).toBe('~"label 20"');
+ });
+
+ it('should quote integer labels', () => {
+ expect(beforeInsert(atwhoInstance, '~1234')).toBe('~"1234"');
+ });
+
+ it('should escape Markdown emphasis characters, except in the first character', () => {
+ expect(beforeInsert(atwhoInstance, '@_group')).toEqual('@\\_group');
+ expect(beforeInsert(atwhoInstance, '~_bug')).toEqual('~\\_bug');
+ expect(beforeInsert(atwhoInstance, '~a `bug`')).toEqual('~"a \\`bug\\`"');
+ expect(beforeInsert(atwhoInstance, '~a ~bug')).toEqual('~"a \\~bug"');
+ expect(beforeInsert(atwhoInstance, '~a **bug')).toEqual('~"a \\*\\*bug"');
+ });
+ });
+
+ describe('DefaultOptions.matcher', () => {
+ const defaultMatcher = (context, flag, subtext) =>
+ gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext);
+
+ const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
+ const otherFlags = ['/', ':'];
+ const flags = flagsUseDefaultMatcher.concat(otherFlags);
+
+ const flagsHash = flags.reduce((hash, el) => {
+ hash[el] = null;
+ return hash;
+ }, {});
+
+ beforeEach(() => {
+ atwhoInstance = { setting: {}, app: { controllers: flagsHash } };
+ });
+
+ const minLen = 1;
+ const maxLen = 20;
+ const argumentSize = [minLen, maxLen / 2, maxLen];
+
+ const allowedSymbols = [
+ '',
+ 'a',
+ 'n',
+ 'z',
+ 'A',
+ 'Z',
+ 'N',
+ '0',
+ '5',
+ '9',
+ 'Ð',
+ 'а',
+ 'Я',
+ 'Ñ',
+ '.',
+ "'",
+ '+',
+ '-',
+ '_',
+ ];
+ const jointAllowedSymbols = allowedSymbols.join('');
+
+ describe('should match regular symbols', () => {
+ flagsUseDefaultMatcher.forEach(flag => {
+ allowedSymbols.forEach(symbol => {
+ argumentSize.forEach(size => {
+ const query = new Array(size + 1).join(symbol);
+ const subtext = flag + query;
+
+ it(`matches argument "${flag}" with query "${subtext}"`, () => {
+ expect(defaultMatcher(atwhoInstance, flag, subtext)).toBe(query);
+ });
+ });
+ });
+
+ it(`matches combination of allowed symbols for flag "${flag}"`, () => {
+ const subtext = flag + jointAllowedSymbols;
+
+ expect(defaultMatcher(atwhoInstance, flag, subtext)).toBe(jointAllowedSymbols);
+ });
+ });
+ });
+
+ describe('should not match special sequences', () => {
+ const shouldNotBeFollowedBy = flags.concat(['\x00', '\x10', '\x3f', '\n', ' ']);
+ const shouldNotBePrependedBy = ['`'];
+
+ flagsUseDefaultMatcher.forEach(atSign => {
+ shouldNotBeFollowedBy.forEach(followedSymbol => {
+ const seq = atSign + followedSymbol;
+
+ it(`should not match ${JSON.stringify(seq)}`, () => {
+ expect(defaultMatcher(atwhoInstance, atSign, seq)).toBe(null);
+ });
+ });
+
+ shouldNotBePrependedBy.forEach(prependedSymbol => {
+ const seq = prependedSymbol + atSign;
+
+ it(`should not match "${seq}"`, () => {
+ expect(defaultMatcher(atwhoInstance, atSign, seq)).toBe(null);
+ });
+ });
+ });
+ });
+ });
+
+ describe('isLoading', () => {
+ it('should be true with loading data object item', () => {
+ expect(GfmAutoComplete.isLoading({ name: 'loading' })).toBe(true);
+ });
+
+ it('should be true with loading data array', () => {
+ expect(GfmAutoComplete.isLoading(['loading'])).toBe(true);
+ });
+
+ it('should be true with loading data object array', () => {
+ expect(GfmAutoComplete.isLoading([{ name: 'loading' }])).toBe(true);
+ });
+
+ it('should be false with actual array data', () => {
+ expect(
+ GfmAutoComplete.isLoading([{ title: 'Foo' }, { title: 'Bar' }, { title: 'Qux' }]),
+ ).toBe(false);
+ });
+
+ it('should be false with actual data item', () => {
+ expect(GfmAutoComplete.isLoading({ title: 'Foo' })).toBe(false);
+ });
+ });
+
+ describe('Issues.insertTemplateFunction', () => {
+ it('should return default template', () => {
+ expect(GfmAutoComplete.Issues.insertTemplateFunction({ id: 5, title: 'Some Issue' })).toBe(
+ '${atwho-at}${id}', // eslint-disable-line no-template-curly-in-string
+ );
+ });
+
+ it('should return reference when reference is set', () => {
+ expect(
+ GfmAutoComplete.Issues.insertTemplateFunction({
+ id: 5,
+ title: 'Some Issue',
+ reference: 'grp/proj#5',
+ }),
+ ).toBe('grp/proj#5');
+ });
+ });
+
+ describe('Issues.templateFunction', () => {
+ it('should return html with id and title', () => {
+ expect(GfmAutoComplete.Issues.templateFunction({ id: 5, title: 'Some Issue' })).toBe(
+ '<li><small>5</small> Some Issue</li>',
+ );
+ });
+
+ it('should replace id with reference if reference is set', () => {
+ expect(
+ GfmAutoComplete.Issues.templateFunction({
+ id: 5,
+ title: 'Some Issue',
+ reference: 'grp/proj#5',
+ }),
+ ).toBe('<li><small>grp/proj#5</small> Some Issue</li>');
+ });
+ });
+
+ describe('labels', () => {
+ const dataSources = {
+ labels: `${TEST_HOST}/autocomplete_sources/labels`,
+ };
+
+ const allLabels = labelsFixture;
+ const assignedLabels = allLabels.filter(label => label.set);
+ const unassignedLabels = allLabels.filter(label => !label.set);
+
+ let autocomplete;
+ let $textarea;
+
+ beforeEach(() => {
+ autocomplete = new GfmAutoComplete(dataSources);
+ $textarea = $('<textarea></textarea>');
+ autocomplete.setup($textarea, { labels: true });
+ });
+
+ afterEach(() => {
+ autocomplete.destroy();
+ });
+
+ const triggerDropdown = text => {
+ $textarea
+ .trigger('focus')
+ .val(text)
+ .caret('pos', -1);
+ $textarea.trigger('keyup');
+
+ return new Promise(window.requestAnimationFrame);
+ };
+
+ const getDropdownItems = () => {
+ const dropdown = document.getElementById('at-view-labels');
+ const items = dropdown.getElementsByTagName('li');
+ return [].map.call(items, item => item.textContent.trim());
+ };
+
+ const expectLabels = ({ input, output }) =>
+ triggerDropdown(input).then(() => {
+ expect(getDropdownItems()).toEqual(output.map(label => label.title));
+ });
+
+ describe('with no labels assigned', () => {
+ beforeEach(() => {
+ autocomplete.cachedData['~'] = [...unassignedLabels];
+ });
+
+ it.each`
+ input | output
+ ${'~'} | ${unassignedLabels}
+ ${'/label ~'} | ${unassignedLabels}
+ ${'/relabel ~'} | ${unassignedLabels}
+ ${'/unlabel ~'} | ${[]}
+ `('$input shows $output.length labels', expectLabels);
+ });
+
+ describe('with some labels assigned', () => {
+ beforeEach(() => {
+ autocomplete.cachedData['~'] = allLabels;
+ });
+
+ it.each`
+ input | output
+ ${'~'} | ${allLabels}
+ ${'/label ~'} | ${unassignedLabels}
+ ${'/relabel ~'} | ${allLabels}
+ ${'/unlabel ~'} | ${assignedLabels}
+ `('$input shows $output.length labels', expectLabels);
+ });
+
+ describe('with all labels assigned', () => {
+ beforeEach(() => {
+ autocomplete.cachedData['~'] = [...assignedLabels];
+ });
+
+ it.each`
+ input | output
+ ${'~'} | ${assignedLabels}
+ ${'/label ~'} | ${[]}
+ ${'/relabel ~'} | ${assignedLabels}
+ ${'/unlabel ~'} | ${assignedLabels}
+ `('$input shows $output.length labels', expectLabels);
+ });
+ });
+});
diff --git a/spec/frontend/helpers/fixtures.js b/spec/frontend/helpers/fixtures.js
new file mode 100644
index 00000000000..de9058d7832
--- /dev/null
+++ b/spec/frontend/helpers/fixtures.js
@@ -0,0 +1,22 @@
+/* eslint-disable import/prefer-default-export, global-require, import/no-dynamic-require */
+
+import fs from 'fs';
+import path from 'path';
+
+import { ErrorWithStack } from 'jest-util';
+
+const fixturesBasePath = path.join(process.cwd(), 'spec', 'javascripts', 'fixtures');
+
+export function getJSONFixture(relativePath, ee = false) {
+ const absolutePath = path.join(fixturesBasePath, ee ? 'ee' : '', relativePath);
+ if (!fs.existsSync(absolutePath)) {
+ throw new ErrorWithStack(
+ `Fixture file ${relativePath} does not exist.
+
+Did you run bin/rake karma:fixtures?`,
+ getJSONFixture,
+ );
+ }
+
+ return require(absolutePath);
+}
diff --git a/spec/frontend/helpers/timeout.js b/spec/frontend/helpers/timeout.js
new file mode 100644
index 00000000000..318593a48a4
--- /dev/null
+++ b/spec/frontend/helpers/timeout.js
@@ -0,0 +1,24 @@
+let testTimeoutInMs;
+
+export const setTestTimeout = newTimeoutInMs => {
+ testTimeoutInMs = newTimeoutInMs;
+ jest.setTimeout(newTimeoutInMs);
+};
+
+export const initializeTestTimeout = defaultTimeoutInMs => {
+ setTestTimeout(defaultTimeoutInMs);
+
+ let testStartTime;
+
+ // https://github.com/facebook/jest/issues/6947
+ beforeEach(() => {
+ testStartTime = Date.now();
+ });
+
+ afterEach(() => {
+ const elapsedTimeInMs = Date.now() - testStartTime;
+ if (elapsedTimeInMs > testTimeoutInMs) {
+ throw new Error(`Test took too long (${elapsedTimeInMs}ms > ${testTimeoutInMs}ms)!`);
+ }
+ });
+};
diff --git a/spec/frontend/helpers/vue_mount_component_helper.js b/spec/frontend/helpers/vue_mount_component_helper.js
new file mode 100644
index 00000000000..6848c95d95d
--- /dev/null
+++ b/spec/frontend/helpers/vue_mount_component_helper.js
@@ -0,0 +1,38 @@
+import Vue from 'vue';
+
+const mountComponent = (Component, props = {}, el = null) =>
+ new Component({
+ propsData: props,
+ }).$mount(el);
+
+export const createComponentWithStore = (Component, store, propsData = {}) =>
+ new Component({
+ store,
+ propsData,
+ });
+
+export const mountComponentWithStore = (Component, { el, props, store }) =>
+ new Component({
+ store,
+ propsData: props || {},
+ }).$mount(el);
+
+export const mountComponentWithSlots = (Component, { props, slots }) => {
+ const component = new Component({
+ propsData: props || {},
+ });
+
+ component.$slots = slots;
+
+ return component.$mount();
+};
+
+/**
+ * Mount a component with the given render method.
+ *
+ * This helps with inserting slots that need to be compiled.
+ */
+export const mountComponentWithRender = (render, el = null) =>
+ mountComponent(Vue.extend({ render }), {}, el);
+
+export default mountComponent;
diff --git a/spec/javascripts/ide/lib/common/disposable_spec.js b/spec/frontend/ide/lib/common/disposable_spec.js
index af12ca15369..af12ca15369 100644
--- a/spec/javascripts/ide/lib/common/disposable_spec.js
+++ b/spec/frontend/ide/lib/common/disposable_spec.js
diff --git a/spec/javascripts/ide/lib/diff/diff_spec.js b/spec/frontend/ide/lib/diff/diff_spec.js
index 57f3ac3d365..57f3ac3d365 100644
--- a/spec/javascripts/ide/lib/diff/diff_spec.js
+++ b/spec/frontend/ide/lib/diff/diff_spec.js
diff --git a/spec/javascripts/ide/lib/editor_options_spec.js b/spec/frontend/ide/lib/editor_options_spec.js
index d149a883166..d149a883166 100644
--- a/spec/javascripts/ide/lib/editor_options_spec.js
+++ b/spec/frontend/ide/lib/editor_options_spec.js
diff --git a/spec/frontend/ide/lib/files_spec.js b/spec/frontend/ide/lib/files_spec.js
new file mode 100644
index 00000000000..fe791aa2b74
--- /dev/null
+++ b/spec/frontend/ide/lib/files_spec.js
@@ -0,0 +1,77 @@
+import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils';
+import { decorateFiles, splitParent } from '~/ide/lib/files';
+import { decorateData } from '~/ide/stores/utils';
+
+const TEST_BRANCH_ID = 'lorem-ipsum';
+const TEST_PROJECT_ID = 10;
+
+const createEntries = paths => {
+ const createEntry = (acc, { path, type, children }) => {
+ // Sometimes we need to end the url with a '/'
+ const createUrl = base => (type === 'tree' ? `${base}/` : base);
+
+ const { name, parent } = splitParent(path);
+ const parentEntry = acc[parent];
+
+ acc[path] = {
+ ...decorateData({
+ projectId: TEST_PROJECT_ID,
+ branchId: TEST_BRANCH_ID,
+ id: path,
+ name,
+ path,
+ url: createUrl(`/${TEST_PROJECT_ID}/${type}/${TEST_BRANCH_ID}/-/${path}`),
+ type,
+ previewMode: viewerInformationForPath(path),
+ parentPath: parent,
+ parentTreeUrl: parentEntry
+ ? parentEntry.url
+ : createUrl(`/${TEST_PROJECT_ID}/${type}/${TEST_BRANCH_ID}`),
+ }),
+ tree: children.map(childName => jasmine.objectContaining({ name: childName })),
+ };
+
+ return acc;
+ };
+
+ const entries = paths.reduce(createEntry, {});
+
+ // Wrap entries in jasmine.objectContaining.
+ // We couldn't do this earlier because we still need to select properties from parent entries.
+ return Object.keys(entries).reduce((acc, key) => {
+ acc[key] = jasmine.objectContaining(entries[key]);
+
+ return acc;
+ }, {});
+};
+
+describe('IDE lib decorate files', () => {
+ it('creates entries and treeList', () => {
+ const data = ['app/assets/apples/foo.js', 'app/bugs.js', 'README.md'];
+ const expectedEntries = createEntries([
+ { path: 'app', type: 'tree', children: ['assets', 'bugs.js'] },
+ { path: 'app/assets', type: 'tree', children: ['apples'] },
+ { path: 'app/assets/apples', type: 'tree', children: ['foo.js'] },
+ { path: 'app/assets/apples/foo.js', type: 'blob', children: [] },
+ { path: 'app/bugs.js', type: 'blob', children: [] },
+ { path: 'README.md', type: 'blob', children: [] },
+ ]);
+
+ const { entries, treeList } = decorateFiles({
+ data,
+ branchId: TEST_BRANCH_ID,
+ projectId: TEST_PROJECT_ID,
+ });
+
+ // Here we test the keys and then each key/value individually because `expect(entries).toEqual(expectedEntries)`
+ // was taking a very long time for some reason. Probably due to large objects and nested `jasmine.objectContaining`.
+ const entryKeys = Object.keys(entries);
+
+ expect(entryKeys).toEqual(Object.keys(expectedEntries));
+ entryKeys.forEach(key => {
+ expect(entries[key]).toEqual(expectedEntries[key]);
+ });
+
+ expect(treeList).toEqual([expectedEntries.app, expectedEntries['README.md']]);
+ });
+});
diff --git a/spec/javascripts/ide/stores/modules/commit/mutations_spec.js b/spec/frontend/ide/stores/modules/commit/mutations_spec.js
index 5de7a281d34..5de7a281d34 100644
--- a/spec/javascripts/ide/stores/modules/commit/mutations_spec.js
+++ b/spec/frontend/ide/stores/modules/commit/mutations_spec.js
diff --git a/spec/javascripts/ide/stores/modules/file_templates/getters_spec.js b/spec/frontend/ide/stores/modules/file_templates/getters_spec.js
index 17cb457881f..17cb457881f 100644
--- a/spec/javascripts/ide/stores/modules/file_templates/getters_spec.js
+++ b/spec/frontend/ide/stores/modules/file_templates/getters_spec.js
diff --git a/spec/javascripts/ide/stores/modules/file_templates/mutations_spec.js b/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js
index 8e0e3ae99a1..8e0e3ae99a1 100644
--- a/spec/javascripts/ide/stores/modules/file_templates/mutations_spec.js
+++ b/spec/frontend/ide/stores/modules/file_templates/mutations_spec.js
diff --git a/spec/javascripts/ide/stores/modules/pane/getters_spec.js b/spec/frontend/ide/stores/modules/pane/getters_spec.js
index 8a213323de0..8a213323de0 100644
--- a/spec/javascripts/ide/stores/modules/pane/getters_spec.js
+++ b/spec/frontend/ide/stores/modules/pane/getters_spec.js
diff --git a/spec/javascripts/ide/stores/modules/pane/mutations_spec.js b/spec/frontend/ide/stores/modules/pane/mutations_spec.js
index b5fcd35912e..b5fcd35912e 100644
--- a/spec/javascripts/ide/stores/modules/pane/mutations_spec.js
+++ b/spec/frontend/ide/stores/modules/pane/mutations_spec.js
diff --git a/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js b/spec/frontend/ide/stores/modules/pipelines/getters_spec.js
index 4514896b5ea..4514896b5ea 100644
--- a/spec/javascripts/ide/stores/modules/pipelines/getters_spec.js
+++ b/spec/frontend/ide/stores/modules/pipelines/getters_spec.js
diff --git a/spec/javascripts/ide/stores/mutations/branch_spec.js b/spec/frontend/ide/stores/mutations/branch_spec.js
index 29eb859ddaf..29eb859ddaf 100644
--- a/spec/javascripts/ide/stores/mutations/branch_spec.js
+++ b/spec/frontend/ide/stores/mutations/branch_spec.js
diff --git a/spec/javascripts/ide/stores/mutations/merge_request_spec.js b/spec/frontend/ide/stores/mutations/merge_request_spec.js
index e30ca22022f..e30ca22022f 100644
--- a/spec/javascripts/ide/stores/mutations/merge_request_spec.js
+++ b/spec/frontend/ide/stores/mutations/merge_request_spec.js
diff --git a/spec/javascripts/image_diff/view_types_spec.js b/spec/frontend/image_diff/view_types_spec.js
index e9639f46497..e9639f46497 100644
--- a/spec/javascripts/image_diff/view_types_spec.js
+++ b/spec/frontend/image_diff/view_types_spec.js
diff --git a/spec/javascripts/import_projects/store/getters_spec.js b/spec/frontend/import_projects/store/getters_spec.js
index e5e4a95f473..e5e4a95f473 100644
--- a/spec/javascripts/import_projects/store/getters_spec.js
+++ b/spec/frontend/import_projects/store/getters_spec.js
diff --git a/spec/javascripts/import_projects/store/mutations_spec.js b/spec/frontend/import_projects/store/mutations_spec.js
index 8db8e9819ba..8db8e9819ba 100644
--- a/spec/javascripts/import_projects/store/mutations_spec.js
+++ b/spec/frontend/import_projects/store/mutations_spec.js
diff --git a/spec/javascripts/issuable_suggestions/components/app_spec.js b/spec/frontend/issuable_suggestions/components/app_spec.js
index 7bb8e26b81a..7bb8e26b81a 100644
--- a/spec/javascripts/issuable_suggestions/components/app_spec.js
+++ b/spec/frontend/issuable_suggestions/components/app_spec.js
diff --git a/spec/javascripts/issuable_suggestions/components/item_spec.js b/spec/frontend/issuable_suggestions/components/item_spec.js
index 7bd1fe678f4..7bd1fe678f4 100644
--- a/spec/javascripts/issuable_suggestions/components/item_spec.js
+++ b/spec/frontend/issuable_suggestions/components/item_spec.js
diff --git a/spec/javascripts/issuable_suggestions/mock_data.js b/spec/frontend/issuable_suggestions/mock_data.js
index 4f0f9ef8d62..4f0f9ef8d62 100644
--- a/spec/javascripts/issuable_suggestions/mock_data.js
+++ b/spec/frontend/issuable_suggestions/mock_data.js
diff --git a/spec/javascripts/jobs/components/empty_state_spec.js b/spec/frontend/jobs/components/empty_state_spec.js
index a2df79bdda0..a2df79bdda0 100644
--- a/spec/javascripts/jobs/components/empty_state_spec.js
+++ b/spec/frontend/jobs/components/empty_state_spec.js
diff --git a/spec/javascripts/jobs/components/erased_block_spec.js b/spec/frontend/jobs/components/erased_block_spec.js
index 8e0433d3fb7..8e0433d3fb7 100644
--- a/spec/javascripts/jobs/components/erased_block_spec.js
+++ b/spec/frontend/jobs/components/erased_block_spec.js
diff --git a/spec/javascripts/jobs/components/sidebar_detail_row_spec.js b/spec/frontend/jobs/components/sidebar_detail_row_spec.js
index 42d11266dad..42d11266dad 100644
--- a/spec/javascripts/jobs/components/sidebar_detail_row_spec.js
+++ b/spec/frontend/jobs/components/sidebar_detail_row_spec.js
diff --git a/spec/javascripts/jobs/components/stuck_block_spec.js b/spec/frontend/jobs/components/stuck_block_spec.js
index c320793b2be..c320793b2be 100644
--- a/spec/javascripts/jobs/components/stuck_block_spec.js
+++ b/spec/frontend/jobs/components/stuck_block_spec.js
diff --git a/spec/frontend/jobs/store/getters_spec.js b/spec/frontend/jobs/store/getters_spec.js
new file mode 100644
index 00000000000..379114c3737
--- /dev/null
+++ b/spec/frontend/jobs/store/getters_spec.js
@@ -0,0 +1,243 @@
+import * as getters from '~/jobs/store/getters';
+import state from '~/jobs/store/state';
+
+describe('Job Store Getters', () => {
+ let localState;
+
+ beforeEach(() => {
+ localState = state();
+ });
+
+ describe('headerTime', () => {
+ describe('when the job has started key', () => {
+ it('returns started key', () => {
+ const started = '2018-08-31T16:20:49.023Z';
+ localState.job.started = started;
+
+ expect(getters.headerTime(localState)).toEqual(started);
+ });
+ });
+
+ describe('when the job does not have started key', () => {
+ it('returns created_at key', () => {
+ const created = '2018-08-31T16:20:49.023Z';
+ localState.job.created_at = created;
+
+ expect(getters.headerTime(localState)).toEqual(created);
+ });
+ });
+ });
+
+ describe('shouldRenderCalloutMessage', () => {
+ describe('with status and callout message', () => {
+ it('returns true', () => {
+ localState.job.callout_message = 'Callout message';
+ localState.job.status = { icon: 'passed' };
+
+ expect(getters.shouldRenderCalloutMessage(localState)).toEqual(true);
+ });
+ });
+
+ describe('without status & with callout message', () => {
+ it('returns false', () => {
+ localState.job.callout_message = 'Callout message';
+
+ expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false);
+ });
+ });
+
+ describe('with status & without callout message', () => {
+ it('returns false', () => {
+ localState.job.status = { icon: 'passed' };
+
+ expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false);
+ });
+ });
+ });
+
+ describe('shouldRenderTriggeredLabel', () => {
+ describe('when started equals null', () => {
+ it('returns false', () => {
+ localState.job.started = null;
+
+ expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(false);
+ });
+ });
+
+ describe('when started equals string', () => {
+ it('returns true', () => {
+ localState.job.started = '2018-08-31T16:20:49.023Z';
+
+ expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(true);
+ });
+ });
+ });
+
+ describe('hasEnvironment', () => {
+ describe('without `deployment_status`', () => {
+ it('returns false', () => {
+ expect(getters.hasEnvironment(localState)).toEqual(false);
+ });
+ });
+
+ describe('with an empty object for `deployment_status`', () => {
+ it('returns false', () => {
+ localState.job.deployment_status = {};
+
+ expect(getters.hasEnvironment(localState)).toEqual(false);
+ });
+ });
+
+ describe('when `deployment_status` is defined and not empty', () => {
+ it('returns true', () => {
+ localState.job.deployment_status = {
+ status: 'creating',
+ environment: {
+ last_deployment: {},
+ },
+ };
+
+ expect(getters.hasEnvironment(localState)).toEqual(true);
+ });
+ });
+ });
+
+ describe('hasTrace', () => {
+ describe('when has_trace is true', () => {
+ it('returns true', () => {
+ localState.job.has_trace = true;
+ localState.job.status = {};
+
+ expect(getters.hasTrace(localState)).toEqual(true);
+ });
+ });
+
+ describe('when job is running', () => {
+ it('returns true', () => {
+ localState.job.has_trace = false;
+ localState.job.status = { group: 'running' };
+
+ expect(getters.hasTrace(localState)).toEqual(true);
+ });
+ });
+
+ describe('when has_trace is false and job is not running', () => {
+ it('returns false', () => {
+ localState.job.has_trace = false;
+ localState.job.status = { group: 'pending' };
+
+ expect(getters.hasTrace(localState)).toEqual(false);
+ });
+ });
+ });
+
+ describe('emptyStateIllustration', () => {
+ describe('with defined illustration', () => {
+ it('returns the state illustration object', () => {
+ localState.job.status = {
+ illustration: {
+ path: 'foo',
+ },
+ };
+
+ expect(getters.emptyStateIllustration(localState)).toEqual({ path: 'foo' });
+ });
+ });
+
+ describe('when illustration is not defined', () => {
+ it('returns an empty object', () => {
+ expect(getters.emptyStateIllustration(localState)).toEqual({});
+ });
+ });
+ });
+
+ describe('shouldRenderSharedRunnerLimitWarning', () => {
+ describe('without runners information', () => {
+ it('returns false', () => {
+ expect(getters.shouldRenderSharedRunnerLimitWarning(localState)).toEqual(false);
+ });
+ });
+
+ describe('with runners information', () => {
+ describe('when used quota is less than limit', () => {
+ it('returns false', () => {
+ localState.job.runners = {
+ quota: {
+ used: 33,
+ limit: 2000,
+ },
+ available: true,
+ online: true,
+ };
+
+ expect(getters.shouldRenderSharedRunnerLimitWarning(localState)).toEqual(false);
+ });
+ });
+
+ describe('when used quota is equal to limit', () => {
+ it('returns true', () => {
+ localState.job.runners = {
+ quota: {
+ used: 2000,
+ limit: 2000,
+ },
+ available: true,
+ online: true,
+ };
+
+ expect(getters.shouldRenderSharedRunnerLimitWarning(localState)).toEqual(true);
+ });
+ });
+
+ describe('when used quota is bigger than limit', () => {
+ it('returns true', () => {
+ localState.job.runners = {
+ quota: {
+ used: 2002,
+ limit: 2000,
+ },
+ available: true,
+ online: true,
+ };
+
+ expect(getters.shouldRenderSharedRunnerLimitWarning(localState)).toEqual(true);
+ });
+ });
+ });
+ });
+
+ describe('hasRunnersForProject', () => {
+ describe('with available and offline runners', () => {
+ it('returns true', () => {
+ localState.job.runners = {
+ available: true,
+ online: false,
+ };
+
+ expect(getters.hasRunnersForProject(localState)).toEqual(true);
+ });
+ });
+
+ describe('with non available runners', () => {
+ it('returns false', () => {
+ localState.job.runners = {
+ available: false,
+ online: false,
+ };
+
+ expect(getters.hasRunnersForProject(localState)).toEqual(false);
+ });
+ });
+
+ describe('with online runners', () => {
+ it('returns false', () => {
+ localState.job.runners = {
+ available: false,
+ online: true,
+ };
+
+ expect(getters.hasRunnersForProject(localState)).toEqual(false);
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/jobs/store/mutations_spec.js b/spec/frontend/jobs/store/mutations_spec.js
index d7908efcf13..d7908efcf13 100644
--- a/spec/javascripts/jobs/store/mutations_spec.js
+++ b/spec/frontend/jobs/store/mutations_spec.js
diff --git a/spec/javascripts/labels_select_spec.js b/spec/frontend/labels_select_spec.js
index acfdc885032..acfdc885032 100644
--- a/spec/javascripts/labels_select_spec.js
+++ b/spec/frontend/labels_select_spec.js
diff --git a/spec/frontend/lib/utils/ajax_cache_spec.js b/spec/frontend/lib/utils/ajax_cache_spec.js
new file mode 100644
index 00000000000..e2ee70b9d69
--- /dev/null
+++ b/spec/frontend/lib/utils/ajax_cache_spec.js
@@ -0,0 +1,161 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import AjaxCache from '~/lib/utils/ajax_cache';
+
+describe('AjaxCache', () => {
+ const dummyEndpoint = '/AjaxCache/dummyEndpoint';
+ const dummyResponse = {
+ important: 'dummy data',
+ };
+
+ beforeEach(() => {
+ AjaxCache.internalStorage = {};
+ AjaxCache.pendingRequests = {};
+ });
+
+ describe('get', () => {
+ it('returns undefined if cache is empty', () => {
+ const data = AjaxCache.get(dummyEndpoint);
+
+ expect(data).toBe(undefined);
+ });
+
+ it('returns undefined if cache contains no matching data', () => {
+ AjaxCache.internalStorage['not matching'] = dummyResponse;
+
+ const data = AjaxCache.get(dummyEndpoint);
+
+ expect(data).toBe(undefined);
+ });
+
+ it('returns matching data', () => {
+ AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
+
+ const data = AjaxCache.get(dummyEndpoint);
+
+ expect(data).toBe(dummyResponse);
+ });
+ });
+
+ describe('hasData', () => {
+ it('returns false if cache is empty', () => {
+ expect(AjaxCache.hasData(dummyEndpoint)).toBe(false);
+ });
+
+ it('returns false if cache contains no matching data', () => {
+ AjaxCache.internalStorage['not matching'] = dummyResponse;
+
+ expect(AjaxCache.hasData(dummyEndpoint)).toBe(false);
+ });
+
+ it('returns true if data is available', () => {
+ AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
+
+ expect(AjaxCache.hasData(dummyEndpoint)).toBe(true);
+ });
+ });
+
+ describe('remove', () => {
+ it('does nothing if cache is empty', () => {
+ AjaxCache.remove(dummyEndpoint);
+
+ expect(AjaxCache.internalStorage).toEqual({});
+ });
+
+ it('does nothing if cache contains no matching data', () => {
+ AjaxCache.internalStorage['not matching'] = dummyResponse;
+
+ AjaxCache.remove(dummyEndpoint);
+
+ expect(AjaxCache.internalStorage['not matching']).toBe(dummyResponse);
+ });
+
+ it('removes matching data', () => {
+ AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
+
+ AjaxCache.remove(dummyEndpoint);
+
+ expect(AjaxCache.internalStorage).toEqual({});
+ });
+ });
+
+ describe('override', () => {
+ it('overrides existing cache', () => {
+ AjaxCache.internalStorage.endpoint = 'existing-endpoint';
+ AjaxCache.override('endpoint', 'new-endpoint');
+
+ expect(AjaxCache.internalStorage.endpoint).toEqual('new-endpoint');
+ });
+ });
+
+ describe('retrieve', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+
+ jest.spyOn(axios, 'get');
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('stores and returns data from Ajax call if cache is empty', () => {
+ mock.onGet(dummyEndpoint).reply(200, dummyResponse);
+
+ return AjaxCache.retrieve(dummyEndpoint).then(data => {
+ expect(data).toEqual(dummyResponse);
+ expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse);
+ });
+ });
+
+ it('makes no Ajax call if request is pending', () => {
+ mock.onGet(dummyEndpoint).reply(200, dummyResponse);
+
+ return Promise.all([
+ AjaxCache.retrieve(dummyEndpoint),
+ AjaxCache.retrieve(dummyEndpoint),
+ ]).then(() => {
+ expect(axios.get).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ it('returns undefined if Ajax call fails and cache is empty', () => {
+ const errorMessage = 'Network Error';
+ mock.onGet(dummyEndpoint).networkError();
+
+ expect.assertions(2);
+ return AjaxCache.retrieve(dummyEndpoint).catch(error => {
+ expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`);
+ expect(error.textStatus).toBe(errorMessage);
+ });
+ });
+
+ it('makes no Ajax call if matching data exists', () => {
+ AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
+
+ return AjaxCache.retrieve(dummyEndpoint).then(data => {
+ expect(data).toBe(dummyResponse);
+ expect(axios.get).not.toHaveBeenCalled();
+ });
+ });
+
+ it('makes Ajax call even if matching data exists when forceRequest parameter is provided', () => {
+ const oldDummyResponse = {
+ important: 'old dummy data',
+ };
+
+ AjaxCache.internalStorage[dummyEndpoint] = oldDummyResponse;
+
+ mock.onGet(dummyEndpoint).reply(200, dummyResponse);
+
+ return Promise.all([
+ AjaxCache.retrieve(dummyEndpoint),
+ AjaxCache.retrieve(dummyEndpoint, true),
+ ]).then(data => {
+ expect(data).toEqual([oldDummyResponse, dummyResponse]);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/lib/utils/autosave_spec.js b/spec/frontend/lib/utils/autosave_spec.js
new file mode 100644
index 00000000000..12e97f6cdec
--- /dev/null
+++ b/spec/frontend/lib/utils/autosave_spec.js
@@ -0,0 +1,64 @@
+import { clearDraft, getDraft, updateDraft } from '~/lib/utils/autosave';
+
+describe('autosave utils', () => {
+ const autosaveKey = 'dummy-autosave-key';
+ const text = 'some dummy text';
+
+ describe('clearDraft', () => {
+ beforeEach(() => {
+ localStorage.setItem(`autosave/${autosaveKey}`, text);
+ });
+
+ afterEach(() => {
+ localStorage.removeItem(`autosave/${autosaveKey}`);
+ });
+
+ it('removes the draft from localStorage', () => {
+ clearDraft(autosaveKey);
+
+ expect(localStorage.getItem(`autosave/${autosaveKey}`)).toBe(null);
+ });
+ });
+
+ describe('getDraft', () => {
+ beforeEach(() => {
+ localStorage.setItem(`autosave/${autosaveKey}`, text);
+ });
+
+ afterEach(() => {
+ localStorage.removeItem(`autosave/${autosaveKey}`);
+ });
+
+ it('returns the draft from localStorage', () => {
+ const result = getDraft(autosaveKey);
+
+ expect(result).toBe(text);
+ });
+
+ it('returns null if no entry exists in localStorage', () => {
+ localStorage.removeItem(`autosave/${autosaveKey}`);
+
+ const result = getDraft(autosaveKey);
+
+ expect(result).toBe(null);
+ });
+ });
+
+ describe('updateDraft', () => {
+ beforeEach(() => {
+ localStorage.setItem(`autosave/${autosaveKey}`, text);
+ });
+
+ afterEach(() => {
+ localStorage.removeItem(`autosave/${autosaveKey}`);
+ });
+
+ it('removes the draft from localStorage', () => {
+ const newText = 'new text';
+
+ updateDraft(autosaveKey, newText);
+
+ expect(localStorage.getItem(`autosave/${autosaveKey}`)).toBe(newText);
+ });
+ });
+});
diff --git a/spec/javascripts/lib/utils/cache_spec.js b/spec/frontend/lib/utils/cache_spec.js
index 2fe02a7592c..2fe02a7592c 100644
--- a/spec/javascripts/lib/utils/cache_spec.js
+++ b/spec/frontend/lib/utils/cache_spec.js
diff --git a/spec/javascripts/lib/utils/grammar_spec.js b/spec/frontend/lib/utils/grammar_spec.js
index 377b2ffb48c..377b2ffb48c 100644
--- a/spec/javascripts/lib/utils/grammar_spec.js
+++ b/spec/frontend/lib/utils/grammar_spec.js
diff --git a/spec/javascripts/lib/utils/image_utility_spec.js b/spec/frontend/lib/utils/image_utility_spec.js
index a7eff419fba..a7eff419fba 100644
--- a/spec/javascripts/lib/utils/image_utility_spec.js
+++ b/spec/frontend/lib/utils/image_utility_spec.js
diff --git a/spec/frontend/lib/utils/number_utility_spec.js b/spec/frontend/lib/utils/number_utility_spec.js
new file mode 100644
index 00000000000..818404bad81
--- /dev/null
+++ b/spec/frontend/lib/utils/number_utility_spec.js
@@ -0,0 +1,101 @@
+import {
+ formatRelevantDigits,
+ bytesToKiB,
+ bytesToMiB,
+ bytesToGiB,
+ numberToHumanSize,
+ sum,
+} from '~/lib/utils/number_utils';
+
+describe('Number Utils', () => {
+ describe('formatRelevantDigits', () => {
+ it('returns an empty string when the number is NaN', () => {
+ expect(formatRelevantDigits('fail')).toBe('');
+ });
+
+ it('returns 4 decimals when there is 4 plus digits to the left', () => {
+ const formattedNumber = formatRelevantDigits('1000.1234567');
+ const rightFromDecimal = formattedNumber.split('.')[1];
+ const leftFromDecimal = formattedNumber.split('.')[0];
+
+ expect(rightFromDecimal.length).toBe(4);
+ expect(leftFromDecimal.length).toBe(4);
+ });
+
+ it('returns 3 decimals when there is 1 digit to the left', () => {
+ const formattedNumber = formatRelevantDigits('0.1234567');
+ const rightFromDecimal = formattedNumber.split('.')[1];
+ const leftFromDecimal = formattedNumber.split('.')[0];
+
+ expect(rightFromDecimal.length).toBe(3);
+ expect(leftFromDecimal.length).toBe(1);
+ });
+
+ it('returns 2 decimals when there is 2 digits to the left', () => {
+ const formattedNumber = formatRelevantDigits('10.1234567');
+ const rightFromDecimal = formattedNumber.split('.')[1];
+ const leftFromDecimal = formattedNumber.split('.')[0];
+
+ expect(rightFromDecimal.length).toBe(2);
+ expect(leftFromDecimal.length).toBe(2);
+ });
+
+ it('returns 1 decimal when there is 3 digits to the left', () => {
+ const formattedNumber = formatRelevantDigits('100.1234567');
+ const rightFromDecimal = formattedNumber.split('.')[1];
+ const leftFromDecimal = formattedNumber.split('.')[0];
+
+ expect(rightFromDecimal.length).toBe(1);
+ expect(leftFromDecimal.length).toBe(3);
+ });
+ });
+
+ describe('bytesToKiB', () => {
+ it('calculates KiB for the given bytes', () => {
+ expect(bytesToKiB(1024)).toEqual(1);
+ expect(bytesToKiB(1000)).toEqual(0.9765625);
+ });
+ });
+
+ describe('bytesToMiB', () => {
+ it('calculates MiB for the given bytes', () => {
+ expect(bytesToMiB(1048576)).toEqual(1);
+ expect(bytesToMiB(1000000)).toEqual(0.95367431640625);
+ });
+ });
+
+ describe('bytesToGiB', () => {
+ it('calculates GiB for the given bytes', () => {
+ expect(bytesToGiB(1073741824)).toEqual(1);
+ expect(bytesToGiB(10737418240)).toEqual(10);
+ });
+ });
+
+ describe('numberToHumanSize', () => {
+ it('should return bytes', () => {
+ expect(numberToHumanSize(654)).toEqual('654 bytes');
+ });
+
+ it('should return KiB', () => {
+ expect(numberToHumanSize(1079)).toEqual('1.05 KiB');
+ });
+
+ it('should return MiB', () => {
+ expect(numberToHumanSize(10485764)).toEqual('10.00 MiB');
+ });
+
+ it('should return GiB', () => {
+ expect(numberToHumanSize(10737418240)).toEqual('10.00 GiB');
+ });
+ });
+
+ describe('sum', () => {
+ it('should add up two values', () => {
+ expect(sum(1, 2)).toEqual(3);
+ });
+
+ it('should add up all the values in an array when passed to a reducer', () => {
+ expect([1, 2, 3, 4, 5].reduce(sum)).toEqual(15);
+ });
+ });
+});
diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/frontend/lib/utils/text_utility_spec.js
index 0a266b19ea5..0a266b19ea5 100644
--- a/spec/javascripts/lib/utils/text_utility_spec.js
+++ b/spec/frontend/lib/utils/text_utility_spec.js
diff --git a/spec/javascripts/locale/ensure_single_line_spec.js b/spec/frontend/locale/ensure_single_line_spec.js
index 20b04cab9c8..20b04cab9c8 100644
--- a/spec/javascripts/locale/ensure_single_line_spec.js
+++ b/spec/frontend/locale/ensure_single_line_spec.js
diff --git a/spec/javascripts/locale/sprintf_spec.js b/spec/frontend/locale/sprintf_spec.js
index 52e903b819f..52e903b819f 100644
--- a/spec/javascripts/locale/sprintf_spec.js
+++ b/spec/frontend/locale/sprintf_spec.js
diff --git a/spec/frontend/mr_popover/__snapshots__/mr_popover_spec.js.snap b/spec/frontend/mr_popover/__snapshots__/mr_popover_spec.js.snap
new file mode 100644
index 00000000000..5f9f13d591d
--- /dev/null
+++ b/spec/frontend/mr_popover/__snapshots__/mr_popover_spec.js.snap
@@ -0,0 +1,93 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`MR Popover loaded state matches the snapshot 1`] = `
+<glpopover-stub
+ boundary="viewport"
+ placement="top"
+ show=""
+ target=""
+>
+ <div
+ class="mr-popover"
+ >
+ <div
+ class="d-flex align-items-center justify-content-between"
+ >
+ <div
+ class="d-inline-flex align-items-center"
+ >
+ <div
+ class="issuable-status-box status-box status-box-open"
+ >
+
+ Open
+
+ </div>
+
+ <span
+ class="text-secondary"
+ >
+ Opened
+ <time>
+ just now
+ </time>
+ </span>
+ </div>
+
+ <ciicon-stub
+ cssclasses=""
+ size="16"
+ status="[object Object]"
+ />
+ </div>
+
+ <h5
+ class="my-2"
+ >
+ MR Title
+ </h5>
+
+ <div
+ class="text-secondary"
+ >
+
+ foo/bar!1
+
+ </div>
+ </div>
+</glpopover-stub>
+`;
+
+exports[`MR Popover shows skeleton-loader while apollo is loading 1`] = `
+<glpopover-stub
+ boundary="viewport"
+ placement="top"
+ show=""
+ target=""
+>
+ <div
+ class="mr-popover"
+ >
+ <div>
+ <glskeletonloading-stub
+ class="animation-container-small mt-1"
+ lines="1"
+ />
+ </div>
+
+ <h5
+ class="my-2"
+ >
+ MR Title
+ </h5>
+
+ <div
+ class="text-secondary"
+ >
+
+ foo/bar!1
+
+ </div>
+ </div>
+</glpopover-stub>
+`;
diff --git a/spec/frontend/mr_popover/mr_popover_spec.js b/spec/frontend/mr_popover/mr_popover_spec.js
new file mode 100644
index 00000000000..79ed4163010
--- /dev/null
+++ b/spec/frontend/mr_popover/mr_popover_spec.js
@@ -0,0 +1,61 @@
+import MRPopover from '~/mr_popover/components/mr_popover';
+import { shallowMount } from '@vue/test-utils';
+
+describe('MR Popover', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(MRPopover, {
+ propsData: {
+ target: document.createElement('a'),
+ projectPath: 'foo/bar',
+ mergeRequestIID: '1',
+ mergeRequestTitle: 'MR Title',
+ },
+ mocks: {
+ $apollo: {
+ loading: false,
+ },
+ },
+ });
+ });
+
+ it('shows skeleton-loader while apollo is loading', () => {
+ wrapper.vm.$apollo.loading = true;
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ describe('loaded state', () => {
+ it('matches the snapshot', () => {
+ wrapper.setData({
+ mergeRequest: {
+ state: 'opened',
+ createdAt: new Date(),
+ headPipeline: {
+ detailedStatus: {
+ group: 'success',
+ status: 'status_success',
+ },
+ },
+ },
+ });
+
+ expect(wrapper.element).toMatchSnapshot();
+ });
+
+ it('does not show CI Icon if there is no pipeline data', () => {
+ wrapper.setData({
+ mergeRequest: {
+ state: 'opened',
+ headPipeline: null,
+ stateHumanName: 'Open',
+ title: 'Merge Request Title',
+ createdAt: new Date(),
+ },
+ });
+
+ expect(wrapper.contains('ciicon-stub')).toBe(false);
+ });
+ });
+});
diff --git a/spec/javascripts/notebook/lib/highlight_spec.js b/spec/frontend/notebook/lib/highlight_spec.js
index d71c5718858..d71c5718858 100644
--- a/spec/javascripts/notebook/lib/highlight_spec.js
+++ b/spec/frontend/notebook/lib/highlight_spec.js
diff --git a/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap b/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap
new file mode 100644
index 00000000000..11d65ced180
--- /dev/null
+++ b/spec/frontend/notes/components/__snapshots__/discussion_jump_to_next_button_spec.js.snap
@@ -0,0 +1,20 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`JumpToNextDiscussionButton matches the snapshot 1`] = `
+<div
+ class="btn-group"
+ role="group"
+>
+ <button
+ class="btn btn-default discussion-next-btn"
+ data-original-title="Jump to next unresolved discussion"
+ title=""
+ >
+ <icon-stub
+ cssclasses=""
+ name="comment-next"
+ size="16"
+ />
+ </button>
+</div>
+`;
diff --git a/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js b/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
new file mode 100644
index 00000000000..989b0458481
--- /dev/null
+++ b/spec/frontend/notes/components/discussion_jump_to_next_button_spec.js
@@ -0,0 +1,30 @@
+import JumpToNextDiscussionButton from '~/notes/components/discussion_jump_to_next_button.vue';
+import { shallowMount } from '@vue/test-utils';
+
+describe('JumpToNextDiscussionButton', () => {
+ let wrapper;
+
+ beforeEach(() => {
+ wrapper = shallowMount(JumpToNextDiscussionButton, {
+ sync: false,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('matches the snapshot', () => {
+ expect(wrapper.vm.$el).toMatchSnapshot();
+ });
+
+ it('emits onClick event on button click', () => {
+ const button = wrapper.find({ ref: 'button' });
+
+ button.trigger('click');
+
+ expect(wrapper.emitted()).toEqual({
+ onClick: [[]],
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/discussion_reply_placeholder_spec.js b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js
index 07a366cf339..07a366cf339 100644
--- a/spec/javascripts/notes/components/discussion_reply_placeholder_spec.js
+++ b/spec/frontend/notes/components/discussion_reply_placeholder_spec.js
diff --git a/spec/javascripts/notes/components/discussion_resolve_button_spec.js b/spec/frontend/notes/components/discussion_resolve_button_spec.js
index 5024f40ec5d..5024f40ec5d 100644
--- a/spec/javascripts/notes/components/discussion_resolve_button_spec.js
+++ b/spec/frontend/notes/components/discussion_resolve_button_spec.js
diff --git a/spec/javascripts/notes/components/note_attachment_spec.js b/spec/frontend/notes/components/note_attachment_spec.js
index b14a518b622..b14a518b622 100644
--- a/spec/javascripts/notes/components/note_attachment_spec.js
+++ b/spec/frontend/notes/components/note_attachment_spec.js
diff --git a/spec/javascripts/notes/components/note_edited_text_spec.js b/spec/frontend/notes/components/note_edited_text_spec.js
index e4c8d954d50..e4c8d954d50 100644
--- a/spec/javascripts/notes/components/note_edited_text_spec.js
+++ b/spec/frontend/notes/components/note_edited_text_spec.js
diff --git a/spec/javascripts/performance_bar/services/performance_bar_service_spec.js b/spec/frontend/performance_bar/services/performance_bar_service_spec.js
index cfec4b779e4..cfec4b779e4 100644
--- a/spec/javascripts/performance_bar/services/performance_bar_service_spec.js
+++ b/spec/frontend/performance_bar/services/performance_bar_service_spec.js
diff --git a/spec/javascripts/pipelines/blank_state_spec.js b/spec/frontend/pipelines/blank_state_spec.js
index 033bd5ccb73..033bd5ccb73 100644
--- a/spec/javascripts/pipelines/blank_state_spec.js
+++ b/spec/frontend/pipelines/blank_state_spec.js
diff --git a/spec/javascripts/pipelines/empty_state_spec.js b/spec/frontend/pipelines/empty_state_spec.js
index f12950b8fce..f12950b8fce 100644
--- a/spec/javascripts/pipelines/empty_state_spec.js
+++ b/spec/frontend/pipelines/empty_state_spec.js
diff --git a/spec/javascripts/pipelines/pipeline_store_spec.js b/spec/frontend/pipelines/pipeline_store_spec.js
index 1d5754d1f05..1d5754d1f05 100644
--- a/spec/javascripts/pipelines/pipeline_store_spec.js
+++ b/spec/frontend/pipelines/pipeline_store_spec.js
diff --git a/spec/javascripts/pipelines/pipelines_store_spec.js b/spec/frontend/pipelines/pipelines_store_spec.js
index ce21f788ed5..ce21f788ed5 100644
--- a/spec/javascripts/pipelines/pipelines_store_spec.js
+++ b/spec/frontend/pipelines/pipelines_store_spec.js
diff --git a/spec/javascripts/registry/getters_spec.js b/spec/frontend/registry/getters_spec.js
index 839aa718997..839aa718997 100644
--- a/spec/javascripts/registry/getters_spec.js
+++ b/spec/frontend/registry/getters_spec.js
diff --git a/spec/javascripts/reports/components/report_link_spec.js b/spec/frontend/reports/components/report_link_spec.js
index f879899e9c5..f879899e9c5 100644
--- a/spec/javascripts/reports/components/report_link_spec.js
+++ b/spec/frontend/reports/components/report_link_spec.js
diff --git a/spec/javascripts/reports/store/utils_spec.js b/spec/frontend/reports/store/utils_spec.js
index 1679d120db2..1679d120db2 100644
--- a/spec/javascripts/reports/store/utils_spec.js
+++ b/spec/frontend/reports/store/utils_spec.js
diff --git a/spec/javascripts/sidebar/confidential_edit_buttons_spec.js b/spec/frontend/sidebar/confidential_edit_buttons_spec.js
index 32da9f83112..32da9f83112 100644
--- a/spec/javascripts/sidebar/confidential_edit_buttons_spec.js
+++ b/spec/frontend/sidebar/confidential_edit_buttons_spec.js
diff --git a/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js b/spec/frontend/sidebar/confidential_edit_form_buttons_spec.js
index 369088cb258..369088cb258 100644
--- a/spec/javascripts/sidebar/confidential_edit_form_buttons_spec.js
+++ b/spec/frontend/sidebar/confidential_edit_form_buttons_spec.js
diff --git a/spec/javascripts/sidebar/lock/edit_form_spec.js b/spec/frontend/sidebar/lock/edit_form_spec.js
index ec10a999a40..ec10a999a40 100644
--- a/spec/javascripts/sidebar/lock/edit_form_spec.js
+++ b/spec/frontend/sidebar/lock/edit_form_spec.js
diff --git a/spec/frontend/test_setup.js b/spec/frontend/test_setup.js
index 7ad2e97e7e6..006fc60ef57 100644
--- a/spec/frontend/test_setup.js
+++ b/spec/frontend/test_setup.js
@@ -1,16 +1,25 @@
-const testTimeoutInMs = 300;
-jest.setTimeout(testTimeoutInMs);
+import Vue from 'vue';
+import Translate from '~/vue_shared/translate';
+import axios from '~/lib/utils/axios_utils';
+import { initializeTestTimeout } from './helpers/timeout';
-let testStartTime;
-
-// https://github.com/facebook/jest/issues/6947
-beforeEach(() => {
- testStartTime = Date.now();
+// wait for pending setTimeout()s
+afterEach(() => {
+ jest.runAllTimers();
});
-afterEach(() => {
- const elapsedTimeInMs = Date.now() - testStartTime;
- if (elapsedTimeInMs > testTimeoutInMs) {
- throw new Error(`Test took too long (${elapsedTimeInMs}ms > ${testTimeoutInMs}ms)!`);
- }
+initializeTestTimeout(300);
+
+// fail tests for unmocked requests
+beforeEach(done => {
+ axios.defaults.adapter = config => {
+ const error = new Error(`Unexpected unmocked request: ${JSON.stringify(config, null, 2)}`);
+ error.config = config;
+ done.fail(error);
+ return Promise.reject(error);
+ };
+
+ done();
});
+
+Vue.use(Translate);
diff --git a/spec/javascripts/u2f/util_spec.js b/spec/frontend/u2f/util_spec.js
index 32cd6891384..32cd6891384 100644
--- a/spec/javascripts/u2f/util_spec.js
+++ b/spec/frontend/u2f/util_spec.js
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_container_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_container_spec.js
index 16c8c939a6f..16c8c939a6f 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_container_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_widget_container_spec.js
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_icon_spec.js b/spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js
index f7c2376eebf..f7c2376eebf 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_icon_spec.js
+++ b/spec/frontend/vue_mr_widget/components/mr_widget_icon_spec.js
diff --git a/spec/javascripts/vue_mr_widget/components/states/commit_edit_spec.js b/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js
index 994d6255324..994d6255324 100644
--- a/spec/javascripts/vue_mr_widget/components/states/commit_edit_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/commit_edit_spec.js
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js
index daf1cc2d98b..daf1cc2d98b 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_commit_message_dropdown_spec.js
diff --git a/spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js b/spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js
new file mode 100644
index 00000000000..9ee2f88c78d
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/components/states/mr_widget_commits_header_spec.js
@@ -0,0 +1,136 @@
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import CommitsHeader from '~/vue_merge_request_widget/components/states/commits_header.vue';
+import Icon from '~/vue_shared/components/icon.vue';
+
+const localVue = createLocalVue();
+
+describe('Commits header component', () => {
+ let wrapper;
+
+ const createComponent = props => {
+ wrapper = shallowMount(localVue.extend(CommitsHeader), {
+ localVue,
+ sync: false,
+ propsData: {
+ isSquashEnabled: false,
+ targetBranch: 'master',
+ commitsCount: 5,
+ isFastForwardEnabled: false,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ const findHeaderWrapper = () => wrapper.find('.js-mr-widget-commits-count');
+ const findCommitToggle = () => wrapper.find('.commit-edit-toggle');
+ const findIcon = () => wrapper.find(Icon);
+ const findCommitsCountMessage = () => wrapper.find('.commits-count-message');
+ const findTargetBranchMessage = () => wrapper.find('.label-branch');
+ const findModifyButton = () => wrapper.find('.modify-message-button');
+
+ describe('when fast-forward is enabled', () => {
+ beforeEach(() => {
+ createComponent({
+ isFastForwardEnabled: true,
+ isSquashEnabled: true,
+ });
+ });
+
+ it('has commits count message showing 1 commit', () => {
+ expect(findCommitsCountMessage().text()).toBe('1 commit');
+ });
+
+ it('has button with modify commit message', () => {
+ expect(findModifyButton().text()).toBe('Modify commit message');
+ });
+
+ it('does not have merge commit part of the message', () => {
+ expect(findHeaderWrapper().text()).not.toContain('1 merge commit');
+ });
+ });
+
+ describe('when collapsed', () => {
+ it('toggle has aria-label equal to Expand', () => {
+ createComponent();
+
+ expect(findCommitToggle().attributes('aria-label')).toBe('Expand');
+ });
+
+ it('has a chevron-right icon', () => {
+ createComponent();
+ wrapper.setData({ expanded: false });
+
+ expect(findIcon().props('name')).toBe('chevron-right');
+ });
+
+ describe('when squash is disabled', () => {
+ beforeEach(() => {
+ createComponent();
+ });
+
+ it('has commits count message showing correct amount of commits', () => {
+ expect(findCommitsCountMessage().text()).toBe('5 commits');
+ });
+
+ it('has button with modify merge commit message', () => {
+ expect(findModifyButton().text()).toBe('Modify merge commit');
+ });
+ });
+
+ describe('when squash is enabled', () => {
+ beforeEach(() => {
+ createComponent({ isSquashEnabled: true });
+ });
+
+ it('has commits count message showing one commit when squash is enabled', () => {
+ expect(findCommitsCountMessage().text()).toBe('1 commit');
+ });
+
+ it('has button with modify commit messages text', () => {
+ expect(findModifyButton().text()).toBe('Modify commit messages');
+ });
+ });
+
+ it('has correct target branch displayed', () => {
+ createComponent();
+
+ expect(findTargetBranchMessage().text()).toBe('master');
+ });
+
+ it('does has merge commit part of the message', () => {
+ expect(findHeaderWrapper().text()).toContain('1 merge commit');
+ });
+ });
+
+ describe('when expanded', () => {
+ beforeEach(() => {
+ createComponent();
+ wrapper.setData({ expanded: true });
+ });
+
+ it('toggle has aria-label equal to collapse', done => {
+ wrapper.vm.$nextTick(() => {
+ expect(findCommitToggle().attributes('aria-label')).toBe('Collapse');
+ done();
+ });
+ });
+
+ it('has a chevron-down icon', done => {
+ wrapper.vm.$nextTick(() => {
+ expect(findIcon().props('name')).toBe('chevron-down');
+ done();
+ });
+ });
+
+ it('has a collapse text', done => {
+ wrapper.vm.$nextTick(() => {
+ expect(findHeaderWrapper().text()).toBe('Collapse');
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js b/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js
index b356ea85cad..b356ea85cad 100644
--- a/spec/javascripts/vue_mr_widget/stores/get_state_key_spec.js
+++ b/spec/frontend/vue_mr_widget/stores/get_state_key_spec.js
diff --git a/spec/javascripts/vue_shared/components/callout_spec.js b/spec/frontend/vue_shared/components/callout_spec.js
index 91208dfb31a..91208dfb31a 100644
--- a/spec/javascripts/vue_shared/components/callout_spec.js
+++ b/spec/frontend/vue_shared/components/callout_spec.js
diff --git a/spec/javascripts/vue_shared/components/code_block_spec.js b/spec/frontend/vue_shared/components/code_block_spec.js
index 6b91a20ff76..6b91a20ff76 100644
--- a/spec/javascripts/vue_shared/components/code_block_spec.js
+++ b/spec/frontend/vue_shared/components/code_block_spec.js
diff --git a/spec/javascripts/vue_shared/components/diff_viewer/viewers/mode_changed_spec.js b/spec/frontend/vue_shared/components/diff_viewer/viewers/mode_changed_spec.js
index c4358f0d9cb..c4358f0d9cb 100644
--- a/spec/javascripts/vue_shared/components/diff_viewer/viewers/mode_changed_spec.js
+++ b/spec/frontend/vue_shared/components/diff_viewer/viewers/mode_changed_spec.js
diff --git a/spec/javascripts/vue_shared/components/identicon_spec.js b/spec/frontend/vue_shared/components/identicon_spec.js
index 0b3dbb61c96..0b3dbb61c96 100644
--- a/spec/javascripts/vue_shared/components/identicon_spec.js
+++ b/spec/frontend/vue_shared/components/identicon_spec.js
diff --git a/spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js b/spec/frontend/vue_shared/components/lib/utils/dom_utils_spec.js
index 2388660b0c2..2388660b0c2 100644
--- a/spec/javascripts/vue_shared/components/lib/utils/dom_utils_spec.js
+++ b/spec/frontend/vue_shared/components/lib/utils/dom_utils_spec.js
diff --git a/spec/javascripts/vue_shared/components/pagination_links_spec.js b/spec/frontend/vue_shared/components/pagination_links_spec.js
index d0cb3731050..d0cb3731050 100644
--- a/spec/javascripts/vue_shared/components/pagination_links_spec.js
+++ b/spec/frontend/vue_shared/components/pagination_links_spec.js
diff --git a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js b/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js
index 536bb57b946..536bb57b946 100644
--- a/spec/javascripts/vue_shared/components/time_ago_tooltip_spec.js
+++ b/spec/frontend/vue_shared/components/time_ago_tooltip_spec.js
diff --git a/spec/javascripts/vuex_shared/modules/modal/mutations_spec.js b/spec/frontend/vuex_shared/modules/modal/mutations_spec.js
index d07f8ba1e65..d07f8ba1e65 100644
--- a/spec/javascripts/vuex_shared/modules/modal/mutations_spec.js
+++ b/spec/frontend/vuex_shared/modules/modal/mutations_spec.js
diff --git a/spec/graphql/features/authorization_spec.rb b/spec/graphql/features/authorization_spec.rb
new file mode 100644
index 00000000000..a229d29afa6
--- /dev/null
+++ b/spec/graphql/features/authorization_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'Gitlab::Graphql::Authorization' do
+ set(:user) { create(:user) }
+
+ let(:test_object) { double(name: 'My name') }
+ let(:object_type) { object_type_class }
+ let(:query_type) { query_type_class(object_type, test_object) }
+ let(:schema) { schema_class(query_type) }
+
+ let(:execute) do
+ schema.execute(
+ query_string,
+ context: { current_user: user },
+ variables: {}
+ )
+ end
+
+ let(:result) { execute['data'] }
+
+ before do
+ # By default, disallow all permissions.
+ allow(Ability).to receive(:allowed?).and_return(false)
+ end
+
+ describe 'authorizing with a single permission' do
+ let(:query_string) { '{ singlePermission() { name } }' }
+
+ subject { result['singlePermission'] }
+
+ it 'should return the protected field when user has permission' do
+ permit(:foo)
+
+ expect(subject['name']).to eq(test_object.name)
+ end
+
+ it 'should return nil when user is not authorized' do
+ expect(subject).to be_nil
+ end
+ end
+
+ describe 'authorizing with an Array of permissions' do
+ let(:query_string) { '{ permissionCollection() { name } }' }
+
+ subject { result['permissionCollection'] }
+
+ it 'should return the protected field when user has all permissions' do
+ permit(:foo, :bar)
+
+ expect(subject['name']).to eq(test_object.name)
+ end
+
+ it 'should return nil when user only has one of the permissions' do
+ permit(:foo)
+
+ expect(subject).to be_nil
+ end
+
+ it 'should return nil when user only has none of the permissions' do
+ expect(subject).to be_nil
+ end
+ end
+
+ private
+
+ def permit(*permissions)
+ permissions.each do |permission|
+ allow(Ability).to receive(:allowed?).with(user, permission, test_object).and_return(true)
+ end
+ end
+
+ def object_type_class
+ Class.new(Types::BaseObject) do
+ graphql_name 'TestObject'
+
+ field :name, GraphQL::STRING_TYPE, null: true
+ end
+ end
+
+ def query_type_class(type, object)
+ Class.new(Types::BaseObject) do
+ graphql_name 'TestQuery'
+
+ field :single_permission, type,
+ null: true,
+ authorize: :foo,
+ resolve: ->(obj, args, ctx) { object }
+
+ field :permission_collection, type,
+ null: true,
+ resolve: ->(obj, args, ctx) { object } do
+ authorize [:foo, :bar]
+ end
+ end
+ end
+
+ def schema_class(query)
+ Class.new(GraphQL::Schema) do
+ use Gitlab::Graphql::Authorize
+
+ query(query)
+ end
+ end
+end
diff --git a/spec/graphql/resolvers/issues_resolver_spec.rb b/spec/graphql/resolvers/issues_resolver_spec.rb
index 66de372e9fe..5f9c180cbb7 100644
--- a/spec/graphql/resolvers/issues_resolver_spec.rb
+++ b/spec/graphql/resolvers/issues_resolver_spec.rb
@@ -5,16 +5,63 @@ describe Resolvers::IssuesResolver do
let(:current_user) { create(:user) }
set(:project) { create(:project) }
- set(:issue) { create(:issue, project: project) }
- set(:issue2) { create(:issue, project: project, title: 'foo') }
+ set(:issue1) { create(:issue, project: project, state: :opened, created_at: 3.hours.ago, updated_at: 3.hours.ago) }
+ set(:issue2) { create(:issue, project: project, state: :closed, title: 'foo', created_at: 1.hour.ago, updated_at: 1.hour.ago, closed_at: 1.hour.ago) }
+ set(:label1) { create(:label, project: project) }
+ set(:label2) { create(:label, project: project) }
before do
project.add_developer(current_user)
+ create(:label_link, label: label1, target: issue1)
+ create(:label_link, label: label1, target: issue2)
+ create(:label_link, label: label2, target: issue2)
end
describe '#resolve' do
it 'finds all issues' do
- expect(resolve_issues).to contain_exactly(issue, issue2)
+ expect(resolve_issues).to contain_exactly(issue1, issue2)
+ end
+
+ it 'filters by state' do
+ expect(resolve_issues(state: 'opened')).to contain_exactly(issue1)
+ expect(resolve_issues(state: 'closed')).to contain_exactly(issue2)
+ end
+
+ it 'filters by labels' do
+ expect(resolve_issues(label_name: [label1.title])).to contain_exactly(issue1, issue2)
+ expect(resolve_issues(label_name: [label1.title, label2.title])).to contain_exactly(issue2)
+ end
+
+ describe 'filters by created_at' do
+ it 'filters by created_before' do
+ expect(resolve_issues(created_before: 2.hours.ago)).to contain_exactly(issue1)
+ end
+
+ it 'filters by created_after' do
+ expect(resolve_issues(created_after: 2.hours.ago)).to contain_exactly(issue2)
+ end
+ end
+
+ describe 'filters by updated_at' do
+ it 'filters by updated_before' do
+ expect(resolve_issues(updated_before: 2.hours.ago)).to contain_exactly(issue1)
+ end
+
+ it 'filters by updated_after' do
+ expect(resolve_issues(updated_after: 2.hours.ago)).to contain_exactly(issue2)
+ end
+ end
+
+ describe 'filters by closed_at' do
+ let!(:issue3) { create(:issue, project: project, state: :closed, closed_at: 3.hours.ago) }
+
+ it 'filters by closed_before' do
+ expect(resolve_issues(closed_before: 2.hours.ago)).to contain_exactly(issue3)
+ end
+
+ it 'filters by closed_after' do
+ expect(resolve_issues(closed_after: 2.hours.ago)).to contain_exactly(issue2)
+ end
end
it 'searches issues' do
@@ -22,7 +69,7 @@ describe Resolvers::IssuesResolver do
end
it 'sort issues' do
- expect(resolve_issues(sort: 'created_desc')).to eq [issue2, issue]
+ expect(resolve_issues(sort: 'created_desc')).to eq [issue2, issue1]
end
it 'returns issues user can see' do
@@ -30,31 +77,31 @@ describe Resolvers::IssuesResolver do
create(:issue, confidential: true)
- expect(resolve_issues).to contain_exactly(issue, issue2)
+ expect(resolve_issues).to contain_exactly(issue1, issue2)
end
it 'finds a specific issue with iid' do
- expect(resolve_issues(iid: issue.iid)).to contain_exactly(issue)
+ expect(resolve_issues(iid: issue1.iid)).to contain_exactly(issue1)
end
it 'finds a specific issue with iids' do
- expect(resolve_issues(iids: issue.iid)).to contain_exactly(issue)
+ expect(resolve_issues(iids: issue1.iid)).to contain_exactly(issue1)
end
it 'finds multiple issues with iids' do
- expect(resolve_issues(iids: [issue.iid, issue2.iid]))
- .to contain_exactly(issue, issue2)
+ expect(resolve_issues(iids: [issue1.iid, issue2.iid]))
+ .to contain_exactly(issue1, issue2)
end
it 'finds only the issues within the project we are looking at' do
another_project = create(:project)
- iids = [issue, issue2].map(&:iid)
+ iids = [issue1, issue2].map(&:iid)
iids.each do |iid|
create(:issue, project: another_project, iid: iid)
end
- expect(resolve_issues(iids: iids)).to contain_exactly(issue, issue2)
+ expect(resolve_issues(iids: iids)).to contain_exactly(issue1, issue2)
end
end
diff --git a/spec/graphql/resolvers/metadata_resolver_spec.rb b/spec/graphql/resolvers/metadata_resolver_spec.rb
new file mode 100644
index 00000000000..e662ed127a5
--- /dev/null
+++ b/spec/graphql/resolvers/metadata_resolver_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe Resolvers::MetadataResolver do
+ include GraphqlHelpers
+
+ describe '#resolve' do
+ it 'returns version and revision' do
+ expect(resolve(described_class)).to eq(version: Gitlab::VERSION, revision: Gitlab.revision)
+ end
+ end
+end
diff --git a/spec/graphql/types/ci/detailed_status_type_spec.rb b/spec/graphql/types/ci/detailed_status_type_spec.rb
new file mode 100644
index 00000000000..a21162adb42
--- /dev/null
+++ b/spec/graphql/types/ci/detailed_status_type_spec.rb
@@ -0,0 +1,11 @@
+require 'spec_helper'
+
+describe Types::Ci::DetailedStatusType do
+ it { expect(described_class.graphql_name).to eq('DetailedStatus') }
+
+ it "has all fields" do
+ expect(described_class).to have_graphql_fields(:group, :icon, :favicon,
+ :details_path, :has_details,
+ :label, :text, :tooltip)
+ end
+end
diff --git a/spec/graphql/types/issuable_state_enum_spec.rb b/spec/graphql/types/issuable_state_enum_spec.rb
new file mode 100644
index 00000000000..65a80fa4176
--- /dev/null
+++ b/spec/graphql/types/issuable_state_enum_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['IssuableState'] do
+ it { expect(described_class.graphql_name).to eq('IssuableState') }
+
+ it_behaves_like 'issuable state'
+end
diff --git a/spec/graphql/types/issue_state_enum_spec.rb b/spec/graphql/types/issue_state_enum_spec.rb
new file mode 100644
index 00000000000..de19e6fc505
--- /dev/null
+++ b/spec/graphql/types/issue_state_enum_spec.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['IssueState'] do
+ it { expect(described_class.graphql_name).to eq('IssueState') }
+
+ it_behaves_like 'issuable state'
+end
diff --git a/spec/graphql/types/merge_request_state_enum_spec.rb b/spec/graphql/types/merge_request_state_enum_spec.rb
new file mode 100644
index 00000000000..626e33b18d3
--- /dev/null
+++ b/spec/graphql/types/merge_request_state_enum_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe GitlabSchema.types['MergeRequestState'] do
+ it { expect(described_class.graphql_name).to eq('MergeRequestState') }
+
+ it_behaves_like 'issuable state'
+
+ it 'exposes all the existing merge request states' do
+ expect(described_class.values.keys).to include('merged')
+ end
+end
diff --git a/spec/graphql/types/metadata_type_spec.rb b/spec/graphql/types/metadata_type_spec.rb
new file mode 100644
index 00000000000..55205bf5b6a
--- /dev/null
+++ b/spec/graphql/types/metadata_type_spec.rb
@@ -0,0 +1,5 @@
+require 'spec_helper'
+
+describe GitlabSchema.types['Metadata'] do
+ it { expect(described_class.graphql_name).to eq('Metadata') }
+end
diff --git a/spec/graphql/types/query_type_spec.rb b/spec/graphql/types/query_type_spec.rb
index e1df6f9811d..07c61ea7647 100644
--- a/spec/graphql/types/query_type_spec.rb
+++ b/spec/graphql/types/query_type_spec.rb
@@ -5,7 +5,7 @@ describe GitlabSchema.types['Query'] do
expect(described_class.graphql_name).to eq('Query')
end
- it { is_expected.to have_graphql_fields(:project, :echo) }
+ it { is_expected.to have_graphql_fields(:project, :echo, :metadata) }
describe 'project field' do
subject { described_class.fields['project'] }
@@ -20,4 +20,17 @@ describe GitlabSchema.types['Query'] do
is_expected.to require_graphql_authorizations(:read_project)
end
end
+
+ describe 'metadata field' do
+ subject { described_class.fields['metadata'] }
+
+ it 'returns metadata' do
+ is_expected.to have_graphql_type(Types::MetadataType)
+ is_expected.to have_graphql_resolver(Resolvers::MetadataResolver)
+ end
+
+ it 'authorizes with read_instance_metadata' do
+ is_expected.to require_graphql_authorizations(:read_instance_metadata)
+ end
+ end
end
diff --git a/spec/helpers/appearances_helper_spec.rb b/spec/helpers/appearances_helper_spec.rb
new file mode 100644
index 00000000000..8d717b968dd
--- /dev/null
+++ b/spec/helpers/appearances_helper_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe AppearancesHelper do
+ before do
+ user = create(:user)
+ allow(helper).to receive(:current_user).and_return(user)
+ end
+
+ describe '#header_message' do
+ it 'returns nil when header message field is not set' do
+ create(:appearance)
+
+ expect(helper.header_message).to be_nil
+ end
+
+ context 'when header message is set' do
+ it 'includes current message' do
+ message = "Foo bar"
+ create(:appearance, header_message: message)
+
+ expect(helper.header_message).to include(message)
+ end
+ end
+ end
+
+ describe '#footer_message' do
+ it 'returns nil when footer message field is not set' do
+ create(:appearance)
+
+ expect(helper.footer_message).to be_nil
+ end
+
+ context 'when footer message is set' do
+ it 'includes current message' do
+ message = "Foo bar"
+ create(:appearance, footer_message: message)
+
+ expect(helper.footer_message).to include(message)
+ end
+ end
+ end
+
+ describe '#brand_image' do
+ let!(:appearance) { create(:appearance, :with_logo) }
+
+ context 'when there is a logo' do
+ it 'returns a path' do
+ expect(helper.brand_image).to match(%r(img data-src="/uploads/-/system/appearance/.*png))
+ end
+ end
+
+ context 'when there is a logo but no associated upload' do
+ before do
+ # Legacy attachments were not tracked in the uploads table
+ appearance.logo.upload.destroy
+ appearance.reload
+ end
+
+ it 'falls back to using the original path' do
+ expect(helper.brand_image).to match(%r(img data-src="/uploads/-/system/appearance/.*png))
+ end
+ end
+ end
+
+ describe '#brand_title' do
+ it 'returns the default CE title when no appearance is present' do
+ allow(helper)
+ .to receive(:current_appearance)
+ .and_return(nil)
+
+ expect(helper.brand_title).to eq('GitLab Community Edition')
+ end
+ end
+end
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index f0c2e4768ec..2ba8b3dbf22 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -97,17 +97,37 @@ describe AuthHelper do
end
end
- describe 'unlink_allowed?' do
- [:saml, :cas3].each do |provider|
- it "returns true if the provider is #{provider}" do
- expect(helper.unlink_allowed?(provider)).to be false
- end
+ describe '#link_provider_allowed?' do
+ let(:policy) { instance_double('IdentityProviderPolicy') }
+ let(:current_user) { instance_double('User') }
+ let(:provider) { double }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(IdentityProviderPolicy).to receive(:new).with(current_user, provider).and_return(policy)
end
- [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0, :authentiq].each do |provider|
- it "returns false if the provider is #{provider}" do
- expect(helper.unlink_allowed?(provider)).to be true
- end
+ it 'delegates to identity provider policy' do
+ allow(policy).to receive(:can?).with(:link).and_return('policy_link_result')
+
+ expect(helper.link_provider_allowed?(provider)).to eq 'policy_link_result'
+ end
+ end
+
+ describe '#unlink_provider_allowed?' do
+ let(:policy) { instance_double('IdentityProviderPolicy') }
+ let(:current_user) { instance_double('User') }
+ let(:provider) { double }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(current_user)
+ allow(IdentityProviderPolicy).to receive(:new).with(current_user, provider).and_return(policy)
+ end
+
+ it 'delegates to identity provider policy' do
+ allow(policy).to receive(:can?).with(:unlink).and_return('policy_unlink_result')
+
+ expect(helper.unlink_provider_allowed?(provider)).to eq 'policy_unlink_result'
end
end
end
diff --git a/spec/helpers/auto_devops_helper_spec.rb b/spec/helpers/auto_devops_helper_spec.rb
index 223e562238d..d2540696b17 100644
--- a/spec/helpers/auto_devops_helper_spec.rb
+++ b/spec/helpers/auto_devops_helper_spec.rb
@@ -29,11 +29,11 @@ describe AutoDevopsHelper do
end
context 'when the banner is disabled by feature flag' do
- it 'allows the feature flag to disable' do
+ before do
Feature.get(:auto_devops_banner_disabled).enable
-
- expect(subject).to be(false)
end
+
+ it { is_expected.to be_falsy }
end
context 'when dismissed' do
@@ -90,4 +90,136 @@ describe AutoDevopsHelper do
it { is_expected.to eq(false) }
end
end
+
+ describe '#badge_for_auto_devops_scope' do
+ subject { helper.badge_for_auto_devops_scope(receiver) }
+
+ context 'when receiver is a group' do
+ context 'when explicitly enabled' do
+ let(:receiver) { create(:group, :auto_devops_enabled) }
+
+ it { is_expected.to eq('group enabled') }
+ end
+
+ context 'when explicitly disabled' do
+ let(:receiver) { create(:group, :auto_devops_disabled) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when auto devops is implicitly enabled' do
+ let(:receiver) { create(:group) }
+
+ context 'by instance' do
+ before do
+ stub_application_setting(auto_devops_enabled: true)
+ end
+
+ it { is_expected.to eq('instance enabled') }
+ end
+
+ context 'with groups', :nested_groups do
+ before do
+ receiver.update(parent: parent)
+ end
+
+ context 'when auto devops is enabled on parent' do
+ let(:parent) { create(:group, :auto_devops_enabled) }
+
+ it { is_expected.to eq('group enabled') }
+ end
+
+ context 'when auto devops is enabled on parent group' do
+ let(:root_parent) { create(:group, :auto_devops_enabled) }
+ let(:parent) { create(:group, parent: root_parent) }
+
+ it { is_expected.to eq('group enabled') }
+ end
+
+ context 'when auto devops disabled set on parent group' do
+ let(:root_parent) { create(:group, :auto_devops_disabled) }
+ let(:parent) { create(:group, parent: root_parent) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+ end
+ end
+
+ context 'when receiver is a project' do
+ context 'when auto devops is enabled at project level' do
+ let(:receiver) { create(:project, :auto_devops) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when auto devops is disabled at project level' do
+ let(:receiver) { create(:project, :auto_devops_disabled) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when auto devops is implicitly enabled' do
+ let(:receiver) { create(:project) }
+
+ context 'by instance' do
+ before do
+ stub_application_setting(auto_devops_enabled: true)
+ end
+
+ it { is_expected.to eq('instance enabled') }
+ end
+
+ context 'with groups', :nested_groups do
+ let(:receiver) { create(:project, :repository, namespace: group) }
+
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+ end
+
+ context 'when auto devops is enabled on group level' do
+ let(:group) { create(:group, :auto_devops_enabled) }
+
+ it { is_expected.to eq('group enabled') }
+ end
+
+ context 'when auto devops is enabled on root group' do
+ let(:root_parent) { create(:group, :auto_devops_enabled) }
+ let(:group) { create(:group, parent: root_parent) }
+
+ it { is_expected.to eq('group enabled') }
+ end
+ end
+ end
+
+ context 'when auto devops is implicitly disabled' do
+ let(:receiver) { create(:project) }
+
+ context 'by instance' do
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'with groups', :nested_groups do
+ let(:receiver) { create(:project, :repository, namespace: group) }
+
+ context 'when auto devops is disabled on group level' do
+ let(:group) { create(:group, :auto_devops_disabled) }
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'when root group is enabled and parent disabled' do
+ let(:root_parent) { create(:group, :auto_devops_enabled) }
+ let(:group) { create(:group, :auto_devops_disabled, parent: root_parent) }
+
+ it { is_expected.to be_nil }
+ end
+ end
+ end
+ end
+ end
end
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index f709f152c92..2bc3933809f 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -50,12 +50,20 @@ describe BlobHelper do
end
it 'returns a link with the proper route' do
+ stub_feature_flags(web_ide_default: false)
link = edit_blob_button(project, 'master', 'README.md')
expect(Capybara.string(link).find_link('Edit')[:href]).to eq("/#{project.full_path}/edit/master/README.md")
end
+ it 'returns a link with a Web IDE route' do
+ link = edit_blob_button(project, 'master', 'README.md')
+
+ expect(Capybara.string(link).find_link('Edit')[:href]).to eq("/-/ide/project/#{project.full_path}/edit/master/-/README.md")
+ end
+
it 'returns a link with the passed link_opts on the expected route' do
+ stub_feature_flags(web_ide_default: false)
link = edit_blob_button(project, 'master', 'README.md', link_opts: { mr_id: 10 })
expect(Capybara.string(link).find_link('Edit')[:href]).to eq("/#{project.full_path}/edit/master/README.md?mr_id=10")
diff --git a/spec/helpers/clusters_helper_spec.rb b/spec/helpers/clusters_helper_spec.rb
new file mode 100644
index 00000000000..4ea0f76fc28
--- /dev/null
+++ b/spec/helpers/clusters_helper_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ClustersHelper do
+ describe '#has_rbac_enabled?' do
+ context 'when kubernetes platform has been created' do
+ let(:platform_kubernetes) { build_stubbed(:cluster_platform_kubernetes) }
+ let(:cluster) { build_stubbed(:cluster, :provided_by_gcp, platform_kubernetes: platform_kubernetes) }
+
+ it 'returns kubernetes platform value' do
+ expect(helper.has_rbac_enabled?(cluster)).to be_truthy
+ end
+ end
+
+ context 'when kubernetes platform has not been created yet' do
+ let(:cluster) { build_stubbed(:cluster, :providing_by_gcp) }
+
+ it 'delegates to cluster provider' do
+ expect(helper.has_rbac_enabled?(cluster)).to be_truthy
+ end
+
+ context 'when ABAC cluster is created' do
+ let(:provider) { build_stubbed(:cluster_provider_gcp, :abac_enabled) }
+ let(:cluster) { build_stubbed(:cluster, :providing_by_gcp, provider_gcp: provider) }
+
+ it 'delegates to cluster provider' do
+ expect(helper.has_rbac_enabled?(cluster)).to be_falsy
+ end
+ end
+ end
+ end
+end
diff --git a/spec/helpers/emails_helper_spec.rb b/spec/helpers/emails_helper_spec.rb
index 23d7e41803e..03b4c19ec22 100644
--- a/spec/helpers/emails_helper_spec.rb
+++ b/spec/helpers/emails_helper_spec.rb
@@ -142,4 +142,58 @@ describe EmailsHelper do
end
end
end
+
+ describe 'header and footer messages' do
+ context 'when email_header_and_footer_enabled is enabled' do
+ it 'returns header and footer messages' do
+ create :appearance, header_message: 'Foo', footer_message: 'Bar', email_header_and_footer_enabled: true
+
+ aggregate_failures do
+ expect(html_header_message).to eq(%{<div class="header-message" style=""><p>Foo</p></div>})
+ expect(html_footer_message).to eq(%{<div class="footer-message" style=""><p>Bar</p></div>})
+ expect(text_header_message).to eq('Foo')
+ expect(text_footer_message).to eq('Bar')
+ end
+ end
+
+ context 'when header and footer messages are empty' do
+ it 'returns nil' do
+ create :appearance, header_message: '', footer_message: '', email_header_and_footer_enabled: true
+
+ aggregate_failures do
+ expect(html_header_message).to eq(nil)
+ expect(html_footer_message).to eq(nil)
+ expect(text_header_message).to eq(nil)
+ expect(text_footer_message).to eq(nil)
+ end
+ end
+ end
+
+ context 'when header and footer messages are nil' do
+ it 'returns nil' do
+ create :appearance, header_message: nil, footer_message: nil, email_header_and_footer_enabled: true
+
+ aggregate_failures do
+ expect(html_header_message).to eq(nil)
+ expect(html_footer_message).to eq(nil)
+ expect(text_header_message).to eq(nil)
+ expect(text_footer_message).to eq(nil)
+ end
+ end
+ end
+ end
+
+ context 'when email_header_and_footer_enabled is disabled' do
+ it 'returns header and footer messages' do
+ create :appearance, header_message: 'Foo', footer_message: 'Bar', email_header_and_footer_enabled: false
+
+ aggregate_failures do
+ expect(html_header_message).to eq(nil)
+ expect(html_footer_message).to eq(nil)
+ expect(text_header_message).to eq(nil)
+ expect(text_footer_message).to eq(nil)
+ end
+ end
+ end
+ end
end
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index c04f679bcf0..012678db9c2 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -236,4 +236,17 @@ describe LabelsHelper do
expect(labels_filter_path(format: :json)).to eq(dashboard_labels_path(format: :json))
end
end
+
+ describe 'labels_sorted_by_title' do
+ it 'sorts labels alphabetically' do
+ label1 = double(:label, title: 'a')
+ label2 = double(:label, title: 'B')
+ label3 = double(:label, title: 'c')
+ label4 = double(:label, title: 'D')
+ labels = [label1, label2, label3, label4]
+
+ expect(labels_sorted_by_title(labels))
+ .to match_array([label2, label4, label1, label3])
+ end
+ end
end
diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb
index 4c395248644..db0d45c3692 100644
--- a/spec/helpers/preferences_helper_spec.rb
+++ b/spec/helpers/preferences_helper_spec.rb
@@ -36,10 +36,11 @@ describe PreferencesHelper do
end
describe '#first_day_of_week_choices' do
- it 'returns Sunday and Monday as choices' do
+ it 'returns Saturday, Sunday and Monday as choices' do
expect(helper.first_day_of_week_choices).to eq [
['Sunday', 0],
- ['Monday', 1]
+ ['Monday', 1],
+ ['Saturday', 6]
]
end
end
@@ -47,14 +48,21 @@ describe PreferencesHelper do
describe '#first_day_of_week_choices_with_default' do
it 'returns choices including system default' do
expect(helper.first_day_of_week_choices_with_default).to eq [
- ['System default (Sunday)', nil], ['Sunday', 0], ['Monday', 1]
+ ['System default (Sunday)', nil], ['Sunday', 0], ['Monday', 1], ['Saturday', 6]
]
end
it 'returns choices including system default set to Monday' do
stub_application_setting(first_day_of_week: 1)
expect(helper.first_day_of_week_choices_with_default).to eq [
- ['System default (Monday)', nil], ['Sunday', 0], ['Monday', 1]
+ ['System default (Monday)', nil], ['Sunday', 0], ['Monday', 1], ['Saturday', 6]
+ ]
+ end
+
+ it 'returns choices including system default set to Saturday' do
+ stub_application_setting(first_day_of_week: 6)
+ expect(helper.first_day_of_week_choices_with_default).to eq [
+ ['System default (Saturday)', nil], ['Sunday', 0], ['Monday', 1], ['Saturday', 6]
]
end
end
@@ -110,6 +118,13 @@ describe PreferencesHelper do
end
end
+ describe '#language_choices' do
+ it 'returns an array of all available languages' do
+ expect(helper.language_choices).to be_an(Array)
+ expect(helper.language_choices.map(&:second)).to eq(Gitlab::I18n.available_locales)
+ end
+ end
+
def stub_user(messages = {})
if messages.empty?
allow(helper).to receive(:current_user).and_return(nil)
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 49895b0680b..291eafece94 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -3,6 +3,56 @@ require 'spec_helper'
describe ProjectsHelper do
include ProjectForksHelper
+ describe '#error_tracking_setting_project_json' do
+ let(:project) { create(:project) }
+
+ context 'error tracking setting does not exist' do
+ before do
+ helper.instance_variable_set(:@project, project)
+ end
+
+ it 'returns nil' do
+ expect(helper.error_tracking_setting_project_json).to be_nil
+ end
+ end
+
+ context 'error tracking setting exists' do
+ let!(:error_tracking_setting) { create(:project_error_tracking_setting, project: project) }
+
+ context 'api_url present' do
+ let(:json) do
+ {
+ name: error_tracking_setting.project_name,
+ organization_name: error_tracking_setting.organization_name,
+ organization_slug: error_tracking_setting.organization_slug,
+ slug: error_tracking_setting.project_slug
+ }.to_json
+ end
+
+ before do
+ helper.instance_variable_set(:@project, project)
+ end
+
+ it 'returns error tracking json' do
+ expect(helper.error_tracking_setting_project_json).to eq(json)
+ end
+ end
+
+ context 'api_url not present' do
+ before do
+ project.error_tracking_setting.api_url = nil
+ project.error_tracking_setting.enabled = false
+
+ helper.instance_variable_set(:@project, project)
+ end
+
+ it 'returns nil' do
+ expect(helper.error_tracking_setting_project_json).to be_nil
+ end
+ end
+ end
+ end
+
describe "#project_status_css_class" do
it "returns appropriate class" do
expect(project_status_css_class("started")).to eq("table-active")
diff --git a/spec/javascripts/activities_spec.js b/spec/javascripts/activities_spec.js
index 068b8eb65bc..23b6de7e4e0 100644
--- a/spec/javascripts/activities_spec.js
+++ b/spec/javascripts/activities_spec.js
@@ -7,7 +7,7 @@ import Pager from '~/pager';
describe('Activities', () => {
window.gon || (window.gon = {});
- const fixtureTemplate = 'static/event_filter.html.raw';
+ const fixtureTemplate = 'static/event_filter.html';
const filters = [
{
id: 'all',
diff --git a/spec/javascripts/ajax_loading_spinner_spec.js b/spec/javascripts/ajax_loading_spinner_spec.js
index 9389fc94f17..89195a4397f 100644
--- a/spec/javascripts/ajax_loading_spinner_spec.js
+++ b/spec/javascripts/ajax_loading_spinner_spec.js
@@ -2,7 +2,7 @@ import $ from 'jquery';
import AjaxLoadingSpinner from '~/ajax_loading_spinner';
describe('Ajax Loading Spinner', () => {
- const fixtureTemplate = 'static/ajax_loading_spinner.html.raw';
+ const fixtureTemplate = 'static/ajax_loading_spinner.html';
preloadFixtures(fixtureTemplate);
beforeEach(() => {
diff --git a/spec/javascripts/api_spec.js b/spec/javascripts/api_spec.js
index 1e9470970ff..e537e0e8afc 100644
--- a/spec/javascripts/api_spec.js
+++ b/spec/javascripts/api_spec.js
@@ -139,6 +139,40 @@ describe('Api', () => {
});
});
+ describe('projectMergeRequests', () => {
+ const projectPath = 'abc';
+ const expectedUrl = `${dummyUrlRoot}/api/${dummyApiVersion}/projects/${projectPath}/merge_requests`;
+
+ it('fetches all merge requests for a project', done => {
+ const mockData = [{ source_branch: 'foo' }, { source_branch: 'bar' }];
+ mock.onGet(expectedUrl).reply(200, mockData);
+ Api.projectMergeRequests(projectPath)
+ .then(({ data }) => {
+ expect(data.length).toEqual(2);
+ expect(data[0].source_branch).toBe('foo');
+ expect(data[1].source_branch).toBe('bar');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('fetches merge requests filtered with passed params', done => {
+ const params = {
+ source_branch: 'bar',
+ };
+ const mockData = [{ source_branch: 'bar' }];
+ mock.onGet(expectedUrl, { params }).reply(200, mockData);
+
+ Api.projectMergeRequests(projectPath, params)
+ .then(({ data }) => {
+ expect(data.length).toEqual(1);
+ expect(data[0].source_branch).toBe('bar');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
describe('projectMergeRequest', () => {
it('fetches a merge request', done => {
const projectPath = 'abc';
diff --git a/spec/javascripts/awards_handler_spec.js b/spec/javascripts/awards_handler_spec.js
index ce5d2022441..e10df1b45e7 100644
--- a/spec/javascripts/awards_handler_spec.js
+++ b/spec/javascripts/awards_handler_spec.js
@@ -1,12 +1,16 @@
import $ from 'jquery';
import Cookies from 'js-cookie';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
import loadAwardsHandler from '~/awards_handler';
import '~/lib/utils/common_utils';
+import { EMOJI_VERSION } from '~/emoji';
window.gl = window.gl || {};
window.gon = window.gon || {};
let openAndWaitForEmojiMenu;
+let mock;
let awardsHandler = null;
const urlRoot = gon.relative_url_root;
@@ -19,9 +23,14 @@ const lazyAssert = function(done, assertFn) {
};
describe('AwardsHandler', function() {
- preloadFixtures('snippets/show.html.raw');
+ const emojiData = getJSONFixture('emojis/emojis.json');
+ preloadFixtures('snippets/show.html');
+
beforeEach(function(done) {
- loadFixtures('snippets/show.html.raw');
+ mock = new MockAdapter(axios);
+ mock.onGet(`/-/emojis/${EMOJI_VERSION}/emojis.json`).reply(200, emojiData);
+
+ loadFixtures('snippets/show.html');
loadAwardsHandler(true)
.then(obj => {
awardsHandler = obj;
@@ -53,6 +62,8 @@ describe('AwardsHandler', function() {
// restore original url root value
gon.relative_url_root = urlRoot;
+ mock.restore();
+
// Undo what we did to the shared <body>
$('body').removeAttr('data-page');
diff --git a/spec/javascripts/badges/components/badge_list_spec.js b/spec/javascripts/badges/components/badge_list_spec.js
index 536671db377..2f72c9ed89d 100644
--- a/spec/javascripts/badges/components/badge_list_spec.js
+++ b/spec/javascripts/badges/components/badge_list_spec.js
@@ -60,7 +60,7 @@ describe('BadgeList component', () => {
Vue.nextTick()
.then(() => {
- const loadingIcon = vm.$el.querySelector('.fa-spinner');
+ const loadingIcon = vm.$el.querySelector('.spinner');
expect(loadingIcon).toBeVisible();
})
diff --git a/spec/javascripts/badges/components/badge_spec.js b/spec/javascripts/badges/components/badge_spec.js
index 29805408bcf..4e4d1ae2e99 100644
--- a/spec/javascripts/badges/components/badge_spec.js
+++ b/spec/javascripts/badges/components/badge_spec.js
@@ -15,7 +15,7 @@ describe('Badge component', () => {
const buttons = vm.$el.querySelectorAll('button');
return {
badgeImage: vm.$el.querySelector('img.project-badge'),
- loadingIcon: vm.$el.querySelector('.fa-spinner'),
+ loadingIcon: vm.$el.querySelector('.spinner'),
reloadButton: buttons[buttons.length - 1],
};
};
diff --git a/spec/javascripts/badges/store/actions_spec.js b/spec/javascripts/badges/store/actions_spec.js
index 2623465ebd6..e8d5f8c3aac 100644
--- a/spec/javascripts/badges/store/actions_spec.js
+++ b/spec/javascripts/badges/store/actions_spec.js
@@ -411,7 +411,7 @@ describe('Badges store actions', () => {
it('escapes user input', done => {
spyOn(axios, 'get').and.callFake(() => Promise.resolve({ data: createDummyBadgeResponse() }));
- badgeInForm.imageUrl = '&make-sandwhich=true';
+ badgeInForm.imageUrl = '&make-sandwich=true';
badgeInForm.linkUrl = '<script>I am dangerous!</script>';
actions
@@ -422,7 +422,7 @@ describe('Badges store actions', () => {
expect(url).toMatch(`^${dummyEndpointUrl}/render?`);
expect(url).toMatch('\\?link_url=%3Cscript%3EI%20am%20dangerous!%3C%2Fscript%3E&');
- expect(url).toMatch('&image_url=%26make-sandwhich%3Dtrue$');
+ expect(url).toMatch('&image_url=%26make-sandwich%3Dtrue$');
})
.then(done)
.catch(done.fail);
diff --git a/spec/javascripts/behaviors/copy_as_gfm_spec.js b/spec/javascripts/behaviors/copy_as_gfm_spec.js
index ca849f75860..d653fca0988 100644
--- a/spec/javascripts/behaviors/copy_as_gfm_spec.js
+++ b/spec/javascripts/behaviors/copy_as_gfm_spec.js
@@ -100,7 +100,7 @@ describe('CopyAsGFM', () => {
simulateCopy();
setTimeout(() => {
- const expectedGFM = '* List Item1\n\n* List Item2';
+ const expectedGFM = '* List Item1\n* List Item2';
expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM);
done();
@@ -114,7 +114,7 @@ describe('CopyAsGFM', () => {
simulateCopy();
setTimeout(() => {
- const expectedGFM = '1. List Item1\n\n1. List Item2';
+ const expectedGFM = '1. List Item1\n1. List Item2';
expect(clipboardData.setData).toHaveBeenCalledWith('text/x-gfm', expectedGFM);
done();
diff --git a/spec/javascripts/behaviors/quick_submit_spec.js b/spec/javascripts/behaviors/quick_submit_spec.js
index 681463aab66..7af8c984841 100644
--- a/spec/javascripts/behaviors/quick_submit_spec.js
+++ b/spec/javascripts/behaviors/quick_submit_spec.js
@@ -4,10 +4,10 @@ import '~/behaviors/quick_submit';
describe('Quick Submit behavior', function() {
const keydownEvent = (options = { keyCode: 13, metaKey: true }) => $.Event('keydown', options);
- preloadFixtures('snippets/show.html.raw');
+ preloadFixtures('snippets/show.html');
beforeEach(() => {
- loadFixtures('snippets/show.html.raw');
+ loadFixtures('snippets/show.html');
$('form').submit(e => {
// Prevent a form submit from moving us off the testing page
e.preventDefault();
diff --git a/spec/javascripts/behaviors/requires_input_spec.js b/spec/javascripts/behaviors/requires_input_spec.js
index 1bde2bb3024..617fe49b059 100644
--- a/spec/javascripts/behaviors/requires_input_spec.js
+++ b/spec/javascripts/behaviors/requires_input_spec.js
@@ -3,10 +3,10 @@ import '~/behaviors/requires_input';
describe('requiresInput', () => {
let submitButton;
- preloadFixtures('branches/new_branch.html.raw');
+ preloadFixtures('branches/new_branch.html');
beforeEach(() => {
- loadFixtures('branches/new_branch.html.raw');
+ loadFixtures('branches/new_branch.html');
submitButton = $('button[type="submit"]');
});
diff --git a/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js b/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js
index 4843a0386b5..5e457a4e823 100644
--- a/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js
+++ b/spec/javascripts/behaviors/shortcuts/shortcuts_issuable_spec.js
@@ -9,7 +9,7 @@ import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable';
const FORM_SELECTOR = '.js-main-target-form .js-vue-comment-form';
describe('ShortcutsIssuable', function() {
- const fixtureName = 'snippets/show.html.raw';
+ const fixtureName = 'snippets/show.html';
preloadFixtures(fixtureName);
beforeAll(done => {
diff --git a/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js b/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js
index 5f027f59fcf..68b4f261617 100644
--- a/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js
+++ b/spec/javascripts/blob/balsamiq/balsamiq_viewer_integration_spec.js
@@ -6,10 +6,10 @@ describe('Balsamiq integration spec', () => {
let endpoint;
let balsamiqViewer;
- preloadFixtures('static/balsamiq_viewer.html.raw');
+ preloadFixtures('static/balsamiq_viewer.html');
beforeEach(() => {
- loadFixtures('static/balsamiq_viewer.html.raw');
+ loadFixtures('static/balsamiq_viewer.html');
container = document.getElementById('js-balsamiq-viewer');
balsamiqViewer = new BalsamiqViewer(container);
diff --git a/spec/javascripts/blob/blob_file_dropzone_spec.js b/spec/javascripts/blob/blob_file_dropzone_spec.js
index 432d8a65b0a..cab06a0a9be 100644
--- a/spec/javascripts/blob/blob_file_dropzone_spec.js
+++ b/spec/javascripts/blob/blob_file_dropzone_spec.js
@@ -2,10 +2,10 @@ import $ from 'jquery';
import BlobFileDropzone from '~/blob/blob_file_dropzone';
describe('BlobFileDropzone', function() {
- preloadFixtures('blob/show.html.raw');
+ preloadFixtures('blob/show.html');
beforeEach(() => {
- loadFixtures('blob/show.html.raw');
+ loadFixtures('blob/show.html');
const form = $('.js-upload-blob-form');
this.blobFileDropzone = new BlobFileDropzone(form, 'POST');
this.dropzone = $('.js-upload-blob-form .dropzone').get(0).dropzone;
diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js
index 28d3b2f5ea3..6bb5bac007f 100644
--- a/spec/javascripts/blob/notebook/index_spec.js
+++ b/spec/javascripts/blob/notebook/index_spec.js
@@ -3,10 +3,10 @@ import axios from '~/lib/utils/axios_utils';
import renderNotebook from '~/blob/notebook';
describe('iPython notebook renderer', () => {
- preloadFixtures('static/notebook_viewer.html.raw');
+ preloadFixtures('static/notebook_viewer.html');
beforeEach(() => {
- loadFixtures('static/notebook_viewer.html.raw');
+ loadFixtures('static/notebook_viewer.html');
});
it('shows loading icon', () => {
diff --git a/spec/javascripts/blob/pdf/index_spec.js b/spec/javascripts/blob/pdf/index_spec.js
index be917a0613f..acf87580777 100644
--- a/spec/javascripts/blob/pdf/index_spec.js
+++ b/spec/javascripts/blob/pdf/index_spec.js
@@ -15,10 +15,10 @@ describe('PDF renderer', () => {
}
};
- preloadFixtures('static/pdf_viewer.html.raw');
+ preloadFixtures('static/pdf_viewer.html');
beforeEach(() => {
- loadFixtures('static/pdf_viewer.html.raw');
+ loadFixtures('static/pdf_viewer.html');
viewer = document.getElementById('js-pdf-viewer');
viewer.dataset.endpoint = testPDF;
});
diff --git a/spec/javascripts/blob/sketch/index_spec.js b/spec/javascripts/blob/sketch/index_spec.js
index 2b1e81e9cbc..3d3129e10da 100644
--- a/spec/javascripts/blob/sketch/index_spec.js
+++ b/spec/javascripts/blob/sketch/index_spec.js
@@ -13,10 +13,10 @@ describe('Sketch viewer', () => {
});
};
- preloadFixtures('static/sketch_viewer.html.raw');
+ preloadFixtures('static/sketch_viewer.html');
beforeEach(() => {
- loadFixtures('static/sketch_viewer.html.raw');
+ loadFixtures('static/sketch_viewer.html');
});
describe('with error message', () => {
diff --git a/spec/javascripts/blob/viewer/index_spec.js b/spec/javascripts/blob/viewer/index_spec.js
index 93a942fe8d4..4ac15ca5aa2 100644
--- a/spec/javascripts/blob/viewer/index_spec.js
+++ b/spec/javascripts/blob/viewer/index_spec.js
@@ -9,12 +9,12 @@ describe('Blob viewer', () => {
let blob;
let mock;
- preloadFixtures('snippets/show.html.raw');
+ preloadFixtures('snippets/show.html');
beforeEach(() => {
mock = new MockAdapter(axios);
- loadFixtures('snippets/show.html.raw');
+ loadFixtures('snippets/show.html');
$('#modal-upload-blob').remove();
blob = new BlobViewer();
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index 2642c8b1bdb..396fc823ef5 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -195,7 +195,7 @@ describe('Board list component', () => {
component.list.loadingMore = true;
Vue.nextTick(() => {
- expect(component.$el.querySelector('.board-list-count .fa-spinner')).not.toBeNull();
+ expect(component.$el.querySelector('.board-list-count .spinner')).not.toBeNull();
done();
});
diff --git a/spec/javascripts/boards/components/board_spec.js b/spec/javascripts/boards/components/board_spec.js
index dee7841c088..6e6b3e6950b 100644
--- a/spec/javascripts/boards/components/board_spec.js
+++ b/spec/javascripts/boards/components/board_spec.js
@@ -9,7 +9,7 @@ describe('Board component', () => {
let el;
beforeEach(done => {
- loadFixtures('boards/show.html.raw');
+ loadFixtures('boards/show.html');
el = document.createElement('div');
document.body.appendChild(el);
diff --git a/spec/javascripts/boards/components/issue_due_date_spec.js b/spec/javascripts/boards/components/issue_due_date_spec.js
index 054cf8c5b7d..68e26b68f04 100644
--- a/spec/javascripts/boards/components/issue_due_date_spec.js
+++ b/spec/javascripts/boards/components/issue_due_date_spec.js
@@ -43,7 +43,7 @@ describe('Issue Due Date component', () => {
date.setDate(date.getDate() + 5);
vm = createComponent(date);
- expect(vm.$el.querySelector('time').textContent.trim()).toEqual(dateFormat(date, 'dddd', true));
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual(dateFormat(date, 'dddd'));
});
it('should render month and day for other dates', () => {
@@ -53,7 +53,7 @@ describe('Issue Due Date component', () => {
const isDueInCurrentYear = today.getFullYear() === date.getFullYear();
const format = isDueInCurrentYear ? 'mmm d' : 'mmm d, yyyy';
- expect(vm.$el.querySelector('time').textContent.trim()).toEqual(dateFormat(date, format, true));
+ expect(vm.$el.querySelector('time').textContent.trim()).toEqual(dateFormat(date, format));
});
it('should contain the correct `.text-danger` css class for overdue issue', () => {
diff --git a/spec/javascripts/bootstrap_linked_tabs_spec.js b/spec/javascripts/bootstrap_linked_tabs_spec.js
index c3e3d78ff63..1d21637ceae 100644
--- a/spec/javascripts/bootstrap_linked_tabs_spec.js
+++ b/spec/javascripts/bootstrap_linked_tabs_spec.js
@@ -1,10 +1,10 @@
import LinkedTabs from '~/lib/utils/bootstrap_linked_tabs';
describe('Linked Tabs', () => {
- preloadFixtures('static/linked_tabs.html.raw');
+ preloadFixtures('static/linked_tabs.html');
beforeEach(() => {
- loadFixtures('static/linked_tabs.html.raw');
+ loadFixtures('static/linked_tabs.html');
});
describe('when is initialized', () => {
diff --git a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
index 1fc0e206d5e..481b1a4d4b0 100644
--- a/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ajax_variable_list_spec.js
@@ -7,8 +7,8 @@ const VARIABLE_PATCH_ENDPOINT = 'http://test.host/frontend-fixtures/builds-proje
const HIDE_CLASS = 'hide';
describe('AjaxFormVariableList', () => {
- preloadFixtures('projects/ci_cd_settings.html.raw');
- preloadFixtures('projects/ci_cd_settings_with_variables.html.raw');
+ preloadFixtures('projects/ci_cd_settings.html');
+ preloadFixtures('projects/ci_cd_settings_with_variables.html');
let container;
let saveButton;
@@ -18,7 +18,7 @@ describe('AjaxFormVariableList', () => {
let ajaxVariableList;
beforeEach(() => {
- loadFixtures('projects/ci_cd_settings.html.raw');
+ loadFixtures('projects/ci_cd_settings.html');
container = document.querySelector('.js-ci-variable-list-section');
mock = new MockAdapter(axios);
@@ -168,7 +168,7 @@ describe('AjaxFormVariableList', () => {
describe('updateRowsWithPersistedVariables', () => {
beforeEach(() => {
- loadFixtures('projects/ci_cd_settings_with_variables.html.raw');
+ loadFixtures('projects/ci_cd_settings_with_variables.html');
container = document.querySelector('.js-ci-variable-list-section');
const ajaxVariableListEl = document.querySelector('.js-ci-variable-list-section');
diff --git a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
index bef59b86d0c..70f49469300 100644
--- a/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/ci_variable_list_spec.js
@@ -5,9 +5,9 @@ import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
const HIDE_CLASS = 'hide';
describe('VariableList', () => {
- preloadFixtures('pipeline_schedules/edit.html.raw');
- preloadFixtures('pipeline_schedules/edit_with_variables.html.raw');
- preloadFixtures('projects/ci_cd_settings.html.raw');
+ preloadFixtures('pipeline_schedules/edit.html');
+ preloadFixtures('pipeline_schedules/edit_with_variables.html');
+ preloadFixtures('projects/ci_cd_settings.html');
let $wrapper;
let variableList;
@@ -15,7 +15,7 @@ describe('VariableList', () => {
describe('with only key/value inputs', () => {
describe('with no variables', () => {
beforeEach(() => {
- loadFixtures('pipeline_schedules/edit.html.raw');
+ loadFixtures('pipeline_schedules/edit.html');
$wrapper = $('.js-ci-variable-list-section');
variableList = new VariableList({
@@ -82,7 +82,7 @@ describe('VariableList', () => {
describe('with persisted variables', () => {
beforeEach(() => {
- loadFixtures('pipeline_schedules/edit_with_variables.html.raw');
+ loadFixtures('pipeline_schedules/edit_with_variables.html');
$wrapper = $('.js-ci-variable-list-section');
variableList = new VariableList({
@@ -115,7 +115,7 @@ describe('VariableList', () => {
describe('with all inputs(key, value, protected)', () => {
beforeEach(() => {
- loadFixtures('projects/ci_cd_settings.html.raw');
+ loadFixtures('projects/ci_cd_settings.html');
$wrapper = $('.js-ci-variable-list-section');
$wrapper.find('.js-ci-variable-input-protected').attr('data-default', 'false');
@@ -149,7 +149,7 @@ describe('VariableList', () => {
describe('toggleEnableRow method', () => {
beforeEach(() => {
- loadFixtures('pipeline_schedules/edit_with_variables.html.raw');
+ loadFixtures('pipeline_schedules/edit_with_variables.html');
$wrapper = $('.js-ci-variable-list-section');
variableList = new VariableList({
@@ -198,7 +198,7 @@ describe('VariableList', () => {
describe('hideValues', () => {
beforeEach(() => {
- loadFixtures('projects/ci_cd_settings.html.raw');
+ loadFixtures('projects/ci_cd_settings.html');
$wrapper = $('.js-ci-variable-list-section');
variableList = new VariableList({
diff --git a/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js b/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js
index 997d0d54d79..4982b68fa81 100644
--- a/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js
+++ b/spec/javascripts/ci_variable_list/native_form_variable_list_spec.js
@@ -2,12 +2,12 @@ import $ from 'jquery';
import setupNativeFormVariableList from '~/ci_variable_list/native_form_variable_list';
describe('NativeFormVariableList', () => {
- preloadFixtures('pipeline_schedules/edit.html.raw');
+ preloadFixtures('pipeline_schedules/edit.html');
let $wrapper;
beforeEach(() => {
- loadFixtures('pipeline_schedules/edit.html.raw');
+ loadFixtures('pipeline_schedules/edit.html');
$wrapper = $('.js-ci-variable-list-section');
setupNativeFormVariableList({
diff --git a/spec/javascripts/clusters/clusters_bundle_spec.js b/spec/javascripts/clusters/clusters_bundle_spec.js
index 7928feeadfa..0d3dcc29f22 100644
--- a/spec/javascripts/clusters/clusters_bundle_spec.js
+++ b/spec/javascripts/clusters/clusters_bundle_spec.js
@@ -1,13 +1,18 @@
import Clusters from '~/clusters/clusters_bundle';
-import { REQUEST_SUBMITTED, REQUEST_FAILURE, APPLICATION_STATUS } from '~/clusters/constants';
+import {
+ REQUEST_SUBMITTED,
+ REQUEST_FAILURE,
+ APPLICATION_STATUS,
+ INGRESS_DOMAIN_SUFFIX,
+} from '~/clusters/constants';
import getSetTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
describe('Clusters', () => {
let cluster;
- preloadFixtures('clusters/show_cluster.html.raw');
+ preloadFixtures('clusters/show_cluster.html');
beforeEach(() => {
- loadFixtures('clusters/show_cluster.html.raw');
+ loadFixtures('clusters/show_cluster.html');
cluster = new Clusters();
});
@@ -265,4 +270,77 @@ describe('Clusters', () => {
.catch(done.fail);
});
});
+
+ describe('handleSuccess', () => {
+ beforeEach(() => {
+ spyOn(cluster.store, 'updateStateFromServer');
+ spyOn(cluster, 'toggleIngressDomainHelpText');
+ spyOn(cluster, 'checkForNewInstalls');
+ spyOn(cluster, 'updateContainer');
+
+ cluster.handleSuccess({ data: {} });
+ });
+
+ it('updates clusters store', () => {
+ expect(cluster.store.updateStateFromServer).toHaveBeenCalled();
+ });
+
+ it('checks for new installable apps', () => {
+ expect(cluster.checkForNewInstalls).toHaveBeenCalled();
+ });
+
+ it('toggles ingress domain help text', () => {
+ expect(cluster.toggleIngressDomainHelpText).toHaveBeenCalled();
+ });
+
+ it('updates message containers', () => {
+ expect(cluster.updateContainer).toHaveBeenCalled();
+ });
+ });
+
+ describe('toggleIngressDomainHelpText', () => {
+ const { INSTALLED, INSTALLABLE, NOT_INSTALLABLE } = APPLICATION_STATUS;
+
+ const ingressPreviousState = { status: INSTALLABLE };
+ const ingressNewState = { status: INSTALLED, externalIp: '127.0.0.1' };
+
+ describe(`when ingress application new status is ${INSTALLED}`, () => {
+ beforeEach(() => {
+ ingressNewState.status = INSTALLED;
+ cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState);
+ });
+
+ it('displays custom domain help text', () => {
+ expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(false);
+ });
+
+ it('updates ingress external ip address', () => {
+ expect(cluster.ingressDomainSnippet.textContent).toEqual(
+ `${ingressNewState.externalIp}${INGRESS_DOMAIN_SUFFIX}`,
+ );
+ });
+ });
+
+ describe(`when ingress application new status is different from ${INSTALLED}`, () => {
+ it('hides custom domain help text', () => {
+ ingressNewState.status = NOT_INSTALLABLE;
+ cluster.ingressDomainHelpText.classList.remove('hide');
+
+ cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState);
+
+ expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(true);
+ });
+ });
+
+ describe('when ingress application new status and old status are the same', () => {
+ it('does not modify custom domain help text', () => {
+ ingressPreviousState.status = INSTALLED;
+ ingressNewState.status = ingressPreviousState.status;
+
+ cluster.toggleIngressDomainHelpText(ingressPreviousState, ingressNewState);
+
+ expect(cluster.ingressDomainHelpText.classList.contains('hide')).toEqual(true);
+ });
+ });
+ });
});
diff --git a/spec/javascripts/clusters/components/application_row_spec.js b/spec/javascripts/clusters/components/application_row_spec.js
index 8cb9713964e..a2dd4e93daf 100644
--- a/spec/javascripts/clusters/components/application_row_spec.js
+++ b/spec/javascripts/clusters/components/application_row_spec.js
@@ -230,7 +230,7 @@ describe('Application Row', () => {
expect(upgradeBtn.innerHTML).toContain('Upgrade');
});
- it('has enabled "Retry upgrade" when APPLICATION_STATUS.UPDATE_ERRORED', () => {
+ it('has enabled "Retry update" when APPLICATION_STATUS.UPDATE_ERRORED', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.UPDATE_ERRORED,
@@ -239,10 +239,10 @@ describe('Application Row', () => {
expect(upgradeBtn).not.toBe(null);
expect(vm.upgradeFailed).toBe(true);
- expect(upgradeBtn.innerHTML).toContain('Retry upgrade');
+ expect(upgradeBtn.innerHTML).toContain('Retry update');
});
- it('has disabled "Retry upgrade" when APPLICATION_STATUS.UPDATING', () => {
+ it('has disabled "Updating" when APPLICATION_STATUS.UPDATING', () => {
vm = mountComponent(ApplicationRow, {
...DEFAULT_APPLICATION_STATE,
status: APPLICATION_STATUS.UPDATING,
@@ -251,7 +251,7 @@ describe('Application Row', () => {
expect(upgradeBtn).not.toBe(null);
expect(vm.isUpgrading).toBe(true);
- expect(upgradeBtn.innerHTML).toContain('Upgrading');
+ expect(upgradeBtn.innerHTML).toContain('Updating');
});
it('clicking upgrade button emits event', () => {
@@ -295,7 +295,7 @@ describe('Application Row', () => {
expect(failureMessage).not.toBe(null);
expect(failureMessage.innerHTML).toContain(
- 'Something went wrong when upgrading GitLab Runner. Please check the logs and try again.',
+ 'Update failed. Please check the logs and try again.',
);
});
});
diff --git a/spec/javascripts/clusters/components/applications_spec.js b/spec/javascripts/clusters/components/applications_spec.js
index 14ef1193984..0f8153ad493 100644
--- a/spec/javascripts/clusters/components/applications_spec.js
+++ b/spec/javascripts/clusters/components/applications_spec.js
@@ -1,7 +1,9 @@
import Vue from 'vue';
import applications from '~/clusters/components/applications.vue';
import { CLUSTER_TYPE } from '~/clusters/constants';
+import eventHub from '~/clusters/event_hub';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { APPLICATIONS_MOCK_STATE } from '../services/mock_data';
describe('Applications', () => {
let vm;
@@ -18,16 +20,8 @@ describe('Applications', () => {
describe('Project cluster applications', () => {
beforeEach(() => {
vm = mountComponent(Applications, {
+ applications: APPLICATIONS_MOCK_STATE,
type: CLUSTER_TYPE.PROJECT,
- applications: {
- helm: { title: 'Helm Tiller' },
- ingress: { title: 'Ingress' },
- cert_manager: { title: 'Cert-Manager' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub' },
- knative: { title: 'Knative' },
- },
});
});
@@ -64,15 +58,7 @@ describe('Applications', () => {
beforeEach(() => {
vm = mountComponent(Applications, {
type: CLUSTER_TYPE.GROUP,
- applications: {
- helm: { title: 'Helm Tiller' },
- ingress: { title: 'Ingress' },
- cert_manager: { title: 'Cert-Manager' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub' },
- knative: { title: 'Knative' },
- },
+ applications: APPLICATIONS_MOCK_STATE,
});
});
@@ -93,7 +79,7 @@ describe('Applications', () => {
});
it('renders a row for GitLab Runner', () => {
- expect(vm.$el.querySelector('.js-cluster-application-row-runner')).toBeNull();
+ expect(vm.$el.querySelector('.js-cluster-application-row-runner')).not.toBeNull();
});
it('renders a row for Jupyter', () => {
@@ -111,21 +97,16 @@ describe('Applications', () => {
it('renders ip address with a clipboard button', () => {
vm = mountComponent(Applications, {
applications: {
+ ...APPLICATIONS_MOCK_STATE,
ingress: {
title: 'Ingress',
status: 'installed',
externalIp: '0.0.0.0',
},
- helm: { title: 'Helm Tiller' },
- cert_manager: { title: 'Cert-Manager' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', hostname: '' },
- knative: { title: 'Knative', hostname: '' },
},
});
- expect(vm.$el.querySelector('.js-ip-address').value).toEqual('0.0.0.0');
+ expect(vm.$el.querySelector('.js-endpoint').value).toEqual('0.0.0.0');
expect(
vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'),
@@ -133,13 +114,14 @@ describe('Applications', () => {
});
});
- describe('without ip address', () => {
- it('renders an input text with a question mark and an alert text', () => {
+ describe('with hostname', () => {
+ it('renders hostname with a clipboard button', () => {
vm = mountComponent(Applications, {
applications: {
ingress: {
title: 'Ingress',
status: 'installed',
+ externalHostname: 'localhost.localdomain',
},
helm: { title: 'Helm Tiller' },
cert_manager: { title: 'Cert-Manager' },
@@ -150,9 +132,28 @@ describe('Applications', () => {
},
});
- expect(vm.$el.querySelector('.js-ip-address').value).toEqual('?');
+ expect(vm.$el.querySelector('.js-endpoint').value).toEqual('localhost.localdomain');
+
+ expect(
+ vm.$el.querySelector('.js-clipboard-btn').getAttribute('data-clipboard-text'),
+ ).toEqual('localhost.localdomain');
+ });
+ });
+
+ describe('without ip address', () => {
+ it('renders an input text with a loading icon and an alert text', () => {
+ vm = mountComponent(Applications, {
+ applications: {
+ ...APPLICATIONS_MOCK_STATE,
+ ingress: {
+ title: 'Ingress',
+ status: 'installed',
+ },
+ },
+ });
- expect(vm.$el.querySelector('.js-no-ip-message')).not.toBe(null);
+ expect(vm.$el.querySelector('.js-ingress-ip-loading-icon')).not.toBe(null);
+ expect(vm.$el.querySelector('.js-no-endpoint-message')).not.toBe(null);
});
});
});
@@ -160,19 +161,11 @@ describe('Applications', () => {
describe('before installing', () => {
it('does not render the IP address', () => {
vm = mountComponent(Applications, {
- applications: {
- helm: { title: 'Helm Tiller' },
- ingress: { title: 'Ingress' },
- cert_manager: { title: 'Cert-Manager' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', hostname: '' },
- knative: { title: 'Knative', hostname: '' },
- },
+ applications: APPLICATIONS_MOCK_STATE,
});
expect(vm.$el.textContent).not.toContain('Ingress IP Address');
- expect(vm.$el.querySelector('.js-ip-address')).toBe(null);
+ expect(vm.$el.querySelector('.js-endpoint')).toBe(null);
});
});
@@ -181,17 +174,12 @@ describe('Applications', () => {
it('renders email & allows editing', () => {
vm = mountComponent(Applications, {
applications: {
- helm: { title: 'Helm Tiller', status: 'installed' },
- ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
+ ...APPLICATIONS_MOCK_STATE,
cert_manager: {
title: 'Cert-Manager',
email: 'before@example.com',
status: 'installable',
},
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
- knative: { title: 'Knative', hostname: '', status: 'installable' },
},
});
@@ -204,17 +192,12 @@ describe('Applications', () => {
it('renders email in readonly', () => {
vm = mountComponent(Applications, {
applications: {
- helm: { title: 'Helm Tiller', status: 'installed' },
- ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
+ ...APPLICATIONS_MOCK_STATE,
cert_manager: {
title: 'Cert-Manager',
email: 'after@example.com',
status: 'installed',
},
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
- knative: { title: 'Knative', hostname: '', status: 'installable' },
},
});
@@ -229,13 +212,12 @@ describe('Applications', () => {
it('renders hostname active input', () => {
vm = mountComponent(Applications, {
applications: {
- helm: { title: 'Helm Tiller', status: 'installed' },
- ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
- cert_manager: { title: 'Cert-Manager' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
- knative: { title: 'Knative', hostname: '', status: 'installable' },
+ ...APPLICATIONS_MOCK_STATE,
+ ingress: {
+ title: 'Ingress',
+ status: 'installed',
+ externalIp: '1.1.1.1',
+ },
},
});
@@ -247,13 +229,8 @@ describe('Applications', () => {
it('does not render hostname input', () => {
vm = mountComponent(Applications, {
applications: {
- helm: { title: 'Helm Tiller', status: 'installed' },
+ ...APPLICATIONS_MOCK_STATE,
ingress: { title: 'Ingress', status: 'installed' },
- cert_manager: { title: 'Cert-Manager' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', hostname: '', status: 'installable' },
- knative: { title: 'Knative', hostname: '', status: 'installable' },
},
});
@@ -265,13 +242,9 @@ describe('Applications', () => {
it('renders readonly input', () => {
vm = mountComponent(Applications, {
applications: {
- helm: { title: 'Helm Tiller', status: 'installed' },
+ ...APPLICATIONS_MOCK_STATE,
ingress: { title: 'Ingress', status: 'installed', externalIp: '1.1.1.1' },
- cert_manager: { title: 'Cert-Manager' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
jupyter: { title: 'JupyterHub', status: 'installed', hostname: '' },
- knative: { title: 'Knative', status: 'installed', hostname: '' },
},
});
@@ -282,15 +255,7 @@ describe('Applications', () => {
describe('without ingress installed', () => {
beforeEach(() => {
vm = mountComponent(Applications, {
- applications: {
- helm: { title: 'Helm Tiller' },
- ingress: { title: 'Ingress' },
- cert_manager: { title: 'Cert-Manager' },
- runner: { title: 'GitLab Runner' },
- prometheus: { title: 'Prometheus' },
- jupyter: { title: 'JupyterHub', status: 'not_installable' },
- knative: { title: 'Knative' },
- },
+ applications: APPLICATIONS_MOCK_STATE,
});
});
@@ -310,4 +275,76 @@ describe('Applications', () => {
});
});
});
+
+ describe('Knative application', () => {
+ describe('when installed', () => {
+ describe('with ip address', () => {
+ const props = {
+ applications: {
+ ...APPLICATIONS_MOCK_STATE,
+ knative: {
+ title: 'Knative',
+ hostname: 'example.com',
+ status: 'installed',
+ externalIp: '1.1.1.1',
+ },
+ },
+ };
+ it('renders ip address with a clipboard button', () => {
+ vm = mountComponent(Applications, props);
+
+ expect(vm.$el.querySelector('.js-knative-endpoint').value).toEqual('1.1.1.1');
+
+ expect(
+ vm.$el
+ .querySelector('.js-knative-endpoint-clipboard-btn')
+ .getAttribute('data-clipboard-text'),
+ ).toEqual('1.1.1.1');
+ });
+
+ it('renders domain & allows editing', () => {
+ expect(vm.$el.querySelector('.js-knative-domainname').value).toEqual('example.com');
+ expect(vm.$el.querySelector('.js-knative-domainname').getAttribute('readonly')).toBe(
+ null,
+ );
+ });
+
+ it('renders an update/save Knative domain button', () => {
+ expect(vm.$el.querySelector('.js-knative-save-domain-button')).not.toBe(null);
+ });
+
+ it('emits event when clicking Save changes button', () => {
+ spyOn(eventHub, '$emit');
+ vm = mountComponent(Applications, props);
+
+ const saveButton = vm.$el.querySelector('.js-knative-save-domain-button');
+
+ saveButton.click();
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('saveKnativeDomain', {
+ id: 'knative',
+ params: { hostname: 'example.com' },
+ });
+ });
+ });
+
+ describe('without ip address', () => {
+ it('renders an input text with a loading icon and an alert text', () => {
+ vm = mountComponent(Applications, {
+ applications: {
+ ...APPLICATIONS_MOCK_STATE,
+ knative: {
+ title: 'Knative',
+ hostname: 'example.com',
+ status: 'installed',
+ },
+ },
+ });
+
+ expect(vm.$el.querySelector('.js-knative-ip-loading-icon')).not.toBe(null);
+ expect(vm.$el.querySelector('.js-no-knative-endpoint-message')).not.toBe(null);
+ });
+ });
+ });
+ });
});
diff --git a/spec/javascripts/clusters/services/mock_data.js b/spec/javascripts/clusters/services/mock_data.js
index 3c3d9977ffb..b4d1bb710e0 100644
--- a/spec/javascripts/clusters/services/mock_data.js
+++ b/spec/javascripts/clusters/services/mock_data.js
@@ -17,6 +17,7 @@ const CLUSTERS_MOCK_DATA = {
status: APPLICATION_STATUS.ERROR,
status_reason: 'Cannot connect',
external_ip: null,
+ external_hostname: null,
},
{
name: 'runner',
@@ -62,6 +63,7 @@ const CLUSTERS_MOCK_DATA = {
status: APPLICATION_STATUS.INSTALLED,
status_reason: 'Cannot connect',
external_ip: '1.1.1.1',
+ external_hostname: null,
},
{
name: 'runner',
@@ -115,4 +117,14 @@ const DEFAULT_APPLICATION_STATE = {
requestReason: null,
};
-export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE };
+const APPLICATIONS_MOCK_STATE = {
+ helm: { title: 'Helm Tiller', status: 'installable' },
+ ingress: { title: 'Ingress', status: 'installable' },
+ cert_manager: { title: 'Cert-Manager', status: 'installable' },
+ runner: { title: 'GitLab Runner' },
+ prometheus: { title: 'Prometheus' },
+ jupyter: { title: 'JupyterHub', status: 'installable', hostname: '' },
+ knative: { title: 'Knative ', status: 'installable', hostname: '' },
+};
+
+export { CLUSTERS_MOCK_DATA, DEFAULT_APPLICATION_STATE, APPLICATIONS_MOCK_STATE };
diff --git a/spec/javascripts/clusters/stores/clusters_store_spec.js b/spec/javascripts/clusters/stores/clusters_store_spec.js
index 37a4d6614f6..161722ec571 100644
--- a/spec/javascripts/clusters/stores/clusters_store_spec.js
+++ b/spec/javascripts/clusters/stores/clusters_store_spec.js
@@ -78,6 +78,7 @@ describe('Clusters Store', () => {
requestStatus: null,
requestReason: null,
externalIp: null,
+ externalHostname: null,
},
runner: {
title: 'GitLab Runner',
@@ -111,7 +112,9 @@ describe('Clusters Store', () => {
requestStatus: null,
requestReason: null,
hostname: null,
+ isEditingHostName: false,
externalIp: null,
+ externalHostname: null,
},
cert_manager: {
title: 'Cert-Manager',
diff --git a/spec/javascripts/collapsed_sidebar_todo_spec.js b/spec/javascripts/collapsed_sidebar_todo_spec.js
index dc5737558c0..bb90e53e525 100644
--- a/spec/javascripts/collapsed_sidebar_todo_spec.js
+++ b/spec/javascripts/collapsed_sidebar_todo_spec.js
@@ -6,7 +6,7 @@ import Sidebar from '~/right_sidebar';
import timeoutPromise from './helpers/set_timeout_promise_helper';
describe('Issuable right sidebar collapsed todo toggle', () => {
- const fixtureName = 'issues/open-issue.html.raw';
+ const fixtureName = 'issues/open-issue.html';
const jsonFixtureName = 'todos/todos.json';
let mock;
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index 04c8ab44405..fec01b1f0a3 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -87,7 +87,7 @@ describe('Pipelines table in Commits and Merge requests', function() {
};
vm.$nextTick(() => {
- vm.$el.querySelector('.js-next-button a').click();
+ vm.$el.querySelector('.js-next-button .page-link').click();
expect(vm.updateContent).toHaveBeenCalledWith({ page: '2' });
done();
diff --git a/spec/javascripts/create_item_dropdown_spec.js b/spec/javascripts/create_item_dropdown_spec.js
index 9cf72d7c55b..a814952faab 100644
--- a/spec/javascripts/create_item_dropdown_spec.js
+++ b/spec/javascripts/create_item_dropdown_spec.js
@@ -20,7 +20,7 @@ const DROPDOWN_ITEM_DATA = [
];
describe('CreateItemDropdown', () => {
- preloadFixtures('static/create_item_dropdown.html.raw');
+ preloadFixtures('static/create_item_dropdown.html');
let $wrapperEl;
let createItemDropdown;
@@ -44,7 +44,7 @@ describe('CreateItemDropdown', () => {
}
beforeEach(() => {
- loadFixtures('static/create_item_dropdown.html.raw');
+ loadFixtures('static/create_item_dropdown.html');
$wrapperEl = $('.js-create-item-dropdown-fixture-root');
});
diff --git a/spec/javascripts/diffs/components/app_spec.js b/spec/javascripts/diffs/components/app_spec.js
index 5abdfe695d0..8d7c52a2876 100644
--- a/spec/javascripts/diffs/components/app_spec.js
+++ b/spec/javascripts/diffs/components/app_spec.js
@@ -1,15 +1,24 @@
import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { GlLoadingIcon } from '@gitlab/ui';
import { TEST_HOST } from 'spec/test_constants';
import App from '~/diffs/components/app.vue';
import NoChanges from '~/diffs/components/no_changes.vue';
import DiffFile from '~/diffs/components/diff_file.vue';
+import Mousetrap from 'mousetrap';
+import CompareVersions from '~/diffs/components/compare_versions.vue';
+import HiddenFilesWarning from '~/diffs/components/hidden_files_warning.vue';
+import CommitWidget from '~/diffs/components/commit_widget.vue';
+import TreeList from '~/diffs/components/tree_list.vue';
import createDiffsStore from '../create_diffs_store';
+import diffsMockData from '../mock_data/merge_request_diffs';
+
+const mergeRequestDiff = { version_index: 1 };
describe('diffs/components/app', () => {
const oldMrTabs = window.mrTabs;
let store;
- let vm;
+ let wrapper;
function createComponent(props = {}, extendStore = () => {}) {
const localVue = createLocalVue();
@@ -21,7 +30,7 @@ describe('diffs/components/app', () => {
extendStore(store);
- vm = shallowMount(localVue.extend(App), {
+ wrapper = shallowMount(localVue.extend(App), {
localVue,
propsData: {
endpoint: `${TEST_HOST}/diff/endpoint`,
@@ -38,7 +47,6 @@ describe('diffs/components/app', () => {
// setup globals (needed for component to mount :/)
window.mrTabs = jasmine.createSpyObj('mrTabs', ['resetViewContainer']);
window.mrTabs.expandViewContainer = jasmine.createSpy();
- window.location.hash = 'ABC_123';
});
afterEach(() => {
@@ -46,25 +54,59 @@ describe('diffs/components/app', () => {
window.mrTabs = oldMrTabs;
// reset component
- vm.destroy();
+ wrapper.destroy();
+ });
+
+ it('displays loading icon on loading', () => {
+ createComponent({}, ({ state }) => {
+ state.diffs.isLoading = true;
+ });
+
+ expect(wrapper.contains(GlLoadingIcon)).toBe(true);
+ });
+
+ it('displays diffs container when not loading', () => {
+ createComponent();
+
+ expect(wrapper.contains(GlLoadingIcon)).toBe(false);
+ expect(wrapper.contains('#diffs')).toBe(true);
});
it('does not show commit info', () => {
createComponent();
- expect(vm.contains('.blob-commit-info')).toBe(false);
+ expect(wrapper.contains('.blob-commit-info')).toBe(false);
});
- it('sets highlighted row if hash exists in location object', done => {
- createComponent({
- shouldShow: true,
+ describe('row highlighting', () => {
+ beforeEach(() => {
+ window.location.hash = 'ABC_123';
});
- // Component uses $nextTick so we wait until that has finished
- setTimeout(() => {
- expect(store.state.diffs.highlightedRow).toBe('ABC_123');
+ it('sets highlighted row if hash exists in location object', done => {
+ createComponent({
+ shouldShow: true,
+ });
- done();
+ // Component uses $nextTick so we wait until that has finished
+ setTimeout(() => {
+ expect(store.state.diffs.highlightedRow).toBe('ABC_123');
+
+ done();
+ });
+ });
+
+ it('marks current diff file based on currently highlighted row', done => {
+ createComponent({
+ shouldShow: true,
+ });
+
+ // Component uses $nextTick so we wait until that has finished
+ setTimeout(() => {
+ expect(store.state.diffs.currentDiffFileId).toBe('ABC');
+
+ done();
+ });
});
});
@@ -76,7 +118,7 @@ describe('diffs/components/app', () => {
it('sets initial width when no localStorage has been set', () => {
createComponent();
- expect(vm.vm.treeWidth).toEqual(320);
+ expect(wrapper.vm.treeWidth).toEqual(320);
});
it('sets initial width to localStorage size', () => {
@@ -84,13 +126,26 @@ describe('diffs/components/app', () => {
createComponent();
- expect(vm.vm.treeWidth).toEqual(200);
+ expect(wrapper.vm.treeWidth).toEqual(200);
});
it('sets width of tree list', () => {
createComponent();
- expect(vm.find('.js-diff-tree-list').element.style.width).toEqual('320px');
+ expect(wrapper.find('.js-diff-tree-list').element.style.width).toEqual('320px');
+ });
+ });
+
+ it('marks current diff file based on currently highlighted row', done => {
+ createComponent({
+ shouldShow: true,
+ });
+
+ // Component uses $nextTick so we wait until that has finished
+ setTimeout(() => {
+ expect(store.state.diffs.currentDiffFileId).toBe('ABC');
+
+ done();
});
});
@@ -98,27 +153,305 @@ describe('diffs/components/app', () => {
it('renders empty state when no diff files exist', () => {
createComponent();
- expect(vm.contains(NoChanges)).toBe(true);
+ expect(wrapper.contains(NoChanges)).toBe(true);
});
it('does not render empty state when diff files exist', () => {
- createComponent({}, () => {
- store.state.diffs.diffFiles.push({
+ createComponent({}, ({ state }) => {
+ state.diffs.diffFiles.push({
id: 1,
});
});
- expect(vm.contains(NoChanges)).toBe(false);
- expect(vm.findAll(DiffFile).length).toBe(1);
+ expect(wrapper.contains(NoChanges)).toBe(false);
+ expect(wrapper.findAll(DiffFile).length).toBe(1);
});
it('does not render empty state when versions match', () => {
+ createComponent({}, ({ state }) => {
+ state.diffs.startVersion = mergeRequestDiff;
+ state.diffs.mergeRequestDiff = mergeRequestDiff;
+ });
+
+ expect(wrapper.contains(NoChanges)).toBe(false);
+ });
+ });
+
+ describe('keyboard shortcut navigation', () => {
+ const mappings = {
+ '[': -1,
+ k: -1,
+ ']': +1,
+ j: +1,
+ };
+ let spy;
+
+ describe('visible app', () => {
+ beforeEach(() => {
+ spy = jasmine.createSpy('spy');
+
+ createComponent({
+ shouldShow: true,
+ });
+ wrapper.setMethods({
+ jumpToFile: spy,
+ });
+ });
+
+ it('calls `jumpToFile()` with correct parameter whenever pre-defined key is pressed', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ Object.keys(mappings).forEach(function(key) {
+ Mousetrap.trigger(key);
+
+ expect(spy.calls.mostRecent().args).toEqual([mappings[key]]);
+ });
+
+ expect(spy.calls.count()).toEqual(Object.keys(mappings).length);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not call `jumpToFile()` when unknown key is pressed', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ Mousetrap.trigger('d');
+
+ expect(spy).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('hideen app', () => {
+ beforeEach(() => {
+ spy = jasmine.createSpy('spy');
+
+ createComponent({
+ shouldShow: false,
+ });
+ wrapper.setMethods({
+ jumpToFile: spy,
+ });
+ });
+
+ it('stops calling `jumpToFile()` when application is hidden', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ Object.keys(mappings).forEach(function(key) {
+ Mousetrap.trigger(key);
+
+ expect(spy).not.toHaveBeenCalled();
+ });
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('jumpToFile', () => {
+ let spy;
+
+ beforeEach(() => {
+ spy = jasmine.createSpy();
+
createComponent({}, () => {
- store.state.diffs.startVersion = { version_index: 1 };
- store.state.diffs.mergeRequestDiff = { version_index: 1 };
+ store.state.diffs.diffFiles = [
+ { file_hash: '111', file_path: '111.js' },
+ { file_hash: '222', file_path: '222.js' },
+ { file_hash: '333', file_path: '333.js' },
+ ];
+ });
+
+ wrapper.setMethods({
+ scrollToFile: spy,
});
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('jumps to next and previous files in the list', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ wrapper.vm.jumpToFile(+1);
+
+ expect(spy.calls.mostRecent().args).toEqual(['222.js']);
+ store.state.diffs.currentDiffFileId = '222';
+ wrapper.vm.jumpToFile(+1);
+
+ expect(spy.calls.mostRecent().args).toEqual(['333.js']);
+ store.state.diffs.currentDiffFileId = '333';
+ wrapper.vm.jumpToFile(-1);
+
+ expect(spy.calls.mostRecent().args).toEqual(['222.js']);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not jump to previous file from the first one', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ store.state.diffs.currentDiffFileId = '333';
+
+ expect(wrapper.vm.currentDiffIndex).toEqual(2);
+
+ wrapper.vm.jumpToFile(+1);
+
+ expect(wrapper.vm.currentDiffIndex).toEqual(2);
+ expect(spy).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not jump to next file from the last one', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(wrapper.vm.currentDiffIndex).toEqual(0);
+
+ wrapper.vm.jumpToFile(-1);
+
+ expect(wrapper.vm.currentDiffIndex).toEqual(0);
+ expect(spy).not.toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('diffs', () => {
+ it('should render compare versions component', () => {
+ createComponent({}, ({ state }) => {
+ state.diffs.mergeRequestDiffs = diffsMockData;
+ state.diffs.targetBranchName = 'target-branch';
+ state.diffs.mergeRequestDiff = mergeRequestDiff;
+ });
+
+ expect(wrapper.contains(CompareVersions)).toBe(true);
+ expect(wrapper.find(CompareVersions).props()).toEqual(
+ jasmine.objectContaining({
+ targetBranch: {
+ branchName: 'target-branch',
+ versionIndex: -1,
+ path: '',
+ },
+ mergeRequestDiffs: diffsMockData,
+ mergeRequestDiff,
+ }),
+ );
+ });
+
+ it('should render hidden files warning if render overflow warning is present', () => {
+ createComponent({}, ({ state }) => {
+ state.diffs.renderOverflowWarning = true;
+ state.diffs.realSize = '5';
+ state.diffs.plainDiffPath = 'plain diff path';
+ state.diffs.emailPatchPath = 'email patch path';
+ state.diffs.size = 1;
+ });
+
+ expect(wrapper.contains(HiddenFilesWarning)).toBe(true);
+ expect(wrapper.find(HiddenFilesWarning).props()).toEqual(
+ jasmine.objectContaining({
+ total: '5',
+ plainDiffPath: 'plain diff path',
+ emailPatchPath: 'email patch path',
+ visible: 1,
+ }),
+ );
+ });
+
+ it('should display commit widget if store has a commit', () => {
+ createComponent({}, () => {
+ store.state.diffs.commit = {
+ author: 'John Doe',
+ };
+ });
+
+ expect(wrapper.contains(CommitWidget)).toBe(true);
+ });
+
+ it('should display diff file if there are diff files', () => {
+ createComponent({}, ({ state }) => {
+ state.diffs.diffFiles.push({ sha: '123' });
+ });
+
+ expect(wrapper.contains(DiffFile)).toBe(true);
+ });
+
+ it('should render tree list', () => {
+ createComponent();
+
+ expect(wrapper.find(TreeList).exists()).toBe(true);
+ });
+ });
+
+ describe('hideTreeListIfJustOneFile', () => {
+ let toggleShowTreeList;
+
+ beforeEach(() => {
+ toggleShowTreeList = jasmine.createSpy('toggleShowTreeList');
+ });
+
+ afterEach(() => {
+ localStorage.removeItem('mr_tree_show');
+ });
+
+ it('calls toggleShowTreeList when only 1 file', () => {
+ createComponent({}, ({ state }) => {
+ state.diffs.diffFiles.push({ sha: '123' });
+ });
+
+ wrapper.setMethods({
+ toggleShowTreeList,
+ });
+
+ wrapper.vm.hideTreeListIfJustOneFile();
+
+ expect(toggleShowTreeList).toHaveBeenCalledWith(false);
+ });
+
+ it('does not call toggleShowTreeList when more than 1 file', () => {
+ createComponent({}, ({ state }) => {
+ state.diffs.diffFiles.push({ sha: '123' });
+ state.diffs.diffFiles.push({ sha: '124' });
+ });
+
+ wrapper.setMethods({
+ toggleShowTreeList,
+ });
+
+ wrapper.vm.hideTreeListIfJustOneFile();
+
+ expect(toggleShowTreeList).not.toHaveBeenCalled();
+ });
+
+ it('does not call toggleShowTreeList when localStorage is set', () => {
+ localStorage.setItem('mr_tree_show', 'true');
+
+ createComponent({}, ({ state }) => {
+ state.diffs.diffFiles.push({ sha: '123' });
+ });
+
+ wrapper.setMethods({
+ toggleShowTreeList,
+ });
+
+ wrapper.vm.hideTreeListIfJustOneFile();
- expect(vm.contains(NoChanges)).toBe(false);
+ expect(toggleShowTreeList).not.toHaveBeenCalled();
});
});
});
diff --git a/spec/javascripts/diffs/components/changed_files_dropdown_spec.js b/spec/javascripts/diffs/components/changed_files_dropdown_spec.js
deleted file mode 100644
index 7237274eb43..00000000000
--- a/spec/javascripts/diffs/components/changed_files_dropdown_spec.js
+++ /dev/null
@@ -1 +0,0 @@
-// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js b/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js
index 53b9ac22fc0..8a3834d542f 100644
--- a/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js
+++ b/spec/javascripts/diffs/components/compare_versions_dropdown_spec.js
@@ -1,34 +1,161 @@
-// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
import { shallowMount, createLocalVue } from '@vue/test-utils';
import CompareVersionsDropdown from '~/diffs/components/compare_versions_dropdown.vue';
import diffsMockData from '../mock_data/merge_request_diffs';
+import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
+
+const localVue = createLocalVue();
+const targetBranch = { branchName: 'tmp-wine-dev', versionIndex: -1 };
+const startVersion = { version_index: 4 };
+const mergeRequestVersion = {
+ version_path: '123',
+};
+const baseVersionPath = '/gnuwget/wget2/merge_requests/6/diffs?diff_id=37';
describe('CompareVersionsDropdown', () => {
let wrapper;
- const targetBranch = { branchName: 'tmp-wine-dev', versionIndex: -1 };
- const factory = (options = {}) => {
- const localVue = createLocalVue();
+ const findSelectedVersion = () => wrapper.find('.dropdown-menu-toggle');
+ const findVersionsListElements = () => wrapper.findAll('li');
+ const findLinkElement = index =>
+ findVersionsListElements()
+ .at(index)
+ .find('a');
+ const findLastLink = () => findLinkElement(findVersionsListElements().length - 1);
- wrapper = shallowMount(CompareVersionsDropdown, { localVue, ...options });
+ const createComponent = (props = {}) => {
+ wrapper = shallowMount(localVue.extend(CompareVersionsDropdown), {
+ localVue,
+ sync: false,
+ propsData: { ...props },
+ });
};
afterEach(() => {
wrapper.destroy();
});
- it('should render a correct base version link', () => {
- factory({
- propsData: {
- baseVersionPath: '/gnuwget/wget2/merge_requests/6/diffs?diff_id=37',
+ describe('selected version name', () => {
+ it('shows latest version when latest is selected', () => {
+ createComponent({
+ mergeRequestVersion,
+ startVersion,
+ otherVersions: diffsMockData,
+ });
+
+ expect(findSelectedVersion().text()).toBe('latest version');
+ });
+
+ it('shows target branch name for base branch', () => {
+ createComponent({
+ targetBranch,
+ });
+
+ expect(findSelectedVersion().text()).toBe('tmp-wine-dev');
+ });
+
+ it('shows correct version for non-base and non-latest branches', () => {
+ createComponent({
+ startVersion,
+ targetBranch,
+ });
+
+ expect(findSelectedVersion().text()).toBe(`version ${startVersion.version_index}`);
+ });
+ });
+
+ describe('target versions list', () => {
+ it('should have the same length as otherVersions if merge request version is present', () => {
+ createComponent({
+ mergeRequestVersion,
+ otherVersions: diffsMockData,
+ });
+
+ expect(findVersionsListElements().length).toEqual(diffsMockData.length);
+ });
+
+ it('should have an otherVersions length plus 1 if no merge request version is present', () => {
+ createComponent({
+ targetBranch,
+ otherVersions: diffsMockData,
+ });
+
+ expect(findVersionsListElements().length).toEqual(diffsMockData.length + 1);
+ });
+
+ it('should have base branch link as active on base branch', () => {
+ createComponent({
+ targetBranch,
+ otherVersions: diffsMockData,
+ });
+
+ expect(findLastLink().classes()).toContain('is-active');
+ });
+
+ it('should have correct branch link as active if start version present', () => {
+ createComponent({
+ targetBranch,
+ startVersion,
+ otherVersions: diffsMockData,
+ });
+
+ expect(findLinkElement(0).classes()).toContain('is-active');
+ });
+
+ it('should render a correct base version link', () => {
+ createComponent({
+ baseVersionPath,
otherVersions: diffsMockData.slice(1),
targetBranch,
- },
+ });
+
+ expect(findLastLink().attributes('href')).toEqual(baseVersionPath);
+ expect(findLastLink().text()).toContain('(base)');
+ });
+
+ it('should not render commits count if no showCommitsCount is passed', () => {
+ createComponent({
+ otherVersions: diffsMockData,
+ targetBranch,
+ });
+
+ const commitsCount = diffsMockData[0].commits_count;
+
+ expect(findLinkElement(0).text()).not.toContain(`${commitsCount} commit`);
+ });
+
+ it('should render correct commits count if showCommitsCount is passed', () => {
+ createComponent({
+ otherVersions: diffsMockData,
+ targetBranch,
+ showCommitCount: true,
+ });
+
+ const commitsCount = diffsMockData[0].commits_count;
+
+ expect(findLinkElement(0).text()).toContain(`${commitsCount} commit`);
+ });
+
+ it('should render correct commit sha', () => {
+ createComponent({
+ otherVersions: diffsMockData,
+ targetBranch,
+ });
+
+ const commitShaElement = findLinkElement(0).find('.commit-sha');
+
+ expect(commitShaElement.text()).toBe(diffsMockData[0].short_commit_sha);
});
- const links = wrapper.findAll('a');
- const lastLink = links.wrappers[links.length - 1];
+ it('should render correct time-ago ', () => {
+ createComponent({
+ otherVersions: diffsMockData,
+ targetBranch,
+ });
+
+ const timeAgoElement = findLinkElement(0).find(TimeAgo);
- expect(lastLink.attributes('href')).toEqual(wrapper.props('baseVersionPath'));
+ expect(timeAgoElement.exists()).toBe(true);
+ expect(timeAgoElement.props('time')).toBe(diffsMockData[0].created_at);
+ });
});
});
diff --git a/spec/javascripts/diffs/components/diff_content_spec.js b/spec/javascripts/diffs/components/diff_content_spec.js
index a1bb51963d6..bc9288e4150 100644
--- a/spec/javascripts/diffs/components/diff_content_spec.js
+++ b/spec/javascripts/diffs/components/diff_content_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import DiffContentComponent from '~/diffs/components/diff_content.vue';
-import { createStore } from '~/mr_notes/stores';
+import { createStore } from 'ee_else_ce/mr_notes/stores';
import { mountComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import { GREEN_BOX_IMAGE_URL, RED_BOX_IMAGE_URL } from 'spec/test_constants';
import '~/behaviors/markdown/render_gfm';
diff --git a/spec/javascripts/diffs/components/diff_file_header_spec.js b/spec/javascripts/diffs/components/diff_file_header_spec.js
index 005a4751ea1..e10193c25b7 100644
--- a/spec/javascripts/diffs/components/diff_file_header_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_header_spec.js
@@ -382,7 +382,7 @@ describe('diff_file_header', () => {
props.diffFile.edit_path = '/';
vm = mountComponentWithStore(Component, { props, store });
- expect(vm.$el.querySelector('.js-edit-blob')).toContainText('Edit');
+ expect(vm.$el.querySelector('.js-edit-blob')).not.toBe(null);
});
it('should not show edit button when file is deleted', () => {
@@ -491,5 +491,151 @@ describe('diff_file_header', () => {
});
});
});
+
+ describe('file actions', () => {
+ it('should not render if diff file has a submodule', () => {
+ props.diffFile.submodule = 'submodule';
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(vm.$el.querySelector('.file-actions')).toEqual(null);
+ });
+
+ it('should not render if add merge request buttons is false', () => {
+ props.addMergeRequestButtons = false;
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(vm.$el.querySelector('.file-actions')).toEqual(null);
+ });
+
+ describe('with add merge request buttons enabled', () => {
+ beforeEach(() => {
+ props.addMergeRequestButtons = true;
+ props.diffFile.edit_path = 'edit-path';
+ });
+
+ const viewReplacedFileButton = () => vm.$el.querySelector('.js-view-replaced-file');
+ const viewFileButton = () => vm.$el.querySelector('.js-view-file-button');
+ const externalUrl = () => vm.$el.querySelector('.js-external-url');
+
+ it('should render if add merge request buttons is true and diff file does not have a submodule', () => {
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(vm.$el.querySelector('.file-actions')).not.toEqual(null);
+ });
+
+ it('should not render view replaced file button if no replaced view path is present', () => {
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(viewReplacedFileButton()).toEqual(null);
+ });
+
+ it('should render view replaced file button if replaced view path is present', () => {
+ props.diffFile.replaced_view_path = 'replaced-view-path';
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(viewReplacedFileButton()).not.toEqual(null);
+ expect(viewReplacedFileButton().getAttribute('href')).toBe('replaced-view-path');
+ });
+
+ it('should render correct file view button path', () => {
+ props.diffFile.view_path = 'view-path';
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(viewFileButton().getAttribute('href')).toBe('view-path');
+ });
+
+ it('should not render external url view link if diff file has no external url', () => {
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(externalUrl()).toEqual(null);
+ });
+
+ it('should render external url view link if diff file has external url', () => {
+ props.diffFile.external_url = 'external_url';
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(externalUrl()).not.toEqual(null);
+ expect(externalUrl().getAttribute('href')).toBe('external_url');
+ });
+ });
+
+ describe('without file blob', () => {
+ beforeEach(() => {
+ props.diffFile.blob = null;
+ props.addMergeRequestButtons = true;
+ vm = mountComponentWithStore(Component, { props, store });
+ });
+
+ it('should not render toggle discussions button', () => {
+ expect(vm.$el.querySelector('.js-btn-vue-toggle-comments')).toEqual(null);
+ });
+
+ it('should not render edit button', () => {
+ expect(vm.$el.querySelector('.js-edit-blob')).toEqual(null);
+ });
+ });
+ });
+ });
+
+ describe('expand full file button', () => {
+ beforeEach(() => {
+ props.addMergeRequestButtons = true;
+ props.diffFile.edit_path = '/';
+ });
+
+ it('does not render button', () => {
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(vm.$el.querySelector('.js-expand-file')).toBe(null);
+ });
+
+ it('renders button', () => {
+ props.diffFile.is_fully_expanded = false;
+
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(vm.$el.querySelector('.js-expand-file')).not.toBe(null);
+ });
+
+ it('shows fully expanded text', () => {
+ props.diffFile.is_fully_expanded = false;
+ props.diffFile.isShowingFullFile = true;
+
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(vm.$el.querySelector('.js-expand-file').textContent).toContain('Show changes only');
+ });
+
+ it('shows expand text', () => {
+ props.diffFile.is_fully_expanded = false;
+
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(vm.$el.querySelector('.js-expand-file').textContent).toContain('Show full file');
+ });
+
+ it('renders loading icon', () => {
+ props.diffFile.is_fully_expanded = false;
+ props.diffFile.isLoadingFullFile = true;
+
+ vm = mountComponentWithStore(Component, { props, store });
+
+ expect(vm.$el.querySelector('.js-expand-file .loading-container')).not.toBe(null);
+ });
+
+ it('calls toggleFullDiff on click', () => {
+ props.diffFile.is_fully_expanded = false;
+
+ vm = mountComponentWithStore(Component, { props, store });
+
+ spyOn(vm.$store, 'dispatch').and.stub();
+
+ vm.$el.querySelector('.js-expand-file').click();
+
+ expect(vm.$store.dispatch).toHaveBeenCalledWith(
+ 'diffs/toggleFullDiff',
+ props.diffFile.file_path,
+ );
+ });
});
});
diff --git a/spec/javascripts/diffs/components/diff_file_spec.js b/spec/javascripts/diffs/components/diff_file_spec.js
index 65a1c9b8f15..d9b298e84da 100644
--- a/spec/javascripts/diffs/components/diff_file_spec.js
+++ b/spec/javascripts/diffs/components/diff_file_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import DiffFileComponent from '~/diffs/components/diff_file.vue';
import { diffViewerModes, diffViewerErrors } from '~/ide/constants';
-import store from '~/mr_notes/stores';
+import store from 'ee_else_ce/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
@@ -109,6 +109,31 @@ describe('DiffFile', () => {
done();
});
});
+
+ it('should update store state', done => {
+ spyOn(vm.$store, 'dispatch');
+
+ vm.isCollapsed = true;
+
+ vm.$nextTick(() => {
+ expect(vm.$store.dispatch).toHaveBeenCalledWith('diffs/setFileCollapsed', {
+ filePath: vm.file.file_path,
+ collapsed: true,
+ });
+
+ done();
+ });
+ });
+
+ it('updates local state when changing file state', done => {
+ vm.file.viewer.collapsed = true;
+
+ vm.$nextTick(() => {
+ expect(vm.isCollapsed).toBe(true);
+
+ done();
+ });
+ });
});
});
diff --git a/spec/javascripts/diffs/components/edit_button_spec.js b/spec/javascripts/diffs/components/edit_button_spec.js
deleted file mode 100644
index 7237274eb43..00000000000
--- a/spec/javascripts/diffs/components/edit_button_spec.js
+++ /dev/null
@@ -1 +0,0 @@
-// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/hidden_files_warning_spec.js b/spec/javascripts/diffs/components/hidden_files_warning_spec.js
deleted file mode 100644
index 7237274eb43..00000000000
--- a/spec/javascripts/diffs/components/hidden_files_warning_spec.js
+++ /dev/null
@@ -1 +0,0 @@
-// TODO: https://gitlab.com/gitlab-org/gitlab-ce/issues/48034
diff --git a/spec/javascripts/diffs/components/inline_diff_view_spec.js b/spec/javascripts/diffs/components/inline_diff_view_spec.js
index 2316ee29106..4452106580a 100644
--- a/spec/javascripts/diffs/components/inline_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/inline_diff_view_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import '~/behaviors/markdown/render_gfm';
import InlineDiffView from '~/diffs/components/inline_diff_view.vue';
-import store from '~/mr_notes/stores';
+import store from 'ee_else_ce/mr_notes/stores';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
import discussionsMockData from '../mock_data/diff_discussions';
diff --git a/spec/javascripts/diffs/components/parallel_diff_view_spec.js b/spec/javascripts/diffs/components/parallel_diff_view_spec.js
index 6f6b1c41915..236bda96145 100644
--- a/spec/javascripts/diffs/components/parallel_diff_view_spec.js
+++ b/spec/javascripts/diffs/components/parallel_diff_view_spec.js
@@ -1,6 +1,6 @@
import Vue from 'vue';
import ParallelDiffView from '~/diffs/components/parallel_diff_view.vue';
-import store from '~/mr_notes/stores';
+import store from 'ee_else_ce/mr_notes/stores';
import * as constants from '~/diffs/constants';
import { createComponentWithStore } from 'spec/helpers/vue_mount_component_helper';
import diffFileMockData from '../mock_data/diff_file';
@@ -18,6 +18,10 @@ describe('ParallelDiffView', () => {
}).$mount();
});
+ afterEach(() => {
+ component.$destroy();
+ });
+
describe('assigned', () => {
describe('diffLines', () => {
it('should normalize lines for empty cells', () => {
diff --git a/spec/javascripts/diffs/mock_data/diff_discussions.js b/spec/javascripts/diffs/mock_data/diff_discussions.js
index 4a091b4580b..711ab543411 100644
--- a/spec/javascripts/diffs/mock_data/diff_discussions.js
+++ b/spec/javascripts/diffs/mock_data/diff_discussions.js
@@ -288,6 +288,7 @@ export default {
external_storage: null,
old_path_html: 'CHANGELOG_OLD',
new_path_html: 'CHANGELOG',
+ is_fully_expanded: true,
context_lines_path:
'/gitlab-org/gitlab-test/blob/c48ee0d1bf3b30453f5b32250ce03134beaa6d13/CHANGELOG/diff',
highlighted_diff_lines: [
@@ -495,7 +496,7 @@ export default {
{
text: 'line',
rich_text:
- '<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="1">\n1\n</td>\n<td class="line_content new noteable_line"><span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n</td>\n</tr>\n<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="2">\n2\n</td>\n<td class="line_content new noteable_line"><span id="LC2" class="line" lang="plaintext"></span>\n</td>\n</tr>\n',
+ '<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="1">\n1\n</td>\n<td class="line_content new"><span id="LC1" class="line" lang="plaintext"> - Bad dates</span>\n</td>\n</tr>\n<tr class="line_holder new" id="">\n<td class="diff-line-num new old_line" data-linenumber="1">\n \n</td>\n<td class="diff-line-num new new_line" data-linenumber="2">\n2\n</td>\n<td class="line_content new"><span id="LC2" class="line" lang="plaintext"></span>\n</td>\n</tr>\n',
can_receive_suggestion: true,
line_code: '6f209374f7e565f771b95720abf46024c41d1885_1_1',
type: 'new',
diff --git a/spec/javascripts/diffs/store/actions_spec.js b/spec/javascripts/diffs/store/actions_spec.js
index acff80bca62..bca99caa920 100644
--- a/spec/javascripts/diffs/store/actions_spec.js
+++ b/spec/javascripts/diffs/store/actions_spec.js
@@ -30,6 +30,12 @@ import actions, {
setRenderTreeList,
setShowWhitespace,
setRenderIt,
+ requestFullDiff,
+ receiveFullDiffSucess,
+ receiveFullDiffError,
+ fetchFullDiff,
+ toggleFullDiff,
+ setFileCollapsed,
} from '~/diffs/store/actions';
import eventHub from '~/notes/event_hub';
import * as types from '~/diffs/store/mutation_types';
@@ -100,9 +106,10 @@ describe('DiffsStoreActions', () => {
});
describe('setHighlightedRow', () => {
- it('should set lineHash and fileHash of highlightedRow', () => {
+ it('should mark currently selected diff and set lineHash and fileHash of highlightedRow', () => {
testAction(setHighlightedRow, 'ABC_123', {}, [
{ type: types.SET_HIGHLIGHTED_ROW, payload: 'ABC_123' },
+ { type: types.UPDATE_CURRENT_DIFF_FILE_ID, payload: 'ABC' },
]);
});
});
@@ -713,22 +720,6 @@ describe('DiffsStoreActions', () => {
expect(commit).toHaveBeenCalledWith(types.UPDATE_CURRENT_DIFF_FILE_ID, 'test');
});
-
- it('resets currentDiffId after timeout', () => {
- const state = {
- treeEntries: {
- path: {
- fileHash: 'test',
- },
- },
- };
-
- scrollToFile({ state, commit }, 'path');
-
- jasmine.clock().tick(1000);
-
- expect(commit.calls.argsFor(1)).toEqual([types.UPDATE_CURRENT_DIFF_FILE_ID, '']);
- });
});
describe('toggleShowTreeList', () => {
@@ -743,6 +734,14 @@ describe('DiffsStoreActions', () => {
expect(localStorage.setItem).toHaveBeenCalledWith('mr_tree_show', true);
});
+
+ it('does not update localStorage', () => {
+ spyOn(localStorage, 'setItem');
+
+ toggleShowTreeList({ commit() {}, state: { showTreeList: true } }, false);
+
+ expect(localStorage.setItem).not.toHaveBeenCalled();
+ });
});
describe('renderFileForDiscussionId', () => {
@@ -862,4 +861,142 @@ describe('DiffsStoreActions', () => {
testAction(setRenderIt, 'file', {}, [{ type: types.RENDER_FILE, payload: 'file' }], [], done);
});
});
+
+ describe('requestFullDiff', () => {
+ it('commits REQUEST_FULL_DIFF', done => {
+ testAction(
+ requestFullDiff,
+ 'file',
+ {},
+ [{ type: types.REQUEST_FULL_DIFF, payload: 'file' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveFullDiffSucess', () => {
+ it('commits REQUEST_FULL_DIFF', done => {
+ testAction(
+ receiveFullDiffSucess,
+ { filePath: 'test', data: 'test' },
+ {},
+ [{ type: types.RECEIVE_FULL_DIFF_SUCCESS, payload: { filePath: 'test', data: 'test' } }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveFullDiffError', () => {
+ it('commits REQUEST_FULL_DIFF', done => {
+ testAction(
+ receiveFullDiffError,
+ 'file',
+ {},
+ [{ type: types.RECEIVE_FULL_DIFF_ERROR, payload: 'file' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchFullDiff', () => {
+ let mock;
+ let scrollToElementSpy;
+
+ beforeEach(() => {
+ scrollToElementSpy = spyOnDependency(actions, 'scrollToElement').and.stub();
+
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ describe('success', () => {
+ beforeEach(() => {
+ mock.onGet(`${gl.TEST_HOST}/context`).replyOnce(200, ['test']);
+ });
+
+ it('dispatches receiveFullDiffSucess', done => {
+ testAction(
+ fetchFullDiff,
+ { context_lines_path: `${gl.TEST_HOST}/context`, file_path: 'test', file_hash: 'test' },
+ null,
+ [],
+ [{ type: 'receiveFullDiffSucess', payload: { filePath: 'test', data: ['test'] } }],
+ done,
+ );
+ });
+
+ it('scrolls to element', done => {
+ fetchFullDiff(
+ { dispatch() {} },
+ { context_lines_path: `${gl.TEST_HOST}/context`, file_path: 'test', file_hash: 'test' },
+ )
+ .then(() => {
+ expect(scrollToElementSpy).toHaveBeenCalledWith('#test');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${gl.TEST_HOST}/context`).replyOnce(500);
+ });
+
+ it('dispatches receiveFullDiffError', done => {
+ testAction(
+ fetchFullDiff,
+ { context_lines_path: `${gl.TEST_HOST}/context`, file_path: 'test', file_hash: 'test' },
+ null,
+ [],
+ [{ type: 'receiveFullDiffError', payload: 'test' }],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('toggleFullDiff', () => {
+ let state;
+
+ beforeEach(() => {
+ state = {
+ diffFiles: [{ file_path: 'test', isShowingFullFile: false }],
+ };
+ });
+
+ it('dispatches fetchFullDiff when file is not expanded', done => {
+ testAction(
+ toggleFullDiff,
+ 'test',
+ state,
+ [],
+ [
+ { type: 'requestFullDiff', payload: 'test' },
+ { type: 'fetchFullDiff', payload: state.diffFiles[0] },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('setFileCollapsed', () => {
+ it('commits SET_FILE_COLLAPSED', done => {
+ testAction(
+ setFileCollapsed,
+ { filePath: 'test', collapsed: true },
+ null,
+ [{ type: types.SET_FILE_COLLAPSED, payload: { filePath: 'test', collapsed: true } }],
+ [],
+ done,
+ );
+ });
+ });
});
diff --git a/spec/javascripts/diffs/store/getters_spec.js b/spec/javascripts/diffs/store/getters_spec.js
index 0ab88e6b2aa..eab5703dfb2 100644
--- a/spec/javascripts/diffs/store/getters_spec.js
+++ b/spec/javascripts/diffs/store/getters_spec.js
@@ -270,4 +270,24 @@ describe('Diffs Module Getters', () => {
expect(getters.diffFilesLength(localState)).toBe(2);
});
});
+
+ describe('currentDiffIndex', () => {
+ it('returns index of currently selected diff in diffList', () => {
+ localState.diffFiles = [{ file_hash: '111' }, { file_hash: '222' }, { file_hash: '333' }];
+ localState.currentDiffFileId = '222';
+
+ expect(getters.currentDiffIndex(localState)).toEqual(1);
+
+ localState.currentDiffFileId = '333';
+
+ expect(getters.currentDiffIndex(localState)).toEqual(2);
+ });
+
+ it('returns 0 if no diff is selected yet or diff is not found', () => {
+ localState.diffFiles = [{ file_hash: '111' }, { file_hash: '222' }, { file_hash: '333' }];
+ localState.currentDiffFileId = '';
+
+ expect(getters.currentDiffIndex(localState)).toEqual(0);
+ });
+ });
});
diff --git a/spec/javascripts/diffs/store/mutations_spec.js b/spec/javascripts/diffs/store/mutations_spec.js
index 09ee691b602..5556a994a14 100644
--- a/spec/javascripts/diffs/store/mutations_spec.js
+++ b/spec/javascripts/diffs/store/mutations_spec.js
@@ -58,13 +58,15 @@ describe('DiffsStoreMutations', () => {
describe('EXPAND_ALL_FILES', () => {
it('should change the collapsed prop from diffFiles', () => {
const diffFile = {
- collapsed: true,
+ viewer: {
+ collapsed: true,
+ },
};
const state = { expandAllFiles: true, diffFiles: [diffFile] };
mutations[types.EXPAND_ALL_FILES](state);
- expect(state.diffFiles[0].collapsed).toEqual(false);
+ expect(state.diffFiles[0].viewer.collapsed).toEqual(false);
});
});
@@ -680,4 +682,78 @@ describe('DiffsStoreMutations', () => {
expect(state.showWhitespace).toBe(false);
});
});
+
+ describe('REQUEST_FULL_DIFF', () => {
+ it('sets isLoadingFullFile to true', () => {
+ const state = {
+ diffFiles: [{ file_path: 'test', isLoadingFullFile: false }],
+ };
+
+ mutations[types.REQUEST_FULL_DIFF](state, 'test');
+
+ expect(state.diffFiles[0].isLoadingFullFile).toBe(true);
+ });
+ });
+
+ describe('RECEIVE_FULL_DIFF_ERROR', () => {
+ it('sets isLoadingFullFile to false', () => {
+ const state = {
+ diffFiles: [{ file_path: 'test', isLoadingFullFile: true }],
+ };
+
+ mutations[types.RECEIVE_FULL_DIFF_ERROR](state, 'test');
+
+ expect(state.diffFiles[0].isLoadingFullFile).toBe(false);
+ });
+ });
+
+ describe('RECEIVE_FULL_DIFF_SUCCESS', () => {
+ it('sets isLoadingFullFile to false', () => {
+ const state = {
+ diffFiles: [
+ {
+ file_path: 'test',
+ isLoadingFullFile: true,
+ isShowingFullFile: false,
+ highlighted_diff_lines: [],
+ parallel_diff_lines: [],
+ },
+ ],
+ };
+
+ mutations[types.RECEIVE_FULL_DIFF_SUCCESS](state, { filePath: 'test', data: [] });
+
+ expect(state.diffFiles[0].isLoadingFullFile).toBe(false);
+ });
+
+ it('sets isShowingFullFile to true', () => {
+ const state = {
+ diffFiles: [
+ {
+ file_path: 'test',
+ isLoadingFullFile: true,
+ isShowingFullFile: false,
+ highlighted_diff_lines: [],
+ parallel_diff_lines: [],
+ },
+ ],
+ };
+
+ mutations[types.RECEIVE_FULL_DIFF_SUCCESS](state, { filePath: 'test', data: [] });
+
+ expect(state.diffFiles[0].isShowingFullFile).toBe(true);
+ });
+ });
+
+ describe('SET_FILE_COLLAPSED', () => {
+ it('sets collapsed', () => {
+ const state = {
+ diffFiles: [{ file_path: 'test', viewer: { collapsed: false } }],
+ };
+
+ mutations[types.SET_FILE_COLLAPSED](state, { filePath: 'test', collapsed: true });
+
+ expect(state.diffFiles[0].viewer.collapsed).toBe(true);
+ });
+ });
});
diff --git a/spec/javascripts/diffs/store/utils_spec.js b/spec/javascripts/diffs/store/utils_spec.js
index 599ea9cd420..1f877910125 100644
--- a/spec/javascripts/diffs/store/utils_spec.js
+++ b/spec/javascripts/diffs/store/utils_spec.js
@@ -781,4 +781,49 @@ describe('DiffsStoreUtils', () => {
]);
});
});
+
+ describe('convertExpandLines', () => {
+ it('converts expanded lines to normal lines', () => {
+ const diffLines = [
+ {
+ type: 'match',
+ old_line: 1,
+ new_line: 1,
+ },
+ {
+ type: '',
+ old_line: 2,
+ new_line: 2,
+ },
+ ];
+
+ const lines = utils.convertExpandLines({
+ diffLines,
+ data: [{ text: 'expanded' }],
+ typeKey: 'type',
+ oldLineKey: 'old_line',
+ newLineKey: 'new_line',
+ mapLine: ({ line, oldLine, newLine }) => ({
+ ...line,
+ old_line: oldLine,
+ new_line: newLine,
+ }),
+ });
+
+ expect(lines).toEqual([
+ {
+ text: 'expanded',
+ new_line: 1,
+ old_line: 1,
+ discussions: [],
+ hasForm: false,
+ },
+ {
+ type: '',
+ old_line: 2,
+ new_line: 2,
+ },
+ ]);
+ });
+ });
});
diff --git a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js
index ae2a785de52..95cc90dcb0f 100644
--- a/spec/javascripts/dirty_submit/dirty_submit_form_spec.js
+++ b/spec/javascripts/dirty_submit/dirty_submit_form_spec.js
@@ -13,6 +13,8 @@ function expectToToggleDisableOnDirtyUpdate(submit, input) {
}
describe('DirtySubmitForm', () => {
+ DirtySubmitForm.THROTTLE_DURATION = 0;
+
it('disables submit until there are changes', done => {
const { form, input, submit } = createForm();
diff --git a/spec/javascripts/emoji_spec.js b/spec/javascripts/emoji_spec.js
index 3db4d9800f1..0ac375145be 100644
--- a/spec/javascripts/emoji_spec.js
+++ b/spec/javascripts/emoji_spec.js
@@ -1,4 +1,6 @@
-import { glEmojiTag } from '~/emoji';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import { initEmojiMap, glEmojiTag, EMOJI_VERSION } from '~/emoji';
import isEmojiUnicodeSupported, {
isFlagEmoji,
isRainbowFlagEmoji,
@@ -7,6 +9,7 @@ import isEmojiUnicodeSupported, {
isHorceRacingSkinToneComboEmoji,
isPersonZwjEmoji,
} from '~/emoji/support/is_emoji_unicode_supported';
+import installGlEmojiElement from '~/behaviors/gl_emoji';
const emptySupportMap = {
personZwj: false,
@@ -31,34 +34,35 @@ const emojiFixtureMap = {
bomb: {
name: 'bomb',
moji: '💣',
- unicodeVersion: '6.0',
+ uni: '6.0',
},
construction_worker_tone5: {
name: 'construction_worker_tone5',
moji: '👷ðŸ¿',
- unicodeVersion: '8.0',
+ uni: '8.0',
},
five: {
name: 'five',
moji: '5ï¸âƒ£',
- unicodeVersion: '3.0',
+ uni: '3.0',
},
grey_question: {
name: 'grey_question',
moji: 'â”',
- unicodeVersion: '6.0',
+ uni: '6.0',
},
};
function markupToDomElement(markup) {
const div = document.createElement('div');
div.innerHTML = markup;
+ document.body.appendChild(div);
return div.firstElementChild;
}
-function testGlEmojiImageFallback(element, name, src) {
+function testGlEmojiImageFallback(element, name) {
expect(element.tagName.toLowerCase()).toBe('img');
- expect(element.getAttribute('src')).toBe(src);
+ expect(element.getAttribute('src')).toBe(`/-/emojis/${EMOJI_VERSION}/${name}.png`);
expect(element.getAttribute('title')).toBe(`:${name}:`);
expect(element.getAttribute('alt')).toBe(`:${name}:`);
}
@@ -68,12 +72,11 @@ const defaults = {
sprite: false,
};
-function testGlEmojiElement(element, name, unicodeVersion, unicodeMoji, options = {}) {
+function testGlEmojiElement(element, name, uni, unicodeMoji, options = {}) {
const opts = Object.assign({}, defaults, options);
expect(element.tagName.toLowerCase()).toBe('gl-emoji');
expect(element.dataset.name).toBe(name);
- expect(element.dataset.fallbackSrc.length).toBeGreaterThan(0);
- expect(element.dataset.unicodeVersion).toBe(unicodeVersion);
+ expect(element.dataset.uni).toBe(uni);
const fallbackSpriteClass = `emoji-${name}`;
if (opts.sprite) {
@@ -86,7 +89,7 @@ function testGlEmojiElement(element, name, unicodeVersion, unicodeMoji, options
if (opts.forceFallback && !opts.sprite) {
// Check for image fallback
- testGlEmojiImageFallback(element.firstElementChild, name, element.dataset.fallbackSrc);
+ testGlEmojiImageFallback(element.firstElementChild, name);
} else {
// Otherwise make sure things are still unicode text
expect(element.textContent.trim()).toBe(unicodeMoji);
@@ -94,101 +97,143 @@ function testGlEmojiElement(element, name, unicodeVersion, unicodeMoji, options
}
describe('gl_emoji', () => {
+ beforeAll(() => {
+ installGlEmojiElement();
+ });
+
+ let mock;
+ const emojiData = getJSONFixture('emojis/emojis.json');
+
+ beforeEach(function(done) {
+ mock = new MockAdapter(axios);
+ mock.onGet(`/-/emojis/${EMOJI_VERSION}/emojis.json`).reply(200, emojiData);
+
+ initEmojiMap()
+ .then(() => {
+ done();
+ })
+ .catch(() => {
+ done();
+ });
+ });
+
+ afterEach(function() {
+ mock.restore();
+ });
+
describe('glEmojiTag', () => {
- it('bomb emoji', () => {
+ it('bomb emoji', done => {
const emojiKey = 'bomb';
const markup = glEmojiTag(emojiFixtureMap[emojiKey].name);
const glEmojiElement = markupToDomElement(markup);
- testGlEmojiElement(
- glEmojiElement,
- emojiFixtureMap[emojiKey].name,
- emojiFixtureMap[emojiKey].unicodeVersion,
- emojiFixtureMap[emojiKey].moji,
- );
+ setTimeout(() => {
+ testGlEmojiElement(
+ glEmojiElement,
+ emojiFixtureMap[emojiKey].name,
+ emojiFixtureMap[emojiKey].uni,
+ emojiFixtureMap[emojiKey].moji,
+ );
+ done();
+ });
});
- it('bomb emoji with image fallback', () => {
+ it('bomb emoji with image fallback', done => {
const emojiKey = 'bomb';
const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, {
forceFallback: true,
});
const glEmojiElement = markupToDomElement(markup);
- testGlEmojiElement(
- glEmojiElement,
- emojiFixtureMap[emojiKey].name,
- emojiFixtureMap[emojiKey].unicodeVersion,
- emojiFixtureMap[emojiKey].moji,
- {
- forceFallback: true,
- },
- );
+ setTimeout(() => {
+ testGlEmojiElement(
+ glEmojiElement,
+ emojiFixtureMap[emojiKey].name,
+ emojiFixtureMap[emojiKey].uni,
+ emojiFixtureMap[emojiKey].moji,
+ {
+ forceFallback: true,
+ },
+ );
+ done();
+ });
});
- it('bomb emoji with sprite fallback readiness', () => {
+ it('bomb emoji with sprite fallback readiness', done => {
const emojiKey = 'bomb';
const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, {
sprite: true,
});
const glEmojiElement = markupToDomElement(markup);
- testGlEmojiElement(
- glEmojiElement,
- emojiFixtureMap[emojiKey].name,
- emojiFixtureMap[emojiKey].unicodeVersion,
- emojiFixtureMap[emojiKey].moji,
- {
- sprite: true,
- },
- );
+ setTimeout(() => {
+ testGlEmojiElement(
+ glEmojiElement,
+ emojiFixtureMap[emojiKey].name,
+ emojiFixtureMap[emojiKey].uni,
+ emojiFixtureMap[emojiKey].moji,
+ {
+ sprite: true,
+ },
+ );
+ done();
+ });
});
- it('bomb emoji with sprite fallback', () => {
+ it('bomb emoji with sprite fallback', done => {
const emojiKey = 'bomb';
const markup = glEmojiTag(emojiFixtureMap[emojiKey].name, {
forceFallback: true,
sprite: true,
});
const glEmojiElement = markupToDomElement(markup);
- testGlEmojiElement(
- glEmojiElement,
- emojiFixtureMap[emojiKey].name,
- emojiFixtureMap[emojiKey].unicodeVersion,
- emojiFixtureMap[emojiKey].moji,
- {
- forceFallback: true,
- sprite: true,
- },
- );
+ setTimeout(() => {
+ testGlEmojiElement(
+ glEmojiElement,
+ emojiFixtureMap[emojiKey].name,
+ emojiFixtureMap[emojiKey].uni,
+ emojiFixtureMap[emojiKey].moji,
+ {
+ forceFallback: true,
+ sprite: true,
+ },
+ );
+ done();
+ });
});
- it('question mark when invalid emoji name given', () => {
+ it('question mark when invalid emoji name given', done => {
const name = 'invalid_emoji';
const emojiKey = 'grey_question';
const markup = glEmojiTag(name);
const glEmojiElement = markupToDomElement(markup);
- testGlEmojiElement(
- glEmojiElement,
- emojiFixtureMap[emojiKey].name,
- emojiFixtureMap[emojiKey].unicodeVersion,
- emojiFixtureMap[emojiKey].moji,
- );
+ setTimeout(() => {
+ testGlEmojiElement(
+ glEmojiElement,
+ emojiFixtureMap[emojiKey].name,
+ emojiFixtureMap[emojiKey].uni,
+ emojiFixtureMap[emojiKey].moji,
+ );
+ done();
+ });
});
- it('question mark with image fallback when invalid emoji name given', () => {
+ it('question mark with image fallback when invalid emoji name given', done => {
const name = 'invalid_emoji';
const emojiKey = 'grey_question';
const markup = glEmojiTag(name, {
forceFallback: true,
});
const glEmojiElement = markupToDomElement(markup);
- testGlEmojiElement(
- glEmojiElement,
- emojiFixtureMap[emojiKey].name,
- emojiFixtureMap[emojiKey].unicodeVersion,
- emojiFixtureMap[emojiKey].moji,
- {
- forceFallback: true,
- },
- );
+ setTimeout(() => {
+ testGlEmojiElement(
+ glEmojiElement,
+ emojiFixtureMap[emojiKey].name,
+ emojiFixtureMap[emojiKey].uni,
+ emojiFixtureMap[emojiKey].moji,
+ {
+ forceFallback: true,
+ },
+ );
+ done();
+ });
});
});
@@ -389,7 +434,7 @@ describe('gl_emoji', () => {
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
- emojiFixtureMap[emojiKey].unicodeVersion,
+ emojiFixtureMap[emojiKey].uni,
);
expect(isSupported).toBeTruthy();
@@ -401,7 +446,7 @@ describe('gl_emoji', () => {
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
- emojiFixtureMap[emojiKey].unicodeVersion,
+ emojiFixtureMap[emojiKey].uni,
);
expect(isSupported).toBeFalsy();
@@ -415,7 +460,7 @@ describe('gl_emoji', () => {
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
- emojiFixtureMap[emojiKey].unicodeVersion,
+ emojiFixtureMap[emojiKey].uni,
);
expect(isSupported).toBeFalsy();
@@ -441,7 +486,7 @@ describe('gl_emoji', () => {
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
- emojiFixtureMap[emojiKey].unicodeVersion,
+ emojiFixtureMap[emojiKey].uni,
);
expect(isSupported).toBeFalsy();
@@ -459,7 +504,7 @@ describe('gl_emoji', () => {
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
- emojiFixtureMap[emojiKey].unicodeVersion,
+ emojiFixtureMap[emojiKey].uni,
);
expect(isSupported).toBeTruthy();
@@ -477,7 +522,7 @@ describe('gl_emoji', () => {
const isSupported = isEmojiUnicodeSupported(
unicodeSupportMap,
emojiFixtureMap[emojiKey].moji,
- emojiFixtureMap[emojiKey].unicodeVersion,
+ emojiFixtureMap[emojiKey].uni,
);
expect(isSupported).toBeFalsy();
diff --git a/spec/javascripts/environments/confirm_rollback_modal_spec.js b/spec/javascripts/environments/confirm_rollback_modal_spec.js
new file mode 100644
index 00000000000..05715bce38f
--- /dev/null
+++ b/spec/javascripts/environments/confirm_rollback_modal_spec.js
@@ -0,0 +1,70 @@
+import { shallowMount } from '@vue/test-utils';
+import { GlModal } from '@gitlab/ui';
+import ConfirmRollbackModal from '~/environments/components/confirm_rollback_modal.vue';
+import eventHub from '~/environments/event_hub';
+
+describe('Confirm Rollback Modal Component', () => {
+ let environment;
+
+ beforeEach(() => {
+ environment = {
+ name: 'test',
+ last_deployment: {
+ commit: {
+ short_id: 'abc0123',
+ },
+ },
+ modalId: 'test',
+ };
+ });
+
+ it('should show "Rollback" when isLastDeployment is false', () => {
+ const component = shallowMount(ConfirmRollbackModal, {
+ propsData: {
+ environment: {
+ ...environment,
+ isLastDeployment: false,
+ },
+ },
+ });
+ const modal = component.find(GlModal);
+
+ expect(modal.attributes('title')).toContain('Rollback');
+ expect(modal.attributes('title')).toContain('test');
+ expect(modal.attributes('ok-title')).toBe('Rollback');
+ expect(modal.text()).toContain('commit abc0123');
+ expect(modal.text()).toContain('Are you sure you want to continue?');
+ });
+
+ it('should show "Re-deploy" when isLastDeployment is true', () => {
+ const component = shallowMount(ConfirmRollbackModal, {
+ propsData: {
+ environment: {
+ ...environment,
+ isLastDeployment: true,
+ },
+ },
+ });
+ const modal = component.find(GlModal);
+
+ expect(modal.attributes('title')).toContain('Re-deploy');
+ expect(modal.attributes('title')).toContain('test');
+ expect(modal.attributes('ok-title')).toBe('Re-deploy');
+ expect(modal.text()).toContain('commit abc0123');
+ expect(modal.text()).toContain('Are you sure you want to continue?');
+ });
+
+ it('should emit the "rollback" event when "ok" is clicked', () => {
+ environment = { ...environment, isLastDeployment: true };
+ const component = shallowMount(ConfirmRollbackModal, {
+ propsData: {
+ environment,
+ },
+ });
+ const eventHubSpy = spyOn(eventHub, '$emit');
+ const modal = component.find(GlModal);
+ modal.vm.$emit('ok');
+
+ expect(eventHubSpy).toHaveBeenCalledWith('rollbackEnvironment', environment);
+ });
+});
diff --git a/spec/javascripts/environments/environment_item_spec.js b/spec/javascripts/environments/environment_item_spec.js
index a89e50045da..388d7063d13 100644
--- a/spec/javascripts/environments/environment_item_spec.js
+++ b/spec/javascripts/environments/environment_item_spec.js
@@ -20,23 +20,23 @@ describe('Environment item', () => {
size: 3,
isFolder: true,
environment_path: 'url',
+ log_path: 'url',
};
component = new EnvironmentItem({
propsData: {
model: mockItem,
canReadEnvironment: true,
- service: {},
},
}).$mount();
});
- it('Should render folder icon and name', () => {
+ it('should render folder icon and name', () => {
expect(component.$el.querySelector('.folder-name').textContent).toContain(mockItem.name);
expect(component.$el.querySelector('.folder-icon')).toBeDefined();
});
- it('Should render the number of children in a badge', () => {
+ it('should render the number of children in a badge', () => {
expect(component.$el.querySelector('.folder-name .badge').textContent).toContain(
mockItem.size,
);
@@ -60,7 +60,7 @@ describe('Environment item', () => {
sha: '500aabcb17c97bdcf2d0c410b70cb8556f0362dd',
ref: {
name: 'master',
- ref_path: 'root/ci-folders/tree/master',
+ ref_url: 'root/ci-folders/tree/master',
},
tag: true,
'last?': true,
@@ -109,6 +109,7 @@ describe('Environment item', () => {
},
has_stop_action: true,
environment_path: 'root/ci-folders/environments/31',
+ log_path: 'root/ci-folders/environments/31/logs',
created_at: '2016-11-07T11:11:16.525Z',
updated_at: '2016-11-10T15:55:58.778Z',
};
@@ -117,7 +118,6 @@ describe('Environment item', () => {
propsData: {
model: environment,
canReadEnvironment: true,
- service: {},
},
}).$mount();
});
@@ -157,13 +157,13 @@ describe('Environment item', () => {
});
describe('With build url', () => {
- it('Should link to build url provided', () => {
+ it('should link to build url provided', () => {
expect(component.$el.querySelector('.build-link').getAttribute('href')).toEqual(
environment.last_deployment.deployable.build_path,
);
});
- it('Should render deployable name and id', () => {
+ it('should render deployable name and id', () => {
expect(component.$el.querySelector('.build-link').getAttribute('href')).toEqual(
environment.last_deployment.deployable.build_path,
);
@@ -178,7 +178,7 @@ describe('Environment item', () => {
});
describe('With manual actions', () => {
- it('Should render actions component', () => {
+ it('should render actions component', () => {
expect(component.$el.querySelector('.js-manual-actions-container')).toBeDefined();
});
});
@@ -190,13 +190,13 @@ describe('Environment item', () => {
});
describe('With stop action', () => {
- it('Should render stop action component', () => {
+ it('should render stop action component', () => {
expect(component.$el.querySelector('.js-stop-component-container')).toBeDefined();
});
});
describe('With retry action', () => {
- it('Should render rollback component', () => {
+ it('should render rollback component', () => {
expect(component.$el.querySelector('.js-rollback-component-container')).toBeDefined();
});
});
diff --git a/spec/javascripts/environments/environment_rollback_spec.js b/spec/javascripts/environments/environment_rollback_spec.js
index 79f33c5bc8a..8c47f6a12c0 100644
--- a/spec/javascripts/environments/environment_rollback_spec.js
+++ b/spec/javascripts/environments/environment_rollback_spec.js
@@ -1,8 +1,11 @@
import Vue from 'vue';
+import { shallowMount } from '@vue/test-utils';
+import { GlButton } from '@gitlab/ui';
+import eventHub from '~/environments/event_hub';
import rollbackComp from '~/environments/components/environment_rollback.vue';
describe('Rollback Component', () => {
- const retryURL = 'https://gitlab.com/retry';
+ const retryUrl = 'https://gitlab.com/retry';
let RollbackComponent;
beforeEach(() => {
@@ -13,8 +16,9 @@ describe('Rollback Component', () => {
const component = new RollbackComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
- retryUrl: retryURL,
+ retryUrl,
isLastDeployment: true,
+ environment: {},
},
}).$mount();
@@ -25,11 +29,33 @@ describe('Rollback Component', () => {
const component = new RollbackComponent({
el: document.querySelector('.test-dom-element'),
propsData: {
- retryUrl: retryURL,
+ retryUrl,
isLastDeployment: false,
+ environment: {},
},
}).$mount();
expect(component.$el).toHaveSpriteIcon('redo');
});
+
+ it('should emit a "rollback" event on button click', () => {
+ const eventHubSpy = spyOn(eventHub, '$emit');
+ const component = shallowMount(RollbackComponent, {
+ propsData: {
+ retryUrl,
+ environment: {
+ name: 'test',
+ },
+ },
+ });
+ const button = component.find(GlButton);
+
+ button.vm.$emit('click');
+
+ expect(eventHubSpy).toHaveBeenCalledWith('requestRollbackEnvironment', {
+ retryUrl,
+ isLastDeployment: true,
+ name: 'test',
+ });
+ });
});
diff --git a/spec/javascripts/environments/environment_table_spec.js b/spec/javascripts/environments/environment_table_spec.js
index 52895f35f3a..a3f34232a85 100644
--- a/spec/javascripts/environments/environment_table_spec.js
+++ b/spec/javascripts/environments/environment_table_spec.js
@@ -6,6 +6,14 @@ describe('Environment table', () => {
let Component;
let vm;
+ const eeOnlyProps = {
+ canaryDeploymentFeatureId: 'canary_deployment',
+ showCanaryDeploymentCallout: true,
+ userCalloutsPath: '/callouts',
+ lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
+ helpCanaryDeploymentsPath: 'help/canary-deployments',
+ };
+
beforeEach(() => {
Component = Vue.extend(environmentTableComp);
});
@@ -27,8 +35,234 @@ describe('Environment table', () => {
vm = mountComponent(Component, {
environments: [mockItem],
canReadEnvironment: true,
+ ...eeOnlyProps,
});
expect(vm.$el.getAttribute('class')).toContain('ci-table');
});
+
+ describe('sortEnvironments', () => {
+ it('should sort environments by last updated', () => {
+ const mockItems = [
+ {
+ name: 'old',
+ size: 3,
+ isFolder: false,
+ last_deployment: {
+ created_at: new Date(2019, 0, 5).toISOString(),
+ },
+ },
+ {
+ name: 'new',
+ size: 3,
+ isFolder: false,
+ last_deployment: {
+ created_at: new Date(2019, 1, 5).toISOString(),
+ },
+ },
+ {
+ name: 'older',
+ size: 3,
+ isFolder: false,
+ last_deployment: {
+ created_at: new Date(2018, 0, 5).toISOString(),
+ },
+ },
+ {
+ name: 'an environment with no deployment',
+ },
+ ];
+
+ vm = mountComponent(Component, {
+ environments: mockItems,
+ canReadEnvironment: true,
+ ...eeOnlyProps,
+ });
+
+ const [old, newer, older, noDeploy] = mockItems;
+
+ expect(vm.sortEnvironments(mockItems)).toEqual([newer, old, older, noDeploy]);
+ });
+
+ it('should push environments with no deployments to the bottom', () => {
+ const mockItems = [
+ {
+ name: 'production',
+ size: 1,
+ id: 2,
+ state: 'available',
+ external_url: 'https://google.com/production',
+ environment_type: null,
+ last_deployment: null,
+ has_stop_action: false,
+ environment_path: '/Commit451/lab-coat/environments/2',
+ stop_path: '/Commit451/lab-coat/environments/2/stop',
+ folder_path: '/Commit451/lab-coat/environments/folders/production',
+ created_at: '2019-01-17T16:26:10.064Z',
+ updated_at: '2019-01-17T16:27:37.717Z',
+ can_stop: true,
+ },
+ {
+ name: 'review/225addcibuildstatus',
+ size: 2,
+ isFolder: true,
+ isLoadingFolderContent: false,
+ folderName: 'review',
+ isOpen: false,
+ children: [],
+ id: 12,
+ state: 'available',
+ external_url: 'https://google.com/review/225addcibuildstatus',
+ environment_type: 'review',
+ last_deployment: null,
+ has_stop_action: false,
+ environment_path: '/Commit451/lab-coat/environments/12',
+ stop_path: '/Commit451/lab-coat/environments/12/stop',
+ folder_path: '/Commit451/lab-coat/environments/folders/review',
+ created_at: '2019-01-17T16:27:37.877Z',
+ updated_at: '2019-01-17T16:27:37.883Z',
+ can_stop: true,
+ },
+ {
+ name: 'staging',
+ size: 1,
+ id: 1,
+ state: 'available',
+ external_url: 'https://google.com/staging',
+ environment_type: null,
+ last_deployment: {
+ created_at: '2019-01-17T16:26:15.125Z',
+ scheduled_actions: [],
+ },
+ },
+ ];
+
+ vm = mountComponent(Component, {
+ environments: mockItems,
+ canReadEnvironment: true,
+ ...eeOnlyProps,
+ });
+
+ const [prod, review, staging] = mockItems;
+
+ expect(vm.sortEnvironments(mockItems)).toEqual([review, staging, prod]);
+ });
+
+ it('should sort environments by folder first', () => {
+ const mockItems = [
+ {
+ name: 'old',
+ size: 3,
+ isFolder: false,
+ last_deployment: {
+ created_at: new Date(2019, 0, 5).toISOString(),
+ },
+ },
+ {
+ name: 'new',
+ size: 3,
+ isFolder: false,
+ last_deployment: {
+ created_at: new Date(2019, 1, 5).toISOString(),
+ },
+ },
+ {
+ name: 'older',
+ size: 3,
+ isFolder: true,
+ children: [],
+ },
+ ];
+
+ vm = mountComponent(Component, {
+ environments: mockItems,
+ canReadEnvironment: true,
+ ...eeOnlyProps,
+ });
+
+ const [old, newer, older] = mockItems;
+
+ expect(vm.sortEnvironments(mockItems)).toEqual([older, newer, old]);
+ });
+
+ it('should break ties by name', () => {
+ const mockItems = [
+ {
+ name: 'old',
+ isFolder: false,
+ },
+ {
+ name: 'new',
+ isFolder: false,
+ },
+ {
+ folderName: 'older',
+ isFolder: true,
+ },
+ ];
+
+ vm = mountComponent(Component, {
+ environments: mockItems,
+ canReadEnvironment: true,
+ ...eeOnlyProps,
+ });
+
+ const [old, newer, older] = mockItems;
+
+ expect(vm.sortEnvironments(mockItems)).toEqual([older, newer, old]);
+ });
+ });
+
+ describe('sortedEnvironments', () => {
+ it('it should sort children as well', () => {
+ const mockItems = [
+ {
+ name: 'production',
+ last_deployment: null,
+ },
+ {
+ name: 'review/225addcibuildstatus',
+ isFolder: true,
+ folderName: 'review',
+ isOpen: true,
+ children: [
+ {
+ name: 'review/225addcibuildstatus',
+ last_deployment: {
+ created_at: '2019-01-17T16:26:15.125Z',
+ },
+ },
+ {
+ name: 'review/master',
+ last_deployment: {
+ created_at: '2019-02-17T16:26:15.125Z',
+ },
+ },
+ ],
+ },
+ {
+ name: 'staging',
+ last_deployment: {
+ created_at: '2019-01-17T16:26:15.125Z',
+ },
+ },
+ ];
+ const [production, review, staging] = mockItems;
+ const [addcibuildstatus, master] = mockItems[1].children;
+
+ vm = mountComponent(Component, {
+ environments: mockItems,
+ canReadEnvironment: true,
+ ...eeOnlyProps,
+ });
+
+ expect(vm.sortedEnvironments.map(env => env.name)).toEqual([
+ review.name,
+ staging.name,
+ production.name,
+ ]);
+
+ expect(vm.sortedEnvironments[0].children).toEqual([master, addcibuildstatus]);
+ });
+ });
});
diff --git a/spec/javascripts/environments/environments_app_spec.js b/spec/javascripts/environments/environments_app_spec.js
index 9220f7a264f..0dcd8868aba 100644
--- a/spec/javascripts/environments/environments_app_spec.js
+++ b/spec/javascripts/environments/environments_app_spec.js
@@ -13,6 +13,11 @@ describe('Environment', () => {
cssContainerClass: 'container',
newEnvironmentPath: 'environments/new',
helpPagePath: 'help',
+ canaryDeploymentFeatureId: 'canary_deployment',
+ showCanaryDeploymentCallout: true,
+ userCalloutsPath: '/callouts',
+ lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
+ helpCanaryDeploymentsPath: 'help/canary-deployments',
};
let EnvironmentsComponent;
@@ -94,7 +99,7 @@ describe('Environment', () => {
it('should make an API request when page is clicked', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
- component.$el.querySelector('.gl-pagination li:nth-child(5) a').click();
+ component.$el.querySelector('.gl-pagination li:nth-child(5) .page-link').click();
expect(component.updateContent).toHaveBeenCalledWith({ scope: 'available', page: '2' });
done();
diff --git a/spec/javascripts/environments/environments_store_spec.js b/spec/javascripts/environments/environments_store_spec.js
index c3d16f10d72..8abdbcbbe54 100644
--- a/spec/javascripts/environments/environments_store_spec.js
+++ b/spec/javascripts/environments/environments_store_spec.js
@@ -34,54 +34,46 @@ describe('Store', () => {
expect(store.state.stoppedCounter).toEqual(2);
});
- describe('store environments', () => {
- it('should store environments', () => {
- store.storeEnvironments(serverData);
-
- expect(store.state.environments.length).toEqual(serverData.length);
- });
-
- it('should add folder keys when environment is a folder', () => {
- const environment = {
- name: 'bar',
- size: 3,
- id: 2,
- };
+ it('should add folder keys when environment is a folder', () => {
+ const environment = {
+ name: 'bar',
+ size: 3,
+ id: 2,
+ };
- store.storeEnvironments([environment]);
+ store.storeEnvironments([environment]);
- expect(store.state.environments[0].isFolder).toEqual(true);
- expect(store.state.environments[0].folderName).toEqual('bar');
- });
-
- it('should extract content of `latest` key when provided', () => {
- const environment = {
- name: 'bar',
- size: 3,
- id: 2,
- latest: {
- last_deployment: {},
- isStoppable: true,
- },
- };
-
- store.storeEnvironments([environment]);
+ expect(store.state.environments[0].isFolder).toEqual(true);
+ expect(store.state.environments[0].folderName).toEqual('bar');
+ });
- expect(store.state.environments[0].last_deployment).toEqual({});
- expect(store.state.environments[0].isStoppable).toEqual(true);
- });
+ it('should extract content of `latest` key when provided', () => {
+ const environment = {
+ name: 'bar',
+ size: 3,
+ id: 2,
+ latest: {
+ last_deployment: {},
+ isStoppable: true,
+ },
+ };
+
+ store.storeEnvironments([environment]);
+
+ expect(store.state.environments[0].last_deployment).toEqual({});
+ expect(store.state.environments[0].isStoppable).toEqual(true);
+ });
- it('should store latest.name when the environment is not a folder', () => {
- store.storeEnvironments(serverData);
+ it('should store latest.name when the environment is not a folder', () => {
+ store.storeEnvironments(serverData);
- expect(store.state.environments[0].name).toEqual(serverData[0].latest.name);
- });
+ expect(store.state.environments[0].name).toEqual(serverData[0].latest.name);
+ });
- it('should store root level name when environment is a folder', () => {
- store.storeEnvironments(serverData);
+ it('should store root level name when environment is a folder', () => {
+ store.storeEnvironments(serverData);
- expect(store.state.environments[1].folderName).toEqual(serverData[1].name);
- });
+ expect(store.state.environments[1].folderName).toEqual(serverData[1].name);
});
describe('toggleFolder', () => {
diff --git a/spec/javascripts/environments/folder/environments_folder_view_spec.js b/spec/javascripts/environments/folder/environments_folder_view_spec.js
index d9ee7e74e28..ff15067aeac 100644
--- a/spec/javascripts/environments/folder/environments_folder_view_spec.js
+++ b/spec/javascripts/environments/folder/environments_folder_view_spec.js
@@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import environmentsFolderViewComponent from '~/environments/folder/environments_folder_view.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { removeBreakLine, removeWhitespace } from 'spec/helpers/vue_component_helper';
import { environmentsList } from '../mock_data';
describe('Environments Folder View', () => {
@@ -15,6 +16,11 @@ describe('Environments Folder View', () => {
folderName: 'review',
canReadEnvironment: true,
cssContainerClass: 'container',
+ canaryDeploymentFeatureId: 'canary_deployment',
+ showCanaryDeploymentCallout: true,
+ userCalloutsPath: '/callouts',
+ lockPromotionSvgPath: '/assets/illustrations/lock-promotion.svg',
+ helpCanaryDeploymentsPath: 'help/canary-deployments',
};
beforeEach(() => {
@@ -89,9 +95,11 @@ describe('Environments Folder View', () => {
it('should render parent folder name', done => {
setTimeout(() => {
- expect(component.$el.querySelector('.js-folder-name').textContent.trim()).toContain(
- 'Environments / review',
- );
+ expect(
+ removeBreakLine(
+ removeWhitespace(component.$el.querySelector('.js-folder-name').textContent.trim()),
+ ),
+ ).toContain('Environments / review');
done();
}, 0);
});
@@ -107,7 +115,7 @@ describe('Environments Folder View', () => {
it('should make an API request when changing page', done => {
spyOn(component, 'updateContent');
setTimeout(() => {
- component.$el.querySelector('.gl-pagination .js-last-button a').click();
+ component.$el.querySelector('.gl-pagination .js-last-button .page-link').click();
expect(component.updateContent).toHaveBeenCalledWith({
scope: component.scope,
diff --git a/spec/javascripts/error_tracking/components/error_tracking_list_spec.js b/spec/javascripts/error_tracking/components/error_tracking_list_spec.js
deleted file mode 100644
index 08bbb390993..00000000000
--- a/spec/javascripts/error_tracking/components/error_tracking_list_spec.js
+++ /dev/null
@@ -1,100 +0,0 @@
-import { createLocalVue, shallowMount } from '@vue/test-utils';
-import Vuex from 'vuex';
-import ErrorTrackingList from '~/error_tracking/components/error_tracking_list.vue';
-import { GlButton, GlEmptyState, GlLoadingIcon, GlTable } from '@gitlab/ui';
-
-const localVue = createLocalVue();
-localVue.use(Vuex);
-
-describe('ErrorTrackingList', () => {
- let store;
- let wrapper;
-
- function mountComponent({ errorTrackingEnabled = true } = {}) {
- wrapper = shallowMount(ErrorTrackingList, {
- localVue,
- store,
- propsData: {
- indexPath: '/path',
- enableErrorTrackingLink: '/link',
- errorTrackingEnabled,
- illustrationPath: 'illustration/path',
- },
- });
- }
-
- beforeEach(() => {
- const actions = {
- getErrorList: () => {},
- };
-
- const state = {
- errors: [],
- loading: true,
- };
-
- store = new Vuex.Store({
- actions,
- state,
- });
- });
-
- afterEach(() => {
- if (wrapper) {
- wrapper.destroy();
- }
- });
-
- describe('loading', () => {
- beforeEach(() => {
- mountComponent();
- });
-
- it('shows spinner', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBeTruthy();
- expect(wrapper.find(GlTable).exists()).toBeFalsy();
- expect(wrapper.find(GlButton).exists()).toBeFalsy();
- });
- });
-
- describe('results', () => {
- beforeEach(() => {
- store.state.loading = false;
-
- mountComponent();
- });
-
- it('shows table', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy();
- expect(wrapper.find(GlTable).exists()).toBeTruthy();
- expect(wrapper.find(GlButton).exists()).toBeTruthy();
- });
- });
-
- describe('no results', () => {
- beforeEach(() => {
- store.state.loading = false;
-
- mountComponent();
- });
-
- it('shows empty table', () => {
- expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy();
- expect(wrapper.find(GlTable).exists()).toBeTruthy();
- expect(wrapper.find(GlButton).exists()).toBeTruthy();
- });
- });
-
- describe('error tracking feature disabled', () => {
- beforeEach(() => {
- mountComponent({ errorTrackingEnabled: false });
- });
-
- it('shows empty state', () => {
- expect(wrapper.find(GlEmptyState).exists()).toBeTruthy();
- expect(wrapper.find(GlLoadingIcon).exists()).toBeFalsy();
- expect(wrapper.find(GlTable).exists()).toBeFalsy();
- expect(wrapper.find(GlButton).exists()).toBeFalsy();
- });
- });
-});
diff --git a/spec/javascripts/error_tracking_settings/components/app_spec.js b/spec/javascripts/error_tracking_settings/components/app_spec.js
new file mode 100644
index 00000000000..2e52a45fd34
--- /dev/null
+++ b/spec/javascripts/error_tracking_settings/components/app_spec.js
@@ -0,0 +1,63 @@
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import ErrorTrackingSettings from '~/error_tracking_settings/components/app.vue';
+import ErrorTrackingForm from '~/error_tracking_settings/components/error_tracking_form.vue';
+import ProjectDropdown from '~/error_tracking_settings/components/project_dropdown.vue';
+import createStore from '~/error_tracking_settings/store';
+import { TEST_HOST } from 'spec/test_constants';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('error tracking settings app', () => {
+ let store;
+ let wrapper;
+
+ function mountComponent() {
+ wrapper = shallowMount(ErrorTrackingSettings, {
+ localVue,
+ store, // Override the imported store
+ propsData: {
+ initialEnabled: 'true',
+ initialApiHost: TEST_HOST,
+ initialToken: 'someToken',
+ initialProject: null,
+ listProjectsEndpoint: TEST_HOST,
+ operationsSettingsEndpoint: TEST_HOST,
+ },
+ });
+ }
+
+ beforeEach(() => {
+ store = createStore();
+
+ mountComponent();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ describe('section', () => {
+ it('renders the form and dropdown', () => {
+ expect(wrapper.find(ErrorTrackingForm).exists()).toBeTruthy();
+ expect(wrapper.find(ProjectDropdown).exists()).toBeTruthy();
+ });
+
+ it('renders the Save Changes button', () => {
+ expect(wrapper.find('.js-error-tracking-button').exists()).toBeTruthy();
+ });
+
+ it('enables the button by default', () => {
+ expect(wrapper.find('.js-error-tracking-button').attributes('disabled')).toBeFalsy();
+ });
+
+ it('disables the button when saving', () => {
+ store.state.settingsLoading = true;
+
+ expect(wrapper.find('.js-error-tracking-button').attributes('disabled')).toBeTruthy();
+ });
+ });
+});
diff --git a/spec/javascripts/error_tracking_settings/components/error_tracking_form_spec.js b/spec/javascripts/error_tracking_settings/components/error_tracking_form_spec.js
new file mode 100644
index 00000000000..23e57c4bbf1
--- /dev/null
+++ b/spec/javascripts/error_tracking_settings/components/error_tracking_form_spec.js
@@ -0,0 +1,91 @@
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { GlButton, GlFormInput } from '@gitlab/ui';
+import ErrorTrackingForm from '~/error_tracking_settings/components/error_tracking_form.vue';
+import { defaultProps } from '../mock';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('error tracking settings form', () => {
+ let wrapper;
+
+ function mountComponent() {
+ wrapper = shallowMount(ErrorTrackingForm, {
+ localVue,
+ propsData: defaultProps,
+ });
+ }
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ describe('an empty form', () => {
+ it('is rendered', () => {
+ expect(wrapper.findAll(GlFormInput).length).toBe(2);
+ expect(wrapper.find(GlFormInput).attributes('id')).toBe('error-tracking-api-host');
+ expect(
+ wrapper
+ .findAll(GlFormInput)
+ .at(1)
+ .attributes('id'),
+ ).toBe('error-tracking-token');
+
+ expect(wrapper.findAll(GlButton).exists()).toBe(true);
+ });
+
+ it('is rendered with labels and placeholders', () => {
+ const pageText = wrapper.text();
+
+ expect(pageText).toContain('Find your hostname in your Sentry account settings page');
+ expect(pageText).toContain(
+ "After adding your Auth Token, use the 'Connect' button to load projects",
+ );
+
+ expect(pageText).not.toContain('Connection has failed. Re-check Auth Token and try again');
+ expect(
+ wrapper
+ .findAll(GlFormInput)
+ .at(0)
+ .attributes('placeholder'),
+ ).toContain('https://mysentryserver.com');
+ });
+ });
+
+ describe('after a successful connection', () => {
+ beforeEach(() => {
+ wrapper.setProps({ connectSuccessful: true });
+ });
+
+ it('shows the success checkmark', () => {
+ expect(wrapper.find('.js-error-tracking-connect-success').isVisible()).toBe(true);
+ });
+
+ it('does not show an error', () => {
+ expect(wrapper.text()).not.toContain(
+ 'Connection has failed. Re-check Auth Token and try again',
+ );
+ });
+ });
+
+ describe('after an unsuccessful connection', () => {
+ beforeEach(() => {
+ wrapper.setProps({ connectError: true });
+ });
+
+ it('does not show the check mark', () => {
+ expect(wrapper.find('.js-error-tracking-connect-success').isVisible()).toBe(false);
+ });
+
+ it('shows an error', () => {
+ expect(wrapper.text()).toContain('Connection has failed. Re-check Auth Token and try again');
+ });
+ });
+});
diff --git a/spec/javascripts/error_tracking_settings/components/project_dropdown_spec.js b/spec/javascripts/error_tracking_settings/components/project_dropdown_spec.js
new file mode 100644
index 00000000000..8e5dbe28452
--- /dev/null
+++ b/spec/javascripts/error_tracking_settings/components/project_dropdown_spec.js
@@ -0,0 +1,109 @@
+import _ from 'underscore';
+import Vuex from 'vuex';
+import { createLocalVue, shallowMount } from '@vue/test-utils';
+import { GlDropdown, GlDropdownItem } from '@gitlab/ui';
+import ProjectDropdown from '~/error_tracking_settings/components/project_dropdown.vue';
+import { defaultProps, projectList, staleProject } from '../mock';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('error tracking settings project dropdown', () => {
+ let wrapper;
+
+ function mountComponent() {
+ wrapper = shallowMount(ProjectDropdown, {
+ localVue,
+ propsData: {
+ ..._.pick(
+ defaultProps,
+ 'dropdownLabel',
+ 'invalidProjectLabel',
+ 'projects',
+ 'projectSelectionLabel',
+ 'selectedProject',
+ 'token',
+ ),
+ hasProjects: false,
+ isProjectInvalid: false,
+ },
+ });
+ }
+
+ beforeEach(() => {
+ mountComponent();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ describe('empty project list', () => {
+ it('renders the dropdown', () => {
+ expect(wrapper.find('#project-dropdown').exists()).toBeTruthy();
+ expect(wrapper.find(GlDropdown).exists()).toBeTruthy();
+ });
+
+ it('shows helper text', () => {
+ expect(wrapper.find('.js-project-dropdown-label').exists()).toBeTruthy();
+ expect(wrapper.find('.js-project-dropdown-label').text()).toContain(
+ 'To enable project selection',
+ );
+ });
+
+ it('does not show an error', () => {
+ expect(wrapper.find('.js-project-dropdown-error').exists()).toBeFalsy();
+ });
+
+ it('does not contain any dropdown items', () => {
+ expect(wrapper.find(GlDropdownItem).exists()).toBeFalsy();
+ expect(wrapper.find(GlDropdown).props('text')).toBe('No projects available');
+ });
+ });
+
+ describe('populated project list', () => {
+ beforeEach(() => {
+ wrapper.setProps({ projects: _.clone(projectList), hasProjects: true });
+ });
+
+ it('renders the dropdown', () => {
+ expect(wrapper.find('#project-dropdown').exists()).toBeTruthy();
+ expect(wrapper.find(GlDropdown).exists()).toBeTruthy();
+ });
+
+ it('contains a number of dropdown items', () => {
+ expect(wrapper.find(GlDropdownItem).exists()).toBeTruthy();
+ expect(wrapper.findAll(GlDropdownItem).length).toBe(2);
+ });
+ });
+
+ describe('selected project', () => {
+ const selectedProject = _.clone(projectList[0]);
+
+ beforeEach(() => {
+ wrapper.setProps({ projects: _.clone(projectList), selectedProject, hasProjects: true });
+ });
+
+ it('does not show helper text', () => {
+ expect(wrapper.find('.js-project-dropdown-label').exists()).toBeFalsy();
+ expect(wrapper.find('.js-project-dropdown-error').exists()).toBeFalsy();
+ });
+ });
+
+ describe('invalid project selected', () => {
+ beforeEach(() => {
+ wrapper.setProps({
+ projects: _.clone(projectList),
+ selectedProject: staleProject,
+ isProjectInvalid: true,
+ });
+ });
+
+ it('displays a error', () => {
+ expect(wrapper.find('.js-project-dropdown-label').exists()).toBeFalsy();
+ expect(wrapper.find('.js-project-dropdown-error').exists()).toBeTruthy();
+ });
+ });
+});
diff --git a/spec/javascripts/error_tracking_settings/mock.js b/spec/javascripts/error_tracking_settings/mock.js
new file mode 100644
index 00000000000..32cdba33c14
--- /dev/null
+++ b/spec/javascripts/error_tracking_settings/mock.js
@@ -0,0 +1,92 @@
+import createStore from '~/error_tracking_settings/store';
+import { TEST_HOST } from 'spec/test_constants';
+
+const defaultStore = createStore();
+
+export const projectList = [
+ {
+ name: 'name',
+ slug: 'slug',
+ organizationName: 'organizationName',
+ organizationSlug: 'organizationSlug',
+ },
+ {
+ name: 'name2',
+ slug: 'slug2',
+ organizationName: 'organizationName2',
+ organizationSlug: 'organizationSlug2',
+ },
+];
+
+export const staleProject = {
+ name: 'staleName',
+ slug: 'staleSlug',
+ organizationName: 'staleOrganizationName',
+ organizationSlug: 'staleOrganizationSlug',
+};
+
+export const normalizedProject = {
+ name: 'name',
+ slug: 'slug',
+ organizationName: 'organization_name',
+ organizationSlug: 'organization_slug',
+};
+
+export const sampleBackendProject = {
+ name: normalizedProject.name,
+ slug: normalizedProject.slug,
+ organization_name: normalizedProject.organizationName,
+ organization_slug: normalizedProject.organizationSlug,
+};
+
+export const sampleFrontendSettings = {
+ apiHost: 'apiHost',
+ enabled: false,
+ token: 'token',
+ selectedProject: {
+ slug: normalizedProject.slug,
+ name: normalizedProject.name,
+ organizationName: normalizedProject.organizationName,
+ organizationSlug: normalizedProject.organizationSlug,
+ },
+};
+
+export const transformedSettings = {
+ api_host: 'apiHost',
+ enabled: false,
+ token: 'token',
+ project: {
+ slug: normalizedProject.slug,
+ name: normalizedProject.name,
+ organization_name: normalizedProject.organizationName,
+ organization_slug: normalizedProject.organizationSlug,
+ },
+};
+
+export const defaultProps = {
+ ...defaultStore.state,
+ ...defaultStore.getters,
+};
+
+export const initialEmptyState = {
+ apiHost: '',
+ enabled: false,
+ project: null,
+ token: '',
+ listProjectsEndpoint: TEST_HOST,
+ operationsSettingsEndpoint: TEST_HOST,
+};
+
+export const initialPopulatedState = {
+ apiHost: 'apiHost',
+ enabled: true,
+ project: JSON.stringify(projectList[0]),
+ token: 'token',
+ listProjectsEndpoint: TEST_HOST,
+ operationsSettingsEndpoint: TEST_HOST,
+};
+
+export const projectWithHtmlTemplate = {
+ ...projectList[0],
+ name: '<strong>bold</strong>',
+};
diff --git a/spec/javascripts/error_tracking_settings/store/actions_spec.js b/spec/javascripts/error_tracking_settings/store/actions_spec.js
new file mode 100644
index 00000000000..0255b3a7aa4
--- /dev/null
+++ b/spec/javascripts/error_tracking_settings/store/actions_spec.js
@@ -0,0 +1,191 @@
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+import axios from '~/lib/utils/axios_utils';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+import actionsDefaultExport, * as actions from '~/error_tracking_settings/store/actions';
+import * as types from '~/error_tracking_settings/store/mutation_types';
+import defaultState from '~/error_tracking_settings/store/state';
+import { projectList } from '../mock';
+
+describe('error tracking settings actions', () => {
+ let state;
+
+ describe('project list actions', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ state = { ...defaultState(), listProjectsEndpoint: TEST_HOST };
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('should request and transform the project list', done => {
+ mock.onPost(TEST_HOST).reply(() => [200, { projects: projectList }]);
+ testAction(
+ actions.fetchProjects,
+ null,
+ state,
+ [],
+ [
+ { type: 'requestProjects' },
+ {
+ type: 'receiveProjectsSuccess',
+ payload: projectList.map(convertObjectPropsToCamelCase),
+ },
+ ],
+ () => {
+ expect(mock.history.post.length).toBe(1);
+ done();
+ },
+ );
+ });
+
+ it('should handle a server error', done => {
+ mock.onPost(`${TEST_HOST}.json`).reply(() => [400]);
+ testAction(
+ actions.fetchProjects,
+ null,
+ state,
+ [],
+ [
+ { type: 'requestProjects' },
+ {
+ type: 'receiveProjectsError',
+ },
+ ],
+ () => {
+ expect(mock.history.post.length).toBe(1);
+ done();
+ },
+ );
+ });
+
+ it('should request projects correctly', done => {
+ testAction(actions.requestProjects, null, state, [{ type: types.RESET_CONNECT }], [], done);
+ });
+
+ it('should receive projects correctly', done => {
+ const testPayload = [];
+ testAction(
+ actions.receiveProjectsSuccess,
+ testPayload,
+ state,
+ [
+ { type: types.UPDATE_CONNECT_SUCCESS },
+ { type: types.RECEIVE_PROJECTS, payload: testPayload },
+ ],
+ [],
+ done,
+ );
+ });
+
+ it('should handle errors when receiving projects', done => {
+ const testPayload = [];
+ testAction(
+ actions.receiveProjectsError,
+ testPayload,
+ state,
+ [{ type: types.UPDATE_CONNECT_ERROR }, { type: types.CLEAR_PROJECTS }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('save changes actions', () => {
+ let mock;
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ state = {
+ operationsSettingsEndpoint: TEST_HOST,
+ };
+ });
+
+ afterEach(() => {
+ mock.restore();
+ });
+
+ it('should save the page', done => {
+ const refreshCurrentPage = spyOnDependency(actionsDefaultExport, 'refreshCurrentPage');
+ mock.onPatch(TEST_HOST).reply(200);
+ testAction(actions.updateSettings, null, state, [], [{ type: 'requestSettings' }], () => {
+ expect(mock.history.patch.length).toBe(1);
+ expect(refreshCurrentPage).toHaveBeenCalled();
+ done();
+ });
+ });
+
+ it('should handle a server error', done => {
+ mock.onPatch(TEST_HOST).reply(400);
+ testAction(
+ actions.updateSettings,
+ null,
+ state,
+ [],
+ [
+ { type: 'requestSettings' },
+ {
+ type: 'receiveSettingsError',
+ payload: new Error('Request failed with status code 400'),
+ },
+ ],
+ () => {
+ expect(mock.history.patch.length).toBe(1);
+ done();
+ },
+ );
+ });
+
+ it('should request to save the page', done => {
+ testAction(
+ actions.requestSettings,
+ null,
+ state,
+ [{ type: types.UPDATE_SETTINGS_LOADING, payload: true }],
+ [],
+ done,
+ );
+ });
+
+ it('should handle errors when requesting to save the page', done => {
+ testAction(
+ actions.receiveSettingsError,
+ {},
+ state,
+ [{ type: types.UPDATE_SETTINGS_LOADING, payload: false }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('generic actions to update the store', () => {
+ const testData = 'test';
+ it('should reset the `connect success` flag when updating the api host', done => {
+ testAction(
+ actions.updateApiHost,
+ testData,
+ state,
+ [{ type: types.UPDATE_API_HOST, payload: testData }, { type: types.RESET_CONNECT }],
+ [],
+ done,
+ );
+ });
+
+ it('should reset the `connect success` flag when updating the token', done => {
+ testAction(
+ actions.updateToken,
+ testData,
+ state,
+ [{ type: types.UPDATE_TOKEN, payload: testData }, { type: types.RESET_CONNECT }],
+ [],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/error_tracking_settings/store/getters_spec.js b/spec/javascripts/error_tracking_settings/store/getters_spec.js
new file mode 100644
index 00000000000..2c5ff084b8a
--- /dev/null
+++ b/spec/javascripts/error_tracking_settings/store/getters_spec.js
@@ -0,0 +1,93 @@
+import * as getters from '~/error_tracking_settings/store/getters';
+import defaultState from '~/error_tracking_settings/store/state';
+import { projectList, projectWithHtmlTemplate, staleProject } from '../mock';
+
+describe('Error Tracking Settings - Getters', () => {
+ let state;
+
+ beforeEach(() => {
+ state = defaultState();
+ });
+
+ describe('hasProjects', () => {
+ it('should reflect when no projects exist', () => {
+ expect(getters.hasProjects(state)).toEqual(false);
+ });
+
+ it('should reflect when projects exist', () => {
+ state.projects = projectList;
+
+ expect(getters.hasProjects(state)).toEqual(true);
+ });
+ });
+
+ describe('isProjectInvalid', () => {
+ const mockGetters = { hasProjects: true };
+ it('should show when a project is valid', () => {
+ state.projects = projectList;
+ [state.selectedProject] = projectList;
+
+ expect(getters.isProjectInvalid(state, mockGetters)).toEqual(false);
+ });
+
+ it('should show when a project is invalid', () => {
+ state.projects = projectList;
+ state.selectedProject = staleProject;
+
+ expect(getters.isProjectInvalid(state, mockGetters)).toEqual(true);
+ });
+ });
+
+ describe('dropdownLabel', () => {
+ const mockGetters = { hasProjects: false };
+ it('should display correctly when there are no projects available', () => {
+ expect(getters.dropdownLabel(state, mockGetters)).toEqual('No projects available');
+ });
+
+ it('should display correctly when a project is selected', () => {
+ [state.selectedProject] = projectList;
+
+ expect(getters.dropdownLabel(state, mockGetters)).toEqual('organizationName | name');
+ });
+
+ it('should display correctly when no project is selected', () => {
+ state.projects = projectList;
+
+ expect(getters.dropdownLabel(state, { hasProjects: true })).toEqual('Select project');
+ });
+ });
+
+ describe('invalidProjectLabel', () => {
+ it('should display an error containing the project name', () => {
+ [state.selectedProject] = projectList;
+
+ expect(getters.invalidProjectLabel(state)).toEqual(
+ 'Project "name" is no longer available. Select another project to continue.',
+ );
+ });
+
+ it('should properly escape the label text', () => {
+ state.selectedProject = projectWithHtmlTemplate;
+
+ expect(getters.invalidProjectLabel(state)).toEqual(
+ 'Project "&lt;strong&gt;bold&lt;/strong&gt;" is no longer available. Select another project to continue.',
+ );
+ });
+ });
+
+ describe('projectSelectionLabel', () => {
+ it('should show the correct message when the token is empty', () => {
+ expect(getters.projectSelectionLabel(state)).toEqual(
+ 'To enable project selection, enter a valid Auth Token',
+ );
+ });
+
+ it('should show the correct message when token exists', () => {
+ state.token = 'test-token';
+
+ expect(getters.projectSelectionLabel(state)).toEqual(
+ "Click 'Connect' to re-establish the connection to Sentry and activate the dropdown.",
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/error_tracking_settings/store/mutation_spec.js b/spec/javascripts/error_tracking_settings/store/mutation_spec.js
new file mode 100644
index 00000000000..bb1f1da784e
--- /dev/null
+++ b/spec/javascripts/error_tracking_settings/store/mutation_spec.js
@@ -0,0 +1,82 @@
+import { TEST_HOST } from 'spec/test_constants';
+import mutations from '~/error_tracking_settings/store/mutations';
+import defaultState from '~/error_tracking_settings/store/state';
+import * as types from '~/error_tracking_settings/store/mutation_types';
+import {
+ initialEmptyState,
+ initialPopulatedState,
+ projectList,
+ sampleBackendProject,
+ normalizedProject,
+} from '../mock';
+
+describe('error tracking settings mutations', () => {
+ describe('mutations', () => {
+ let state;
+
+ beforeEach(() => {
+ state = defaultState();
+ });
+
+ it('should create an empty initial state correctly', () => {
+ mutations[types.SET_INITIAL_STATE](state, {
+ ...initialEmptyState,
+ });
+
+ expect(state.apiHost).toEqual('');
+ expect(state.enabled).toEqual(false);
+ expect(state.selectedProject).toEqual(null);
+ expect(state.token).toEqual('');
+ expect(state.listProjectsEndpoint).toEqual(TEST_HOST);
+ expect(state.operationsSettingsEndpoint).toEqual(TEST_HOST);
+ });
+
+ it('should populate the initial state correctly', () => {
+ mutations[types.SET_INITIAL_STATE](state, {
+ ...initialPopulatedState,
+ });
+
+ expect(state.apiHost).toEqual('apiHost');
+ expect(state.enabled).toEqual(true);
+ expect(state.selectedProject).toEqual(projectList[0]);
+ expect(state.token).toEqual('token');
+ expect(state.listProjectsEndpoint).toEqual(TEST_HOST);
+ expect(state.operationsSettingsEndpoint).toEqual(TEST_HOST);
+ });
+
+ it('should receive projects successfully', () => {
+ mutations[types.RECEIVE_PROJECTS](state, [sampleBackendProject]);
+
+ expect(state.projects).toEqual([normalizedProject]);
+ });
+
+ it('should strip out unnecessary project properties', () => {
+ mutations[types.RECEIVE_PROJECTS](state, [
+ { ...sampleBackendProject, extra_property: 'extra_property' },
+ ]);
+
+ expect(state.projects).toEqual([normalizedProject]);
+ });
+
+ it('should update state when connect is successful', () => {
+ mutations[types.UPDATE_CONNECT_SUCCESS](state);
+
+ expect(state.connectSuccessful).toBe(true);
+ expect(state.connectError).toBe(false);
+ });
+
+ it('should update state when connect fails', () => {
+ mutations[types.UPDATE_CONNECT_ERROR](state);
+
+ expect(state.connectSuccessful).toBe(false);
+ expect(state.connectError).toBe(true);
+ });
+
+ it('should update state when connect is reset', () => {
+ mutations[types.RESET_CONNECT](state);
+
+ expect(state.connectSuccessful).toBe(false);
+ expect(state.connectError).toBe(false);
+ });
+ });
+});
diff --git a/spec/javascripts/error_tracking_settings/utils_spec.js b/spec/javascripts/error_tracking_settings/utils_spec.js
new file mode 100644
index 00000000000..4b144f7daf1
--- /dev/null
+++ b/spec/javascripts/error_tracking_settings/utils_spec.js
@@ -0,0 +1,29 @@
+import { transformFrontendSettings } from '~/error_tracking_settings/utils';
+import { sampleFrontendSettings, transformedSettings } from './mock';
+
+describe('error tracking settings utils', () => {
+ describe('data transform functions', () => {
+ it('should transform settings successfully for the backend', () => {
+ expect(transformFrontendSettings(sampleFrontendSettings)).toEqual(transformedSettings);
+ });
+
+ it('should transform empty values in the settings object to null', () => {
+ const emptyFrontendSettingsObject = {
+ apiHost: '',
+ enabled: false,
+ token: '',
+ selectedProject: null,
+ };
+ const transformedEmptySettingsObject = {
+ api_host: null,
+ enabled: false,
+ token: null,
+ project: null,
+ };
+
+ expect(transformFrontendSettings(emptyFrontendSettingsObject)).toEqual(
+ transformedEmptySettingsObject,
+ );
+ });
+ });
+});
diff --git a/spec/javascripts/filtered_search/dropdown_user_spec.js b/spec/javascripts/filtered_search/dropdown_user_spec.js
index e8fcc8592eb..f764800fff0 100644
--- a/spec/javascripts/filtered_search/dropdown_user_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_user_spec.js
@@ -72,7 +72,7 @@ describe('Dropdown User', () => {
});
describe('hideCurrentUser', () => {
- const fixtureTemplate = 'issues/issue_list.html.raw';
+ const fixtureTemplate = 'issues/issue_list.html';
preloadFixtures(fixtureTemplate);
let dropdown;
diff --git a/spec/javascripts/filtered_search/dropdown_utils_spec.js b/spec/javascripts/filtered_search/dropdown_utils_spec.js
index cfd0b96ec43..62d1bd69635 100644
--- a/spec/javascripts/filtered_search/dropdown_utils_spec.js
+++ b/spec/javascripts/filtered_search/dropdown_utils_spec.js
@@ -4,7 +4,7 @@ import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
describe('Dropdown Utils', () => {
- const issueListFixture = 'issues/issue_list.html.raw';
+ const issueListFixture = 'issues/issue_list.html';
preloadFixtures(issueListFixture);
describe('getEscapedText', () => {
diff --git a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
index 6230da77f49..a72ea6ab547 100644
--- a/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
+++ b/spec/javascripts/filtered_search/filtered_search_visual_tokens_spec.js
@@ -1,9 +1,4 @@
-import _ from 'underscore';
-import AjaxCache from '~/lib/utils/ajax_cache';
-import UsersCache from '~/lib/utils/users_cache';
-
import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens';
-import DropdownUtils from '~/filtered_search//dropdown_utils';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
describe('Filtered Search Visual Tokens', () => {
@@ -298,6 +293,7 @@ describe('Filtered Search Visual Tokens', () => {
subject.addVisualTokenElement('milestone');
const token = tokensContainer.querySelector('.js-visual-token');
+ expect(token.classList.contains('search-token-milestone')).toEqual(true);
expect(token.classList.contains('filtered-search-token')).toEqual(true);
expect(token.querySelector('.name').innerText).toEqual('milestone');
expect(token.querySelector('.value')).toEqual(null);
@@ -307,6 +303,7 @@ describe('Filtered Search Visual Tokens', () => {
subject.addVisualTokenElement('label', 'Frontend');
const token = tokensContainer.querySelector('.js-visual-token');
+ expect(token.classList.contains('search-token-label')).toEqual(true);
expect(token.classList.contains('filtered-search-token')).toEqual(true);
expect(token.querySelector('.name').innerText).toEqual('label');
expect(token.querySelector('.value').innerText).toEqual('Frontend');
@@ -322,10 +319,12 @@ describe('Filtered Search Visual Tokens', () => {
const labelToken = tokens[0];
const assigneeToken = tokens[1];
+ expect(labelToken.classList.contains('search-token-label')).toEqual(true);
expect(labelToken.classList.contains('filtered-search-token')).toEqual(true);
expect(labelToken.querySelector('.name').innerText).toEqual('label');
expect(labelToken.querySelector('.value').innerText).toEqual('Frontend');
+ expect(assigneeToken.classList.contains('search-token-assignee')).toEqual(true);
expect(assigneeToken.classList.contains('filtered-search-token')).toEqual(true);
expect(assigneeToken.querySelector('.name').innerText).toEqual('assignee');
expect(assigneeToken.querySelector('.value').innerText).toEqual('@root');
@@ -685,349 +684,21 @@ describe('Filtered Search Visual Tokens', () => {
});
describe('renderVisualTokenValue', () => {
- const keywordToken = FilteredSearchSpecHelper.createFilterVisualToken('search');
- const milestoneToken = FilteredSearchSpecHelper.createFilterVisualToken(
- 'milestone',
- 'upcoming',
- );
-
- let updateLabelTokenColorSpy;
- let updateUserTokenAppearanceSpy;
-
beforeEach(() => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
${authorToken.outerHTML}
${bugLabelToken.outerHTML}
- ${keywordToken.outerHTML}
- ${milestoneToken.outerHTML}
`);
-
- spyOn(subject, 'updateLabelTokenColor');
- updateLabelTokenColorSpy = subject.updateLabelTokenColor;
-
- spyOn(subject, 'updateUserTokenAppearance');
- updateUserTokenAppearanceSpy = subject.updateUserTokenAppearance;
});
it('renders a author token value element', () => {
- const { tokenNameElement, tokenValueContainer, tokenValueElement } = findElements(
- authorToken,
- );
+ const { tokenNameElement, tokenValueElement } = findElements(authorToken);
const tokenName = tokenNameElement.innerText;
const tokenValue = 'new value';
subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
expect(tokenValueElement.innerText).toBe(tokenValue);
- expect(updateUserTokenAppearanceSpy.calls.count()).toBe(1);
- const expectedArgs = [tokenValueContainer, tokenValueElement, tokenValue];
-
- expect(updateUserTokenAppearanceSpy.calls.argsFor(0)).toEqual(expectedArgs);
- expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
- });
-
- it('renders a label token value element', () => {
- const { tokenNameElement, tokenValueContainer, tokenValueElement } = findElements(
- bugLabelToken,
- );
- const tokenName = tokenNameElement.innerText;
- const tokenValue = 'new value';
-
- subject.renderVisualTokenValue(bugLabelToken, tokenName, tokenValue);
-
- expect(tokenValueElement.innerText).toBe(tokenValue);
- expect(updateLabelTokenColorSpy.calls.count()).toBe(1);
- const expectedArgs = [tokenValueContainer, tokenValue];
-
- expect(updateLabelTokenColorSpy.calls.argsFor(0)).toEqual(expectedArgs);
- expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
- });
-
- it('renders a milestone token value element', () => {
- const { tokenNameElement, tokenValueElement } = findElements(milestoneToken);
- const tokenName = tokenNameElement.innerText;
- const tokenValue = 'new value';
-
- subject.renderVisualTokenValue(milestoneToken, tokenName, tokenValue);
-
- expect(tokenValueElement.innerText).toBe(tokenValue);
- expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
- expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
- });
-
- it('does not update user token appearance for `None` filter', () => {
- const { tokenNameElement } = findElements(authorToken);
-
- const tokenName = tokenNameElement.innerText;
- const tokenValue = 'None';
-
- subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
-
- expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
- });
-
- it('does not update user token appearance for `none` filter', () => {
- const { tokenNameElement } = findElements(authorToken);
-
- const tokenName = tokenNameElement.innerText;
- const tokenValue = 'none';
-
- subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
-
- expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
- });
-
- it('does not update user token appearance for `any` filter', () => {
- const { tokenNameElement } = findElements(authorToken);
-
- const tokenName = tokenNameElement.innerText;
- const tokenValue = 'any';
-
- subject.renderVisualTokenValue(authorToken, tokenName, tokenValue);
-
- expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
- });
-
- it('does not update label token color for `none` filter', () => {
- const { tokenNameElement } = findElements(bugLabelToken);
-
- const tokenName = tokenNameElement.innerText;
- const tokenValue = 'none';
-
- subject.renderVisualTokenValue(bugLabelToken, tokenName, tokenValue);
-
- expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
- });
-
- it('does not update label token color for `any` filter', () => {
- const { tokenNameElement } = findElements(bugLabelToken);
-
- const tokenName = tokenNameElement.innerText;
- const tokenValue = 'any';
-
- subject.renderVisualTokenValue(bugLabelToken, tokenName, tokenValue);
-
- expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
- });
- });
-
- describe('updateUserTokenAppearance', () => {
- let usersCacheSpy;
-
- beforeEach(() => {
- spyOn(UsersCache, 'retrieve').and.callFake(username => usersCacheSpy(username));
- });
-
- it('ignores error if UsersCache throws', done => {
- spyOn(window, 'Flash');
- const dummyError = new Error('Earth rotated backwards');
- const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
- const tokenValue = tokenValueElement.innerText;
- usersCacheSpy = username => {
- expect(`@${username}`).toBe(tokenValue);
- return Promise.reject(dummyError);
- };
-
- subject
- .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
- .then(() => {
- expect(window.Flash.calls.count()).toBe(0);
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('does nothing if user cannot be found', done => {
- const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
- const tokenValue = tokenValueElement.innerText;
- usersCacheSpy = username => {
- expect(`@${username}`).toBe(tokenValue);
- return Promise.resolve(undefined);
- };
-
- subject
- .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
- .then(() => {
- expect(tokenValueElement.innerText).toBe(tokenValue);
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('replaces author token with avatar and display name', done => {
- const dummyUser = {
- name: 'Important Person',
- avatar_url: 'https://host.invalid/mypics/avatar.png',
- };
- const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
- const tokenValue = tokenValueElement.innerText;
- usersCacheSpy = username => {
- expect(`@${username}`).toBe(tokenValue);
- return Promise.resolve(dummyUser);
- };
-
- subject
- .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
- .then(() => {
- expect(tokenValueContainer.dataset.originalValue).toBe(tokenValue);
- expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
- const avatar = tokenValueElement.querySelector('img.avatar');
-
- expect(avatar.src).toBe(dummyUser.avatar_url);
- expect(avatar.alt).toBe('');
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('escapes user name when creating token', done => {
- const dummyUser = {
- name: '<script>',
- avatar_url: `${gl.TEST_HOST}/mypics/avatar.png`,
- };
- const { tokenValueContainer, tokenValueElement } = findElements(authorToken);
- const tokenValue = tokenValueElement.innerText;
- usersCacheSpy = username => {
- expect(`@${username}`).toBe(tokenValue);
- return Promise.resolve(dummyUser);
- };
-
- subject
- .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
- .then(() => {
- expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
- tokenValueElement.querySelector('.avatar').remove();
-
- expect(tokenValueElement.innerHTML.trim()).toBe(_.escape(dummyUser.name));
- })
- .then(done)
- .catch(done.fail);
- });
- });
-
- describe('setTokenStyle', () => {
- let originalTextColor;
-
- beforeEach(() => {
- originalTextColor = bugLabelToken.style.color;
- });
-
- it('should set backgroundColor', () => {
- const originalBackgroundColor = bugLabelToken.style.backgroundColor;
- const token = subject.setTokenStyle(bugLabelToken, 'blue', 'white');
-
- expect(token.style.backgroundColor).toEqual('blue');
- expect(token.style.backgroundColor).not.toEqual(originalBackgroundColor);
- });
-
- it('should set textColor', () => {
- const token = subject.setTokenStyle(bugLabelToken, 'white', 'black');
-
- expect(token.style.color).toEqual('black');
- expect(token.style.color).not.toEqual(originalTextColor);
- });
-
- it('should add inverted class when textColor is #FFFFFF', () => {
- const token = subject.setTokenStyle(bugLabelToken, 'black', '#FFFFFF');
-
- expect(token.style.color).toEqual('rgb(255, 255, 255)');
- expect(token.style.color).not.toEqual(originalTextColor);
- expect(token.querySelector('.remove-token').classList.contains('inverted')).toEqual(true);
- });
- });
-
- describe('updateLabelTokenColor', () => {
- const jsonFixtureName = 'labels/project_labels.json';
- const dummyEndpoint = '/dummy/endpoint';
-
- preloadFixtures(jsonFixtureName);
-
- let labelData;
-
- beforeAll(() => {
- labelData = getJSONFixture(jsonFixtureName);
- });
-
- const missingLabelToken = FilteredSearchSpecHelper.createFilterVisualToken(
- 'label',
- '~doesnotexist',
- );
- const spaceLabelToken = FilteredSearchSpecHelper.createFilterVisualToken(
- 'label',
- '~"some space"',
- );
-
- beforeEach(() => {
- tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
- ${bugLabelToken.outerHTML}
- ${missingLabelToken.outerHTML}
- ${spaceLabelToken.outerHTML}
- `);
-
- const filteredSearchInput = document.querySelector('.filtered-search');
- filteredSearchInput.dataset.baseEndpoint = dummyEndpoint;
-
- AjaxCache.internalStorage = {};
- AjaxCache.internalStorage[`${dummyEndpoint}/labels.json`] = labelData;
- });
-
- const parseColor = color => {
- const dummyElement = document.createElement('div');
- dummyElement.style.color = color;
- return dummyElement.style.color;
- };
-
- const expectValueContainerStyle = (tokenValueContainer, label) => {
- expect(tokenValueContainer.getAttribute('style')).not.toBe(null);
- expect(tokenValueContainer.style.backgroundColor).toBe(parseColor(label.color));
- expect(tokenValueContainer.style.color).toBe(parseColor(label.text_color));
- };
-
- const findLabel = tokenValue =>
- labelData.find(label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`);
-
- it('updates the color of a label token', done => {
- const { tokenValueContainer, tokenValueElement } = findElements(bugLabelToken);
- const tokenValue = tokenValueElement.innerText;
- const matchingLabel = findLabel(tokenValue);
-
- subject
- .updateLabelTokenColor(tokenValueContainer, tokenValue)
- .then(() => {
- expectValueContainerStyle(tokenValueContainer, matchingLabel);
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('updates the color of a label token with spaces', done => {
- const { tokenValueContainer, tokenValueElement } = findElements(spaceLabelToken);
- const tokenValue = tokenValueElement.innerText;
- const matchingLabel = findLabel(tokenValue);
-
- subject
- .updateLabelTokenColor(tokenValueContainer, tokenValue)
- .then(() => {
- expectValueContainerStyle(tokenValueContainer, matchingLabel);
- })
- .then(done)
- .catch(done.fail);
- });
-
- it('does not change color of a missing label', done => {
- const { tokenValueContainer, tokenValueElement } = findElements(missingLabelToken);
- const tokenValue = tokenValueElement.innerText;
- const matchingLabel = findLabel(tokenValue);
-
- expect(matchingLabel).toBe(undefined);
-
- subject
- .updateLabelTokenColor(tokenValueContainer, tokenValue)
- .then(() => {
- expect(tokenValueContainer.getAttribute('style')).toBe(null);
- })
- .then(done)
- .catch(done.fail);
});
});
});
diff --git a/spec/javascripts/filtered_search/visual_token_value_spec.js b/spec/javascripts/filtered_search/visual_token_value_spec.js
new file mode 100644
index 00000000000..14217d460cc
--- /dev/null
+++ b/spec/javascripts/filtered_search/visual_token_value_spec.js
@@ -0,0 +1,383 @@
+import VisualTokenValue from '~/filtered_search/visual_token_value';
+import _ from 'underscore';
+import AjaxCache from '~/lib/utils/ajax_cache';
+import UsersCache from '~/lib/utils/users_cache';
+import DropdownUtils from '~/filtered_search//dropdown_utils';
+import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
+
+describe('Filtered Search Visual Tokens', () => {
+ const findElements = tokenElement => {
+ const tokenNameElement = tokenElement.querySelector('.name');
+ const tokenValueContainer = tokenElement.querySelector('.value-container');
+ const tokenValueElement = tokenValueContainer.querySelector('.value');
+ const tokenType = tokenNameElement.innerText.toLowerCase();
+ const tokenValue = tokenValueElement.innerText;
+ const subject = new VisualTokenValue(tokenValue, tokenType);
+ return { subject, tokenValueContainer, tokenValueElement };
+ };
+
+ let tokensContainer;
+ let authorToken;
+ let bugLabelToken;
+
+ beforeEach(() => {
+ setFixtures(`
+ <ul class="tokens-container">
+ ${FilteredSearchSpecHelper.createInputHTML()}
+ </ul>
+ `);
+ tokensContainer = document.querySelector('.tokens-container');
+
+ authorToken = FilteredSearchSpecHelper.createFilterVisualToken('author', '@user');
+ bugLabelToken = FilteredSearchSpecHelper.createFilterVisualToken('label', '~bug');
+ });
+
+ describe('updateUserTokenAppearance', () => {
+ let usersCacheSpy;
+
+ beforeEach(() => {
+ spyOn(UsersCache, 'retrieve').and.callFake(username => usersCacheSpy(username));
+ });
+
+ it('ignores error if UsersCache throws', done => {
+ spyOn(window, 'Flash');
+ const dummyError = new Error('Earth rotated backwards');
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken);
+ const tokenValue = tokenValueElement.innerText;
+ usersCacheSpy = username => {
+ expect(`@${username}`).toBe(tokenValue);
+ return Promise.reject(dummyError);
+ };
+
+ subject
+ .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
+ .then(() => {
+ expect(window.Flash.calls.count()).toBe(0);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does nothing if user cannot be found', done => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken);
+ const tokenValue = tokenValueElement.innerText;
+ usersCacheSpy = username => {
+ expect(`@${username}`).toBe(tokenValue);
+ return Promise.resolve(undefined);
+ };
+
+ subject
+ .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
+ .then(() => {
+ expect(tokenValueElement.innerText).toBe(tokenValue);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('replaces author token with avatar and display name', done => {
+ const dummyUser = {
+ name: 'Important Person',
+ avatar_url: 'https://host.invalid/mypics/avatar.png',
+ };
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken);
+ const tokenValue = tokenValueElement.innerText;
+ usersCacheSpy = username => {
+ expect(`@${username}`).toBe(tokenValue);
+ return Promise.resolve(dummyUser);
+ };
+
+ subject
+ .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
+ .then(() => {
+ expect(tokenValueContainer.dataset.originalValue).toBe(tokenValue);
+ expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
+ const avatar = tokenValueElement.querySelector('img.avatar');
+
+ expect(avatar.src).toBe(dummyUser.avatar_url);
+ expect(avatar.alt).toBe('');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('escapes user name when creating token', done => {
+ const dummyUser = {
+ name: '<script>',
+ avatar_url: `${gl.TEST_HOST}/mypics/avatar.png`,
+ };
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken);
+ const tokenValue = tokenValueElement.innerText;
+ usersCacheSpy = username => {
+ expect(`@${username}`).toBe(tokenValue);
+ return Promise.resolve(dummyUser);
+ };
+
+ subject
+ .updateUserTokenAppearance(tokenValueContainer, tokenValueElement, tokenValue)
+ .then(() => {
+ expect(tokenValueElement.innerText.trim()).toBe(dummyUser.name);
+ tokenValueElement.querySelector('.avatar').remove();
+
+ expect(tokenValueElement.innerHTML.trim()).toBe(_.escape(dummyUser.name));
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('updateLabelTokenColor', () => {
+ const jsonFixtureName = 'labels/project_labels.json';
+ const dummyEndpoint = '/dummy/endpoint';
+
+ preloadFixtures(jsonFixtureName);
+
+ let labelData;
+
+ beforeAll(() => {
+ labelData = getJSONFixture(jsonFixtureName);
+ });
+
+ const missingLabelToken = FilteredSearchSpecHelper.createFilterVisualToken(
+ 'label',
+ '~doesnotexist',
+ );
+ const spaceLabelToken = FilteredSearchSpecHelper.createFilterVisualToken(
+ 'label',
+ '~"some space"',
+ );
+
+ beforeEach(() => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
+ ${bugLabelToken.outerHTML}
+ ${missingLabelToken.outerHTML}
+ ${spaceLabelToken.outerHTML}
+ `);
+
+ const filteredSearchInput = document.querySelector('.filtered-search');
+ filteredSearchInput.dataset.baseEndpoint = dummyEndpoint;
+
+ AjaxCache.internalStorage = {};
+ AjaxCache.internalStorage[`${dummyEndpoint}/labels.json`] = labelData;
+ });
+
+ const parseColor = color => {
+ const dummyElement = document.createElement('div');
+ dummyElement.style.color = color;
+ return dummyElement.style.color;
+ };
+
+ const expectValueContainerStyle = (tokenValueContainer, label) => {
+ expect(tokenValueContainer.getAttribute('style')).not.toBe(null);
+ expect(tokenValueContainer.style.backgroundColor).toBe(parseColor(label.color));
+ expect(tokenValueContainer.style.color).toBe(parseColor(label.text_color));
+ };
+
+ const findLabel = tokenValue =>
+ labelData.find(label => tokenValue === `~${DropdownUtils.getEscapedText(label.title)}`);
+
+ it('updates the color of a label token', done => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken);
+ const tokenValue = tokenValueElement.innerText;
+ const matchingLabel = findLabel(tokenValue);
+
+ subject
+ .updateLabelTokenColor(tokenValueContainer, tokenValue)
+ .then(() => {
+ expectValueContainerStyle(tokenValueContainer, matchingLabel);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('updates the color of a label token with spaces', done => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(spaceLabelToken);
+ const tokenValue = tokenValueElement.innerText;
+ const matchingLabel = findLabel(tokenValue);
+
+ subject
+ .updateLabelTokenColor(tokenValueContainer, tokenValue)
+ .then(() => {
+ expectValueContainerStyle(tokenValueContainer, matchingLabel);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('does not change color of a missing label', done => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(missingLabelToken);
+ const tokenValue = tokenValueElement.innerText;
+ const matchingLabel = findLabel(tokenValue);
+
+ expect(matchingLabel).toBe(undefined);
+
+ subject
+ .updateLabelTokenColor(tokenValueContainer, tokenValue)
+ .then(() => {
+ expect(tokenValueContainer.getAttribute('style')).toBe(null);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('setTokenStyle', () => {
+ let originalTextColor;
+
+ beforeEach(() => {
+ originalTextColor = bugLabelToken.style.color;
+ });
+
+ it('should set backgroundColor', () => {
+ const originalBackgroundColor = bugLabelToken.style.backgroundColor;
+ const token = VisualTokenValue.setTokenStyle(bugLabelToken, 'blue', 'white');
+
+ expect(token.style.backgroundColor).toEqual('blue');
+ expect(token.style.backgroundColor).not.toEqual(originalBackgroundColor);
+ });
+
+ it('should set textColor', () => {
+ const token = VisualTokenValue.setTokenStyle(bugLabelToken, 'white', 'black');
+
+ expect(token.style.color).toEqual('black');
+ expect(token.style.color).not.toEqual(originalTextColor);
+ });
+
+ it('should add inverted class when textColor is #FFFFFF', () => {
+ const token = VisualTokenValue.setTokenStyle(bugLabelToken, 'black', '#FFFFFF');
+
+ expect(token.style.color).toEqual('rgb(255, 255, 255)');
+ expect(token.style.color).not.toEqual(originalTextColor);
+ expect(token.querySelector('.remove-token').classList.contains('inverted')).toEqual(true);
+ });
+ });
+
+ describe('render', () => {
+ const setupSpies = subject => {
+ spyOn(subject, 'updateLabelTokenColor'); // eslint-disable-line jasmine/no-unsafe-spy
+ const updateLabelTokenColorSpy = subject.updateLabelTokenColor;
+
+ spyOn(subject, 'updateUserTokenAppearance'); // eslint-disable-line jasmine/no-unsafe-spy
+ const updateUserTokenAppearanceSpy = subject.updateUserTokenAppearance;
+
+ return { updateLabelTokenColorSpy, updateUserTokenAppearanceSpy };
+ };
+
+ const keywordToken = FilteredSearchSpecHelper.createFilterVisualToken('search');
+ const milestoneToken = FilteredSearchSpecHelper.createFilterVisualToken(
+ 'milestone',
+ 'upcoming',
+ );
+
+ beforeEach(() => {
+ tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
+ ${authorToken.outerHTML}
+ ${bugLabelToken.outerHTML}
+ ${keywordToken.outerHTML}
+ ${milestoneToken.outerHTML}
+ `);
+ });
+
+ it('renders a author token value element', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken);
+
+ const { updateLabelTokenColorSpy, updateUserTokenAppearanceSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateUserTokenAppearanceSpy.calls.count()).toBe(1);
+ const expectedArgs = [tokenValueContainer, tokenValueElement];
+
+ expect(updateUserTokenAppearanceSpy.calls.argsFor(0)).toEqual(expectedArgs);
+ expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
+ });
+
+ it('renders a label token value element', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken);
+
+ const { updateLabelTokenColorSpy, updateUserTokenAppearanceSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateLabelTokenColorSpy.calls.count()).toBe(1);
+ const expectedArgs = [tokenValueContainer];
+
+ expect(updateLabelTokenColorSpy.calls.argsFor(0)).toEqual(expectedArgs);
+ expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
+ });
+
+ it('renders a milestone token value element', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(milestoneToken);
+
+ const { updateLabelTokenColorSpy, updateUserTokenAppearanceSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
+ expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update user token appearance for `none` filter', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken);
+
+ subject.tokenValue = 'none';
+
+ const { updateUserTokenAppearanceSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update user token appearance for `None` filter', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken);
+
+ subject.tokenValue = 'None';
+
+ const { updateUserTokenAppearanceSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update user token appearance for `any` filter', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(authorToken);
+
+ subject.tokenValue = 'any';
+
+ const { updateUserTokenAppearanceSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateUserTokenAppearanceSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update label token color for `None` filter', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken);
+
+ subject.tokenValue = 'None';
+
+ const { updateLabelTokenColorSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update label token color for `none` filter', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken);
+
+ subject.tokenValue = 'none';
+
+ const { updateLabelTokenColorSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
+ });
+
+ it('does not update label token color for `any` filter', () => {
+ const { subject, tokenValueContainer, tokenValueElement } = findElements(bugLabelToken);
+
+ subject.tokenValue = 'any';
+
+ const { updateLabelTokenColorSpy } = setupSpies(subject);
+ subject.render(tokenValueContainer, tokenValueElement);
+
+ expect(updateLabelTokenColorSpy.calls.count()).toBe(0);
+ });
+ });
+});
diff --git a/spec/javascripts/fixtures/.gitignore b/spec/javascripts/fixtures/.gitignore
index 0c35cdd778e..2507c8e7263 100644
--- a/spec/javascripts/fixtures/.gitignore
+++ b/spec/javascripts/fixtures/.gitignore
@@ -1,2 +1,3 @@
*.html.raw
+*.html
*.json
diff --git a/spec/javascripts/fixtures/abuse_reports.rb b/spec/javascripts/fixtures/abuse_reports.rb
index 387858cba77..54b6419bcdb 100644
--- a/spec/javascripts/fixtures/abuse_reports.rb
+++ b/spec/javascripts/fixtures/abuse_reports.rb
@@ -18,7 +18,7 @@ describe Admin::AbuseReportsController, '(JavaScript fixtures)', type: :controll
sign_in(admin)
end
- it 'abuse_reports/abuse_reports_list.html.raw' do |example|
+ it 'abuse_reports/abuse_reports_list.html' do |example|
get :index
expect(response).to be_success
diff --git a/spec/javascripts/fixtures/admin_users.rb b/spec/javascripts/fixtures/admin_users.rb
index 9989ac4fff2..76dbdf603da 100644
--- a/spec/javascripts/fixtures/admin_users.rb
+++ b/spec/javascripts/fixtures/admin_users.rb
@@ -17,7 +17,7 @@ describe Admin::UsersController, '(JavaScript fixtures)', type: :controller do
clean_frontend_fixtures('admin/users')
end
- it 'admin/users/new_with_internal_user_regex.html.raw' do |example|
+ it 'admin/users/new_with_internal_user_regex.html' do |example|
stub_application_setting(user_default_external: true)
stub_application_setting(user_default_internal_regex: '^(?:(?!\.ext@).)*$\r?')
diff --git a/spec/javascripts/fixtures/ajax_loading_spinner.html.haml b/spec/javascripts/fixtures/ajax_loading_spinner.html.haml
deleted file mode 100644
index 09d8c9df3b2..00000000000
--- a/spec/javascripts/fixtures/ajax_loading_spinner.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-%a.js-ajax-loading-spinner{href: "http://goesnowhere.nothing/whereami", data: {remote: true}}
- %i.fa.fa-trash-o
diff --git a/spec/javascripts/fixtures/application_settings.rb b/spec/javascripts/fixtures/application_settings.rb
index a9d3043f73d..c535e598e12 100644
--- a/spec/javascripts/fixtures/application_settings.rb
+++ b/spec/javascripts/fixtures/application_settings.rb
@@ -23,7 +23,7 @@ describe Admin::ApplicationSettingsController, '(JavaScript fixtures)', type: :c
remove_repository(project)
end
- it 'application_settings/accounts_and_limit.html.raw' do |example|
+ it 'application_settings/accounts_and_limit.html' do |example|
stub_application_setting(user_default_external: false)
get :show
diff --git a/spec/javascripts/fixtures/autocomplete_sources.rb b/spec/javascripts/fixtures/autocomplete_sources.rb
new file mode 100644
index 00000000000..c117fb7cd24
--- /dev/null
+++ b/spec/javascripts/fixtures/autocomplete_sources.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::AutocompleteSourcesController, '(JavaScript fixtures)', type: :controller do
+ include JavaScriptFixturesHelpers
+
+ set(:admin) { create(:admin) }
+ set(:group) { create(:group, name: 'frontend-fixtures') }
+ set(:project) { create(:project, namespace: group, path: 'autocomplete-sources-project') }
+ set(:issue) { create(:issue, project: project) }
+
+ before(:all) do
+ clean_frontend_fixtures('autocomplete_sources/')
+ end
+
+ before do
+ sign_in(admin)
+ end
+
+ it 'autocomplete_sources/labels.json' do |example|
+ issue.labels << create(:label, project: project, title: 'bug')
+ issue.labels << create(:label, project: project, title: 'critical')
+
+ create(:label, project: project, title: 'feature')
+ create(:label, project: project, title: 'documentation')
+
+ get :labels,
+ format: :json,
+ params: {
+ namespace_id: group.path,
+ project_id: project.path,
+ type: issue.class.name,
+ type_id: issue.id
+ }
+
+ expect(response).to be_success
+ store_frontend_fixture(response, example.description)
+ end
+end
diff --git a/spec/javascripts/fixtures/balsamiq_viewer.html.haml b/spec/javascripts/fixtures/balsamiq_viewer.html.haml
deleted file mode 100644
index 18166ba4901..00000000000
--- a/spec/javascripts/fixtures/balsamiq_viewer.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-.file-content.balsamiq-viewer#js-balsamiq-viewer{ data: { endpoint: '/test' } }
diff --git a/spec/javascripts/fixtures/blob.rb b/spec/javascripts/fixtures/blob.rb
index 1b2a3b484bb..db7749bc000 100644
--- a/spec/javascripts/fixtures/blob.rb
+++ b/spec/javascripts/fixtures/blob.rb
@@ -15,13 +15,14 @@ describe Projects::BlobController, '(JavaScript fixtures)', type: :controller do
before do
sign_in(admin)
+ allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
end
after do
remove_repository(project)
end
- it 'blob/show.html.raw' do |example|
+ it 'blob/show.html' do |example|
get(:show, params: {
namespace_id: project.namespace,
project_id: project,
diff --git a/spec/javascripts/fixtures/boards.rb b/spec/javascripts/fixtures/boards.rb
index 1d675e008ba..c4390e89578 100644
--- a/spec/javascripts/fixtures/boards.rb
+++ b/spec/javascripts/fixtures/boards.rb
@@ -17,7 +17,7 @@ describe Projects::BoardsController, '(JavaScript fixtures)', type: :controller
sign_in(admin)
end
- it 'boards/show.html.raw' do |example|
+ it 'boards/show.html' do |example|
get(:index, params: {
namespace_id: project.namespace,
project_id: project
diff --git a/spec/javascripts/fixtures/branches.rb b/spec/javascripts/fixtures/branches.rb
index 3cc713ef90f..5d2d6c7ec0e 100644
--- a/spec/javascripts/fixtures/branches.rb
+++ b/spec/javascripts/fixtures/branches.rb
@@ -21,7 +21,7 @@ describe Projects::BranchesController, '(JavaScript fixtures)', type: :controlle
remove_repository(project)
end
- it 'branches/new_branch.html.raw' do |example|
+ it 'branches/new_branch.html' do |example|
get :new, params: {
namespace_id: project.namespace.to_param,
project_id: project
diff --git a/spec/javascripts/fixtures/clusters.rb b/spec/javascripts/fixtures/clusters.rb
index 69dbe54ffc2..8ebd8a41366 100644
--- a/spec/javascripts/fixtures/clusters.rb
+++ b/spec/javascripts/fixtures/clusters.rb
@@ -22,7 +22,7 @@ describe Projects::ClustersController, '(JavaScript fixtures)', type: :controlle
remove_repository(project)
end
- it 'clusters/show_cluster.html.raw' do |example|
+ it 'clusters/show_cluster.html' do |example|
get :show, params: {
namespace_id: project.namespace.to_param,
project_id: project,
diff --git a/spec/javascripts/fixtures/commit.rb b/spec/javascripts/fixtures/commit.rb
index f0e4bb50c67..ab10f559e4b 100644
--- a/spec/javascripts/fixtures/commit.rb
+++ b/spec/javascripts/fixtures/commit.rb
@@ -16,9 +16,10 @@ describe Projects::CommitController, '(JavaScript fixtures)', type: :controller
before do
project.add_maintainer(user)
sign_in(user)
+ allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
end
- it 'commit/show.html.raw' do |example|
+ it 'commit/show.html' do |example|
params = {
namespace_id: project.namespace,
project_id: project,
diff --git a/spec/javascripts/fixtures/create_item_dropdown.html.haml b/spec/javascripts/fixtures/create_item_dropdown.html.haml
deleted file mode 100644
index d4d91b93caf..00000000000
--- a/spec/javascripts/fixtures/create_item_dropdown.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-.js-create-item-dropdown-fixture-root
- %input{ name: 'variable[environment]', type: 'hidden' }
- = dropdown_tag('some label',
- options: { toggle_class: 'js-dropdown-menu-toggle',
- content_class: 'js-dropdown-content',
- filter: true,
- dropdown_class: "dropdown-menu-selectable",
- footer_content: true }) do
- %ul.dropdown-footer-list
- %li
- %button{ class: "dropdown-create-new-item-button js-dropdown-create-new-item" }
- Create wildcard
- %code
diff --git a/spec/javascripts/fixtures/deploy_keys.rb b/spec/javascripts/fixtures/deploy_keys.rb
index efbda955972..a333d9c0150 100644
--- a/spec/javascripts/fixtures/deploy_keys.rb
+++ b/spec/javascripts/fixtures/deploy_keys.rb
@@ -25,7 +25,7 @@ describe Projects::DeployKeysController, '(JavaScript fixtures)', type: :control
render_views
it 'deploy_keys/keys.json' do |example|
- create(:deploy_key, public: true)
+ create(:rsa_deploy_key_2048, public: true)
project_key = create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCdMHEHyhRjbhEZVddFn6lTWdgEy5Q6Bz4nwGB76xWZI5YT/1WJOMEW+sL5zYd31kk7sd3FJ5L9ft8zWMWrr/iWXQikC2cqZK24H1xy+ZUmrRuJD4qGAaIVoyyzBL+avL+lF8J5lg6YSw8gwJY/lX64/vnJHUlWw2n5BF8IFOWhiw== dummy@gitlab.com')
internal_key = create(:deploy_key, key: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQDNd/UJWhPrpb+b/G5oL109y57yKuCxE+WUGJGYaj7WQKsYRJmLYh1mgjrl+KVyfsWpq4ylOxIfFSnN9xBBFN8mlb0Fma5DC7YsSsibJr3MZ19ZNBprwNcdogET7aW9I0In7Wu5f2KqI6e5W/spJHCy4JVxzVMUvk6Myab0LnJ2iQ== dummy@gitlab.com')
create(:deploy_keys_project, project: project, deploy_key: project_key)
diff --git a/spec/javascripts/fixtures/event_filter.html.haml b/spec/javascripts/fixtures/event_filter.html.haml
deleted file mode 100644
index aa7af61c7eb..00000000000
--- a/spec/javascripts/fixtures/event_filter.html.haml
+++ /dev/null
@@ -1,25 +0,0 @@
-%ul.nav-links.event-filter.scrolling-tabs.nav.nav-tabs
- %li.active
- %a.event-filter-link{ id: "all_event_filter", title: "Filter by all", href: "/dashboard/activity"}
- %span
- All
- %li
- %a.event-filter-link{ id: "push_event_filter", title: "Filter by push events", href: "/dashboard/activity"}
- %span
- Push events
- %li
- %a.event-filter-link{ id: "merged_event_filter", title: "Filter by merge events", href: "/dashboard/activity"}
- %span
- Merge events
- %li
- %a.event-filter-link{ id: "issue_event_filter", title: "Filter by issue events", href: "/dashboard/activity"}
- %span
- Issue events
- %li
- %a.event-filter-link{ id: "comments_event_filter", title: "Filter by comments", href: "/dashboard/activity"}
- %span
- Comments
- %li
- %a.event-filter-link{ id: "team_event_filter", title: "Filter by team", href: "/dashboard/activity"}
- %span
- Team \ No newline at end of file
diff --git a/spec/javascripts/fixtures/gl_dropdown.html.haml b/spec/javascripts/fixtures/gl_dropdown.html.haml
deleted file mode 100644
index 43d57c2c4dc..00000000000
--- a/spec/javascripts/fixtures/gl_dropdown.html.haml
+++ /dev/null
@@ -1,17 +0,0 @@
-%div
- .dropdown.inline
- %button#js-project-dropdown.dropdown-menu-toggle{type: 'button', data: {toggle: 'dropdown'}}
- .dropdown-toggle-text
- Projects
- %i.fa.fa-chevron-down.dropdown-toggle-caret.js-projects-dropdown-toggle
- .dropdown-menu.dropdown-select.dropdown-menu-selectable
- .dropdown-title
- %span Go to project
- %button.dropdown-title-button.dropdown-menu-close{aria: {label: 'Close'}}
- %i.fa.fa-times.dropdown-menu-close-icon
- .dropdown-input
- %input.dropdown-input-field{type: 'search', placeholder: 'Filter results'}
- %i.fa.fa-search.dropdown-input-search
- .dropdown-content
- .dropdown-loading
- %i.fa.fa-spinner.fa-spin
diff --git a/spec/javascripts/fixtures/gl_field_errors.html.haml b/spec/javascripts/fixtures/gl_field_errors.html.haml
deleted file mode 100644
index 69445b61367..00000000000
--- a/spec/javascripts/fixtures/gl_field_errors.html.haml
+++ /dev/null
@@ -1,15 +0,0 @@
-%form.gl-show-field-errors{action: 'submit', method: 'post'}
- .form-group
- %input.required-text{required: true, type: 'text'} Text
- .form-group
- %input.email{type: 'email', title: 'Please provide a valid email address.', required: true } Email
- .form-group
- %input.password{type: 'password', required: true} Password
- .form-group
- %input.alphanumeric{type: 'text', pattern: '[a-zA-Z0-9]', required: true} Alphanumeric
- .form-group
- %input.hidden{ type:'hidden' }
- .form-group
- %input.custom.gl-field-error-ignore{ type:'text' } Custom, do not validate
- .form-group
- %input.submit{type: 'submit'} Submit
diff --git a/spec/javascripts/fixtures/groups.rb b/spec/javascripts/fixtures/groups.rb
index f8d55fc97c3..16e31028b05 100644
--- a/spec/javascripts/fixtures/groups.rb
+++ b/spec/javascripts/fixtures/groups.rb
@@ -4,7 +4,7 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
let(:admin) { create(:admin) }
- let(:group) { create(:group, name: 'frontend-fixtures-group' )}
+ let(:group) { create(:group, name: 'frontend-fixtures-group', runners_token: 'runnerstoken:intabulasreferre')}
render_views
@@ -18,7 +18,7 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
end
describe GroupsController, '(JavaScript fixtures)', type: :controller do
- it 'groups/edit.html.raw' do |example|
+ it 'groups/edit.html' do |example|
get :edit, params: { id: group }
expect(response).to be_success
@@ -27,7 +27,7 @@ describe 'Groups (JavaScript fixtures)', type: :controller do
end
describe Groups::Settings::CiCdController, '(JavaScript fixtures)', type: :controller do
- it 'groups/ci_cd_settings.html.raw' do |example|
+ it 'groups/ci_cd_settings.html' do |example|
get :show, params: { group_id: group }
expect(response).to be_success
diff --git a/spec/javascripts/fixtures/issuable_filter.html.haml b/spec/javascripts/fixtures/issuable_filter.html.haml
deleted file mode 100644
index 84fa5395cb8..00000000000
--- a/spec/javascripts/fixtures/issuable_filter.html.haml
+++ /dev/null
@@ -1,8 +0,0 @@
-%form.js-filter-form{action: '/user/project/issues?scope=all&state=closed'}
- %input{id: 'utf8', name: 'utf8', value: '✓'}
- %input{id: 'check-all-issues', name: 'check-all-issues'}
- %input{id: 'search', name: 'search'}
- %input{id: 'author_id', name: 'author_id'}
- %input{id: 'assignee_id', name: 'assignee_id'}
- %input{id: 'milestone_title', name: 'milestone_title'}
- %input{id: 'label_name', name: 'label_name'}
diff --git a/spec/javascripts/fixtures/issue_sidebar_label.html.haml b/spec/javascripts/fixtures/issue_sidebar_label.html.haml
deleted file mode 100644
index 06ce248dc9c..00000000000
--- a/spec/javascripts/fixtures/issue_sidebar_label.html.haml
+++ /dev/null
@@ -1,16 +0,0 @@
-.block.labels
- .sidebar-collapsed-icon.js-sidebar-labels-tooltip
- .title.hide-collapsed
- %a.edit-link.float-right{ href: "#" }
- Edit
- .selectbox.hide-collapsed{ style: "display: none;" }
- .dropdown
- %button.dropdown-menu-toggle.js-label-select.js-multiselect{ type: "button", data: { ability_name: "issue", field_name: "issue[label_names][]", issue_update: "/root/test/issues/2.json", labels: "/root/test/labels.json", project_id: "12", show_any: "true", show_no: "true", toggle: "dropdown" } }
- %span.dropdown-toggle-text
- Label
- %i.fa.fa-chevron-down
- .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
- .dropdown-page-one
- .dropdown-content
- .dropdown-loading
- %i.fa.fa-spinner.fa-spin
diff --git a/spec/javascripts/fixtures/issues.rb b/spec/javascripts/fixtures/issues.rb
index 18fb1bebf8b..645b3aa788a 100644
--- a/spec/javascripts/fixtures/issues.rb
+++ b/spec/javascripts/fixtures/issues.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- let(:admin) { create(:admin) }
+ let(:admin) { create(:admin, feed_token: 'feedtoken:coldfeed') }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project_empty_repo, namespace: namespace, path: 'issues-project') }
@@ -21,26 +21,26 @@ describe Projects::IssuesController, '(JavaScript fixtures)', type: :controller
remove_repository(project)
end
- it 'issues/open-issue.html.raw' do |example|
+ it 'issues/open-issue.html' do |example|
render_issue(example.description, create(:issue, project: project))
end
- it 'issues/closed-issue.html.raw' do |example|
+ it 'issues/closed-issue.html' do |example|
render_issue(example.description, create(:closed_issue, project: project))
end
- it 'issues/issue-with-task-list.html.raw' do |example|
+ it 'issues/issue-with-task-list.html' do |example|
issue = create(:issue, project: project, description: '- [ ] Task List Item')
render_issue(example.description, issue)
end
- it 'issues/issue_with_comment.html.raw' do |example|
+ it 'issues/issue_with_comment.html' do |example|
issue = create(:issue, project: project)
create(:note, project: project, noteable: issue, note: '- [ ] Task List Item').save
render_issue(example.description, issue)
end
- it 'issues/issue_list.html.raw' do |example|
+ it 'issues/issue_list.html' do |example|
create(:issue, project: project)
get :index, params: {
diff --git a/spec/javascripts/fixtures/jobs.rb b/spec/javascripts/fixtures/jobs.rb
index 433bb690a1c..941235190b5 100644
--- a/spec/javascripts/fixtures/jobs.rb
+++ b/spec/javascripts/fixtures/jobs.rb
@@ -32,7 +32,7 @@ describe Projects::JobsController, '(JavaScript fixtures)', type: :controller do
remove_repository(project)
end
- it 'builds/build-with-artifacts.html.raw' do |example|
+ it 'builds/build-with-artifacts.html' do |example|
get :show, params: {
namespace_id: project.namespace.to_param,
project_id: project,
diff --git a/spec/javascripts/fixtures/line_highlighter.html.haml b/spec/javascripts/fixtures/line_highlighter.html.haml
deleted file mode 100644
index 2782c50e298..00000000000
--- a/spec/javascripts/fixtures/line_highlighter.html.haml
+++ /dev/null
@@ -1,11 +0,0 @@
-.file-holder
- .file-content
- .line-numbers
- - 1.upto(25) do |i|
- %a{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
- %i.fa.fa-link
- = i
- %pre.code.highlight
- %code
- - 1.upto(25) do |i|
- %span.line{id: "LC#{i}"}= "Line #{i}"
diff --git a/spec/javascripts/fixtures/linked_tabs.html.haml b/spec/javascripts/fixtures/linked_tabs.html.haml
deleted file mode 100644
index 632606e0536..00000000000
--- a/spec/javascripts/fixtures/linked_tabs.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-%ul.nav.nav-tabs.new-session-tabs.linked-tabs
- %li.nav-item
- %a.nav-link{ href: 'foo/bar/1', data: { target: 'div#tab1', action: 'tab1', toggle: 'tab' } }
- Tab 1
- %li.nav-item
- %a.nav-link{ href: 'foo/bar/1/context', data: { target: 'div#tab2', action: 'tab2', toggle: 'tab' } }
- Tab 2
-
-.tab-content
- #tab1.tab-pane
- Tab 1 Content
- #tab2.tab-pane
- Tab 2 Content
diff --git a/spec/javascripts/fixtures/merge_requests.rb b/spec/javascripts/fixtures/merge_requests.rb
index 26e81f06c0b..7df1e5cb512 100644
--- a/spec/javascripts/fixtures/merge_requests.rb
+++ b/spec/javascripts/fixtures/merge_requests.rb
@@ -35,37 +35,40 @@ describe Projects::MergeRequestsController, '(JavaScript fixtures)', type: :cont
before do
sign_in(admin)
+ allow(Discussion).to receive(:build_discussion_id).and_return(['discussionid:ceterumcenseo'])
end
after do
remove_repository(project)
end
- it 'merge_requests/merge_request_of_current_user.html.raw' do |example|
+ it 'merge_requests/merge_request_of_current_user.html' do |example|
merge_request.update(author: admin)
render_merge_request(example.description, merge_request)
end
- it 'merge_requests/merge_request_with_task_list.html.raw' do |example|
+ it 'merge_requests/merge_request_with_task_list.html' do |example|
create(:ci_build, :pending, pipeline: pipeline)
render_merge_request(example.description, merge_request)
end
- it 'merge_requests/merged_merge_request.html.raw' do |example|
- allow_any_instance_of(MergeRequest).to receive(:source_branch_exists?).and_return(true)
- allow_any_instance_of(MergeRequest).to receive(:can_remove_source_branch?).and_return(true)
+ it 'merge_requests/merged_merge_request.html' do |example|
+ expect_next_instance_of(MergeRequest) do |merge_request|
+ allow(merge_request).to receive(:source_branch_exists?).and_return(true)
+ allow(merge_request).to receive(:can_remove_source_branch?).and_return(true)
+ end
render_merge_request(example.description, merged_merge_request)
end
- it 'merge_requests/diff_comment.html.raw' do |example|
+ it 'merge_requests/diff_comment.html' do |example|
create(:diff_note_on_merge_request, project: project, author: admin, position: position, noteable: merge_request)
create(:note_on_merge_request, author: admin, project: project, noteable: merge_request)
render_merge_request(example.description, merge_request)
end
- it 'merge_requests/merge_request_with_comment.html.raw' do |example|
+ it 'merge_requests/merge_request_with_comment.html' do |example|
create(:note_on_merge_request, author: admin, project: project, noteable: merge_request, note: '- [ ] Task List Item')
render_merge_request(example.description, merge_request)
end
diff --git a/spec/javascripts/fixtures/merge_requests_show.html.haml b/spec/javascripts/fixtures/merge_requests_show.html.haml
deleted file mode 100644
index 8447dfdda32..00000000000
--- a/spec/javascripts/fixtures/merge_requests_show.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-%a.btn-close
-
-.detail-page-description
- .description.js-task-list-container
- .wiki
- %ul.task-list
- %li.task-list-item
- %input.task-list-item-checkbox{type: 'checkbox'}
- Task List Item
- %textarea.js-task-list-field
- \- [ ] Task List Item
-
-%form.js-issuable-update{action: '/foo'}
diff --git a/spec/javascripts/fixtures/mini_dropdown_graph.html.haml b/spec/javascripts/fixtures/mini_dropdown_graph.html.haml
deleted file mode 100644
index 74584993739..00000000000
--- a/spec/javascripts/fixtures/mini_dropdown_graph.html.haml
+++ /dev/null
@@ -1,10 +0,0 @@
-%div.js-builds-dropdown-tests.dropdown.dropdown.js-mini-pipeline-graph
- %button.js-builds-dropdown-button{'data-stage-endpoint' => 'foobar', data: { toggle: 'dropdown'} }
- Dropdown
-
- %ul.dropdown-menu.mini-pipeline-graph-dropdown-menu.js-builds-dropdown-container
- %li.js-builds-dropdown-list.scrollable-menu
- %ul
-
- %li.js-builds-dropdown-loading.hidden
- %span.fa.fa-spinner
diff --git a/spec/javascripts/fixtures/notebook_viewer.html.haml b/spec/javascripts/fixtures/notebook_viewer.html.haml
deleted file mode 100644
index 17a7a9d8f31..00000000000
--- a/spec/javascripts/fixtures/notebook_viewer.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-.file-content#js-notebook-viewer{ data: { endpoint: '/test' } }
diff --git a/spec/javascripts/fixtures/oauth_remember_me.html.haml b/spec/javascripts/fixtures/oauth_remember_me.html.haml
deleted file mode 100644
index a5d7c4e816a..00000000000
--- a/spec/javascripts/fixtures/oauth_remember_me.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-#oauth-container
- %input#remember_me{ type: "checkbox" }
-
- %a.oauth-login.twitter{ href: "http://example.com/" }
- %a.oauth-login.github{ href: "http://example.com/" }
- %a.oauth-login.facebook{ href: "http://example.com/?redirect_fragment=L1" }
diff --git a/spec/javascripts/fixtures/pdf_viewer.html.haml b/spec/javascripts/fixtures/pdf_viewer.html.haml
deleted file mode 100644
index 2e57beae54b..00000000000
--- a/spec/javascripts/fixtures/pdf_viewer.html.haml
+++ /dev/null
@@ -1 +0,0 @@
-.file-content#js-pdf-viewer{ data: { endpoint: '/test' } }
diff --git a/spec/javascripts/fixtures/pipeline_graph.html.haml b/spec/javascripts/fixtures/pipeline_graph.html.haml
deleted file mode 100644
index c0b5ab4411e..00000000000
--- a/spec/javascripts/fixtures/pipeline_graph.html.haml
+++ /dev/null
@@ -1,14 +0,0 @@
-%div.pipeline-visualization.js-pipeline-graph
- %ul.stage-column-list
- %li.stage-column
- .stage-name
- %a{:href => "/"}
- Test
- .builds-container
- %ul
- %li.build
- .curve
- %a
- %svg
- .ci-status-text
- stop_review
diff --git a/spec/javascripts/fixtures/pipeline_schedules.rb b/spec/javascripts/fixtures/pipeline_schedules.rb
index 05d79ec8de9..e5176a58273 100644
--- a/spec/javascripts/fixtures/pipeline_schedules.rb
+++ b/spec/javascripts/fixtures/pipeline_schedules.rb
@@ -21,7 +21,7 @@ describe Projects::PipelineSchedulesController, '(JavaScript fixtures)', type: :
sign_in(admin)
end
- it 'pipeline_schedules/edit.html.raw' do |example|
+ it 'pipeline_schedules/edit.html' do |example|
get :edit, params: {
namespace_id: project.namespace.to_param,
project_id: project,
@@ -32,7 +32,7 @@ describe Projects::PipelineSchedulesController, '(JavaScript fixtures)', type: :
store_frontend_fixture(response, example.description)
end
- it 'pipeline_schedules/edit_with_variables.html.raw' do |example|
+ it 'pipeline_schedules/edit_with_variables.html' do |example|
get :edit, params: {
namespace_id: project.namespace.to_param,
project_id: project,
diff --git a/spec/javascripts/fixtures/pipelines.html.haml b/spec/javascripts/fixtures/pipelines.html.haml
deleted file mode 100644
index 0161c0550d1..00000000000
--- a/spec/javascripts/fixtures/pipelines.html.haml
+++ /dev/null
@@ -1,12 +0,0 @@
-%div
- #pipelines-list-vue{ data: { endpoint: 'foo',
- "help-page-path" => 'foo',
- "help-auto-devops-path" => 'foo',
- "empty-state-svg-path" => 'foo',
- "error-state-svg-path" => 'foo',
- "new-pipeline-path" => 'foo',
- "can-create-pipeline" => 'true',
- "has-ci" => 'foo',
- "ci-lint-path" => 'foo',
- "reset-cache-path" => 'foo' } }
-
diff --git a/spec/javascripts/fixtures/project_select_combo_button.html.haml b/spec/javascripts/fixtures/project_select_combo_button.html.haml
deleted file mode 100644
index 432cd5fcc74..00000000000
--- a/spec/javascripts/fixtures/project_select_combo_button.html.haml
+++ /dev/null
@@ -1,6 +0,0 @@
-.project-item-select-holder
- %input.project-item-select{ data: { group_id: '12345' , relative_path: 'issues/new' } }
- %a.new-project-item-link{ data: { label: 'New issue', type: 'issues' }, href: ''}
- %i.fa.fa-spinner.spin
- %a.new-project-item-select-button
- %i.fa.fa-caret-down
diff --git a/spec/javascripts/fixtures/projects.rb b/spec/javascripts/fixtures/projects.rb
index 9b48646f8f0..446da83a7f9 100644
--- a/spec/javascripts/fixtures/projects.rb
+++ b/spec/javascripts/fixtures/projects.rb
@@ -3,13 +3,13 @@ require 'spec_helper'
describe 'Projects (JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
+ runners_token = 'runnerstoken:intabulasreferre'
+
let(:admin) { create(:admin) }
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
- let(:project) { create(:project, namespace: namespace, path: 'builds-project') }
+ let(:project) { create(:project, namespace: namespace, path: 'builds-project', runners_token: runners_token) }
let(:project_with_repo) { create(:project, :repository, description: 'Code and stuff') }
- let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2') }
- let!(:variable1) { create(:ci_variable, project: project_variable_populated) }
- let!(:variable2) { create(:ci_variable, project: project_variable_populated) }
+ let(:project_variable_populated) { create(:project, namespace: namespace, path: 'builds-project2', runners_token: runners_token) }
render_views
@@ -20,6 +20,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
before do
project.add_maintainer(admin)
sign_in(admin)
+ allow(SecureRandom).to receive(:hex).and_return('securerandomhex:thereisnospoon')
end
after do
@@ -27,7 +28,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end
describe ProjectsController, '(JavaScript fixtures)', type: :controller do
- it 'projects/dashboard.html.raw' do |example|
+ it 'projects/dashboard.html' do |example|
get :show, params: {
namespace_id: project.namespace.to_param,
id: project
@@ -37,7 +38,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
store_frontend_fixture(response, example.description)
end
- it 'projects/overview.html.raw' do |example|
+ it 'projects/overview.html' do |example|
get :show, params: {
namespace_id: project_with_repo.namespace.to_param,
id: project_with_repo
@@ -47,7 +48,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
store_frontend_fixture(response, example.description)
end
- it 'projects/edit.html.raw' do |example|
+ it 'projects/edit.html' do |example|
get :edit, params: {
namespace_id: project.namespace.to_param,
id: project
@@ -59,7 +60,7 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
end
describe Projects::Settings::CiCdController, '(JavaScript fixtures)', type: :controller do
- it 'projects/ci_cd_settings.html.raw' do |example|
+ it 'projects/ci_cd_settings.html' do |example|
get :show, params: {
namespace_id: project.namespace.to_param,
project_id: project
@@ -69,7 +70,10 @@ describe 'Projects (JavaScript fixtures)', type: :controller do
store_frontend_fixture(response, example.description)
end
- it 'projects/ci_cd_settings_with_variables.html.raw' do |example|
+ it 'projects/ci_cd_settings_with_variables.html' do |example|
+ create(:ci_variable, project: project_variable_populated)
+ create(:ci_variable, project: project_variable_populated)
+
get :show, params: {
namespace_id: project_variable_populated.namespace.to_param,
project_id: project_variable_populated
diff --git a/spec/javascripts/fixtures/prometheus_service.rb b/spec/javascripts/fixtures/prometheus_service.rb
index 746fbfd66dd..29dc95305b7 100644
--- a/spec/javascripts/fixtures/prometheus_service.rb
+++ b/spec/javascripts/fixtures/prometheus_service.rb
@@ -22,7 +22,7 @@ describe Projects::ServicesController, '(JavaScript fixtures)', type: :controlle
remove_repository(project)
end
- it 'services/prometheus/prometheus_service.html.raw' do |example|
+ it 'services/prometheus/prometheus_service.html' do |example|
get :edit, params: {
namespace_id: namespace,
project_id: project,
diff --git a/spec/javascripts/fixtures/search.rb b/spec/javascripts/fixtures/search.rb
index 703cd3d49fa..5f5b4d4e60d 100644
--- a/spec/javascripts/fixtures/search.rb
+++ b/spec/javascripts/fixtures/search.rb
@@ -9,7 +9,7 @@ describe SearchController, '(JavaScript fixtures)', type: :controller do
clean_frontend_fixtures('search/')
end
- it 'search/show.html.raw' do |example|
+ it 'search/show.html' do |example|
get :show
expect(response).to be_success
diff --git a/spec/javascripts/fixtures/search_autocomplete.html.haml b/spec/javascripts/fixtures/search_autocomplete.html.haml
deleted file mode 100644
index 4aa54da9411..00000000000
--- a/spec/javascripts/fixtures/search_autocomplete.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-.search.search-form
- %form.form-inline
- .search-input-container
- .search-input-wrap
- .dropdown
- %input#search.search-input.dropdown-menu-toggle
- .dropdown-menu.dropdown-select
- .dropdown-content
- %input{ type: "hidden", class: "js-search-project-options" }
diff --git a/spec/javascripts/fixtures/services.rb b/spec/javascripts/fixtures/services.rb
index 6ccd74a07ff..dc7ee484c22 100644
--- a/spec/javascripts/fixtures/services.rb
+++ b/spec/javascripts/fixtures/services.rb
@@ -22,7 +22,7 @@ describe Projects::ServicesController, '(JavaScript fixtures)', type: :controlle
remove_repository(project)
end
- it 'services/edit_service.html.raw' do |example|
+ it 'services/edit_service.html' do |example|
get :edit, params: {
namespace_id: namespace,
project_id: project,
diff --git a/spec/javascripts/fixtures/sessions.rb b/spec/javascripts/fixtures/sessions.rb
index e90a58e8c54..8656dea696a 100644
--- a/spec/javascripts/fixtures/sessions.rb
+++ b/spec/javascripts/fixtures/sessions.rb
@@ -16,7 +16,7 @@ describe 'Sessions (JavaScript fixtures)' do
set_devise_mapping(context: @request)
end
- it 'sessions/new.html.raw' do |example|
+ it 'sessions/new.html' do |example|
get :new
expect(response).to be_success
diff --git a/spec/javascripts/fixtures/signin_tabs.html.haml b/spec/javascripts/fixtures/signin_tabs.html.haml
deleted file mode 100644
index 2e00fe7865e..00000000000
--- a/spec/javascripts/fixtures/signin_tabs.html.haml
+++ /dev/null
@@ -1,5 +0,0 @@
-%ul.nav-links.new-session-tabs
- %li.active
- %a{ href: '#ldap' } LDAP
- %li
- %a{ href: '#login-pane'} Standard
diff --git a/spec/javascripts/fixtures/sketch_viewer.html.haml b/spec/javascripts/fixtures/sketch_viewer.html.haml
deleted file mode 100644
index f01bd00925a..00000000000
--- a/spec/javascripts/fixtures/sketch_viewer.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-.file-content#js-sketch-viewer{ data: { endpoint: '/test_sketch_file.sketch' } }
- .js-loading-icon
diff --git a/spec/javascripts/fixtures/snippet.rb b/spec/javascripts/fixtures/snippet.rb
index a14837e4d4a..ebc5b793166 100644
--- a/spec/javascripts/fixtures/snippet.rb
+++ b/spec/javascripts/fixtures/snippet.rb
@@ -7,7 +7,6 @@ describe SnippetsController, '(JavaScript fixtures)', type: :controller do
let(:namespace) { create(:namespace, name: 'frontend-fixtures' )}
let(:project) { create(:project, :repository, namespace: namespace, path: 'branches-project') }
let(:snippet) { create(:personal_snippet, title: 'snippet.md', content: '# snippet', file_name: 'snippet.md', author: admin) }
- let!(:snippet_note) { create(:discussion_note_on_snippet, noteable: snippet, project: project, author: admin, note: '- [ ] Task List Item') }
render_views
@@ -17,13 +16,16 @@ describe SnippetsController, '(JavaScript fixtures)', type: :controller do
before do
sign_in(admin)
+ allow(Discussion).to receive(:build_discussion_id).and_return(['discussionid:ceterumcenseo'])
end
after do
remove_repository(project)
end
- it 'snippets/show.html.raw' do |example|
+ it 'snippets/show.html' do |example|
+ create(:discussion_note_on_snippet, noteable: snippet, project: project, author: admin, note: '- [ ] Task List Item')
+
get(:show, params: { id: snippet.to_param })
expect(response).to be_success
diff --git a/spec/javascripts/fixtures/static/README.md b/spec/javascripts/fixtures/static/README.md
new file mode 100644
index 00000000000..b5c2f8233bf
--- /dev/null
+++ b/spec/javascripts/fixtures/static/README.md
@@ -0,0 +1,3 @@
+# Please do not add new files here!
+
+Instead use a Ruby file in the fixtures root directory (`spec/javascripts/fixtures/`).
diff --git a/spec/javascripts/fixtures/static/ajax_loading_spinner.html b/spec/javascripts/fixtures/static/ajax_loading_spinner.html
new file mode 100644
index 00000000000..0e1ebb32b1c
--- /dev/null
+++ b/spec/javascripts/fixtures/static/ajax_loading_spinner.html
@@ -0,0 +1,3 @@
+<a class="js-ajax-loading-spinner" data-remote href="http://goesnowhere.nothing/whereami">
+<i class="fa fa-trash-o"></i>
+</a>
diff --git a/spec/javascripts/fixtures/static/balsamiq_viewer.html b/spec/javascripts/fixtures/static/balsamiq_viewer.html
new file mode 100644
index 00000000000..cdd723d1a84
--- /dev/null
+++ b/spec/javascripts/fixtures/static/balsamiq_viewer.html
@@ -0,0 +1 @@
+<div class="file-content balsamiq-viewer" data-endpoint="/test" id="js-balsamiq-viewer"></div>
diff --git a/spec/javascripts/fixtures/static/create_item_dropdown.html b/spec/javascripts/fixtures/static/create_item_dropdown.html
new file mode 100644
index 00000000000..d2d38370092
--- /dev/null
+++ b/spec/javascripts/fixtures/static/create_item_dropdown.html
@@ -0,0 +1,11 @@
+<div class="js-create-item-dropdown-fixture-root">
+<input name="variable[environment]" type="hidden">
+<div class="dropdown "><button class="dropdown-menu-toggle js-dropdown-menu-toggle" type="button" data-toggle="dropdown"><span class="dropdown-toggle-text ">some label</span><i aria-hidden="true" data-hidden="true" class="fa fa-chevron-down"></i></button><div class="dropdown-menu dropdown-select dropdown-menu-selectable"><div class="dropdown-input"><input type="search" id="" class="dropdown-input-field" autocomplete="off" /><i aria-hidden="true" data-hidden="true" class="fa fa-search dropdown-input-search"></i><i aria-hidden="true" data-hidden="true" role="button" class="fa fa-times dropdown-input-clear js-dropdown-input-clear"></i></div><div class="dropdown-content js-dropdown-content"></div><div class="dropdown-footer"><ul class="dropdown-footer-list">
+<li>
+<button class="dropdown-create-new-item-button js-dropdown-create-new-item">
+Create wildcard
+<code></code>
+</button>
+</li>
+</ul>
+</div><div class="dropdown-loading"><i aria-hidden="true" data-hidden="true" class="fa fa-spinner fa-spin"></i></div></div></div></div>
diff --git a/spec/javascripts/fixtures/static/event_filter.html b/spec/javascripts/fixtures/static/event_filter.html
new file mode 100644
index 00000000000..8e9b6fb1b5c
--- /dev/null
+++ b/spec/javascripts/fixtures/static/event_filter.html
@@ -0,0 +1,44 @@
+<ul class="nav-links event-filter scrolling-tabs nav nav-tabs">
+<li class="active">
+<a class="event-filter-link" href="/dashboard/activity" id="all_event_filter" title="Filter by all">
+<span>
+All
+</span>
+</a>
+</li>
+<li>
+<a class="event-filter-link" href="/dashboard/activity" id="push_event_filter" title="Filter by push events">
+<span>
+Push events
+</span>
+</a>
+</li>
+<li>
+<a class="event-filter-link" href="/dashboard/activity" id="merged_event_filter" title="Filter by merge events">
+<span>
+Merge events
+</span>
+</a>
+</li>
+<li>
+<a class="event-filter-link" href="/dashboard/activity" id="issue_event_filter" title="Filter by issue events">
+<span>
+Issue events
+</span>
+</a>
+</li>
+<li>
+<a class="event-filter-link" href="/dashboard/activity" id="comments_event_filter" title="Filter by comments">
+<span>
+Comments
+</span>
+</a>
+</li>
+<li>
+<a class="event-filter-link" href="/dashboard/activity" id="team_event_filter" title="Filter by team">
+<span>
+Team
+</span>
+</a>
+</li>
+</ul>
diff --git a/spec/javascripts/fixtures/static/gl_dropdown.html b/spec/javascripts/fixtures/static/gl_dropdown.html
new file mode 100644
index 00000000000..08f6738414e
--- /dev/null
+++ b/spec/javascripts/fixtures/static/gl_dropdown.html
@@ -0,0 +1,26 @@
+<div>
+<div class="dropdown inline">
+<button class="dropdown-menu-toggle" data-toggle="dropdown" id="js-project-dropdown" type="button">
+<div class="dropdown-toggle-text">
+Projects
+</div>
+<i class="fa fa-chevron-down dropdown-toggle-caret js-projects-dropdown-toggle"></i>
+</button>
+<div class="dropdown-menu dropdown-select dropdown-menu-selectable">
+<div class="dropdown-title">
+<span>Go to project</span>
+<button aria="{:label=&gt;&quot;Close&quot;}" class="dropdown-title-button dropdown-menu-close">
+<i class="fa fa-times dropdown-menu-close-icon"></i>
+</button>
+</div>
+<div class="dropdown-input">
+<input class="dropdown-input-field" placeholder="Filter results" type="search">
+<i class="fa fa-search dropdown-input-search"></i>
+</div>
+<div class="dropdown-content"></div>
+<div class="dropdown-loading">
+<i class="fa fa-spinner fa-spin"></i>
+</div>
+</div>
+</div>
+</div>
diff --git a/spec/javascripts/fixtures/static/gl_field_errors.html b/spec/javascripts/fixtures/static/gl_field_errors.html
new file mode 100644
index 00000000000..f8470e02b7c
--- /dev/null
+++ b/spec/javascripts/fixtures/static/gl_field_errors.html
@@ -0,0 +1,22 @@
+<form action="submit" class="gl-show-field-errors" method="post">
+<div class="form-group">
+<input class="required-text" required type="text">Text</input>
+</div>
+<div class="form-group">
+<input class="email" required title="Please provide a valid email address." type="email">Email</input>
+</div>
+<div class="form-group">
+<input class="password" required type="password">Password</input>
+</div>
+<div class="form-group">
+<input class="alphanumeric" pattern="[a-zA-Z0-9]" required type="text">Alphanumeric</input>
+</div>
+<div class="form-group">
+<input class="hidden" type="hidden">
+</div>
+<div class="form-group">
+<input class="custom gl-field-error-ignore" type="text">Custom, do not validate</input>
+</div>
+<div class="form-group"></div>
+<input class="submit" type="submit">Submit</input>
+</form>
diff --git a/spec/javascripts/fixtures/static/issuable_filter.html b/spec/javascripts/fixtures/static/issuable_filter.html
new file mode 100644
index 00000000000..06b70fb43f1
--- /dev/null
+++ b/spec/javascripts/fixtures/static/issuable_filter.html
@@ -0,0 +1,9 @@
+<form action="/user/project/issues?scope=all&amp;state=closed" class="js-filter-form">
+<input id="utf8" name="utf8" value="✓">
+<input id="check-all-issues" name="check-all-issues">
+<input id="search" name="search">
+<input id="author_id" name="author_id">
+<input id="assignee_id" name="assignee_id">
+<input id="milestone_title" name="milestone_title">
+<input id="label_name" name="label_name">
+</form>
diff --git a/spec/javascripts/fixtures/static/issue_sidebar_label.html b/spec/javascripts/fixtures/static/issue_sidebar_label.html
new file mode 100644
index 00000000000..ec8fb30f219
--- /dev/null
+++ b/spec/javascripts/fixtures/static/issue_sidebar_label.html
@@ -0,0 +1,26 @@
+<div class="block labels">
+<div class="sidebar-collapsed-icon js-sidebar-labels-tooltip"></div>
+<div class="title hide-collapsed">
+<a class="edit-link float-right" href="#">
+Edit
+</a>
+</div>
+<div class="selectbox hide-collapsed" style="display: none;">
+<div class="dropdown">
+<button class="dropdown-menu-toggle js-label-select js-multiselect" data-ability-name="issue" data-field-name="issue[label_names][]" data-issue-update="/root/test/issues/2.json" data-labels="/root/test/labels.json" data-project-id="12" data-show-any="true" data-show-no="true" data-toggle="dropdown" type="button">
+<span class="dropdown-toggle-text">
+Label
+</span>
+<i class="fa fa-chevron-down"></i>
+</button>
+<div class="dropdown-menu dropdown-select dropdown-menu-paging dropdown-menu-labels dropdown-menu-selectable">
+<div class="dropdown-page-one">
+<div class="dropdown-content"></div>
+<div class="dropdown-loading">
+<i class="fa fa-spinner fa-spin"></i>
+</div>
+</div>
+</div>
+</div>
+</div>
+</div>
diff --git a/spec/javascripts/fixtures/static/line_highlighter.html b/spec/javascripts/fixtures/static/line_highlighter.html
new file mode 100644
index 00000000000..897a25d6760
--- /dev/null
+++ b/spec/javascripts/fixtures/static/line_highlighter.html
@@ -0,0 +1,107 @@
+<div class="file-holder">
+<div class="file-content">
+<div class="line-numbers">
+<a data-line-number="1" href="#L1" id="L1">
+<i class="fa fa-link"></i>
+1
+</a>
+<a data-line-number="2" href="#L2" id="L2">
+<i class="fa fa-link"></i>
+2
+</a>
+<a data-line-number="3" href="#L3" id="L3">
+<i class="fa fa-link"></i>
+3
+</a>
+<a data-line-number="4" href="#L4" id="L4">
+<i class="fa fa-link"></i>
+4
+</a>
+<a data-line-number="5" href="#L5" id="L5">
+<i class="fa fa-link"></i>
+5
+</a>
+<a data-line-number="6" href="#L6" id="L6">
+<i class="fa fa-link"></i>
+6
+</a>
+<a data-line-number="7" href="#L7" id="L7">
+<i class="fa fa-link"></i>
+7
+</a>
+<a data-line-number="8" href="#L8" id="L8">
+<i class="fa fa-link"></i>
+8
+</a>
+<a data-line-number="9" href="#L9" id="L9">
+<i class="fa fa-link"></i>
+9
+</a>
+<a data-line-number="10" href="#L10" id="L10">
+<i class="fa fa-link"></i>
+10
+</a>
+<a data-line-number="11" href="#L11" id="L11">
+<i class="fa fa-link"></i>
+11
+</a>
+<a data-line-number="12" href="#L12" id="L12">
+<i class="fa fa-link"></i>
+12
+</a>
+<a data-line-number="13" href="#L13" id="L13">
+<i class="fa fa-link"></i>
+13
+</a>
+<a data-line-number="14" href="#L14" id="L14">
+<i class="fa fa-link"></i>
+14
+</a>
+<a data-line-number="15" href="#L15" id="L15">
+<i class="fa fa-link"></i>
+15
+</a>
+<a data-line-number="16" href="#L16" id="L16">
+<i class="fa fa-link"></i>
+16
+</a>
+<a data-line-number="17" href="#L17" id="L17">
+<i class="fa fa-link"></i>
+17
+</a>
+<a data-line-number="18" href="#L18" id="L18">
+<i class="fa fa-link"></i>
+18
+</a>
+<a data-line-number="19" href="#L19" id="L19">
+<i class="fa fa-link"></i>
+19
+</a>
+<a data-line-number="20" href="#L20" id="L20">
+<i class="fa fa-link"></i>
+20
+</a>
+<a data-line-number="21" href="#L21" id="L21">
+<i class="fa fa-link"></i>
+21
+</a>
+<a data-line-number="22" href="#L22" id="L22">
+<i class="fa fa-link"></i>
+22
+</a>
+<a data-line-number="23" href="#L23" id="L23">
+<i class="fa fa-link"></i>
+23
+</a>
+<a data-line-number="24" href="#L24" id="L24">
+<i class="fa fa-link"></i>
+24
+</a>
+<a data-line-number="25" href="#L25" id="L25">
+<i class="fa fa-link"></i>
+25
+</a>
+</div>
+<pre class="code highlight"><code><span class="line" id="LC1">Line 1</span><span class="line" id="LC2">Line 2</span><span class="line" id="LC3">Line 3</span><span class="line" id="LC4">Line 4</span><span class="line" id="LC5">Line 5</span><span class="line" id="LC6">Line 6</span><span class="line" id="LC7">Line 7</span><span class="line" id="LC8">Line 8</span><span class="line" id="LC9">Line 9</span><span class="line" id="LC10">Line 10</span><span class="line" id="LC11">Line 11</span><span class="line" id="LC12">Line 12</span><span class="line" id="LC13">Line 13</span><span class="line" id="LC14">Line 14</span><span class="line" id="LC15">Line 15</span><span class="line" id="LC16">Line 16</span><span class="line" id="LC17">Line 17</span><span class="line" id="LC18">Line 18</span><span class="line" id="LC19">Line 19</span><span class="line" id="LC20">Line 20</span><span class="line" id="LC21">Line 21</span><span class="line" id="LC22">Line 22</span><span class="line" id="LC23">Line 23</span><span class="line" id="LC24">Line 24</span><span class="line" id="LC25">Line 25</span></code></pre>
+</div>
+</div>
diff --git a/spec/javascripts/fixtures/static/linked_tabs.html b/spec/javascripts/fixtures/static/linked_tabs.html
new file mode 100644
index 00000000000..c25463bf1db
--- /dev/null
+++ b/spec/javascripts/fixtures/static/linked_tabs.html
@@ -0,0 +1,20 @@
+<ul class="nav nav-tabs new-session-tabs linked-tabs">
+<li class="nav-item">
+<a class="nav-link" data-action="tab1" data-target="div#tab1" data-toggle="tab" href="foo/bar/1">
+Tab 1
+</a>
+</li>
+<li class="nav-item">
+<a class="nav-link" data-action="tab2" data-target="div#tab2" data-toggle="tab" href="foo/bar/1/context">
+Tab 2
+</a>
+</li>
+</ul>
+<div class="tab-content">
+<div class="tab-pane" id="tab1">
+Tab 1 Content
+</div>
+<div class="tab-pane" id="tab2">
+Tab 2 Content
+</div>
+</div>
diff --git a/spec/javascripts/fixtures/static/merge_requests_show.html b/spec/javascripts/fixtures/static/merge_requests_show.html
new file mode 100644
index 00000000000..87e36c9f315
--- /dev/null
+++ b/spec/javascripts/fixtures/static/merge_requests_show.html
@@ -0,0 +1,15 @@
+<a class="btn-close"></a>
+<div class="detail-page-description">
+<div class="description js-task-list-container">
+<div class="md">
+<ul class="task-list">
+<li class="task-list-item">
+<input class="task-list-item-checkbox" type="checkbox">
+Task List Item
+</li>
+</ul>
+<textarea class="js-task-list-field">- [ ] Task List Item</textarea>
+</div>
+</div>
+</div>
+<form action="/foo" class="js-issuable-update"></form>
diff --git a/spec/javascripts/fixtures/static/mini_dropdown_graph.html b/spec/javascripts/fixtures/static/mini_dropdown_graph.html
new file mode 100644
index 00000000000..cd0b8dec3fc
--- /dev/null
+++ b/spec/javascripts/fixtures/static/mini_dropdown_graph.html
@@ -0,0 +1,13 @@
+<div class="js-builds-dropdown-tests dropdown dropdown js-mini-pipeline-graph">
+<button class="js-builds-dropdown-button" data-toggle="dropdown" data-stage-endpoint="foobar">
+Dropdown
+</button>
+<ul class="dropdown-menu mini-pipeline-graph-dropdown-menu js-builds-dropdown-container">
+<li class="js-builds-dropdown-list scrollable-menu">
+<ul></ul>
+</li>
+<li class="js-builds-dropdown-loading hidden">
+<span class="fa fa-spinner"></span>
+</li>
+</ul>
+</div>
diff --git a/spec/javascripts/fixtures/static/notebook_viewer.html b/spec/javascripts/fixtures/static/notebook_viewer.html
new file mode 100644
index 00000000000..4bbb7bf1094
--- /dev/null
+++ b/spec/javascripts/fixtures/static/notebook_viewer.html
@@ -0,0 +1 @@
+<div class="file-content" data-endpoint="/test" id="js-notebook-viewer"></div>
diff --git a/spec/javascripts/fixtures/static/oauth_remember_me.html b/spec/javascripts/fixtures/static/oauth_remember_me.html
new file mode 100644
index 00000000000..9ba1ffc72fe
--- /dev/null
+++ b/spec/javascripts/fixtures/static/oauth_remember_me.html
@@ -0,0 +1,6 @@
+<div id="oauth-container">
+<input id="remember_me" type="checkbox">
+<a class="oauth-login twitter" href="http://example.com/"></a>
+<a class="oauth-login github" href="http://example.com/"></a>
+<a class="oauth-login facebook" href="http://example.com/?redirect_fragment=L1"></a>
+</div>
diff --git a/spec/javascripts/fixtures/static/pdf_viewer.html b/spec/javascripts/fixtures/static/pdf_viewer.html
new file mode 100644
index 00000000000..350d35a262f
--- /dev/null
+++ b/spec/javascripts/fixtures/static/pdf_viewer.html
@@ -0,0 +1 @@
+<div class="file-content" data-endpoint="/test" id="js-pdf-viewer"></div>
diff --git a/spec/javascripts/fixtures/static/pipeline_graph.html b/spec/javascripts/fixtures/static/pipeline_graph.html
new file mode 100644
index 00000000000..422372bb7d5
--- /dev/null
+++ b/spec/javascripts/fixtures/static/pipeline_graph.html
@@ -0,0 +1,24 @@
+<div class="pipeline-visualization js-pipeline-graph">
+<ul class="stage-column-list">
+<li class="stage-column">
+<div class="stage-name">
+<a href="/">
+Test
+<div class="builds-container">
+<ul>
+<li class="build">
+<div class="curve"></div>
+<a>
+<svg></svg>
+<div class="ci-status-text">
+stop_review
+</div>
+</a>
+</li>
+</ul>
+</div>
+</a>
+</div>
+</li>
+</ul>
+</div>
diff --git a/spec/javascripts/fixtures/static/pipelines.html b/spec/javascripts/fixtures/static/pipelines.html
new file mode 100644
index 00000000000..42333f94f2f
--- /dev/null
+++ b/spec/javascripts/fixtures/static/pipelines.html
@@ -0,0 +1,3 @@
+<div>
+<div data-can-create-pipeline="true" data-ci-lint-path="foo" data-empty-state-svg-path="foo" data-endpoint="foo" data-error-state-svg-path="foo" data-has-ci="foo" data-help-auto-devops-path="foo" data-help-page-path="foo" data-new-pipeline-path="foo" data-reset-cache-path="foo" id="pipelines-list-vue"></div>
+</div>
diff --git a/spec/javascripts/fixtures/static/project_select_combo_button.html b/spec/javascripts/fixtures/static/project_select_combo_button.html
new file mode 100644
index 00000000000..50c826051c0
--- /dev/null
+++ b/spec/javascripts/fixtures/static/project_select_combo_button.html
@@ -0,0 +1,9 @@
+<div class="project-item-select-holder">
+<input class="project-item-select" data-group-id="12345" data-relative-path="issues/new">
+<a class="new-project-item-link" data-label="New issue" data-type="issues" href="">
+<i class="fa fa-spinner spin"></i>
+</a>
+<a class="new-project-item-select-button">
+<i class="fa fa-caret-down"></i>
+</a>
+</div>
diff --git a/spec/javascripts/fixtures/static/search_autocomplete.html b/spec/javascripts/fixtures/static/search_autocomplete.html
new file mode 100644
index 00000000000..29db9020424
--- /dev/null
+++ b/spec/javascripts/fixtures/static/search_autocomplete.html
@@ -0,0 +1,15 @@
+<div class="search search-form">
+<form class="form-inline">
+<div class="search-input-container">
+<div class="search-input-wrap">
+<div class="dropdown">
+<input class="search-input dropdown-menu-toggle" id="search">
+<div class="dropdown-menu dropdown-select">
+<div class="dropdown-content"></div>
+</div>
+</div>
+</div>
+</div>
+<input class="js-search-project-options" type="hidden">
+</form>
+</div>
diff --git a/spec/javascripts/fixtures/static/signin_tabs.html b/spec/javascripts/fixtures/static/signin_tabs.html
new file mode 100644
index 00000000000..7e66ab9394b
--- /dev/null
+++ b/spec/javascripts/fixtures/static/signin_tabs.html
@@ -0,0 +1,8 @@
+<ul class="nav-links new-session-tabs">
+<li class="active">
+<a href="#ldap">LDAP</a>
+</li>
+<li>
+<a href="#login-pane">Standard</a>
+</li>
+</ul>
diff --git a/spec/javascripts/fixtures/static/sketch_viewer.html b/spec/javascripts/fixtures/static/sketch_viewer.html
new file mode 100644
index 00000000000..e25e554e568
--- /dev/null
+++ b/spec/javascripts/fixtures/static/sketch_viewer.html
@@ -0,0 +1,3 @@
+<div class="file-content" data-endpoint="/test_sketch_file.sketch" id="js-sketch-viewer">
+<div class="js-loading-icon"></div>
+</div>
diff --git a/spec/javascripts/fixtures/static_fixtures.rb b/spec/javascripts/fixtures/static_fixtures.rb
index 4569f16f0ca..cb4b90cdca5 100644
--- a/spec/javascripts/fixtures/static_fixtures.rb
+++ b/spec/javascripts/fixtures/static_fixtures.rb
@@ -3,29 +3,17 @@ require 'spec_helper'
describe ApplicationController, '(Static JavaScript fixtures)', type: :controller do
include JavaScriptFixturesHelpers
- before(:all) do
- clean_frontend_fixtures('static/')
- end
-
- fixtures_path = File.expand_path(JavaScriptFixturesHelpers::FIXTURE_PATH, Rails.root)
- haml_fixtures = Dir.glob(File.expand_path('**/*.haml', fixtures_path)).map do |file_path|
- file_path.sub(/\A#{fixtures_path}#{File::SEPARATOR}/, '')
- end
-
- haml_fixtures.each do |template_file_name|
- it "static/#{template_file_name.sub(/\.haml\z/, '.raw')}" do |example|
- fixture_file_name = example.description
- rendered = render_template(template_file_name)
- store_frontend_fixture(rendered, fixture_file_name)
+ Dir.glob('{,ee/}spec/javascripts/fixtures/**/*.haml').map do |file_path|
+ it "static/#{file_path.sub(%r{\A(ee/)?spec/javascripts/fixtures/}, '').sub(/\.haml\z/, '')}" do |example|
+ store_frontend_fixture(render_template(file_path), example.description)
end
end
private
def render_template(template_file_name)
- fixture_path = JavaScriptFixturesHelpers::FIXTURE_PATH
controller = ApplicationController.new
- controller.prepend_view_path(fixture_path)
- controller.render_to_string(template: template_file_name, layout: false)
+ controller.prepend_view_path(File.dirname(template_file_name))
+ controller.render_to_string(template: File.basename(template_file_name), layout: false)
end
end
diff --git a/spec/javascripts/fixtures/todos.rb b/spec/javascripts/fixtures/todos.rb
index b5f6620873b..6e37a2e5a4c 100644
--- a/spec/javascripts/fixtures/todos.rb
+++ b/spec/javascripts/fixtures/todos.rb
@@ -26,7 +26,7 @@ describe 'Todos (JavaScript fixtures)' do
sign_in(admin)
end
- it 'todos/todos.html.raw' do |example|
+ it 'todos/todos.html' do |example|
get :index
expect(response).to be_success
diff --git a/spec/javascripts/fixtures/u2f.rb b/spec/javascripts/fixtures/u2f.rb
index f0aa874bf75..15866d65a4f 100644
--- a/spec/javascripts/fixtures/u2f.rb
+++ b/spec/javascripts/fixtures/u2f.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
context 'U2F' do
include JavaScriptFixturesHelpers
- let(:user) { create(:user, :two_factor_via_u2f) }
+ let(:user) { create(:user, :two_factor_via_u2f, otp_secret: 'otpsecret:coolkids') }
before(:all) do
clean_frontend_fixtures('u2f/')
@@ -18,7 +18,7 @@ context 'U2F' do
set_devise_mapping(context: @request)
end
- it 'u2f/authenticate.html.raw' do |example|
+ it 'u2f/authenticate.html' do |example|
allow(controller).to receive(:find_user).and_return(user)
post :create, params: { user: { login: user.username, password: user.password } }
@@ -33,9 +33,10 @@ context 'U2F' do
before do
sign_in(user)
+ allow_any_instance_of(Profiles::TwoFactorAuthsController).to receive(:build_qr_code).and_return('qrcode:blackandwhitesquares')
end
- it 'u2f/register.html.raw' do |example|
+ it 'u2f/register.html' do |example|
get :show
expect(response).to be_success
diff --git a/spec/javascripts/frequent_items/components/app_spec.js b/spec/javascripts/frequent_items/components/app_spec.js
index b1cc4d8dc8d..6814f656f5d 100644
--- a/spec/javascripts/frequent_items/components/app_spec.js
+++ b/spec/javascripts/frequent_items/components/app_spec.js
@@ -194,7 +194,7 @@ describe('Frequent Items App Component', () => {
expect(loadingEl).toBeDefined();
expect(loadingEl.classList.contains('prepend-top-20')).toBe(true);
- expect(loadingEl.querySelector('i').getAttribute('aria-label')).toBe('Loading projects');
+ expect(loadingEl.querySelector('span').getAttribute('aria-label')).toBe('Loading projects');
done();
});
});
diff --git a/spec/javascripts/gfm_auto_complete_spec.js b/spec/javascripts/gfm_auto_complete_spec.js
deleted file mode 100644
index a14031f43ed..00000000000
--- a/spec/javascripts/gfm_auto_complete_spec.js
+++ /dev/null
@@ -1,244 +0,0 @@
-/* eslint no-param-reassign: "off" */
-
-import $ from 'jquery';
-import GfmAutoComplete from '~/gfm_auto_complete';
-
-import 'vendor/jquery.caret';
-import 'vendor/jquery.atwho';
-
-describe('GfmAutoComplete', function() {
- const gfmAutoCompleteCallbacks = GfmAutoComplete.prototype.getDefaultCallbacks.call({
- fetchData: () => {},
- });
-
- describe('DefaultOptions.sorter', function() {
- describe('assets loading', function() {
- beforeEach(function() {
- spyOn(GfmAutoComplete, 'isLoading').and.returnValue(true);
-
- this.atwhoInstance = { setting: {} };
- this.items = [];
-
- this.sorterValue = gfmAutoCompleteCallbacks.sorter.call(this.atwhoInstance, '', this.items);
- });
-
- it('should disable highlightFirst', function() {
- expect(this.atwhoInstance.setting.highlightFirst).toBe(false);
- });
-
- it('should return the passed unfiltered items', function() {
- expect(this.sorterValue).toEqual(this.items);
- });
- });
-
- describe('assets finished loading', function() {
- beforeEach(function() {
- spyOn(GfmAutoComplete, 'isLoading').and.returnValue(false);
- spyOn($.fn.atwho.default.callbacks, 'sorter');
- });
-
- it('should enable highlightFirst if alwaysHighlightFirst is set', function() {
- const atwhoInstance = { setting: { alwaysHighlightFirst: true } };
-
- gfmAutoCompleteCallbacks.sorter.call(atwhoInstance);
-
- expect(atwhoInstance.setting.highlightFirst).toBe(true);
- });
-
- it('should enable highlightFirst if a query is present', function() {
- const atwhoInstance = { setting: {} };
-
- gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, 'query');
-
- expect(atwhoInstance.setting.highlightFirst).toBe(true);
- });
-
- it('should call the default atwho sorter', function() {
- const atwhoInstance = { setting: {} };
-
- const query = 'query';
- const items = [];
- const searchKey = 'searchKey';
-
- gfmAutoCompleteCallbacks.sorter.call(atwhoInstance, query, items, searchKey);
-
- expect($.fn.atwho.default.callbacks.sorter).toHaveBeenCalledWith(query, items, searchKey);
- });
- });
- });
-
- describe('DefaultOptions.beforeInsert', () => {
- const beforeInsert = (context, value) =>
- gfmAutoCompleteCallbacks.beforeInsert.call(context, value);
-
- const atwhoInstance = { setting: { skipSpecialCharacterTest: false } };
-
- it('should not quote if value only contains alphanumeric charecters', () => {
- expect(beforeInsert(atwhoInstance, '@user1')).toBe('@user1');
- expect(beforeInsert(atwhoInstance, '~label1')).toBe('~label1');
- });
-
- it('should quote if value contains any non-alphanumeric characters', () => {
- expect(beforeInsert(atwhoInstance, '~label-20')).toBe('~"label\\-20"');
- expect(beforeInsert(atwhoInstance, '~label 20')).toBe('~"label 20"');
- });
-
- it('should quote integer labels', () => {
- expect(beforeInsert(atwhoInstance, '~1234')).toBe('~"1234"');
- });
-
- it('should escape Markdown emphasis characters, except in the first character', () => {
- expect(beforeInsert(atwhoInstance, '@_group')).toEqual('@\\_group');
- expect(beforeInsert(atwhoInstance, '~_bug')).toEqual('~\\_bug');
- expect(beforeInsert(atwhoInstance, '~a `bug`')).toEqual('~"a \\`bug\\`"');
- expect(beforeInsert(atwhoInstance, '~a ~bug')).toEqual('~"a \\~bug"');
- expect(beforeInsert(atwhoInstance, '~a **bug')).toEqual('~"a \\*\\*bug"');
- });
- });
-
- describe('DefaultOptions.matcher', function() {
- const defaultMatcher = (context, flag, subtext) =>
- gfmAutoCompleteCallbacks.matcher.call(context, flag, subtext);
-
- const flagsUseDefaultMatcher = ['@', '#', '!', '~', '%', '$'];
- const otherFlags = ['/', ':'];
- const flags = flagsUseDefaultMatcher.concat(otherFlags);
-
- const flagsHash = flags.reduce((hash, el) => {
- hash[el] = null;
- return hash;
- }, {});
- const atwhoInstance = { setting: {}, app: { controllers: flagsHash } };
-
- const minLen = 1;
- const maxLen = 20;
- const argumentSize = [minLen, maxLen / 2, maxLen];
-
- const allowedSymbols = [
- '',
- 'a',
- 'n',
- 'z',
- 'A',
- 'Z',
- 'N',
- '0',
- '5',
- '9',
- 'Ð',
- 'а',
- 'Я',
- 'Ñ',
- '.',
- "'",
- '+',
- '-',
- '_',
- ];
- const jointAllowedSymbols = allowedSymbols.join('');
-
- describe('should match regular symbols', () => {
- flagsUseDefaultMatcher.forEach(flag => {
- allowedSymbols.forEach(symbol => {
- argumentSize.forEach(size => {
- const query = new Array(size + 1).join(symbol);
- const subtext = flag + query;
-
- it(`matches argument "${flag}" with query "${subtext}"`, () => {
- expect(defaultMatcher(atwhoInstance, flag, subtext)).toBe(query);
- });
- });
- });
-
- it(`matches combination of allowed symbols for flag "${flag}"`, () => {
- const subtext = flag + jointAllowedSymbols;
-
- expect(defaultMatcher(atwhoInstance, flag, subtext)).toBe(jointAllowedSymbols);
- });
- });
- });
-
- describe('should not match special sequences', () => {
- const shouldNotBeFollowedBy = flags.concat(['\x00', '\x10', '\x3f', '\n', ' ']);
- const shouldNotBePrependedBy = ['`'];
-
- flagsUseDefaultMatcher.forEach(atSign => {
- shouldNotBeFollowedBy.forEach(followedSymbol => {
- const seq = atSign + followedSymbol;
-
- it(`should not match ${JSON.stringify(seq)}`, () => {
- expect(defaultMatcher(atwhoInstance, atSign, seq)).toBe(null);
- });
- });
-
- shouldNotBePrependedBy.forEach(prependedSymbol => {
- const seq = prependedSymbol + atSign;
-
- it(`should not match "${seq}"`, () => {
- expect(defaultMatcher(atwhoInstance, atSign, seq)).toBe(null);
- });
- });
- });
- });
- });
-
- describe('isLoading', function() {
- it('should be true with loading data object item', function() {
- expect(GfmAutoComplete.isLoading({ name: 'loading' })).toBe(true);
- });
-
- it('should be true with loading data array', function() {
- expect(GfmAutoComplete.isLoading(['loading'])).toBe(true);
- });
-
- it('should be true with loading data object array', function() {
- expect(GfmAutoComplete.isLoading([{ name: 'loading' }])).toBe(true);
- });
-
- it('should be false with actual array data', function() {
- expect(
- GfmAutoComplete.isLoading([{ title: 'Foo' }, { title: 'Bar' }, { title: 'Qux' }]),
- ).toBe(false);
- });
-
- it('should be false with actual data item', function() {
- expect(GfmAutoComplete.isLoading({ title: 'Foo' })).toBe(false);
- });
- });
-
- describe('Issues.insertTemplateFunction', function() {
- it('should return default template', function() {
- expect(GfmAutoComplete.Issues.insertTemplateFunction({ id: 5, title: 'Some Issue' })).toBe(
- '${atwho-at}${id}', // eslint-disable-line no-template-curly-in-string
- );
- });
-
- it('should return reference when reference is set', function() {
- expect(
- GfmAutoComplete.Issues.insertTemplateFunction({
- id: 5,
- title: 'Some Issue',
- reference: 'grp/proj#5',
- }),
- ).toBe('grp/proj#5');
- });
- });
-
- describe('Issues.templateFunction', function() {
- it('should return html with id and title', function() {
- expect(GfmAutoComplete.Issues.templateFunction({ id: 5, title: 'Some Issue' })).toBe(
- '<li><small>5</small> Some Issue</li>',
- );
- });
-
- it('should replace id with reference if reference is set', function() {
- expect(
- GfmAutoComplete.Issues.templateFunction({
- id: 5,
- title: 'Some Issue',
- reference: 'grp/proj#5',
- }),
- ).toBe('<li><small>grp/proj#5</small> Some Issue</li>');
- });
- });
-});
diff --git a/spec/javascripts/gl_dropdown_spec.js b/spec/javascripts/gl_dropdown_spec.js
index 85083653db8..57e31d933ca 100644
--- a/spec/javascripts/gl_dropdown_spec.js
+++ b/spec/javascripts/gl_dropdown_spec.js
@@ -5,7 +5,7 @@ import GLDropdown from '~/gl_dropdown';
import '~/lib/utils/common_utils';
describe('glDropdown', function describeDropdown() {
- preloadFixtures('static/gl_dropdown.html.raw');
+ preloadFixtures('static/gl_dropdown.html');
loadJSONFixtures('projects.json');
const NON_SELECTABLE_CLASSES =
@@ -64,7 +64,7 @@ describe('glDropdown', function describeDropdown() {
}
beforeEach(() => {
- loadFixtures('static/gl_dropdown.html.raw');
+ loadFixtures('static/gl_dropdown.html');
this.dropdownContainerElement = $('.dropdown.inline');
this.$dropdownMenuElement = $('.dropdown-menu', this.dropdownContainerElement);
this.projectsData = getJSONFixture('projects.json');
diff --git a/spec/javascripts/gl_field_errors_spec.js b/spec/javascripts/gl_field_errors_spec.js
index b463c9afbee..294f219d6fe 100644
--- a/spec/javascripts/gl_field_errors_spec.js
+++ b/spec/javascripts/gl_field_errors_spec.js
@@ -4,10 +4,10 @@ import $ from 'jquery';
import GlFieldErrors from '~/gl_field_errors';
describe('GL Style Field Errors', function() {
- preloadFixtures('static/gl_field_errors.html.raw');
+ preloadFixtures('static/gl_field_errors.html');
beforeEach(function() {
- loadFixtures('static/gl_field_errors.html.raw');
+ loadFixtures('static/gl_field_errors.html');
const $form = $('form.gl-show-field-errors');
this.$form = $form;
diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js
index d832441dc93..31873311e16 100644
--- a/spec/javascripts/groups/components/app_spec.js
+++ b/spec/javascripts/groups/components/app_spec.js
@@ -502,7 +502,7 @@ describe('AppComponent', () => {
vm.isLoading = true;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.loading-animation')).toBeDefined();
- expect(vm.$el.querySelector('i.fa').getAttribute('aria-label')).toBe('Loading groups');
+ expect(vm.$el.querySelector('span').getAttribute('aria-label')).toBe('Loading groups');
done();
});
});
diff --git a/spec/javascripts/header_spec.js b/spec/javascripts/header_spec.js
index 2fe34e5a76f..0ddf589f368 100644
--- a/spec/javascripts/header_spec.js
+++ b/spec/javascripts/header_spec.js
@@ -3,7 +3,7 @@ import initTodoToggle from '~/header';
describe('Header', function() {
const todosPendingCount = '.todos-count';
- const fixtureTemplate = 'issues/open-issue.html.raw';
+ const fixtureTemplate = 'issues/open-issue.html';
function isTodosCountHidden() {
return $(todosPendingCount).hasClass('hidden');
diff --git a/spec/javascripts/helpers/filtered_search_spec_helper.js b/spec/javascripts/helpers/filtered_search_spec_helper.js
index 8933dd5def4..fd06bb1f324 100644
--- a/spec/javascripts/helpers/filtered_search_spec_helper.js
+++ b/spec/javascripts/helpers/filtered_search_spec_helper.js
@@ -5,7 +5,7 @@ export default class FilteredSearchSpecHelper {
static createFilterVisualToken(name, value, isSelected = false) {
const li = document.createElement('li');
- li.classList.add('js-visual-token', 'filtered-search-token');
+ li.classList.add('js-visual-token', 'filtered-search-token', `search-token-${name}`);
li.innerHTML = `
<div class="selectable ${isSelected ? 'selected' : ''}" role="button">
diff --git a/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js b/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js
index ffc2a4c9ddb..db1988be3e1 100644
--- a/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js
+++ b/spec/javascripts/ide/components/commit_sidebar/radio_group_spec.js
@@ -76,6 +76,7 @@ describe('IDE commit sidebar radio group', () => {
const Component = Vue.extend(radioGroup);
store.state.commit.commitAction = '1';
+ store.state.commit.newBranchName = 'test-123';
vm = createComponentWithStore(Component, store, {
value: '1',
@@ -113,6 +114,12 @@ describe('IDE commit sidebar radio group', () => {
done();
});
});
+
+ it('renders newBranchName if present', () => {
+ const input = vm.$el.querySelector('.form-control');
+
+ expect(input.value).toBe('test-123');
+ });
});
describe('tooltipTitle', () => {
diff --git a/spec/javascripts/ide/components/new_dropdown/modal_spec.js b/spec/javascripts/ide/components/new_dropdown/modal_spec.js
index d94cc1a8faa..0556feae46a 100644
--- a/spec/javascripts/ide/components/new_dropdown/modal_spec.js
+++ b/spec/javascripts/ide/components/new_dropdown/modal_spec.js
@@ -18,6 +18,9 @@ describe('new file modal component', () => {
store.state.entryModal = {
type,
path: '',
+ entry: {
+ path: '',
+ },
};
vm = createComponentWithStore(Component, store).$mount();
@@ -74,6 +77,7 @@ describe('new file modal component', () => {
entry: {
name: 'test',
type: 'blob',
+ path: 'test-path',
},
};
@@ -97,7 +101,7 @@ describe('new file modal component', () => {
describe('entryName', () => {
it('returns entries name', () => {
- expect(vm.entryName).toBe('test');
+ expect(vm.entryName).toBe('test-path');
});
it('updated name', () => {
@@ -105,6 +109,67 @@ describe('new file modal component', () => {
expect(vm.entryName).toBe('index.js');
});
+
+ it('removes leading/trailing spaces when found in the new name', () => {
+ vm.entryName = ' index.js ';
+
+ expect(vm.entryName).toBe('index.js');
+ });
+
+ it('does not remove internal spaces in the file name', () => {
+ vm.entryName = ' In Praise of Idleness.txt ';
+
+ expect(vm.entryName).toBe('In Praise of Idleness.txt');
+ });
+ });
+ });
+
+ describe('submitForm', () => {
+ it('throws an error when target entry exists', () => {
+ const store = createStore();
+ store.state.entryModal = {
+ type: 'rename',
+ path: 'test-path/test',
+ entry: {
+ name: 'test',
+ type: 'blob',
+ path: 'test-path/test',
+ },
+ };
+ store.state.entries = {
+ 'test-path/test': {
+ name: 'test',
+ deleted: false,
+ },
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
+ const flashSpy = spyOnDependency(modal, 'flash');
+ vm.submitForm();
+
+ expect(flashSpy).toHaveBeenCalled();
+ });
+
+ it('calls createTempEntry when target path does not exist', () => {
+ const store = createStore();
+ store.state.entryModal = {
+ type: 'rename',
+ path: 'test-path/test',
+ entry: {
+ name: 'test',
+ type: 'blob',
+ path: 'test-path1/test',
+ },
+ };
+
+ vm = createComponentWithStore(Component, store).$mount();
+ spyOn(vm, 'createTempEntry').and.callFake(() => Promise.resolve());
+ vm.submitForm();
+
+ expect(vm.createTempEntry).toHaveBeenCalledWith({
+ name: 'test-path1',
+ type: 'tree',
+ });
});
});
});
diff --git a/spec/javascripts/ide/stores/actions/merge_request_spec.js b/spec/javascripts/ide/stores/actions/merge_request_spec.js
index 9bfc7c397b8..a5839630657 100644
--- a/spec/javascripts/ide/stores/actions/merge_request_spec.js
+++ b/spec/javascripts/ide/stores/actions/merge_request_spec.js
@@ -2,6 +2,7 @@ import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import store from '~/ide/stores';
import actions, {
+ getMergeRequestsForBranch,
getMergeRequestData,
getMergeRequestChanges,
getMergeRequestVersions,
@@ -27,6 +28,98 @@ describe('IDE store merge request actions', () => {
resetStore(store);
});
+ describe('getMergeRequestsForBranch', () => {
+ describe('success', () => {
+ const mrData = { iid: 2, source_branch: 'bar' };
+ const mockData = [mrData];
+
+ describe('base case', () => {
+ beforeEach(() => {
+ spyOn(service, 'getProjectMergeRequests').and.callThrough();
+ mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).reply(200, mockData);
+ });
+
+ it('calls getProjectMergeRequests service method', done => {
+ store
+ .dispatch('getMergeRequestsForBranch', { projectId: 'abcproject', branchId: 'bar' })
+ .then(() => {
+ expect(service.getProjectMergeRequests).toHaveBeenCalledWith('abcproject', {
+ source_branch: 'bar',
+ order_by: 'created_at',
+ per_page: 1,
+ });
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('sets the "Merge Request" Object', done => {
+ store
+ .dispatch('getMergeRequestsForBranch', { projectId: 'abcproject', branchId: 'bar' })
+ .then(() => {
+ expect(Object.keys(store.state.projects.abcproject.mergeRequests).length).toEqual(1);
+ expect(Object.keys(store.state.projects.abcproject.mergeRequests)[0]).toEqual('2');
+ expect(store.state.projects.abcproject.mergeRequests[2]).toEqual(
+ jasmine.objectContaining(mrData),
+ );
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('sets "Current Merge Request" object to the most recent MR', done => {
+ store
+ .dispatch('getMergeRequestsForBranch', { projectId: 'abcproject', branchId: 'bar' })
+ .then(() => {
+ expect(store.state.currentMergeRequestId).toEqual('2');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('no merge requests for branch available case', () => {
+ beforeEach(() => {
+ spyOn(service, 'getProjectMergeRequests').and.callThrough();
+ mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).reply(200, []);
+ });
+
+ it('does not fail if there are no merge requests for current branch', done => {
+ store
+ .dispatch('getMergeRequestsForBranch', { projectId: 'abcproject', branchId: 'foo' })
+ .then(() => {
+ expect(Object.keys(store.state.projects.abcproject.mergeRequests).length).toEqual(0);
+ expect(store.state.currentMergeRequestId).toEqual('');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(/api\/(.*)\/projects\/abcproject\/merge_requests/).networkError();
+ });
+
+ it('flashes message, if error', done => {
+ const flashSpy = spyOnDependency(actions, 'flash');
+
+ getMergeRequestsForBranch({ commit() {} }, { projectId: 'abcproject', branchId: 'bar' })
+ .then(() => {
+ fail('Expected getMergeRequestsForBranch to throw an error');
+ })
+ .catch(() => {
+ expect(flashSpy).toHaveBeenCalled();
+ expect(flashSpy.calls.argsFor(0)[0]).toEqual('Error fetching merge requests for bar');
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
describe('getMergeRequestData', () => {
describe('success', () => {
beforeEach(() => {
diff --git a/spec/javascripts/ide/stores/actions/project_spec.js b/spec/javascripts/ide/stores/actions/project_spec.js
index 7d8c9edd965..7b0963713fb 100644
--- a/spec/javascripts/ide/stores/actions/project_spec.js
+++ b/spec/javascripts/ide/stores/actions/project_spec.js
@@ -249,6 +249,7 @@ describe('IDE store project actions', () => {
['setCurrentBranchId', branch.branchId],
['getBranchData', branch],
['getFiles', branch],
+ ['getMergeRequestsForBranch', branch],
]);
})
.then(done)
diff --git a/spec/javascripts/ide/stores/actions/tree_spec.js b/spec/javascripts/ide/stores/actions/tree_spec.js
index bd41e87bf0e..fbb676aab33 100644
--- a/spec/javascripts/ide/stores/actions/tree_spec.js
+++ b/spec/javascripts/ide/stores/actions/tree_spec.js
@@ -20,6 +20,7 @@ describe('Multi-file store tree actions', () => {
};
beforeEach(() => {
+ jasmine.clock().install();
spyOn(router, 'push');
mock = new MockAdapter(axios);
@@ -37,6 +38,7 @@ describe('Multi-file store tree actions', () => {
});
afterEach(() => {
+ jasmine.clock().uninstall();
mock.restore();
resetStore(store);
});
@@ -70,6 +72,11 @@ describe('Multi-file store tree actions', () => {
store
.dispatch('getFiles', basicCallParameters)
.then(() => {
+ // The populating of the tree is deferred for performance reasons.
+ // See this merge request for details: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/25700
+ jasmine.clock().tick(1);
+ })
+ .then(() => {
projectTree = store.state.trees['abcproject/master'];
expect(projectTree.tree.length).toBe(2);
diff --git a/spec/javascripts/ide/stores/actions_spec.js b/spec/javascripts/ide/stores/actions_spec.js
index df291ade3f7..0b5587d02ae 100644
--- a/spec/javascripts/ide/stores/actions_spec.js
+++ b/spec/javascripts/ide/stores/actions_spec.js
@@ -499,12 +499,12 @@ describe('Multi-file store actions', () => {
testAction(
renameEntry,
- { path: 'test', name: 'new-name' },
+ { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
store.state,
[
{
type: types.RENAME_ENTRY,
- payload: { path: 'test', name: 'new-name', entryPath: null },
+ payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
},
],
[{ type: 'deleteEntry', payload: 'test' }],
@@ -527,17 +527,33 @@ describe('Multi-file store actions', () => {
testAction(
renameEntry,
- { path: 'test', name: 'new-name' },
+ { path: 'test', name: 'new-name', parentPath: 'parent-path' },
store.state,
[
{
type: types.RENAME_ENTRY,
- payload: { path: 'test', name: 'new-name', entryPath: null },
+ payload: { path: 'test', name: 'new-name', entryPath: null, parentPath: 'parent-path' },
},
],
[
- { type: 'renameEntry', payload: { path: 'test', name: 'new-name', entryPath: 'tree-1' } },
- { type: 'renameEntry', payload: { path: 'test', name: 'new-name', entryPath: 'tree-2' } },
+ {
+ type: 'renameEntry',
+ payload: {
+ path: 'test',
+ name: 'new-name',
+ entryPath: 'tree-1',
+ parentPath: 'parent-path/new-name',
+ },
+ },
+ {
+ type: 'renameEntry',
+ payload: {
+ path: 'test',
+ name: 'new-name',
+ entryPath: 'tree-2',
+ parentPath: 'parent-path/new-name',
+ },
+ },
{ type: 'deleteEntry', payload: 'test' },
],
done,
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index 06b8b452319..34d97347438 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -396,7 +396,7 @@ describe('IDE commit module actions', () => {
.then(() => {
expect(visitUrl).toHaveBeenCalledWith(
`webUrl/merge_requests/new?merge_request[source_branch]=${
- store.getters['commit/newBranchName']
+ store.getters['commit/placeholderBranchName']
}&merge_request[target_branch]=master`,
);
diff --git a/spec/javascripts/ide/stores/modules/commit/getters_spec.js b/spec/javascripts/ide/stores/modules/commit/getters_spec.js
index 3f4bf407a1f..702e78ef773 100644
--- a/spec/javascripts/ide/stores/modules/commit/getters_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/getters_spec.js
@@ -29,11 +29,11 @@ describe('IDE commit module getters', () => {
});
});
- describe('newBranchName', () => {
+ describe('placeholderBranchName', () => {
it('includes username, currentBranchId, patch & random number', () => {
gon.current_username = 'username';
- const branch = getters.newBranchName(state, null, {
+ const branch = getters.placeholderBranchName(state, null, {
currentBranchId: 'testing',
});
@@ -46,7 +46,7 @@ describe('IDE commit module getters', () => {
currentBranchId: 'master',
};
const localGetters = {
- newBranchName: 'newBranchName',
+ placeholderBranchName: 'newBranchName',
};
beforeEach(() => {
@@ -71,7 +71,7 @@ describe('IDE commit module getters', () => {
expect(getters.branchName(state, localGetters, rootState)).toBe('state-newBranchName');
});
- it('uses getters newBranchName when state newBranchName is empty', () => {
+ it('uses placeholderBranchName when state newBranchName is empty', () => {
Object.assign(state, {
newBranchName: '',
});
diff --git a/spec/javascripts/ide/stores/mutations_spec.js b/spec/javascripts/ide/stores/mutations_spec.js
index 41dd3d3c67f..5ee098bf17f 100644
--- a/spec/javascripts/ide/stores/mutations_spec.js
+++ b/spec/javascripts/ide/stores/mutations_spec.js
@@ -298,7 +298,12 @@ describe('Multi-file store mutations', () => {
});
it('creates new renamed entry', () => {
- mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
+ mutations.RENAME_ENTRY(localState, {
+ path: 'oldPath',
+ name: 'newPath',
+ entryPath: null,
+ parentPath: '',
+ });
expect(localState.entries.newPath).toEqual({
...localState.entries.oldPath,
@@ -335,7 +340,12 @@ describe('Multi-file store mutations', () => {
...file(),
};
- mutations.RENAME_ENTRY(localState, { path: 'oldPath', name: 'newPath' });
+ mutations.RENAME_ENTRY(localState, {
+ path: 'oldPath',
+ name: 'newPath',
+ entryPath: null,
+ parentPath: 'parentPath',
+ });
expect(localState.entries.parentPath.tree.length).toBe(1);
});
diff --git a/spec/javascripts/import_projects/components/import_projects_table_spec.js b/spec/javascripts/import_projects/components/import_projects_table_spec.js
index a1ff84ce259..ab8642bf0dd 100644
--- a/spec/javascripts/import_projects/components/import_projects_table_spec.js
+++ b/spec/javascripts/import_projects/components/import_projects_table_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import store from '~/import_projects/store';
+import createStore from '~/import_projects/store';
import importProjectsTable from '~/import_projects/components/import_projects_table.vue';
import STATUS_MAP from '~/import_projects/constants';
import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
@@ -9,6 +9,7 @@ import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
describe('ImportProjectsTable', () => {
let vm;
let mock;
+ let store;
const reposPath = '/repos-path';
const jobsPath = '/jobs-path';
const providerTitle = 'THE PROVIDER';
@@ -31,12 +32,13 @@ describe('ImportProjectsTable', () => {
},
}).$mount();
- component.$store.dispatch('stopJobsPolling');
+ store.dispatch('stopJobsPolling');
return component;
}
beforeEach(() => {
+ store = createStore();
store.dispatch('setInitialData', { reposPath });
mock = new MockAdapter(axios);
});
@@ -167,7 +169,7 @@ describe('ImportProjectsTable', () => {
expect(vm.$el.querySelector(`.ic-status_${statusObject.icon}`)).not.toBeNull();
mock.onGet(jobsPath).replyOnce(200, updatedProjects);
- return vm.$store.dispatch('restartJobsPolling');
+ return store.dispatch('restartJobsPolling');
})
.then(() => setTimeoutPromise())
.then(() => {
diff --git a/spec/javascripts/import_projects/components/imported_project_table_row_spec.js b/spec/javascripts/import_projects/components/imported_project_table_row_spec.js
index 8af3b5954a9..7dac7e9ccc1 100644
--- a/spec/javascripts/import_projects/components/imported_project_table_row_spec.js
+++ b/spec/javascripts/import_projects/components/imported_project_table_row_spec.js
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import store from '~/import_projects/store';
+import createStore from '~/import_projects/store';
import importedProjectTableRow from '~/import_projects/components/imported_project_table_row.vue';
import STATUS_MAP from '~/import_projects/constants';
@@ -16,6 +16,7 @@ describe('ImportedProjectTableRow', () => {
function createComponent() {
const ImportedProjectTableRow = Vue.extend(importedProjectTableRow);
+ const store = createStore();
return new ImportedProjectTableRow({
store,
propsData: {
diff --git a/spec/javascripts/import_projects/components/provider_repo_table_row_spec.js b/spec/javascripts/import_projects/components/provider_repo_table_row_spec.js
index 7191fc923ce..4d2bacd2ad0 100644
--- a/spec/javascripts/import_projects/components/provider_repo_table_row_spec.js
+++ b/spec/javascripts/import_projects/components/provider_repo_table_row_spec.js
@@ -1,12 +1,13 @@
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
-import store from '~/import_projects/store';
+import createStore from '~/import_projects/store';
import providerRepoTableRow from '~/import_projects/components/provider_repo_table_row.vue';
import STATUS_MAP, { STATUSES } from '~/import_projects/constants';
import setTimeoutPromise from '../../helpers/set_timeout_promise_helper';
describe('ProviderRepoTableRow', () => {
+ let store;
let vm;
const repo = {
id: 10,
@@ -28,6 +29,10 @@ describe('ProviderRepoTableRow', () => {
}).$mount();
}
+ beforeEach(() => {
+ store = createStore();
+ });
+
afterEach(() => {
vm.$destroy();
});
@@ -49,6 +54,19 @@ describe('ProviderRepoTableRow', () => {
expect(vm.$el.querySelector('.js-import-button')).not.toBeNull();
});
+ it('renders a select2 namespace select', () => {
+ vm = createComponent();
+
+ const dropdownTrigger = vm.$el.querySelector('.js-namespace-select');
+
+ expect(dropdownTrigger).not.toBeNull();
+ expect(dropdownTrigger.classList.contains('select2-container')).toBe(true);
+
+ dropdownTrigger.click();
+
+ expect(vm.$el.querySelector('.select2-drop')).not.toBeNull();
+ });
+
it('imports repo when clicking import button', done => {
const importPath = '/import-path';
const defaultTargetNamespace = 'user';
diff --git a/spec/javascripts/integrations/integration_settings_form_spec.js b/spec/javascripts/integrations/integration_settings_form_spec.js
index 4f4c9a7b463..069e2cb07b5 100644
--- a/spec/javascripts/integrations/integration_settings_form_spec.js
+++ b/spec/javascripts/integrations/integration_settings_form_spec.js
@@ -4,7 +4,7 @@ import axios from '~/lib/utils/axios_utils';
import IntegrationSettingsForm from '~/integrations/integration_settings_form';
describe('IntegrationSettingsForm', () => {
- const FIXTURE = 'services/edit_service.html.raw';
+ const FIXTURE = 'services/edit_service.html';
preloadFixtures(FIXTURE);
beforeEach(() => {
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 0ccf771c7ef..dfc889773c1 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -75,7 +75,7 @@ describe('Issuable output', () => {
.then(() => {
expect(document.querySelector('title').innerText).toContain('this is a title (#1)');
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>this is a title</p>');
- expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>this is a description!</p>');
+ expect(vm.$el.querySelector('.md').innerHTML).toContain('<p>this is a description!</p>');
expect(vm.$el.querySelector('.js-task-list-field').value).toContain(
'this is a description',
);
@@ -92,7 +92,7 @@ describe('Issuable output', () => {
.then(() => {
expect(document.querySelector('title').innerText).toContain('2 (#1)');
expect(vm.$el.querySelector('.title').innerHTML).toContain('<p>2</p>');
- expect(vm.$el.querySelector('.wiki').innerHTML).toContain('<p>42</p>');
+ expect(vm.$el.querySelector('.md').innerHTML).toContain('<p>42</p>');
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(
diff --git a/spec/javascripts/issue_show/components/description_spec.js b/spec/javascripts/issue_show/components/description_spec.js
index 2eeed6770be..7e00fbf2745 100644
--- a/spec/javascripts/issue_show/components/description_spec.js
+++ b/spec/javascripts/issue_show/components/description_spec.js
@@ -43,12 +43,12 @@ describe('Description component', () => {
Vue.nextTick(() => {
expect(
- vm.$el.querySelector('.wiki').classList.contains('issue-realtime-pre-pulse'),
+ vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'),
).toBeTruthy();
setTimeout(() => {
expect(
- vm.$el.querySelector('.wiki').classList.contains('issue-realtime-trigger-pulse'),
+ vm.$el.querySelector('.md').classList.contains('issue-realtime-trigger-pulse'),
).toBeTruthy();
done();
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index 7be495d1d35..11ab6c38a55 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -9,9 +9,9 @@ import '~/lib/utils/text_utility';
describe('Issue', function() {
let $boxClosed, $boxOpen, $btn;
- preloadFixtures('issues/closed-issue.html.raw');
- preloadFixtures('issues/issue-with-task-list.html.raw');
- preloadFixtures('issues/open-issue.html.raw');
+ preloadFixtures('issues/closed-issue.html');
+ preloadFixtures('issues/issue-with-task-list.html');
+ preloadFixtures('issues/open-issue.html');
function expectErrorMessage() {
const $flashMessage = $('div.flash-alert');
@@ -105,9 +105,9 @@ describe('Issue', function() {
beforeEach(function() {
if (isIssueInitiallyOpen) {
- loadFixtures('issues/open-issue.html.raw');
+ loadFixtures('issues/open-issue.html');
} else {
- loadFixtures('issues/closed-issue.html.raw');
+ loadFixtures('issues/closed-issue.html');
}
mock = new MockAdapter(axios);
diff --git a/spec/javascripts/jobs/components/commit_block_spec.js b/spec/javascripts/jobs/components/commit_block_spec.js
index 98eba3ac976..c02f564d01a 100644
--- a/spec/javascripts/jobs/components/commit_block_spec.js
+++ b/spec/javascripts/jobs/components/commit_block_spec.js
@@ -9,6 +9,7 @@ describe('Commit block', () => {
const props = {
commit: {
short_id: '1f0fb84f',
+ id: '1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
commit_path: 'commit/1f0fb84fb6770d74d97eee58118fd3909cd4f48c',
title: 'Update README.md',
},
@@ -42,7 +43,7 @@ describe('Commit block', () => {
it('renders clipboard button', () => {
expect(vm.$el.querySelector('button').getAttribute('data-clipboard-text')).toEqual(
- props.commit.short_id,
+ props.commit.id,
);
});
});
diff --git a/spec/javascripts/jobs/components/stages_dropdown_spec.js b/spec/javascripts/jobs/components/stages_dropdown_spec.js
index 9c731ae2f68..eccb4e13d67 100644
--- a/spec/javascripts/jobs/components/stages_dropdown_spec.js
+++ b/spec/javascripts/jobs/components/stages_dropdown_spec.js
@@ -1,59 +1,167 @@
import Vue from 'vue';
import component from '~/jobs/components/stages_dropdown.vue';
+import { trimText } from 'spec/helpers/vue_component_helper';
import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Stages Dropdown', () => {
const Component = Vue.extend(component);
let vm;
- beforeEach(() => {
- vm = mountComponent(Component, {
- pipeline: {
- id: 28029444,
- details: {
- status: {
- details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
- group: 'success',
- has_details: true,
- icon: 'status_success',
- label: 'passed',
- text: 'passed',
- tooltip: 'passed',
- },
- },
- path: 'pipeline/28029444',
+ const mockPipelineData = {
+ id: 28029444,
+ details: {
+ status: {
+ details_path: '/gitlab-org/gitlab-ce/pipelines/28029444',
+ group: 'success',
+ has_details: true,
+ icon: 'status_success',
+ label: 'passed',
+ text: 'passed',
+ tooltip: 'passed',
},
- stages: [
- {
- name: 'build',
- },
- {
- name: 'test',
- },
- ],
- selectedStage: 'deploy',
+ },
+ path: 'pipeline/28029444',
+ flags: {
+ merge_request_pipeline: true,
+ detached_merge_request_pipeline: false,
+ },
+ merge_request: {
+ iid: 1234,
+ path: '/root/detached-merge-request-pipelines/merge_requests/1',
+ title: 'Update README.md',
+ source_branch: 'feature-1234',
+ source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1234',
+ target_branch: 'master',
+ target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
+ },
+ ref: {
+ name: 'test-branch',
+ },
+ };
+
+ describe('without a merge request pipeline', () => {
+ let pipeline;
+
+ beforeEach(() => {
+ pipeline = JSON.parse(JSON.stringify(mockPipelineData));
+ delete pipeline.merge_request;
+ delete pipeline.flags.merge_request_pipeline;
+ delete pipeline.flags.detached_merge_request_pipeline;
+
+ vm = mountComponent(Component, {
+ pipeline,
+ stages: [{ name: 'build' }, { name: 'test' }],
+ selectedStage: 'deploy',
+ });
});
- });
- afterEach(() => {
- vm.$destroy();
- });
+ afterEach(() => {
+ vm.$destroy();
+ });
- it('renders pipeline status', () => {
- expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull();
- });
+ it('renders pipeline status', () => {
+ expect(vm.$el.querySelector('.js-ci-status-icon-success')).not.toBeNull();
+ });
+
+ it('renders pipeline link', () => {
+ expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual(
+ 'pipeline/28029444',
+ );
+ });
+
+ it('renders dropdown with stages', () => {
+ expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build');
+ });
+
+ it('rendes selected stage', () => {
+ expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy');
+ });
- it('renders pipeline link', () => {
- expect(vm.$el.querySelector('.js-pipeline-path').getAttribute('href')).toEqual(
- 'pipeline/28029444',
- );
+ it(`renders the pipeline info text like "Pipeline #123 for source_branch"`, () => {
+ const expected = `Pipeline #${pipeline.id} for ${pipeline.ref.name}`;
+ const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
+
+ expect(actual).toBe(expected);
+ });
});
- it('renders dropdown with stages', () => {
- expect(vm.$el.querySelector('.dropdown .js-stage-item').textContent).toContain('build');
+ describe('with an "attached" merge request pipeline', () => {
+ let pipeline;
+
+ beforeEach(() => {
+ pipeline = JSON.parse(JSON.stringify(mockPipelineData));
+ pipeline.flags.merge_request_pipeline = true;
+ pipeline.flags.detached_merge_request_pipeline = false;
+
+ vm = mountComponent(Component, {
+ pipeline,
+ stages: [],
+ selectedStage: 'deploy',
+ });
+ });
+
+ it(`renders the pipeline info text like "Pipeline #123 for !456 with source_branch into target_branch"`, () => {
+ const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${
+ pipeline.merge_request.source_branch
+ } into ${pipeline.merge_request.target_branch}`;
+ const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
+
+ expect(actual).toBe(expected);
+ });
+
+ it(`renders the correct merge request link`, () => {
+ const actual = vm.$el.querySelector('.js-mr-link').href;
+
+ expect(actual).toContain(pipeline.merge_request.path);
+ });
+
+ it(`renders the correct source branch link`, () => {
+ const actual = vm.$el.querySelector('.js-source-branch-link').href;
+
+ expect(actual).toContain(pipeline.merge_request.source_branch_path);
+ });
+
+ it(`renders the correct target branch link`, () => {
+ const actual = vm.$el.querySelector('.js-target-branch-link').href;
+
+ expect(actual).toContain(pipeline.merge_request.target_branch_path);
+ });
});
- it('rendes selected stage', () => {
- expect(vm.$el.querySelector('.dropdown .js-selected-stage').textContent).toContain('deploy');
+ describe('with a detached merge request pipeline', () => {
+ let pipeline;
+
+ beforeEach(() => {
+ pipeline = JSON.parse(JSON.stringify(mockPipelineData));
+ pipeline.flags.merge_request_pipeline = false;
+ pipeline.flags.detached_merge_request_pipeline = true;
+
+ vm = mountComponent(Component, {
+ pipeline,
+ stages: [],
+ selectedStage: 'deploy',
+ });
+ });
+
+ it(`renders the pipeline info like "Pipeline #123 for !456 with source_branch"`, () => {
+ const expected = `Pipeline #${pipeline.id} for !${pipeline.merge_request.iid} with ${
+ pipeline.merge_request.source_branch
+ }`;
+ const actual = trimText(vm.$el.querySelector('.js-pipeline-info').innerText);
+
+ expect(actual).toBe(expected);
+ });
+
+ it(`renders the correct merge request link`, () => {
+ const actual = vm.$el.querySelector('.js-mr-link').href;
+
+ expect(actual).toContain(pipeline.merge_request.path);
+ });
+
+ it(`renders the correct source branch link`, () => {
+ const actual = vm.$el.querySelector('.js-source-branch-link').href;
+
+ expect(actual).toContain(pipeline.merge_request.source_branch_path);
+ });
});
});
diff --git a/spec/javascripts/jobs/store/getters_spec.js b/spec/javascripts/jobs/store/getters_spec.js
deleted file mode 100644
index 7931b2af79f..00000000000
--- a/spec/javascripts/jobs/store/getters_spec.js
+++ /dev/null
@@ -1,188 +0,0 @@
-import * as getters from '~/jobs/store/getters';
-import state from '~/jobs/store/state';
-
-describe('Job Store Getters', () => {
- let localState;
-
- beforeEach(() => {
- localState = state();
- });
-
- describe('headerTime', () => {
- describe('when the job has started key', () => {
- it('returns started key', () => {
- const started = '2018-08-31T16:20:49.023Z';
- localState.job.started = started;
-
- expect(getters.headerTime(localState)).toEqual(started);
- });
- });
-
- describe('when the job does not have started key', () => {
- it('returns created_at key', () => {
- const created = '2018-08-31T16:20:49.023Z';
- localState.job.created_at = created;
-
- expect(getters.headerTime(localState)).toEqual(created);
- });
- });
- });
-
- describe('shouldRenderCalloutMessage', () => {
- describe('with status and callout message', () => {
- it('returns true', () => {
- localState.job.callout_message = 'Callout message';
- localState.job.status = { icon: 'passed' };
-
- expect(getters.shouldRenderCalloutMessage(localState)).toEqual(true);
- });
- });
-
- describe('without status & with callout message', () => {
- it('returns false', () => {
- localState.job.callout_message = 'Callout message';
-
- expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false);
- });
- });
-
- describe('with status & without callout message', () => {
- it('returns false', () => {
- localState.job.status = { icon: 'passed' };
-
- expect(getters.shouldRenderCalloutMessage(localState)).toEqual(false);
- });
- });
- });
-
- describe('shouldRenderTriggeredLabel', () => {
- describe('when started equals null', () => {
- it('returns false', () => {
- localState.job.started = null;
-
- expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(false);
- });
- });
-
- describe('when started equals string', () => {
- it('returns true', () => {
- localState.job.started = '2018-08-31T16:20:49.023Z';
-
- expect(getters.shouldRenderTriggeredLabel(localState)).toEqual(true);
- });
- });
- });
-
- describe('hasEnvironment', () => {
- describe('without `deployment_status`', () => {
- it('returns false', () => {
- expect(getters.hasEnvironment(localState)).toEqual(false);
- });
- });
-
- describe('with an empty object for `deployment_status`', () => {
- it('returns false', () => {
- localState.job.deployment_status = {};
-
- expect(getters.hasEnvironment(localState)).toEqual(false);
- });
- });
-
- describe('when `deployment_status` is defined and not empty', () => {
- it('returns true', () => {
- localState.job.deployment_status = {
- status: 'creating',
- environment: {
- last_deployment: {},
- },
- };
-
- expect(getters.hasEnvironment(localState)).toEqual(true);
- });
- });
- });
-
- describe('hasTrace', () => {
- describe('when has_trace is true', () => {
- it('returns true', () => {
- localState.job.has_trace = true;
- localState.job.status = {};
-
- expect(getters.hasTrace(localState)).toEqual(true);
- });
- });
-
- describe('when job is running', () => {
- it('returns true', () => {
- localState.job.has_trace = false;
- localState.job.status = { group: 'running' };
-
- expect(getters.hasTrace(localState)).toEqual(true);
- });
- });
-
- describe('when has_trace is false and job is not running', () => {
- it('returns false', () => {
- localState.job.has_trace = false;
- localState.job.status = { group: 'pending' };
-
- expect(getters.hasTrace(localState)).toEqual(false);
- });
- });
- });
-
- describe('emptyStateIllustration', () => {
- describe('with defined illustration', () => {
- it('returns the state illustration object', () => {
- localState.job.status = {
- illustration: {
- path: 'foo',
- },
- };
-
- expect(getters.emptyStateIllustration(localState)).toEqual({ path: 'foo' });
- });
- });
-
- describe('when illustration is not defined', () => {
- it('returns an empty object', () => {
- expect(getters.emptyStateIllustration(localState)).toEqual({});
- });
- });
- });
-
- describe('hasRunnersForProject', () => {
- describe('with available and offline runners', () => {
- it('returns true', () => {
- localState.job.runners = {
- available: true,
- online: false,
- };
-
- expect(getters.hasRunnersForProject(localState)).toEqual(true);
- });
- });
-
- describe('with non available runners', () => {
- it('returns false', () => {
- localState.job.runners = {
- available: false,
- online: false,
- };
-
- expect(getters.hasRunnersForProject(localState)).toEqual(false);
- });
- });
-
- describe('with online runners', () => {
- it('returns false', () => {
- localState.job.runners = {
- available: false,
- online: true,
- };
-
- expect(getters.hasRunnersForProject(localState)).toEqual(false);
- });
- });
- });
-});
diff --git a/spec/javascripts/labels_issue_sidebar_spec.js b/spec/javascripts/labels_issue_sidebar_spec.js
index e5678ee5379..ccf439aac74 100644
--- a/spec/javascripts/labels_issue_sidebar_spec.js
+++ b/spec/javascripts/labels_issue_sidebar_spec.js
@@ -16,10 +16,10 @@ let saveLabelCount = 0;
let mock;
describe('Issue dropdown sidebar', () => {
- preloadFixtures('static/issue_sidebar_label.html.raw');
+ preloadFixtures('static/issue_sidebar_label.html');
beforeEach(() => {
- loadFixtures('static/issue_sidebar_label.html.raw');
+ loadFixtures('static/issue_sidebar_label.html');
mock = new MockAdapter(axios);
diff --git a/spec/javascripts/lazy_loader_spec.js b/spec/javascripts/lazy_loader_spec.js
index cbdc1644430..f3fb792c62d 100644
--- a/spec/javascripts/lazy_loader_spec.js
+++ b/spec/javascripts/lazy_loader_spec.js
@@ -11,11 +11,11 @@ const execImmediately = callback => {
describe('LazyLoader', function() {
let lazyLoader = null;
- preloadFixtures('issues/issue_with_comment.html.raw');
+ preloadFixtures('issues/issue_with_comment.html');
describe('without IntersectionObserver', () => {
beforeEach(function() {
- loadFixtures('issues/issue_with_comment.html.raw');
+ loadFixtures('issues/issue_with_comment.html');
lazyLoader = new LazyLoader({
observerNode: 'foobar',
@@ -131,7 +131,7 @@ describe('LazyLoader', function() {
describe('with IntersectionObserver', () => {
beforeEach(function() {
- loadFixtures('issues/issue_with_comment.html.raw');
+ loadFixtures('issues/issue_with_comment.html');
lazyLoader = new LazyLoader({
observerNode: 'foobar',
diff --git a/spec/javascripts/lib/utils/ajax_cache_spec.js b/spec/javascripts/lib/utils/ajax_cache_spec.js
deleted file mode 100644
index dc0b04173bf..00000000000
--- a/spec/javascripts/lib/utils/ajax_cache_spec.js
+++ /dev/null
@@ -1,184 +0,0 @@
-import MockAdapter from 'axios-mock-adapter';
-import axios from '~/lib/utils/axios_utils';
-import AjaxCache from '~/lib/utils/ajax_cache';
-
-describe('AjaxCache', () => {
- const dummyEndpoint = '/AjaxCache/dummyEndpoint';
- const dummyResponse = {
- important: 'dummy data',
- };
-
- beforeEach(() => {
- AjaxCache.internalStorage = {};
- AjaxCache.pendingRequests = {};
- });
-
- describe('get', () => {
- it('returns undefined if cache is empty', () => {
- const data = AjaxCache.get(dummyEndpoint);
-
- expect(data).toBe(undefined);
- });
-
- it('returns undefined if cache contains no matching data', () => {
- AjaxCache.internalStorage['not matching'] = dummyResponse;
-
- const data = AjaxCache.get(dummyEndpoint);
-
- expect(data).toBe(undefined);
- });
-
- it('returns matching data', () => {
- AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
-
- const data = AjaxCache.get(dummyEndpoint);
-
- expect(data).toBe(dummyResponse);
- });
- });
-
- describe('hasData', () => {
- it('returns false if cache is empty', () => {
- expect(AjaxCache.hasData(dummyEndpoint)).toBe(false);
- });
-
- it('returns false if cache contains no matching data', () => {
- AjaxCache.internalStorage['not matching'] = dummyResponse;
-
- expect(AjaxCache.hasData(dummyEndpoint)).toBe(false);
- });
-
- it('returns true if data is available', () => {
- AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
-
- expect(AjaxCache.hasData(dummyEndpoint)).toBe(true);
- });
- });
-
- describe('remove', () => {
- it('does nothing if cache is empty', () => {
- AjaxCache.remove(dummyEndpoint);
-
- expect(AjaxCache.internalStorage).toEqual({});
- });
-
- it('does nothing if cache contains no matching data', () => {
- AjaxCache.internalStorage['not matching'] = dummyResponse;
-
- AjaxCache.remove(dummyEndpoint);
-
- expect(AjaxCache.internalStorage['not matching']).toBe(dummyResponse);
- });
-
- it('removes matching data', () => {
- AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
-
- AjaxCache.remove(dummyEndpoint);
-
- expect(AjaxCache.internalStorage).toEqual({});
- });
- });
-
- describe('override', () => {
- it('overrides existing cache', () => {
- AjaxCache.internalStorage.endpoint = 'existing-endpoint';
- AjaxCache.override('endpoint', 'new-endpoint');
-
- expect(AjaxCache.internalStorage.endpoint).toEqual('new-endpoint');
- });
- });
-
- describe('retrieve', () => {
- let mock;
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
-
- spyOn(axios, 'get').and.callThrough();
- });
-
- afterEach(() => {
- mock.restore();
- });
-
- it('stores and returns data from Ajax call if cache is empty', done => {
- mock.onGet(dummyEndpoint).reply(200, dummyResponse);
-
- AjaxCache.retrieve(dummyEndpoint)
- .then(data => {
- expect(data).toEqual(dummyResponse);
- expect(AjaxCache.internalStorage[dummyEndpoint]).toEqual(dummyResponse);
- })
- .then(done)
- .catch(fail);
- });
-
- it('makes no Ajax call if request is pending', done => {
- mock.onGet(dummyEndpoint).reply(200, dummyResponse);
-
- AjaxCache.retrieve(dummyEndpoint)
- .then(done)
- .catch(fail);
-
- AjaxCache.retrieve(dummyEndpoint)
- .then(done)
- .catch(fail);
-
- expect(axios.get.calls.count()).toBe(1);
- });
-
- it('returns undefined if Ajax call fails and cache is empty', done => {
- const errorMessage = 'Network Error';
- mock.onGet(dummyEndpoint).networkError();
-
- AjaxCache.retrieve(dummyEndpoint)
- .then(data => fail(`Received unexpected data: ${JSON.stringify(data)}`))
- .catch(error => {
- expect(error.message).toBe(`${dummyEndpoint}: ${errorMessage}`);
- expect(error.textStatus).toBe(errorMessage);
- done();
- })
- .catch(fail);
- });
-
- it('makes no Ajax call if matching data exists', done => {
- AjaxCache.internalStorage[dummyEndpoint] = dummyResponse;
- mock.onGet(dummyEndpoint).reply(() => {
- fail(new Error('expected no Ajax call!'));
- });
-
- AjaxCache.retrieve(dummyEndpoint)
- .then(data => {
- expect(data).toBe(dummyResponse);
- })
- .then(done)
- .catch(fail);
- });
-
- it('makes Ajax call even if matching data exists when forceRequest parameter is provided', done => {
- const oldDummyResponse = {
- important: 'old dummy data',
- };
-
- AjaxCache.internalStorage[dummyEndpoint] = oldDummyResponse;
-
- mock.onGet(dummyEndpoint).reply(200, dummyResponse);
-
- // Call without forceRetrieve param
- AjaxCache.retrieve(dummyEndpoint)
- .then(data => {
- expect(data).toBe(oldDummyResponse);
- })
- .then(done)
- .catch(fail);
-
- // Call with forceRetrieve param
- AjaxCache.retrieve(dummyEndpoint, true)
- .then(data => {
- expect(data).toEqual(dummyResponse);
- })
- .then(done)
- .catch(fail);
- });
- });
-});
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index c02e37950f8..0bb43c94f6a 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -409,13 +409,6 @@ describe('common_utils', () => {
});
});
- describe('convertPermissionToBoolean', () => {
- it('should convert a boolean in a string to a boolean', () => {
- expect(commonUtils.convertPermissionToBoolean('true')).toEqual(true);
- expect(commonUtils.convertPermissionToBoolean('false')).toEqual(false);
- });
- });
-
describe('backOff', () => {
beforeEach(() => {
// shortcut our timeouts otherwise these tests will take a long time to finish
diff --git a/spec/javascripts/lib/utils/number_utility_spec.js b/spec/javascripts/lib/utils/number_utility_spec.js
deleted file mode 100644
index 94c6214c86a..00000000000
--- a/spec/javascripts/lib/utils/number_utility_spec.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import {
- formatRelevantDigits,
- bytesToKiB,
- bytesToMiB,
- bytesToGiB,
- numberToHumanSize,
-} from '~/lib/utils/number_utils';
-
-describe('Number Utils', () => {
- describe('formatRelevantDigits', () => {
- it('returns an empty string when the number is NaN', () => {
- expect(formatRelevantDigits('fail')).toBe('');
- });
-
- it('returns 4 decimals when there is 4 plus digits to the left', () => {
- const formattedNumber = formatRelevantDigits('1000.1234567');
- const rightFromDecimal = formattedNumber.split('.')[1];
- const leftFromDecimal = formattedNumber.split('.')[0];
-
- expect(rightFromDecimal.length).toBe(4);
- expect(leftFromDecimal.length).toBe(4);
- });
-
- it('returns 3 decimals when there is 1 digit to the left', () => {
- const formattedNumber = formatRelevantDigits('0.1234567');
- const rightFromDecimal = formattedNumber.split('.')[1];
- const leftFromDecimal = formattedNumber.split('.')[0];
-
- expect(rightFromDecimal.length).toBe(3);
- expect(leftFromDecimal.length).toBe(1);
- });
-
- it('returns 2 decimals when there is 2 digits to the left', () => {
- const formattedNumber = formatRelevantDigits('10.1234567');
- const rightFromDecimal = formattedNumber.split('.')[1];
- const leftFromDecimal = formattedNumber.split('.')[0];
-
- expect(rightFromDecimal.length).toBe(2);
- expect(leftFromDecimal.length).toBe(2);
- });
-
- it('returns 1 decimal when there is 3 digits to the left', () => {
- const formattedNumber = formatRelevantDigits('100.1234567');
- const rightFromDecimal = formattedNumber.split('.')[1];
- const leftFromDecimal = formattedNumber.split('.')[0];
-
- expect(rightFromDecimal.length).toBe(1);
- expect(leftFromDecimal.length).toBe(3);
- });
- });
-
- describe('bytesToKiB', () => {
- it('calculates KiB for the given bytes', () => {
- expect(bytesToKiB(1024)).toEqual(1);
- expect(bytesToKiB(1000)).toEqual(0.9765625);
- });
- });
-
- describe('bytesToMiB', () => {
- it('calculates MiB for the given bytes', () => {
- expect(bytesToMiB(1048576)).toEqual(1);
- expect(bytesToMiB(1000000)).toEqual(0.95367431640625);
- });
- });
-
- describe('bytesToGiB', () => {
- it('calculates GiB for the given bytes', () => {
- expect(bytesToGiB(1073741824)).toEqual(1);
- expect(bytesToGiB(10737418240)).toEqual(10);
- });
- });
-
- describe('numberToHumanSize', () => {
- it('should return bytes', () => {
- expect(numberToHumanSize(654)).toEqual('654 bytes');
- });
-
- it('should return KiB', () => {
- expect(numberToHumanSize(1079)).toEqual('1.05 KiB');
- });
-
- it('should return MiB', () => {
- expect(numberToHumanSize(10485764)).toEqual('10.00 MiB');
- });
-
- it('should return GiB', () => {
- expect(numberToHumanSize(10737418240)).toEqual('10.00 GiB');
- });
- });
-});
diff --git a/spec/javascripts/lib/utils/poll_spec.js b/spec/javascripts/lib/utils/poll_spec.js
index d0da659c3d7..138041a349f 100644
--- a/spec/javascripts/lib/utils/poll_spec.js
+++ b/spec/javascripts/lib/utils/poll_spec.js
@@ -153,6 +153,36 @@ describe('Poll', () => {
});
});
+ describe('enable', () => {
+ it('should enable polling upon a response', done => {
+ jasmine.clock().install();
+
+ const Polling = new Poll({
+ resource: service,
+ method: 'fetch',
+ data: { page: 1 },
+ successCallback: () => {},
+ });
+
+ Polling.enable({
+ data: { page: 4 },
+ response: { status: 200, headers: { 'poll-interval': 1 } },
+ });
+
+ jasmine.clock().tick(1);
+ jasmine.clock().uninstall();
+
+ waitForAllCallsToFinish(service, 1, () => {
+ Polling.stop();
+
+ expect(service.fetch.calls.count()).toEqual(1);
+ expect(service.fetch).toHaveBeenCalledWith({ page: 4 });
+ expect(Polling.options.data).toEqual({ page: 4 });
+ done();
+ });
+ });
+ });
+
describe('restart', () => {
it('should restart polling when its called', done => {
mockServiceCall(service, { status: 200, headers: { 'poll-interval': 1 } });
@@ -171,6 +201,7 @@ describe('Poll', () => {
});
spyOn(Polling, 'stop').and.callThrough();
+ spyOn(Polling, 'enable').and.callThrough();
spyOn(Polling, 'restart').and.callThrough();
Polling.makeRequest();
@@ -181,6 +212,7 @@ describe('Poll', () => {
expect(service.fetch.calls.count()).toEqual(2);
expect(service.fetch).toHaveBeenCalledWith({ page: 4 });
expect(Polling.stop).toHaveBeenCalled();
+ expect(Polling.enable).toHaveBeenCalled();
expect(Polling.restart).toHaveBeenCalled();
expect(Polling.options.data).toEqual({ page: 4 });
done();
diff --git a/spec/javascripts/line_highlighter_spec.js b/spec/javascripts/line_highlighter_spec.js
index 4eea364bd69..a75470b4db8 100644
--- a/spec/javascripts/line_highlighter_spec.js
+++ b/spec/javascripts/line_highlighter_spec.js
@@ -5,7 +5,7 @@ import LineHighlighter from '~/line_highlighter';
describe('LineHighlighter', function() {
var clickLine;
- preloadFixtures('static/line_highlighter.html.raw');
+ preloadFixtures('static/line_highlighter.html');
clickLine = function(number, eventData = {}) {
if ($.isEmptyObject(eventData)) {
return $('#L' + number).click();
@@ -15,7 +15,7 @@ describe('LineHighlighter', function() {
}
};
beforeEach(function() {
- loadFixtures('static/line_highlighter.html.raw');
+ loadFixtures('static/line_highlighter.html');
this['class'] = new LineHighlighter();
this.css = this['class'].highlightLineClass;
return (this.spies = {
diff --git a/spec/javascripts/merge_request_spec.js b/spec/javascripts/merge_request_spec.js
index ab809930804..431798c6ec3 100644
--- a/spec/javascripts/merge_request_spec.js
+++ b/spec/javascripts/merge_request_spec.js
@@ -11,9 +11,9 @@ describe('MergeRequest', function() {
describe('task lists', function() {
let mock;
- preloadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ preloadFixtures('merge_requests/merge_request_with_task_list.html');
beforeEach(function() {
- loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ loadFixtures('merge_requests/merge_request_with_task_list.html');
spyOn(axios, 'patch').and.callThrough();
mock = new MockAdapter(axios);
@@ -125,7 +125,7 @@ describe('MergeRequest', function() {
describe('hideCloseButton', () => {
describe('merge request of another user', () => {
beforeEach(() => {
- loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ loadFixtures('merge_requests/merge_request_with_task_list.html');
this.el = document.querySelector('.js-issuable-actions');
new MergeRequest(); // eslint-disable-line no-new
MergeRequest.hideCloseButton();
@@ -145,7 +145,7 @@ describe('MergeRequest', function() {
describe('merge request of current_user', () => {
beforeEach(() => {
- loadFixtures('merge_requests/merge_request_of_current_user.html.raw');
+ loadFixtures('merge_requests/merge_request_of_current_user.html');
this.el = document.querySelector('.js-issuable-actions');
MergeRequest.hideCloseButton();
});
diff --git a/spec/javascripts/merge_request_tabs_spec.js b/spec/javascripts/merge_request_tabs_spec.js
index c8df05eccf5..1295d900de7 100644
--- a/spec/javascripts/merge_request_tabs_spec.js
+++ b/spec/javascripts/merge_request_tabs_spec.js
@@ -22,8 +22,8 @@ describe('MergeRequestTabs', function() {
};
preloadFixtures(
- 'merge_requests/merge_request_with_task_list.html.raw',
- 'merge_requests/diff_comment.html.raw',
+ 'merge_requests/merge_request_with_task_list.html',
+ 'merge_requests/diff_comment.html',
);
beforeEach(function() {
@@ -48,7 +48,7 @@ describe('MergeRequestTabs', function() {
var windowTarget = '_blank';
beforeEach(function() {
- loadFixtures('merge_requests/merge_request_with_task_list.html.raw');
+ loadFixtures('merge_requests/merge_request_with_task_list.html');
tabUrl = $('.commits-tab a').attr('href');
});
diff --git a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
index 092ca9e1dab..aa4a376caf7 100644
--- a/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
+++ b/spec/javascripts/mini_pipeline_graph_dropdown_spec.js
@@ -5,10 +5,10 @@ import MiniPipelineGraph from '~/mini_pipeline_graph_dropdown';
import timeoutPromise from './helpers/set_timeout_promise_helper';
describe('Mini Pipeline Graph Dropdown', () => {
- preloadFixtures('static/mini_dropdown_graph.html.raw');
+ preloadFixtures('static/mini_dropdown_graph.html');
beforeEach(() => {
- loadFixtures('static/mini_dropdown_graph.html.raw');
+ loadFixtures('static/mini_dropdown_graph.html');
});
describe('When is initialized', () => {
diff --git a/spec/javascripts/monitoring/charts/area_spec.js b/spec/javascripts/monitoring/charts/area_spec.js
index 0b36fc9f5f7..fb49290be19 100644
--- a/spec/javascripts/monitoring/charts/area_spec.js
+++ b/spec/javascripts/monitoring/charts/area_spec.js
@@ -7,6 +7,7 @@ import MonitoringMock, { deploymentData } from '../mock_data';
describe('Area component', () => {
const mockWidgets = 'mockWidgets';
+ const mockSvgPathContent = 'mockSvgPathContent';
let mockGraphData;
let areaChart;
let spriteSpy;
@@ -30,7 +31,7 @@ describe('Area component', () => {
});
spriteSpy = spyOnDependency(Area, 'getSvgIconPathContent').and.callFake(
- () => new Promise(resolve => resolve()),
+ () => new Promise(resolve => resolve(mockSvgPathContent)),
);
});
@@ -74,15 +75,6 @@ describe('Area component', () => {
expect(shallowWrapperContainsSlotText(glAreaChart, 'tooltipTitle', mockTitle)).toBe(true);
});
- it('recieves tooltip content', () => {
- const mockContent = 'mockContent';
- areaChart.vm.tooltip.content = mockContent;
-
- expect(shallowWrapperContainsSlotText(glAreaChart, 'tooltipContent', mockContent)).toBe(
- true,
- );
- });
-
describe('when tooltip is showing deployment data', () => {
beforeEach(() => {
areaChart.vm.tooltip.isDeployment = true;
@@ -110,6 +102,7 @@ describe('Area component', () => {
const generateSeriesData = type => ({
seriesData: [
{
+ seriesName: areaChart.vm.chartData[0].name,
componentSubType: type,
value: [mockDate, 5.55555],
},
@@ -127,7 +120,14 @@ describe('Area component', () => {
});
it('formats tooltip content', () => {
- expect(areaChart.vm.tooltip.content).toBe('CPU (Cores) 5.556');
+ expect(areaChart.vm.tooltip.content).toEqual([{ name: 'Core Usage', value: '5.556' }]);
+ expect(
+ shallowWrapperContainsSlotText(
+ areaChart.find(GlAreaChart),
+ 'tooltipContent',
+ 'Core Usage 5.556',
+ ),
+ ).toBe(true);
});
});
@@ -146,24 +146,31 @@ describe('Area component', () => {
});
});
- describe('getScatterSymbol', () => {
+ describe('setSvg', () => {
+ const mockSvgName = 'mockSvgName';
+
beforeEach(() => {
- areaChart.vm.getScatterSymbol();
+ areaChart.vm.setSvg(mockSvgName);
});
- it('gets rocket svg path content for use as deployment data symbol', () => {
- expect(spriteSpy).toHaveBeenCalledWith('rocket');
+ it('gets svg path content', () => {
+ expect(spriteSpy).toHaveBeenCalledWith(mockSvgName);
+ });
+
+ it('sets svg path content', done => {
+ areaChart.vm.$nextTick(() => {
+ expect(areaChart.vm.svgs[mockSvgName]).toBe(`path://${mockSvgPathContent}`);
+ done();
+ });
});
});
describe('onResize', () => {
const mockWidth = 233;
- const mockHeight = 144;
beforeEach(() => {
spyOn(Element.prototype, 'getBoundingClientRect').and.callFake(() => ({
width: mockWidth,
- height: mockHeight,
}));
areaChart.vm.onResize();
});
@@ -171,22 +178,25 @@ describe('Area component', () => {
it('sets area chart width', () => {
expect(areaChart.vm.width).toBe(mockWidth);
});
-
- it('sets area chart height', () => {
- expect(areaChart.vm.height).toBe(mockHeight);
- });
});
});
describe('computed', () => {
describe('chartData', () => {
+ let chartData;
+ const seriesData = () => chartData[0];
+
+ beforeEach(() => {
+ ({ chartData } = areaChart.vm);
+ });
+
it('utilizes all data points', () => {
- expect(Object.keys(areaChart.vm.chartData)).toEqual(['Cores']);
- expect(areaChart.vm.chartData.Cores.length).toBe(297);
+ expect(chartData.length).toBe(1);
+ expect(seriesData().data.length).toBe(297);
});
it('creates valid data', () => {
- const data = areaChart.vm.chartData.Cores;
+ const { data } = seriesData();
expect(
data.filter(([time, value]) => new Date(time).getTime() > 0 && typeof value === 'number')
@@ -205,15 +215,9 @@ describe('Area component', () => {
});
});
- describe('xAxisLabel', () => {
- it('constructs a label for the chart x-axis', () => {
- expect(areaChart.vm.xAxisLabel).toBe('Core Usage');
- });
- });
-
describe('yAxisLabel', () => {
it('constructs a label for the chart y-axis', () => {
- expect(areaChart.vm.yAxisLabel).toBe('CPU (Cores)');
+ expect(areaChart.vm.yAxisLabel).toBe('CPU');
});
});
});
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index b1778029a77..6078a0e7872 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -98,7 +98,7 @@ describe('Dashboard', () => {
});
});
- it('renders the dropdown with a number of environments', done => {
+ it('renders the environments dropdown with a number of environments', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showPanels: false },
@@ -107,14 +107,16 @@ describe('Dashboard', () => {
component.store.storeEnvironmentsData(environmentData);
setTimeout(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll('.dropdown-menu ul li a');
+ const dropdownMenuEnvironments = component.$el.querySelectorAll(
+ '.js-environments-dropdown .dropdown-item',
+ );
expect(dropdownMenuEnvironments.length).toEqual(component.store.environmentsData.length);
done();
});
});
- it('hides the dropdown list when there is no environments', done => {
+ it('hides the environments dropdown list when there is no environments', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showPanels: false },
@@ -123,14 +125,16 @@ describe('Dashboard', () => {
component.store.storeEnvironmentsData([]);
setTimeout(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll('.dropdown-menu ul');
+ const dropdownMenuEnvironments = component.$el.querySelectorAll(
+ '.js-environments-dropdown .dropdown-item',
+ );
expect(dropdownMenuEnvironments.length).toEqual(0);
done();
});
});
- it('renders the dropdown with a single is-active element', done => {
+ it('renders the environments dropdown with a single is-active element', done => {
const component = new DashboardComponent({
el: document.querySelector('.prometheus-graphs'),
propsData: { ...propsData, hasMetrics: true, showPanels: false },
@@ -139,14 +143,12 @@ describe('Dashboard', () => {
component.store.storeEnvironmentsData(environmentData);
setTimeout(() => {
- const dropdownIsActiveElement = component.$el.querySelectorAll(
- '.dropdown-menu ul li a.is-active',
+ const dropdownItems = component.$el.querySelectorAll(
+ '.js-environments-dropdown .dropdown-item[active="true"]',
);
- expect(dropdownIsActiveElement.length).toEqual(1);
- expect(dropdownIsActiveElement[0].textContent.trim()).toEqual(
- component.currentEnvironmentName,
- );
+ expect(dropdownItems.length).toEqual(1);
+ expect(dropdownItems[0].textContent.trim()).toEqual(component.currentEnvironmentName);
done();
});
});
diff --git a/spec/javascripts/new_branch_spec.js b/spec/javascripts/new_branch_spec.js
index 1d7b885e64f..4e3140ce4f1 100644
--- a/spec/javascripts/new_branch_spec.js
+++ b/spec/javascripts/new_branch_spec.js
@@ -3,7 +3,7 @@ import NewBranchForm from '~/new_branch_form';
describe('Branch', function() {
describe('create a new branch', function() {
- preloadFixtures('branches/new_branch.html.raw');
+ preloadFixtures('branches/new_branch.html');
function fillNameWith(value) {
$('.js-branch-name')
@@ -16,7 +16,7 @@ describe('Branch', function() {
}
beforeEach(function() {
- loadFixtures('branches/new_branch.html.raw');
+ loadFixtures('branches/new_branch.html');
$('form').on('submit', function(e) {
return e.preventDefault();
});
diff --git a/spec/javascripts/notes/components/discussion_filter_note_spec.js b/spec/javascripts/notes/components/discussion_filter_note_spec.js
new file mode 100644
index 00000000000..52d2e7ce947
--- /dev/null
+++ b/spec/javascripts/notes/components/discussion_filter_note_spec.js
@@ -0,0 +1,93 @@
+import Vue from 'vue';
+import DiscussionFilterNote from '~/notes/components/discussion_filter_note.vue';
+import eventHub from '~/notes/event_hub';
+
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('DiscussionFilterNote component', () => {
+ let vm;
+
+ const createComponent = () => {
+ const Component = Vue.extend(DiscussionFilterNote);
+
+ return mountComponent(Component);
+ };
+
+ beforeEach(() => {
+ vm = createComponent();
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('computed', () => {
+ describe('timelineContent', () => {
+ it('returns string containing instruction for switching feed type', () => {
+ expect(vm.timelineContent).toBe(
+ "You're only seeing <b>other activity</b> in the feed. To add a comment, switch to one of the following options.",
+ );
+ });
+ });
+ });
+
+ describe('methods', () => {
+ describe('selectFilter', () => {
+ it('emits `dropdownSelect` event on `eventHub` with provided param', () => {
+ spyOn(eventHub, '$emit');
+
+ vm.selectFilter(1);
+
+ expect(eventHub.$emit).toHaveBeenCalledWith('dropdownSelect', 1);
+ });
+ });
+ });
+
+ describe('template', () => {
+ it('renders component container element', () => {
+ expect(vm.$el.classList.contains('discussion-filter-note')).toBe(true);
+ });
+
+ it('renders comment icon element', () => {
+ expect(vm.$el.querySelector('.timeline-icon svg use').getAttribute('xlink:href')).toContain(
+ 'comment',
+ );
+ });
+
+ it('renders filter information note', () => {
+ expect(vm.$el.querySelector('.timeline-content').innerText.trim()).toContain(
+ "You're only seeing other activity in the feed. To add a comment, switch to one of the following options.",
+ );
+ });
+
+ it('renders filter buttons', () => {
+ const buttonsContainerEl = vm.$el.querySelector('.discussion-filter-actions');
+
+ expect(buttonsContainerEl.querySelector('button:first-child').innerText.trim()).toContain(
+ 'Show all activity',
+ );
+
+ expect(buttonsContainerEl.querySelector('button:last-child').innerText.trim()).toContain(
+ 'Show comments only',
+ );
+ });
+
+ it('clicking `Show all activity` button calls `selectFilter("all")` method', () => {
+ const showAllBtn = vm.$el.querySelector('.discussion-filter-actions button:first-child');
+ spyOn(vm, 'selectFilter');
+
+ showAllBtn.dispatchEvent(new Event('click'));
+
+ expect(vm.selectFilter).toHaveBeenCalledWith(0);
+ });
+
+ it('clicking `Show comments only` button calls `selectFilter("comments")` method', () => {
+ const showAllBtn = vm.$el.querySelector('.discussion-filter-actions button:last-child');
+ spyOn(vm, 'selectFilter');
+
+ showAllBtn.dispatchEvent(new Event('click'));
+
+ expect(vm.selectFilter).toHaveBeenCalledWith(1);
+ });
+ });
+});
diff --git a/spec/javascripts/notes/components/discussion_filter_spec.js b/spec/javascripts/notes/components/discussion_filter_spec.js
index 91dab58ba7f..1c366aee8e2 100644
--- a/spec/javascripts/notes/components/discussion_filter_spec.js
+++ b/spec/javascripts/notes/components/discussion_filter_spec.js
@@ -1,7 +1,7 @@
import Vue from 'vue';
import createStore from '~/notes/stores';
import DiscussionFilter from '~/notes/components/discussion_filter.vue';
-import { DISCUSSION_FILTERS_DEFAULT_VALUE } from '~/notes/constants';
+import { DISCUSSION_FILTERS_DEFAULT_VALUE, DISCUSSION_FILTER_TYPES } from '~/notes/constants';
import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper';
import { discussionFiltersMock, discussionMock } from '../mock_data';
@@ -54,14 +54,18 @@ describe('DiscussionFilter component', () => {
});
it('updates to the selected item', () => {
- const filterItem = vm.$el.querySelector('.dropdown-menu li:last-child button');
+ const filterItem = vm.$el.querySelector(
+ `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.HISTORY}"] button`,
+ );
filterItem.click();
expect(vm.currentFilter.title).toEqual(filterItem.textContent.trim());
});
it('only updates when selected filter changes', () => {
- const filterItem = vm.$el.querySelector('.dropdown-menu li:first-child button');
+ const filterItem = vm.$el.querySelector(
+ `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"] button`,
+ );
spyOn(vm, 'filterDiscussion');
filterItem.click();
@@ -70,21 +74,27 @@ describe('DiscussionFilter component', () => {
});
it('disables commenting when "Show history only" filter is applied', () => {
- const filterItem = vm.$el.querySelector('.dropdown-menu li:last-child button');
+ const filterItem = vm.$el.querySelector(
+ `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.HISTORY}"] button`,
+ );
filterItem.click();
expect(vm.$store.state.commentsDisabled).toBe(true);
});
it('enables commenting when "Show history only" filter is not applied', () => {
- const filterItem = vm.$el.querySelector('.dropdown-menu li:first-child button');
+ const filterItem = vm.$el.querySelector(
+ `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"] button`,
+ );
filterItem.click();
expect(vm.$store.state.commentsDisabled).toBe(false);
});
it('renders a dropdown divider for the default filter', () => {
- const defaultFilter = vm.$el.querySelector('.dropdown-menu li:first-child');
+ const defaultFilter = vm.$el.querySelector(
+ `.dropdown-menu li[data-filter-type="${DISCUSSION_FILTER_TYPES.ALL}"]`,
+ );
expect(defaultFilter.lastChild.classList).toContain('dropdown-divider');
});
diff --git a/spec/javascripts/notes/components/discussion_jump_to_next_button_spec.js b/spec/javascripts/notes/components/discussion_jump_to_next_button_spec.js
deleted file mode 100644
index c41b29fa788..00000000000
--- a/spec/javascripts/notes/components/discussion_jump_to_next_button_spec.js
+++ /dev/null
@@ -1,33 +0,0 @@
-import jumpToNextDiscussionButton from '~/notes/components/discussion_jump_to_next_button.vue';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-
-const localVue = createLocalVue();
-
-describe('jumpToNextDiscussionButton', () => {
- let wrapper;
-
- beforeEach(() => {
- wrapper = shallowMount(jumpToNextDiscussionButton, {
- localVue,
- sync: false,
- });
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('emits onClick event on button click', done => {
- const button = wrapper.find({ ref: 'button' });
-
- button.trigger('click');
-
- localVue.nextTick(() => {
- expect(wrapper.emitted()).toEqual({
- onClick: [[]],
- });
-
- done();
- });
- });
-});
diff --git a/spec/javascripts/notes/components/discussion_resolve_with_issue_button_spec.js b/spec/javascripts/notes/components/discussion_resolve_with_issue_button_spec.js
new file mode 100644
index 00000000000..b2a91c9919a
--- /dev/null
+++ b/spec/javascripts/notes/components/discussion_resolve_with_issue_button_spec.js
@@ -0,0 +1,31 @@
+import { GlButton } from '@gitlab/ui';
+import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_issue_button.vue';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { TEST_HOST } from 'spec/test_constants';
+
+const localVue = createLocalVue();
+
+describe('ResolveWithIssueButton', () => {
+ let wrapper;
+ const url = `${TEST_HOST}/hello-world/`;
+
+ beforeEach(() => {
+ wrapper = shallowMount(ResolveWithIssueButton, {
+ localVue,
+ sync: false,
+ propsData: {
+ url,
+ },
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('it should have a link with the provided link property as href', () => {
+ const button = wrapper.find(GlButton);
+
+ expect(button.attributes().href).toBe(url);
+ });
+});
diff --git a/spec/javascripts/notes/components/note_app_spec.js b/spec/javascripts/notes/components/note_app_spec.js
index d5c0bf6b25d..d716ece3766 100644
--- a/spec/javascripts/notes/components/note_app_spec.js
+++ b/spec/javascripts/notes/components/note_app_spec.js
@@ -83,6 +83,8 @@ describe('note_app', () => {
describe('render', () => {
beforeEach(() => {
+ setFixtures('<div class="js-discussions-count"></div>');
+
Vue.http.interceptors.push(mockData.individualNoteInterceptor);
wrapper = mountComponent();
});
@@ -124,9 +126,24 @@ describe('note_app', () => {
expect(wrapper.find('.js-main-target-form').exists()).toBe(false);
});
+ it('should render discussion filter note `commentsDisabled` is true', () => {
+ store.state.commentsDisabled = true;
+ wrapper = mountComponent();
+
+ expect(wrapper.find('.js-discussion-filter-note').exists()).toBe(true);
+ });
+
it('should render form comment button as disabled', () => {
expect(wrapper.find('.js-note-new-discussion').attributes('disabled')).toEqual('disabled');
});
+
+ it('updates discussions badge', done => {
+ setTimeout(() => {
+ expect(document.querySelector('.js-discussions-count').textContent).toEqual('2');
+
+ done();
+ });
+ });
});
describe('while fetching data', () => {
diff --git a/spec/javascripts/notes/components/note_form_spec.js b/spec/javascripts/notes/components/note_form_spec.js
index 5db20fd285f..b632ee6736d 100644
--- a/spec/javascripts/notes/components/note_form_spec.js
+++ b/spec/javascripts/notes/components/note_form_spec.js
@@ -1,16 +1,36 @@
-import Vue from 'vue';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
import createStore from '~/notes/stores';
-import issueNoteForm from '~/notes/components/note_form.vue';
+import NoteForm from '~/notes/components/note_form.vue';
+import MarkdownField from '~/vue_shared/components/markdown/field.vue';
import { noteableDataMock, notesDataMock } from '../mock_data';
-import { keyboardDownEvent } from '../../issue_show/helpers';
describe('issue_note_form component', () => {
+ const dummyAutosaveKey = 'some-autosave-key';
+ const dummyDraft = 'dummy draft content';
+
let store;
- let vm;
+ let wrapper;
let props;
+ const createComponentWrapper = () => {
+ const localVue = createLocalVue();
+ return shallowMount(NoteForm, {
+ store,
+ propsData: props,
+ // see https://gitlab.com/gitlab-org/gitlab-ce/issues/56317 for the following
+ localVue,
+ sync: false,
+ });
+ };
+
beforeEach(() => {
- const Component = Vue.extend(issueNoteForm);
+ spyOnDependency(NoteForm, 'getDraft').and.callFake(key => {
+ if (key === dummyAutosaveKey) {
+ return dummyDraft;
+ }
+
+ return null;
+ });
store = createStore();
store.dispatch('setNoteableData', noteableDataMock);
@@ -21,27 +41,31 @@ describe('issue_note_form component', () => {
noteBody: 'Magni suscipit eius consectetur enim et ex et commodi.',
noteId: '545',
};
-
- vm = new Component({
- store,
- propsData: props,
- }).$mount();
});
afterEach(() => {
- vm.$destroy();
+ wrapper.destroy();
});
describe('noteHash', () => {
+ beforeEach(() => {
+ wrapper = createComponentWrapper();
+ });
+
it('returns note hash string based on `noteId`', () => {
- expect(vm.noteHash).toBe(`#note_${props.noteId}`);
+ expect(wrapper.vm.noteHash).toBe(`#note_${props.noteId}`);
});
it('return note hash as `#` when `noteId` is empty', done => {
- vm.noteId = '';
- Vue.nextTick()
+ wrapper.setProps({
+ ...props,
+ noteId: '',
+ });
+
+ wrapper.vm
+ .$nextTick()
.then(() => {
- expect(vm.noteHash).toBe('#');
+ expect(wrapper.vm.noteHash).toBe('#');
})
.then(done)
.catch(done.fail);
@@ -49,97 +73,196 @@ describe('issue_note_form component', () => {
});
describe('conflicts editing', () => {
+ beforeEach(() => {
+ wrapper = createComponentWrapper();
+ });
+
it('should show conflict message if note changes outside the component', done => {
- vm.isEditing = true;
- vm.noteBody = 'Foo';
+ wrapper.setProps({
+ ...props,
+ isEditing: true,
+ noteBody: 'Foo',
+ });
+
const message =
'This comment has changed since you started editing, please review the updated comment to ensure information is not lost.';
- Vue.nextTick(() => {
- expect(
- vm.$el
- .querySelector('.js-conflict-edit-warning')
- .textContent.replace(/\s+/g, ' ')
- .trim(),
- ).toEqual(message);
- done();
- });
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const conflictWarning = wrapper.find('.js-conflict-edit-warning');
+
+ expect(conflictWarning.exists()).toBe(true);
+ expect(
+ conflictWarning
+ .text()
+ .replace(/\s+/g, ' ')
+ .trim(),
+ ).toBe(message);
+ })
+ .then(done)
+ .catch(done.fail);
});
});
describe('form', () => {
+ beforeEach(() => {
+ wrapper = createComponentWrapper();
+ });
+
it('should render text area with placeholder', () => {
- expect(vm.$el.querySelector('textarea').getAttribute('placeholder')).toEqual(
+ const textarea = wrapper.find('textarea');
+
+ expect(textarea.attributes('placeholder')).toEqual(
'Write a comment or drag your files here…',
);
});
it('should link to markdown docs', () => {
const { markdownDocsPath } = notesDataMock;
+ const markdownField = wrapper.find(MarkdownField);
+ const markdownFieldProps = markdownField.props();
- expect(vm.$el.querySelector(`a[href="${markdownDocsPath}"]`).textContent.trim()).toEqual(
- 'Markdown',
- );
+ expect(markdownFieldProps.markdownDocsPath).toBe(markdownDocsPath);
});
describe('keyboard events', () => {
+ let textarea;
+
+ beforeEach(() => {
+ textarea = wrapper.find('textarea');
+ textarea.setValue('Foo');
+ });
+
describe('up', () => {
it('should ender edit mode', () => {
- spyOn(vm, 'editMyLastNote').and.callThrough();
- vm.$el.querySelector('textarea').value = 'Foo';
- vm.$el.querySelector('textarea').dispatchEvent(keyboardDownEvent(38, true));
+ // TODO: do not spy on vm
+ spyOn(wrapper.vm, 'editMyLastNote').and.callThrough();
- expect(vm.editMyLastNote).toHaveBeenCalled();
+ textarea.trigger('keydown.up');
+
+ expect(wrapper.vm.editMyLastNote).toHaveBeenCalled();
});
});
describe('enter', () => {
it('should save note when cmd+enter is pressed', () => {
- spyOn(vm, 'handleUpdate').and.callThrough();
- vm.$el.querySelector('textarea').value = 'Foo';
- vm.$el.querySelector('textarea').dispatchEvent(keyboardDownEvent(13, true));
+ textarea.trigger('keydown.enter', { metaKey: true });
+
+ const { handleFormUpdate } = wrapper.emitted();
- expect(vm.handleUpdate).toHaveBeenCalled();
+ expect(handleFormUpdate.length).toBe(1);
});
it('should save note when ctrl+enter is pressed', () => {
- spyOn(vm, 'handleUpdate').and.callThrough();
- vm.$el.querySelector('textarea').value = 'Foo';
- vm.$el.querySelector('textarea').dispatchEvent(keyboardDownEvent(13, false, true));
+ textarea.trigger('keydown.enter', { ctrlKey: true });
- expect(vm.handleUpdate).toHaveBeenCalled();
+ const { handleFormUpdate } = wrapper.emitted();
+
+ expect(handleFormUpdate.length).toBe(1);
});
});
});
describe('actions', () => {
it('should be possible to cancel', done => {
- spyOn(vm, 'cancelHandler').and.callThrough();
- vm.isEditing = true;
+ // TODO: do not spy on vm
+ spyOn(wrapper.vm, 'cancelHandler').and.callThrough();
+ wrapper.setProps({
+ ...props,
+ isEditing: true,
+ });
- Vue.nextTick(() => {
- vm.$el.querySelector('.note-edit-cancel').click();
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const cancelButton = wrapper.find('.note-edit-cancel');
+ cancelButton.trigger('click');
- Vue.nextTick(() => {
- expect(vm.cancelHandler).toHaveBeenCalled();
- done();
- });
- });
+ expect(wrapper.vm.cancelHandler).toHaveBeenCalled();
+ })
+ .then(done)
+ .catch(done.fail);
});
it('should be possible to update the note', done => {
- vm.isEditing = true;
+ wrapper.setProps({
+ ...props,
+ isEditing: true,
+ });
- Vue.nextTick(() => {
- vm.$el.querySelector('textarea').value = 'Foo';
- vm.$el.querySelector('.js-vue-issue-save').click();
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const textarea = wrapper.find('textarea');
+ textarea.setValue('Foo');
+ const saveButton = wrapper.find('.js-vue-issue-save');
+ saveButton.trigger('click');
- Vue.nextTick(() => {
- expect(vm.isSubmitting).toEqual(true);
- done();
- });
+ expect(wrapper.vm.isSubmitting).toEqual(true);
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+ });
+
+ describe('with autosaveKey', () => {
+ describe('with draft', () => {
+ beforeEach(done => {
+ Object.assign(props, {
+ noteBody: '',
+ autosaveKey: dummyAutosaveKey,
});
+ wrapper = createComponentWrapper();
+
+ wrapper.vm
+ .$nextTick()
+ .then(done)
+ .catch(done.fail);
});
+
+ it('displays the draft in textarea', () => {
+ const textarea = wrapper.find('textarea');
+
+ expect(textarea.element.value).toBe(dummyDraft);
+ });
+ });
+
+ describe('without draft', () => {
+ beforeEach(done => {
+ Object.assign(props, {
+ noteBody: '',
+ autosaveKey: 'some key without draft',
+ });
+ wrapper = createComponentWrapper();
+
+ wrapper.vm
+ .$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('leaves the textarea empty', () => {
+ const textarea = wrapper.find('textarea');
+
+ expect(textarea.element.value).toBe('');
+ });
+ });
+
+ it('updates the draft if textarea content changes', () => {
+ const updateDraftSpy = spyOnDependency(NoteForm, 'updateDraft').and.stub();
+ Object.assign(props, {
+ noteBody: '',
+ autosaveKey: dummyAutosaveKey,
+ });
+ wrapper = createComponentWrapper();
+ const textarea = wrapper.find('textarea');
+ const dummyContent = 'some new content';
+
+ textarea.setValue(dummyContent);
+
+ expect(updateDraftSpy).toHaveBeenCalledWith(dummyAutosaveKey, dummyContent);
});
});
});
diff --git a/spec/javascripts/notes/components/noteable_discussion_spec.js b/spec/javascripts/notes/components/noteable_discussion_spec.js
index 2eae22e095f..3304c79cdb7 100644
--- a/spec/javascripts/notes/components/noteable_discussion_spec.js
+++ b/spec/javascripts/notes/components/noteable_discussion_spec.js
@@ -2,6 +2,8 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import createStore from '~/notes/stores';
import noteableDiscussion from '~/notes/components/noteable_discussion.vue';
import ReplyPlaceholder from '~/notes/components/discussion_reply_placeholder.vue';
+import ResolveWithIssueButton from '~/notes/components/discussion_resolve_with_issue_button.vue';
+import NoteForm from '~/notes/components/note_form.vue';
import '~/behaviors/markdown/render_gfm';
import { noteableDataMock, discussionMock, notesDataMock } from '../mock_data';
import mockDiffFile from '../../diffs/mock_data/diff_file';
@@ -71,7 +73,18 @@ describe('noteable_discussion component', () => {
.then(() => wrapper.vm.$nextTick())
.then(() => {
expect(wrapper.vm.isReplying).toEqual(true);
- expect(wrapper.vm.$refs.noteForm).not.toBeNull();
+
+ const noteForm = wrapper.find(NoteForm);
+
+ expect(noteForm.exists()).toBe(true);
+
+ const noteFormProps = noteForm.props();
+
+ expect(noteFormProps.discussion).toBe(discussionMock);
+ expect(noteFormProps.isEditing).toBe(false);
+ expect(noteFormProps.line).toBe(null);
+ expect(noteFormProps.saveButtonTitle).toBe('Comment');
+ expect(noteFormProps.autosaveKey).toBe(`Note/Issue/${discussionMock.id}/Reply`);
})
.then(done)
.catch(done.fail);
@@ -238,4 +251,42 @@ describe('noteable_discussion component', () => {
});
});
});
+
+ describe('for resolved discussion', () => {
+ beforeEach(() => {
+ const discussion = getJSONFixture(discussionWithTwoUnresolvedNotes)[0];
+ wrapper.setProps({ discussion });
+ });
+
+ it('does not display a button to resolve with issue', () => {
+ const button = wrapper.find(ResolveWithIssueButton);
+
+ expect(button.exists()).toBe(false);
+ });
+ });
+
+ describe('for unresolved discussion', () => {
+ beforeEach(done => {
+ const discussion = {
+ ...getJSONFixture(discussionWithTwoUnresolvedNotes)[0],
+ expanded: true,
+ };
+ discussion.notes = discussion.notes.map(note => ({
+ ...note,
+ resolved: false,
+ }));
+
+ wrapper.setProps({ discussion });
+ wrapper.vm
+ .$nextTick()
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('displays a button to resolve with issue', () => {
+ const button = wrapper.find(ResolveWithIssueButton);
+
+ expect(button.exists()).toBe(true);
+ });
+ });
});
diff --git a/spec/javascripts/notes/stores/getters_spec.js b/spec/javascripts/notes/stores/getters_spec.js
index c066975a43b..8f3c493dd4c 100644
--- a/spec/javascripts/notes/stores/getters_spec.js
+++ b/spec/javascripts/notes/stores/getters_spec.js
@@ -261,4 +261,12 @@ describe('Getters Notes Store', () => {
expect(getters.firstUnresolvedDiscussionId(state, localGettersFalsy)(false)).toBeFalsy();
});
});
+
+ describe('getDiscussion', () => {
+ it('returns discussion by ID', () => {
+ state.discussions.push({ id: '1' });
+
+ expect(getters.getDiscussion(state)('1')).toEqual({ id: '1' });
+ });
+ });
});
diff --git a/spec/javascripts/notes/stores/mutation_spec.js b/spec/javascripts/notes/stores/mutation_spec.js
index fcad1f245b6..4a640d589fb 100644
--- a/spec/javascripts/notes/stores/mutation_spec.js
+++ b/spec/javascripts/notes/stores/mutation_spec.js
@@ -1,5 +1,6 @@
import Vue from 'vue';
import mutations from '~/notes/stores/mutations';
+import { DISCUSSION_NOTE } from '~/notes/constants';
import {
note,
discussionMock,
@@ -326,6 +327,18 @@ describe('Notes Store mutations', () => {
expect(state.discussions[0].notes[0].note).toEqual('Foo');
});
+
+ it('transforms an individual note to discussion', () => {
+ const state = {
+ discussions: [individualNote],
+ };
+
+ const transformedNote = { ...individualNote.notes[0], type: DISCUSSION_NOTE };
+
+ mutations.UPDATE_NOTE(state, transformedNote);
+
+ expect(state.discussions[0].individual_note).toEqual(false);
+ });
});
describe('CLOSE_ISSUE', () => {
@@ -530,7 +543,7 @@ describe('Notes Store mutations', () => {
state = { convertedDisscussionIds: [] };
});
- it('adds a disucssion to convertedDisscussionIds', () => {
+ it('adds a discussion to convertedDisscussionIds', () => {
mutations.CONVERT_TO_DISCUSSION(state, discussion.id);
expect(state.convertedDisscussionIds).toContain(discussion.id);
@@ -549,7 +562,7 @@ describe('Notes Store mutations', () => {
state = { convertedDisscussionIds: [41, 42] };
});
- it('removes a disucssion from convertedDisscussionIds', () => {
+ it('removes a discussion from convertedDisscussionIds', () => {
mutations.REMOVE_CONVERTED_DISCUSSION(state, discussion.id);
expect(state.convertedDisscussionIds).not.toContain(discussion.id);
diff --git a/spec/javascripts/notes_spec.js b/spec/javascripts/notes_spec.js
index 7c869d4c326..3d2c617e479 100644
--- a/spec/javascripts/notes_spec.js
+++ b/spec/javascripts/notes_spec.js
@@ -34,7 +34,7 @@ const htmlEscape = comment => {
describe('Notes', function() {
const FLASH_TYPE_ALERT = 'alert';
const NOTES_POST_PATH = /(.*)\/notes\?html=true$/;
- var fixture = 'snippets/show.html.raw';
+ var fixture = 'snippets/show.html';
preloadFixtures(fixture);
beforeEach(function() {
diff --git a/spec/javascripts/oauth_remember_me_spec.js b/spec/javascripts/oauth_remember_me_spec.js
index 4125706a407..381be82697e 100644
--- a/spec/javascripts/oauth_remember_me_spec.js
+++ b/spec/javascripts/oauth_remember_me_spec.js
@@ -2,10 +2,10 @@ import $ from 'jquery';
import OAuthRememberMe from '~/pages/sessions/new/oauth_remember_me';
describe('OAuthRememberMe', () => {
- preloadFixtures('static/oauth_remember_me.html.raw');
+ preloadFixtures('static/oauth_remember_me.html');
beforeEach(() => {
- loadFixtures('static/oauth_remember_me.html.raw');
+ loadFixtures('static/oauth_remember_me.html');
new OAuthRememberMe({ container: $('#oauth-container') }).bindEvents();
});
diff --git a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js
index 23d07056925..f7637964c60 100644
--- a/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js
+++ b/spec/javascripts/pages/admin/abuse_reports/abuse_reports_spec.js
@@ -3,7 +3,7 @@ import '~/lib/utils/text_utility';
import AbuseReports from '~/pages/admin/abuse_reports/abuse_reports';
describe('Abuse Reports', () => {
- const FIXTURE = 'abuse_reports/abuse_reports_list.html.raw';
+ const FIXTURE = 'abuse_reports/abuse_reports_list.html';
const MAX_MESSAGE_LENGTH = 500;
let $messages;
diff --git a/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js b/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js
index 561bd2c96cb..6a239e307e9 100644
--- a/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js
+++ b/spec/javascripts/pages/admin/application_settings/account_and_limits_spec.js
@@ -5,7 +5,7 @@ import initUserInternalRegexPlaceholder, {
} from '~/pages/admin/application_settings/account_and_limits';
describe('AccountAndLimits', () => {
- const FIXTURE = 'application_settings/accounts_and_limit.html.raw';
+ const FIXTURE = 'application_settings/accounts_and_limit.html';
let $userDefaultExternal;
let $userInternalRegex;
preloadFixtures(FIXTURE);
diff --git a/spec/javascripts/pages/admin/users/new/index_spec.js b/spec/javascripts/pages/admin/users/new/index_spec.js
index 5a849f34bc3..3896323eef7 100644
--- a/spec/javascripts/pages/admin/users/new/index_spec.js
+++ b/spec/javascripts/pages/admin/users/new/index_spec.js
@@ -2,7 +2,7 @@ import $ from 'jquery';
import UserInternalRegexHandler from '~/pages/admin/users/new/index';
describe('UserInternalRegexHandler', () => {
- const FIXTURE = 'admin/users/new_with_internal_user_regex.html.raw';
+ const FIXTURE = 'admin/users/new_with_internal_user_regex.html';
let $userExternal;
let $userEmail;
let $warningMessage;
diff --git a/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js b/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js
index 7a8227479d4..1809e92e1d9 100644
--- a/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js
+++ b/spec/javascripts/pages/sessions/new/preserve_url_fragment_spec.js
@@ -2,10 +2,10 @@ import $ from 'jquery';
import preserveUrlFragment from '~/pages/sessions/new/preserve_url_fragment';
describe('preserve_url_fragment', () => {
- preloadFixtures('sessions/new.html.raw');
+ preloadFixtures('sessions/new.html');
beforeEach(() => {
- loadFixtures('sessions/new.html.raw');
+ loadFixtures('sessions/new.html');
});
it('adds the url fragment to all login and sign up form actions', () => {
diff --git a/spec/javascripts/persistent_user_callout_spec.js b/spec/javascripts/persistent_user_callout_spec.js
new file mode 100644
index 00000000000..2fdfff3db03
--- /dev/null
+++ b/spec/javascripts/persistent_user_callout_spec.js
@@ -0,0 +1,88 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import PersistentUserCallout from '~/persistent_user_callout';
+import setTimeoutPromise from 'spec/helpers/set_timeout_promise_helper';
+
+describe('PersistentUserCallout', () => {
+ const dismissEndpoint = '/dismiss';
+ const featureName = 'feature';
+
+ function createFixture() {
+ const fixture = document.createElement('div');
+ fixture.innerHTML = `
+ <div
+ class="container"
+ data-dismiss-endpoint="${dismissEndpoint}"
+ data-feature-id="${featureName}"
+ >
+ <button type="button" class="js-close"></button>
+ </div>
+ `;
+
+ return fixture;
+ }
+
+ describe('dismiss', () => {
+ let button;
+ let mockAxios;
+ let persistentUserCallout;
+
+ beforeEach(() => {
+ const fixture = createFixture();
+ const container = fixture.querySelector('.container');
+ button = fixture.querySelector('.js-close');
+ mockAxios = new MockAdapter(axios);
+ persistentUserCallout = new PersistentUserCallout(container);
+ spyOn(persistentUserCallout.container, 'remove');
+ });
+
+ afterEach(() => {
+ mockAxios.restore();
+ });
+
+ it('POSTs endpoint and removes container when clicking close', done => {
+ mockAxios.onPost(dismissEndpoint).replyOnce(200);
+
+ button.click();
+
+ setTimeoutPromise()
+ .then(() => {
+ expect(persistentUserCallout.container.remove).toHaveBeenCalled();
+ expect(mockAxios.history.post[0].data).toBe(
+ JSON.stringify({ feature_name: featureName }),
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+
+ it('invokes Flash when the dismiss request fails', done => {
+ const Flash = spyOnDependency(PersistentUserCallout, 'Flash');
+ mockAxios.onPost(dismissEndpoint).replyOnce(500);
+
+ button.click();
+
+ setTimeoutPromise()
+ .then(() => {
+ expect(persistentUserCallout.container.remove).not.toHaveBeenCalled();
+ expect(Flash).toHaveBeenCalledWith(
+ 'An error occurred while dismissing the alert. Refresh the page and try again.',
+ );
+ })
+ .then(done)
+ .catch(done.fail);
+ });
+ });
+
+ describe('factory', () => {
+ it('returns an instance of PersistentUserCallout with the provided container property', () => {
+ const fixture = createFixture();
+
+ expect(PersistentUserCallout.factory(fixture) instanceof PersistentUserCallout).toBe(true);
+ });
+
+ it('returns undefined if container is falsey', () => {
+ expect(PersistentUserCallout.factory()).toBe(undefined);
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
index d0b8f877d6f..3240e8e4c1b 100644
--- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js
+++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
@@ -35,6 +35,7 @@ describe('stage column component', () => {
component = mountComponent(StageColumnComponent, {
title: 'foo',
groups: mockGroups,
+ hasTriggeredBy: false,
});
});
@@ -54,13 +55,14 @@ describe('stage column component', () => {
id: 4259,
name: '<img src=x onerror=alert(document.domain)>',
status: {
- icon: 'icon_status_success',
+ icon: 'status_success',
label: 'success',
tooltip: '<img src=x onerror=alert(document.domain)>',
},
},
],
title: 'test',
+ hasTriggeredBy: false,
});
expect(component.$el.querySelector('.builds-container li').getAttribute('id')).toEqual(
diff --git a/spec/javascripts/pipelines/pipeline_url_spec.js b/spec/javascripts/pipelines/pipeline_url_spec.js
index ea917b36526..faad49a78b0 100644
--- a/spec/javascripts/pipelines/pipeline_url_spec.js
+++ b/spec/javascripts/pipelines/pipeline_url_spec.js
@@ -100,7 +100,8 @@ describe('Pipeline Url Component', () => {
latest: true,
yaml_errors: true,
stuck: true,
- merge_request: true,
+ merge_request_pipeline: true,
+ detached_merge_request_pipeline: true,
},
},
autoDevopsHelpPath: 'foo',
@@ -108,15 +109,16 @@ 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-mergerequest').textContent).toContain(
- 'merge request',
- );
-
expect(component.$el.querySelector('.js-pipeline-url-stuck').textContent).toContain('stuck');
+
+ expect(component.$el.querySelector('.js-pipeline-url-detached').textContent).toContain(
+ 'detached',
+ );
});
it('should render a badge for autodevops', () => {
diff --git a/spec/javascripts/pipelines/pipelines_spec.js b/spec/javascripts/pipelines/pipelines_spec.js
index 97ded16db69..78187b69563 100644
--- a/spec/javascripts/pipelines/pipelines_spec.js
+++ b/spec/javascripts/pipelines/pipelines_spec.js
@@ -446,7 +446,7 @@ describe('Pipelines', () => {
};
vm.$nextTick(() => {
- vm.$el.querySelector('.js-next-button a').click();
+ vm.$el.querySelector('.js-next-button .page-link').click();
expect(vm.updateContent).toHaveBeenCalledWith({ scope: 'all', page: '2' });
diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index 4c575536f0e..234fc705a81 100644
--- a/spec/javascripts/pipelines/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -195,8 +195,10 @@ describe('Pipelines Table Row', () => {
it('emits `openConfirmationModal` event when cancel button is clicked and toggles loading', () => {
eventHub.$once('openConfirmationModal', data => {
+ const { id, ref, commit } = pipeline;
+
expect(data.endpoint).toEqual('/cancel');
- expect(data.pipelineId).toEqual(pipeline.id);
+ expect(data.pipeline).toEqual(jasmine.objectContaining({ id, ref, commit }));
});
component.$el.querySelector('.js-pipelines-cancel-button').click();
diff --git a/spec/javascripts/pipelines_spec.js b/spec/javascripts/pipelines_spec.js
index 6b86f9ea437..6d4d634c575 100644
--- a/spec/javascripts/pipelines_spec.js
+++ b/spec/javascripts/pipelines_spec.js
@@ -1,10 +1,10 @@
import Pipelines from '~/pipelines';
describe('Pipelines', () => {
- preloadFixtures('static/pipeline_graph.html.raw');
+ preloadFixtures('static/pipeline_graph.html');
beforeEach(() => {
- loadFixtures('static/pipeline_graph.html.raw');
+ loadFixtures('static/pipeline_graph.html');
});
it('should be defined', () => {
diff --git a/spec/javascripts/project_select_combo_button_spec.js b/spec/javascripts/project_select_combo_button_spec.js
index 109a5000f5d..dc85292c23e 100644
--- a/spec/javascripts/project_select_combo_button_spec.js
+++ b/spec/javascripts/project_select_combo_button_spec.js
@@ -1,7 +1,7 @@
import $ from 'jquery';
import ProjectSelectComboButton from '~/project_select_combo_button';
-const fixturePath = 'static/project_select_combo_button.html.raw';
+const fixturePath = 'static/project_select_combo_button.html';
describe('Project Select Combo Button', function() {
preloadFixtures(fixturePath);
diff --git a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js
index 94e2f959d46..dca3e1553b9 100644
--- a/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js
+++ b/spec/javascripts/prometheus_metrics/prometheus_metrics_spec.js
@@ -5,7 +5,7 @@ import PANEL_STATE from '~/prometheus_metrics/constants';
import { metrics, missingVarMetrics } from './mock_data';
describe('PrometheusMetrics', () => {
- const FIXTURE = 'services/prometheus/prometheus_service.html.raw';
+ const FIXTURE = 'services/prometheus/prometheus_service.html';
preloadFixtures(FIXTURE);
beforeEach(() => {
diff --git a/spec/javascripts/read_more_spec.js b/spec/javascripts/read_more_spec.js
index b1af0f80a50..d1d01272403 100644
--- a/spec/javascripts/read_more_spec.js
+++ b/spec/javascripts/read_more_spec.js
@@ -1,7 +1,7 @@
import initReadMore from '~/read_more';
describe('Read more click-to-expand functionality', () => {
- const fixtureName = 'projects/overview.html.raw';
+ const fixtureName = 'projects/overview.html';
preloadFixtures(fixtureName);
diff --git a/spec/javascripts/registry/components/app_spec.js b/spec/javascripts/registry/components/app_spec.js
index 67118ac03a5..76a17e6fb31 100644
--- a/spec/javascripts/registry/components/app_spec.js
+++ b/spec/javascripts/registry/components/app_spec.js
@@ -99,7 +99,7 @@ describe('Registry List', () => {
it('should render a loading spinner', done => {
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.fa-spinner')).not.toBe(null);
+ expect(vm.$el.querySelector('.spinner')).not.toBe(null);
done();
});
});
diff --git a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
index 69767d9cf1c..a17494966a3 100644
--- a/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
+++ b/spec/javascripts/reports/components/grouped_test_reports_app_spec.js
@@ -61,7 +61,7 @@ describe('Grouped Test Reports App', () => {
it('renders success summary text', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.fa-spinner')).not.toBeNull();
+ expect(vm.$el.querySelector('.spinner')).not.toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary results are being parsed',
);
@@ -81,7 +81,7 @@ describe('Grouped Test Reports App', () => {
it('renders failed summary text + new badge', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.fa-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.spinner')).toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 failed test results out of 11 total tests',
);
@@ -109,7 +109,7 @@ describe('Grouped Test Reports App', () => {
it('renders summary text', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.fa-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.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',
);
@@ -137,7 +137,7 @@ describe('Grouped Test Reports App', () => {
it('renders summary text', done => {
setTimeout(() => {
- expect(vm.$el.querySelector('.fa-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.spinner')).toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary contained 2 fixed test results out of 11 total tests',
);
@@ -190,7 +190,7 @@ describe('Grouped Test Reports App', () => {
});
it('renders loading summary text with loading icon', done => {
- expect(vm.$el.querySelector('.fa-spinner')).not.toBeNull();
+ expect(vm.$el.querySelector('.spinner')).not.toBeNull();
expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
'Test summary results are being parsed',
);
diff --git a/spec/javascripts/right_sidebar_spec.js b/spec/javascripts/right_sidebar_spec.js
index 992e17978c1..9565e3ce546 100644
--- a/spec/javascripts/right_sidebar_spec.js
+++ b/spec/javascripts/right_sidebar_spec.js
@@ -23,7 +23,7 @@ const assertSidebarState = function(state) {
describe('RightSidebar', function() {
describe('fixture tests', () => {
- const fixtureName = 'issues/open-issue.html.raw';
+ const fixtureName = 'issues/open-issue.html';
preloadFixtures(fixtureName);
loadJSONFixtures('todos/todos.json');
let mock;
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index 7a4ca587313..ce7fa7a52ae 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -126,9 +126,9 @@ describe('Search autocomplete dropdown', () => {
expect(list.find(mrsIHaveCreatedLink).text()).toBe("Merge requests I've created");
};
- preloadFixtures('static/search_autocomplete.html.raw');
+ preloadFixtures('static/search_autocomplete.html');
beforeEach(function() {
- loadFixtures('static/search_autocomplete.html.raw');
+ loadFixtures('static/search_autocomplete.html');
window.gon = {};
window.gon.current_user_id = userId;
diff --git a/spec/javascripts/search_spec.js b/spec/javascripts/search_spec.js
index 40bdbac7451..32f60508fa3 100644
--- a/spec/javascripts/search_spec.js
+++ b/spec/javascripts/search_spec.js
@@ -3,7 +3,7 @@ import Api from '~/api';
import Search from '~/pages/search/show/search';
describe('Search', () => {
- const fixturePath = 'search/show.html.raw';
+ const fixturePath = 'search/show.html';
const searchTerm = 'some search';
const fillDropdownInput = dropdownSelector => {
const dropdownElement = document.querySelector(dropdownSelector).parentNode;
diff --git a/spec/javascripts/settings_panels_spec.js b/spec/javascripts/settings_panels_spec.js
index 3b681a9ff28..2c5d91a45bc 100644
--- a/spec/javascripts/settings_panels_spec.js
+++ b/spec/javascripts/settings_panels_spec.js
@@ -2,10 +2,10 @@ import $ from 'jquery';
import initSettingsPanels from '~/settings_panels';
describe('Settings Panels', () => {
- preloadFixtures('groups/edit.html.raw');
+ preloadFixtures('groups/edit.html');
beforeEach(() => {
- loadFixtures('groups/edit.html.raw');
+ loadFixtures('groups/edit.html');
});
describe('initSettingsPane', () => {
diff --git a/spec/javascripts/shortcuts_spec.js b/spec/javascripts/shortcuts_spec.js
index 3ca6ecaa938..df7012bb659 100644
--- a/spec/javascripts/shortcuts_spec.js
+++ b/spec/javascripts/shortcuts_spec.js
@@ -2,7 +2,7 @@ import $ from 'jquery';
import Shortcuts from '~/behaviors/shortcuts/shortcuts';
describe('Shortcuts', () => {
- const fixtureName = 'snippets/show.html.raw';
+ const fixtureName = 'snippets/show.html';
const createEvent = (type, target) =>
$.Event(type, {
target,
diff --git a/spec/javascripts/sidebar/assignees_spec.js b/spec/javascripts/sidebar/assignees_spec.js
index eced4925489..57b16b12cb0 100644
--- a/spec/javascripts/sidebar/assignees_spec.js
+++ b/spec/javascripts/sidebar/assignees_spec.js
@@ -210,6 +210,19 @@ describe('Assignee component', () => {
expect(component.$el.querySelector('.user-list-more')).toBe(null);
});
+ it('sets tooltip container to body', () => {
+ const users = UsersMockHelper.createNumberRandomUsers(2);
+ component = new AssigneeComponent({
+ propsData: {
+ rootPath: 'http://localhost:3000',
+ users,
+ editable: true,
+ },
+ }).$mount();
+
+ expect(component.$el.querySelector('.user-link').getAttribute('data-container')).toBe('body');
+ });
+
it('Shows the "show-less" assignees label', done => {
const users = UsersMockHelper.createNumberRandomUsers(6);
component = new AssigneeComponent({
diff --git a/spec/javascripts/sidebar/sidebar_assignees_spec.js b/spec/javascripts/sidebar/sidebar_assignees_spec.js
index 3f0f67d71ca..016f5e033a5 100644
--- a/spec/javascripts/sidebar/sidebar_assignees_spec.js
+++ b/spec/javascripts/sidebar/sidebar_assignees_spec.js
@@ -11,12 +11,12 @@ describe('sidebar assignees', () => {
let vm;
let mediator;
let sidebarAssigneesEl;
- preloadFixtures('issues/open-issue.html.raw');
+ preloadFixtures('issues/open-issue.html');
beforeEach(() => {
Vue.http.interceptors.push(Mock.sidebarMockInterceptor);
- loadFixtures('issues/open-issue.html.raw');
+ loadFixtures('issues/open-issue.html');
mediator = new SidebarMediator(Mock.mediator);
spyOn(mediator, 'saveAssignees').and.callThrough();
diff --git a/spec/javascripts/signin_tabs_memoizer_spec.js b/spec/javascripts/signin_tabs_memoizer_spec.js
index 52da6a79939..ef5c774736b 100644
--- a/spec/javascripts/signin_tabs_memoizer_spec.js
+++ b/spec/javascripts/signin_tabs_memoizer_spec.js
@@ -2,7 +2,7 @@ import AccessorUtilities from '~/lib/utils/accessor';
import SigninTabsMemoizer from '~/pages/sessions/new/signin_tabs_memoizer';
describe('SigninTabsMemoizer', () => {
- const fixtureTemplate = 'static/signin_tabs.html.raw';
+ const fixtureTemplate = 'static/signin_tabs.html';
const tabSelector = 'ul.new-session-tabs';
const currentTabKey = 'current_signin_tab';
let memo;
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index b2b0a50911d..235a17d13b0 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -8,6 +8,7 @@ import '~/commons';
import Vue from 'vue';
import VueResource from 'vue-resource';
import Translate from '~/vue_shared/translate';
+import CheckEE from '~/vue_shared/mixins/is_ee';
import jasmineDiff from 'jasmine-diff';
import { getDefaultAdapter } from '~/lib/utils/axios_utils';
@@ -43,6 +44,7 @@ Vue.config.errorHandler = function(err) {
Vue.use(VueResource);
Vue.use(Translate);
+Vue.use(CheckEE);
// enable test fixtures
jasmine.getFixtures().fixturesPath = FIXTURES_PATH;
@@ -67,6 +69,7 @@ window.gl = window.gl || {};
window.gl.TEST_HOST = TEST_HOST;
window.gon = window.gon || {};
window.gon.test_env = true;
+window.gon.ee = process.env.EE;
gon.relative_url_root = '';
let hasUnhandledPromiseRejections = false;
@@ -119,19 +122,26 @@ afterEach(() => {
const axiosDefaultAdapter = getDefaultAdapter();
// render all of our tests
-const testsContext = require.context('.', true, /_spec$/);
-testsContext.keys().forEach(function(path) {
- try {
- testsContext(path);
- } catch (err) {
- console.log(err);
- console.error('[GL SPEC RUNNER ERROR] Unable to load spec: ', path);
- describe('Test bundle', function() {
- it(`includes '${path}'`, function() {
- expect(err).toBeNull();
+const testContexts = [require.context('spec', true, /_spec$/)];
+
+if (process.env.EE) {
+ testContexts.push(require.context('ee_spec', true, /_spec$/));
+}
+
+testContexts.forEach(context => {
+ context.keys().forEach(path => {
+ try {
+ context(path);
+ } catch (err) {
+ console.log(err);
+ console.error('[GL SPEC RUNNER ERROR] Unable to load spec: ', path);
+ describe('Test bundle', function() {
+ it(`includes '${path}'`, function() {
+ expect(err).toBeNull();
+ });
});
- });
- }
+ }
+ });
});
describe('test errors', () => {
@@ -201,24 +211,35 @@ if (process.env.BABEL_ENV === 'coverage') {
];
describe('Uncovered files', function() {
- const sourceFiles = require.context('~', true, /\.(js|vue)$/);
+ const sourceFilesContexts = [require.context('~', true, /\.(js|vue)$/)];
+
+ if (process.env.EE) {
+ sourceFilesContexts.push(require.context('ee', true, /\.(js|vue)$/));
+ }
+
+ const allTestFiles = testContexts.reduce(
+ (accumulator, context) => accumulator.concat(context.keys()),
+ [],
+ );
$.holdReady(true);
- sourceFiles.keys().forEach(function(path) {
- // ignore if there is a matching spec file
- if (testsContext.keys().indexOf(`${path.replace(/\.(js|vue)$/, '')}_spec`) > -1) {
- return;
- }
-
- it(`includes '${path}'`, function() {
- try {
- sourceFiles(path);
- } catch (err) {
- if (troubleMakers.indexOf(path) === -1) {
- expect(err).toBeNull();
- }
+ sourceFilesContexts.forEach(context => {
+ context.keys().forEach(path => {
+ // ignore if there is a matching spec file
+ if (allTestFiles.indexOf(`${path.replace(/\.(js|vue)$/, '')}_spec`) > -1) {
+ return;
}
+
+ it(`includes '${path}'`, function() {
+ try {
+ context(path);
+ } catch (err) {
+ if (troubleMakers.indexOf(path) === -1) {
+ expect(err).toBeNull();
+ }
+ }
+ });
});
});
});
diff --git a/spec/javascripts/todos_spec.js b/spec/javascripts/todos_spec.js
index 69e43274250..802f54f6a7e 100644
--- a/spec/javascripts/todos_spec.js
+++ b/spec/javascripts/todos_spec.js
@@ -3,11 +3,11 @@ import Todos from '~/pages/dashboard/todos/index/todos';
import '~/lib/utils/common_utils';
describe('Todos', () => {
- preloadFixtures('todos/todos.html.raw');
+ preloadFixtures('todos/todos.html');
let todoItem;
beforeEach(() => {
- loadFixtures('todos/todos.html.raw');
+ loadFixtures('todos/todos.html');
todoItem = document.querySelector('.todos-list .todo');
return new Todos();
diff --git a/spec/javascripts/u2f/authenticate_spec.js b/spec/javascripts/u2f/authenticate_spec.js
index ddb09811dda..8f9cb270729 100644
--- a/spec/javascripts/u2f/authenticate_spec.js
+++ b/spec/javascripts/u2f/authenticate_spec.js
@@ -4,10 +4,10 @@ import 'vendor/u2f';
import MockU2FDevice from './mock_u2f_device';
describe('U2FAuthenticate', function() {
- preloadFixtures('u2f/authenticate.html.raw');
+ preloadFixtures('u2f/authenticate.html');
beforeEach(() => {
- loadFixtures('u2f/authenticate.html.raw');
+ loadFixtures('u2f/authenticate.html');
this.u2fDevice = new MockU2FDevice();
this.container = $('#js-authenticate-u2f');
this.component = new U2FAuthenticate(
diff --git a/spec/javascripts/u2f/register_spec.js b/spec/javascripts/u2f/register_spec.js
index 261db3d66d7..a75ceca9f4c 100644
--- a/spec/javascripts/u2f/register_spec.js
+++ b/spec/javascripts/u2f/register_spec.js
@@ -4,10 +4,10 @@ import 'vendor/u2f';
import MockU2FDevice from './mock_u2f_device';
describe('U2FRegister', function() {
- preloadFixtures('u2f/register.html.raw');
+ preloadFixtures('u2f/register.html');
beforeEach(done => {
- loadFixtures('u2f/register.html.raw');
+ loadFixtures('u2f/register.html');
this.u2fDevice = new MockU2FDevice();
this.container = $('#js-register-u2f');
this.component = new U2FRegister(this.container, $('#js-register-u2f-templates'), {}, 'token');
diff --git a/spec/javascripts/user_popovers_spec.js b/spec/javascripts/user_popovers_spec.js
index b174a51c1a0..c0d5ee9c446 100644
--- a/spec/javascripts/user_popovers_spec.js
+++ b/spec/javascripts/user_popovers_spec.js
@@ -2,7 +2,7 @@ import initUserPopovers from '~/user_popovers';
import UsersCache from '~/lib/utils/users_cache';
describe('User Popovers', () => {
- const fixtureTemplate = 'merge_requests/diff_comment.html.raw';
+ const fixtureTemplate = 'merge_requests/diff_comment.html';
preloadFixtures(fixtureTemplate);
const selector = '.js-user-link';
diff --git a/spec/javascripts/vue_mr_widget/components/deployment_spec.js b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
index e355416bd27..42bf3b7df09 100644
--- a/spec/javascripts/vue_mr_widget/components/deployment_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/deployment_spec.js
@@ -6,32 +6,36 @@ import mountComponent from '../../helpers/vue_mount_component_helper';
describe('Deployment component', () => {
const Component = Vue.extend(deploymentComponent);
- const deploymentMockData = {
- id: 15,
- name: 'review/diplo',
- url: '/root/review-apps/environments/15',
- stop_url: '/root/review-apps/environments/15/stop',
- metrics_url: '/root/review-apps/environments/15/deployments/1/metrics',
- metrics_monitoring_url: '/root/review-apps/environments/15/metrics',
- external_url: 'http://gitlab.com.',
- external_url_formatted: 'gitlab',
- deployed_at: '2017-03-22T22:44:42.258Z',
- deployed_at_formatted: 'Mar 22, 2017 10:44pm',
- changes: [
- {
- path: 'index.html',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
- },
- {
- path: 'imgs/gallery.html',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
- },
- {
- path: 'about/',
- external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
- },
- ],
- };
+ let deploymentMockData;
+
+ beforeEach(() => {
+ deploymentMockData = {
+ id: 15,
+ name: 'review/diplo',
+ url: '/root/review-apps/environments/15',
+ stop_url: '/root/review-apps/environments/15/stop',
+ metrics_url: '/root/review-apps/environments/15/deployments/1/metrics',
+ metrics_monitoring_url: '/root/review-apps/environments/15/metrics',
+ external_url: 'http://gitlab.com.',
+ external_url_formatted: 'gitlab',
+ deployed_at: '2017-03-22T22:44:42.258Z',
+ deployed_at_formatted: 'Mar 22, 2017 10:44pm',
+ changes: [
+ {
+ path: 'index.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/index.html',
+ },
+ {
+ path: 'imgs/gallery.html',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/imgs/gallery.html',
+ },
+ {
+ path: 'about/',
+ external_url: 'http://root-master-patch-91341.volatile-watch.surge.sh/about/',
+ },
+ ],
+ };
+ });
let vm;
@@ -207,6 +211,31 @@ describe('Deployment component', () => {
});
});
+ describe('with a single change', () => {
+ beforeEach(() => {
+ deploymentMockData.changes = deploymentMockData.changes.slice(0, 1);
+
+ vm = mountComponent(Component, {
+ deployment: { ...deploymentMockData },
+ showMetrics: true,
+ });
+ });
+
+ it('renders the link to the review app without dropdown', () => {
+ expect(vm.$el.querySelector('.js-mr-wigdet-deployment-dropdown')).toBeNull();
+ expect(vm.$el.querySelector('.js-deploy-url-feature-flag')).not.toBeNull();
+ });
+
+ it('renders the link to the review app linked to to the first change', () => {
+ const expectedUrl = deploymentMockData.changes[0].external_url;
+ const deployUrl = vm.$el.querySelector('.js-deploy-url-feature-flag');
+
+ expect(vm.$el.querySelector('.js-mr-wigdet-deployment-dropdown')).toBeNull();
+ expect(deployUrl).not.toBeNull();
+ expect(deployUrl.href).toEqual(expectedUrl);
+ });
+ });
+
describe('deployment status', () => {
describe('running', () => {
beforeEach(() => {
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
index d905bbe4040..de213210cfc 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import pipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
+import { trimText } from 'spec/helpers/vue_component_helper';
import mockData from '../mock_data';
describe('MRWidgetPipeline', () => {
@@ -123,7 +124,7 @@ describe('MRWidgetPipeline', () => {
describe('without commit path', () => {
beforeEach(() => {
- const mockCopy = Object.assign({}, mockData);
+ const mockCopy = JSON.parse(JSON.stringify(mockData));
delete mockCopy.pipeline.commit;
vm = mountComponent(Component, {
@@ -164,7 +165,7 @@ describe('MRWidgetPipeline', () => {
describe('without coverage', () => {
it('should not render a coverage', () => {
- const mockCopy = Object.assign({}, mockData);
+ const mockCopy = JSON.parse(JSON.stringify(mockData));
delete mockCopy.pipeline.coverage;
vm = mountComponent(Component, {
@@ -180,7 +181,7 @@ describe('MRWidgetPipeline', () => {
describe('without a pipeline graph', () => {
it('should not render a pipeline graph', () => {
- const mockCopy = Object.assign({}, mockData);
+ const mockCopy = JSON.parse(JSON.stringify(mockData));
delete mockCopy.pipeline.details.stages;
vm = mountComponent(Component, {
@@ -193,5 +194,81 @@ describe('MRWidgetPipeline', () => {
expect(vm.$el.querySelector('.js-mini-pipeline-graph')).toEqual(null);
});
});
+
+ describe('without pipeline.merge_request', () => {
+ it('should render info that includes the commit and branch details', () => {
+ const mockCopy = JSON.parse(JSON.stringify(mockData));
+ delete mockCopy.pipeline.merge_request;
+ const { pipeline } = mockCopy;
+
+ vm = mountComponent(Component, {
+ pipeline,
+ hasCi: true,
+ ciStatus: 'success',
+ troubleshootingDocsPath: 'help',
+ sourceBranchLink: mockCopy.source_branch_link,
+ });
+
+ const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${
+ pipeline.commit.short_id
+ } on ${mockCopy.source_branch_link}`;
+
+ const actual = trimText(vm.$el.querySelector('.js-pipeline-info-container').innerText);
+
+ expect(actual).toBe(expected);
+ });
+ });
+
+ describe('with pipeline.merge_request and flags.merge_request_pipeline', () => {
+ it('should render info that includes the commit, MR, source branch, and target branch details', () => {
+ const mockCopy = JSON.parse(JSON.stringify(mockData));
+ const { pipeline } = mockCopy;
+ pipeline.flags.merge_request_pipeline = true;
+ pipeline.flags.detached_merge_request_pipeline = false;
+
+ vm = mountComponent(Component, {
+ pipeline,
+ hasCi: true,
+ ciStatus: 'success',
+ troubleshootingDocsPath: 'help',
+ sourceBranchLink: mockCopy.source_branch_link,
+ });
+
+ const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${
+ pipeline.commit.short_id
+ } on !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch} into ${
+ pipeline.merge_request.target_branch
+ }`;
+
+ const actual = trimText(vm.$el.querySelector('.js-pipeline-info-container').innerText);
+
+ expect(actual).toBe(expected);
+ });
+ });
+
+ describe('with pipeline.merge_request and flags.detached_merge_request_pipeline', () => {
+ it('should render info that includes the commit, MR, and source branch details', () => {
+ const mockCopy = JSON.parse(JSON.stringify(mockData));
+ const { pipeline } = mockCopy;
+ pipeline.flags.merge_request_pipeline = false;
+ pipeline.flags.detached_merge_request_pipeline = true;
+
+ vm = mountComponent(Component, {
+ pipeline,
+ hasCi: true,
+ ciStatus: 'success',
+ troubleshootingDocsPath: 'help',
+ sourceBranchLink: mockCopy.source_branch_link,
+ });
+
+ const expected = `Pipeline #${pipeline.id} ${pipeline.details.status.label} for ${
+ pipeline.commit.short_id
+ } on !${pipeline.merge_request.iid} with ${pipeline.merge_request.source_branch}`;
+
+ const actual = trimText(vm.$el.querySelector('.js-pipeline-info-container').innerText);
+
+ expect(actual).toBe(expected);
+ });
+ });
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
index a0a336ae604..f622f52a7b9 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_status_icon_spec.js
@@ -18,7 +18,7 @@ describe('MR widget status icon component', () => {
it('renders loading icon', () => {
vm = mountComponent(Component, { status: 'loading' });
- expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner');
+ expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('spinner');
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
index eb4fa0df727..d93badf8cd3 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_auto_merge_failed_spec.js
@@ -38,7 +38,7 @@ describe('MRWidgetAutoMergeFailed', () => {
Vue.nextTick(() => {
expect(vm.$el.querySelector('button').getAttribute('disabled')).toEqual('disabled');
- expect(vm.$el.querySelector('button i').classList).toContain('fa-spinner');
+ expect(vm.$el.querySelector('button .loading-container span').classList).toContain('spinner');
done();
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
index 7da27bb8890..96e512d222a 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_checking_spec.js
@@ -20,7 +20,7 @@ describe('MRWidgetChecking', () => {
});
it('renders loading icon', () => {
- expect(vm.$el.querySelector('.mr-widget-icon i').classList).toContain('fa-spinner');
+ expect(vm.$el.querySelector('.mr-widget-icon span').classList).toContain('spinner');
});
it('renders information about merging', () => {
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_commits_header_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_commits_header_spec.js
deleted file mode 100644
index 5cf6408cf34..00000000000
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_commits_header_spec.js
+++ /dev/null
@@ -1,110 +0,0 @@
-import { createLocalVue, shallowMount } from '@vue/test-utils';
-import CommitsHeader from '~/vue_merge_request_widget/components/states/commits_header.vue';
-import Icon from '~/vue_shared/components/icon.vue';
-
-const localVue = createLocalVue();
-
-describe('Commits header component', () => {
- let wrapper;
-
- const createComponent = props => {
- wrapper = shallowMount(localVue.extend(CommitsHeader), {
- localVue,
- sync: false,
- propsData: {
- isSquashEnabled: false,
- targetBranch: 'master',
- commitsCount: 5,
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- const findHeaderWrapper = () => wrapper.find('.js-mr-widget-commits-count');
- const findCommitToggle = () => wrapper.find('.commit-edit-toggle');
- const findIcon = () => wrapper.find(Icon);
- const findCommitsCountMessage = () => wrapper.find('.commits-count-message');
- const findTargetBranchMessage = () => wrapper.find('.label-branch');
- const findModifyButton = () => wrapper.find('.modify-message-button');
-
- describe('when collapsed', () => {
- it('toggle has aria-label equal to Expand', () => {
- createComponent();
-
- expect(findCommitToggle().attributes('aria-label')).toBe('Expand');
- });
-
- it('has a chevron-right icon', () => {
- createComponent();
- wrapper.setData({ expanded: false });
-
- expect(findIcon().props('name')).toBe('chevron-right');
- });
-
- describe('when squash is disabled', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('has commits count message showing correct amount of commits', () => {
- expect(findCommitsCountMessage().text()).toBe('5 commits');
- });
-
- it('has button with modify merge commit message', () => {
- expect(findModifyButton().text()).toBe('Modify merge commit');
- });
- });
-
- describe('when squash is enabled', () => {
- beforeEach(() => {
- createComponent({ isSquashEnabled: true });
- });
-
- it('has commits count message showing one commit when squash is enabled', () => {
- expect(findCommitsCountMessage().text()).toBe('1 commit');
- });
-
- it('has button with modify commit messages text', () => {
- expect(findModifyButton().text()).toBe('Modify commit messages');
- });
- });
-
- it('has correct target branch displayed', () => {
- createComponent();
-
- expect(findTargetBranchMessage().text()).toBe('master');
- });
- });
-
- describe('when expanded', () => {
- beforeEach(() => {
- createComponent();
- wrapper.setData({ expanded: true });
- });
-
- it('toggle has aria-label equal to collapse', done => {
- wrapper.vm.$nextTick(() => {
- expect(findCommitToggle().attributes('aria-label')).toBe('Collapse');
- done();
- });
- });
-
- it('has a chevron-down icon', done => {
- wrapper.vm.$nextTick(() => {
- expect(findIcon().props('name')).toBe('chevron-down');
- done();
- });
- });
-
- it('has a collapse text', done => {
- wrapper.vm.$nextTick(() => {
- expect(findHeaderWrapper().text()).toBe('Collapse');
- done();
- });
- });
- });
-});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
index 3229ddd5e27..780bed1bf69 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_failed_to_merge_spec.js
@@ -120,7 +120,7 @@ describe('MRWidgetFailedToMerge', () => {
it('renders given error', () => {
expect(vm.$el.querySelector('.has-error-message').textContent.trim()).toEqual(
- 'Merge error happened.',
+ 'Merge error happened',
);
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
index 631da202d1d..368c997d318 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js
@@ -18,6 +18,7 @@ const createTestMr = customConfig => {
isPipelinePassing: false,
isMergeAllowed: true,
onlyAllowMergeIfPipelineSucceeds: false,
+ ffOnlyEnabled: false,
hasCI: false,
ciStatus: null,
sha: '12345678',
@@ -376,11 +377,29 @@ describe('ReadyToMerge', () => {
});
describe('initiateMergePolling', () => {
+ beforeEach(() => {
+ jasmine.clock().install();
+ });
+
+ afterEach(() => {
+ jasmine.clock().uninstall();
+ });
+
it('should call simplePoll', () => {
const simplePoll = spyOnDependency(ReadyToMerge, 'simplePoll');
vm.initiateMergePolling();
- expect(simplePoll).toHaveBeenCalled();
+ expect(simplePoll).toHaveBeenCalledWith(jasmine.any(Function), { timeout: 0 });
+ });
+
+ it('should call handleMergePolling', () => {
+ spyOn(vm, 'handleMergePolling');
+
+ vm.initiateMergePolling();
+
+ jasmine.clock().tick(2000);
+
+ expect(vm.handleMergePolling).toHaveBeenCalled();
});
});
@@ -396,7 +415,7 @@ describe('ReadyToMerge', () => {
});
beforeEach(() => {
- loadFixtures('merge_requests/merge_request_of_current_user.html.raw');
+ loadFixtures('merge_requests/merge_request_of_current_user.html');
});
it('should call start and stop polling when MR merged', done => {
@@ -624,6 +643,10 @@ describe('ReadyToMerge', () => {
const findCommitsHeaderElement = () => wrapper.find(CommitsHeader);
const findCommitEditElements = () => wrapper.findAll(CommitEdit);
const findCommitDropdownElement = () => wrapper.find(CommitMessageDropdown);
+ const findFirstCommitEditLabel = () =>
+ findCommitEditElements()
+ .at(0)
+ .props('label');
describe('squash checkbox', () => {
it('should be rendered when squash before merge is enabled and there is more than 1 commit', () => {
@@ -648,32 +671,130 @@ describe('ReadyToMerge', () => {
});
describe('commits count collapsible header', () => {
- it('should be rendered if fast-forward is disabled', () => {
+ it('should be rendered when fast-forward is disabled', () => {
createLocalComponent();
expect(findCommitsHeaderElement().exists()).toBeTruthy();
});
- it('should not be rendered if fast-forward is enabled', () => {
- createLocalComponent({ mr: { ffOnlyEnabled: true } });
+ describe('when fast-forward is enabled', () => {
+ it('should be rendered if squash and squash before are enabled and there is more than 1 commit', () => {
+ createLocalComponent({
+ mr: {
+ ffOnlyEnabled: true,
+ enableSquashBeforeMerge: true,
+ squash: true,
+ commitsCount: 2,
+ },
+ });
+
+ expect(findCommitsHeaderElement().exists()).toBeTruthy();
+ });
+
+ it('should not be rendered if squash before merge is disabled', () => {
+ createLocalComponent({
+ mr: {
+ ffOnlyEnabled: true,
+ enableSquashBeforeMerge: false,
+ squash: true,
+ commitsCount: 2,
+ },
+ });
+
+ expect(findCommitsHeaderElement().exists()).toBeFalsy();
+ });
+
+ it('should not be rendered if squash is disabled', () => {
+ createLocalComponent({
+ mr: {
+ ffOnlyEnabled: true,
+ squash: false,
+ enableSquashBeforeMerge: true,
+ commitsCount: 2,
+ },
+ });
+
+ expect(findCommitsHeaderElement().exists()).toBeFalsy();
+ });
+
+ it('should not be rendered if commits count is 1', () => {
+ createLocalComponent({
+ mr: {
+ ffOnlyEnabled: true,
+ squash: true,
+ enableSquashBeforeMerge: true,
+ commitsCount: 1,
+ },
+ });
- expect(findCommitsHeaderElement().exists()).toBeFalsy();
+ expect(findCommitsHeaderElement().exists()).toBeFalsy();
+ });
});
});
describe('commits edit components', () => {
+ describe('when fast-forward merge is enabled', () => {
+ it('should not be rendered if squash is disabled', () => {
+ createLocalComponent({
+ mr: {
+ ffOnlyEnabled: true,
+ squash: false,
+ enableSquashBeforeMerge: true,
+ commitsCount: 2,
+ },
+ });
+
+ expect(findCommitEditElements().length).toBe(0);
+ });
+
+ it('should not be rendered if squash before merge is disabled', () => {
+ createLocalComponent({
+ mr: {
+ ffOnlyEnabled: true,
+ squash: true,
+ enableSquashBeforeMerge: false,
+ commitsCount: 2,
+ },
+ });
+
+ expect(findCommitEditElements().length).toBe(0);
+ });
+
+ it('should not be rendered if there is only one commit', () => {
+ createLocalComponent({
+ mr: {
+ ffOnlyEnabled: true,
+ squash: true,
+ enableSquashBeforeMerge: true,
+ commitsCount: 1,
+ },
+ });
+
+ expect(findCommitEditElements().length).toBe(0);
+ });
+
+ it('should have one edit component if squash is enabled and there is more than 1 commit', () => {
+ createLocalComponent({
+ mr: {
+ ffOnlyEnabled: true,
+ squash: true,
+ enableSquashBeforeMerge: true,
+ commitsCount: 2,
+ },
+ });
+
+ expect(findCommitEditElements().length).toBe(1);
+ expect(findFirstCommitEditLabel()).toBe('Squash commit message');
+ });
+ });
+
it('should have one edit component when squash is disabled', () => {
createLocalComponent();
expect(findCommitEditElements().length).toBe(1);
});
- const findFirstCommitEditLabel = () =>
- findCommitEditElements()
- .at(0)
- .props('label');
-
- it('should have two edit components when squash is enabled', () => {
+ it('should have two edit components when squash is enabled and there is more than 1 commit', () => {
createLocalComponent({
mr: {
commitsCount: 2,
@@ -685,6 +806,18 @@ describe('ReadyToMerge', () => {
expect(findCommitEditElements().length).toBe(2);
});
+ it('should have one edit components when squash is enabled and there is 1 commit only', () => {
+ createLocalComponent({
+ mr: {
+ commitsCount: 1,
+ squash: true,
+ enableSquashBeforeMerge: true,
+ },
+ });
+
+ expect(findCommitEditElements().length).toBe(1);
+ });
+
it('should have correct edit merge commit label', () => {
createLocalComponent();
@@ -711,8 +844,10 @@ describe('ReadyToMerge', () => {
expect(findCommitDropdownElement().exists()).toBeFalsy();
});
- it('should be rendered if squash is enabled', () => {
- createLocalComponent({ mr: { squash: true } });
+ it('should be rendered if squash is enabled and there is more than 1 commit', () => {
+ createLocalComponent({
+ mr: { enableSquashBeforeMerge: true, squash: true, commitsCount: 2 },
+ });
expect(findCommitDropdownElement().exists()).toBeTruthy();
});
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 6ef07f81705..7ab203a6011 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -134,6 +134,8 @@ export default {
yaml_errors: false,
retryable: true,
cancelable: false,
+ merge_request_pipeline: false,
+ detached_merge_request_pipeline: true,
},
ref: {
name: 'daaaa',
@@ -141,6 +143,15 @@ export default {
tag: false,
branch: true,
},
+ merge_request: {
+ iid: 1,
+ path: '/root/detached-merge-request-pipelines/merge_requests/1',
+ title: 'Update README.md',
+ source_branch: 'feature-1',
+ source_branch_path: '/root/detached-merge-request-pipelines/branches/feature-1',
+ target_branch: 'master',
+ target_branch_path: '/root/detached-merge-request-pipelines/branches/master',
+ },
commit: {
id: '104096c51715e12e7ae41f9333e9fa35b73f385d',
short_id: '104096c5',
diff --git a/spec/javascripts/vue_shared/components/commit_spec.js b/spec/javascripts/vue_shared/components/commit_spec.js
index 18fcdf7ede1..f2e20f626b5 100644
--- a/spec/javascripts/vue_shared/components/commit_spec.js
+++ b/spec/javascripts/vue_shared/components/commit_spec.js
@@ -61,7 +61,7 @@ describe('Commit component', () => {
});
it('should render a tag icon if it represents a tag', () => {
- expect(component.$el.querySelector('.icon-container i').classList).toContain('fa-tag');
+ expect(component.$el.querySelector('.icon-container svg.ic-tag')).not.toBeNull();
});
it('should render a link to the ref url', () => {
@@ -143,4 +143,92 @@ describe('Commit component', () => {
);
});
});
+
+ describe('When commit ref is provided, but merge ref is not', () => {
+ it('should render the commit ref', () => {
+ props = {
+ tag: false,
+ commitRef: {
+ name: 'master',
+ ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
+ },
+ commitUrl:
+ 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067',
+ shortSha: 'b7836edd',
+ title: null,
+ author: {},
+ };
+
+ component = mountComponent(CommitComponent, props);
+ const refEl = component.$el.querySelector('.ref-name');
+
+ expect(refEl.textContent).toContain('master');
+
+ expect(refEl.href).toBe(props.commitRef.ref_url);
+
+ expect(refEl.getAttribute('data-original-title')).toBe(props.commitRef.name);
+
+ expect(component.$el.querySelector('.icon-container .ic-branch')).not.toBeNull();
+ });
+ });
+
+ describe('When both commit and merge ref are provided', () => {
+ it('should render the merge ref', () => {
+ props = {
+ tag: false,
+ commitRef: {
+ name: 'master',
+ ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
+ },
+ commitUrl:
+ 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067',
+ mergeRequestRef: {
+ iid: 1234,
+ path: 'https://example.com/path/to/mr',
+ title: 'Test MR',
+ },
+ shortSha: 'b7836edd',
+ title: null,
+ author: {},
+ };
+
+ component = mountComponent(CommitComponent, props);
+ const refEl = component.$el.querySelector('.ref-name');
+
+ expect(refEl.textContent).toContain('1234');
+
+ expect(refEl.href).toBe(props.mergeRequestRef.path);
+
+ expect(refEl.getAttribute('data-original-title')).toBe(props.mergeRequestRef.title);
+
+ expect(component.$el.querySelector('.icon-container .ic-git-merge')).not.toBeNull();
+ });
+ });
+
+ describe('When showRefInfo === false', () => {
+ it('should not render any ref info', () => {
+ props = {
+ tag: false,
+ commitRef: {
+ name: 'master',
+ ref_url: 'http://localhost/namespace2/gitlabhq/tree/master',
+ },
+ commitUrl:
+ 'https://gitlab.com/gitlab-org/gitlab-ce/commit/b7836eddf62d663c665769e1b0960197fd215067',
+ mergeRequestRef: {
+ iid: 1234,
+ path: '/path/to/mr',
+ title: 'Test MR',
+ },
+ shortSha: 'b7836edd',
+ title: null,
+ author: {},
+ showRefInfo: false,
+ };
+
+ component = mountComponent(CommitComponent, props);
+
+ expect(component.$el.querySelector('.ref-name')).toBeNull();
+ });
+ });
});
diff --git a/spec/javascripts/vue_shared/components/file_icon_spec.js b/spec/javascripts/vue_shared/components/file_icon_spec.js
index 34c9b35e02a..5bea8c43da3 100644
--- a/spec/javascripts/vue_shared/components/file_icon_spec.js
+++ b/spec/javascripts/vue_shared/components/file_icon_spec.js
@@ -70,12 +70,9 @@ describe('File Icon component', () => {
loading: true,
});
- const { classList } = vm.$el.querySelector('i');
+ const { classList } = vm.$el.querySelector('.loading-container span');
- expect(classList.contains('fa')).toEqual(true);
- expect(classList.contains('fa-spin')).toEqual(true);
- expect(classList.contains('fa-spinner')).toEqual(true);
- expect(classList.contains('fa-1x')).toEqual(true);
+ expect(classList.contains('spinner')).toEqual(true);
});
it('should add a special class and a size class', () => {
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 7a741bdc067..a9c1a67b39b 100644
--- a/spec/javascripts/vue_shared/components/header_ci_component_spec.js
+++ b/spec/javascripts/vue_shared/components/header_ci_component_spec.js
@@ -88,7 +88,7 @@ describe('Header CI Component', () => {
vm.actions[0].isLoading = true;
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.btn .fa-spinner').getAttribute('style')).toBeFalsy();
+ expect(vm.$el.querySelector('.btn .spinner').getAttribute('style')).toBeFalsy();
done();
});
});
diff --git a/spec/javascripts/vue_shared/components/issue/related_issuable_item_spec.js b/spec/javascripts/vue_shared/components/issue/related_issuable_item_spec.js
new file mode 100644
index 00000000000..42198e92eea
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/issue/related_issuable_item_spec.js
@@ -0,0 +1,194 @@
+import Vue from 'vue';
+import { mount, createLocalVue } from '@vue/test-utils';
+import RelatedIssuableItem from '~/vue_shared/components/issue/related_issuable_item.vue';
+import { defaultMilestone, defaultAssignees } from './related_issuable_mock_data';
+
+describe('RelatedIssuableItem', () => {
+ let wrapper;
+ const props = {
+ idKey: 1,
+ displayReference: 'gitlab-org/gitlab-test#1',
+ pathIdSeparator: '#',
+ path: `${gl.TEST_HOST}/path`,
+ title: 'title',
+ confidential: true,
+ dueDate: '1990-12-31',
+ weight: 10,
+ createdAt: '2018-12-01T00:00:00.00Z',
+ milestone: defaultMilestone,
+ assignees: defaultAssignees,
+ eventNamespace: 'relatedIssue',
+ };
+ const slots = {
+ dueDate: '<div class="js-due-date-slot"></div>',
+ weight: '<div class="js-weight-slot"></div>',
+ };
+
+ beforeEach(() => {
+ const localVue = createLocalVue();
+
+ wrapper = mount(localVue.extend(RelatedIssuableItem), {
+ localVue,
+ slots,
+ sync: false,
+ propsData: props,
+ });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('contains issuable-info-container class when canReorder is false', () => {
+ expect(wrapper.props('canReorder')).toBe(false);
+ expect(wrapper.find('.issuable-info-container').exists()).toBe(true);
+ });
+
+ it('does not render token state', () => {
+ expect(wrapper.find('.text-secondary svg').exists()).toBe(false);
+ });
+
+ it('does not render remove button', () => {
+ expect(wrapper.find({ ref: 'removeButton' }).exists()).toBe(false);
+ });
+
+ describe('token title', () => {
+ it('links to computedPath', () => {
+ expect(wrapper.find('.item-title a').attributes('href')).toEqual(wrapper.props('path'));
+ });
+
+ it('renders confidential icon', () => {
+ expect(wrapper.find('.confidential-icon').exists()).toBe(true);
+ });
+
+ it('renders title', () => {
+ expect(wrapper.find('.item-title a').text()).toEqual(props.title);
+ });
+ });
+
+ describe('token state', () => {
+ let tokenState;
+
+ beforeEach(done => {
+ wrapper.setProps({ state: 'opened' });
+
+ Vue.nextTick(() => {
+ tokenState = wrapper.find('.issue-token-state-icon-open');
+
+ done();
+ });
+ });
+
+ it('renders if hasState', () => {
+ expect(tokenState.exists()).toBe(true);
+ });
+
+ it('renders state title', () => {
+ const stateTitle = tokenState.attributes('data-original-title');
+
+ expect(stateTitle).toContain('<span class="bold">Opened</span>');
+ expect(stateTitle).toContain(
+ '<span class="text-tertiary">Dec 1, 2018 12:00am GMT+0000</span>',
+ );
+ });
+
+ it('renders aria label', () => {
+ expect(tokenState.attributes('aria-label')).toEqual('opened');
+ });
+
+ it('renders open icon when open state', () => {
+ expect(tokenState.classes('issue-token-state-icon-open')).toBe(true);
+ });
+
+ it('renders close icon when close state', done => {
+ wrapper.setProps({
+ state: 'closed',
+ closedAt: '2018-12-01T00:00:00.00Z',
+ });
+
+ Vue.nextTick(() => {
+ expect(tokenState.classes('issue-token-state-icon-closed')).toBe(true);
+
+ done();
+ });
+ });
+ });
+
+ describe('token metadata', () => {
+ let tokenMetadata;
+
+ beforeEach(done => {
+ Vue.nextTick(() => {
+ tokenMetadata = wrapper.find('.item-meta');
+
+ done();
+ });
+ });
+
+ it('renders item path and ID', () => {
+ const pathAndID = tokenMetadata.find('.item-path-id').text();
+
+ expect(pathAndID).toContain('gitlab-org/gitlab-test');
+ expect(pathAndID).toContain('#1');
+ });
+
+ it('renders milestone icon and name', () => {
+ const milestoneIcon = tokenMetadata.find('.item-milestone svg use');
+ const milestoneTitle = tokenMetadata.find('.item-milestone .milestone-title');
+
+ expect(milestoneIcon.attributes('href')).toContain('clock');
+ expect(milestoneTitle.text()).toContain('Milestone title');
+ });
+
+ it('renders due date component', () => {
+ expect(tokenMetadata.find('.js-due-date-slot').exists()).toBe(true);
+ });
+
+ it('renders weight component', () => {
+ expect(tokenMetadata.find('.js-weight-slot').exists()).toBe(true);
+ });
+ });
+
+ describe('token assignees', () => {
+ it('renders assignees avatars', () => {
+ expect(wrapper.findAll('.item-assignees .user-avatar-link').length).toBe(2);
+ expect(wrapper.find('.item-assignees .avatar-counter').text()).toContain('+2');
+ });
+ });
+
+ describe('remove button', () => {
+ let removeBtn;
+
+ beforeEach(done => {
+ wrapper.setProps({ canRemove: true });
+ Vue.nextTick(() => {
+ removeBtn = wrapper.find({ ref: 'removeButton' });
+
+ done();
+ });
+ });
+
+ it('renders if canRemove', () => {
+ expect(removeBtn.exists()).toBe(true);
+ });
+
+ it('renders disabled button when removeDisabled', done => {
+ wrapper.vm.removeDisabled = true;
+
+ Vue.nextTick(() => {
+ expect(removeBtn.attributes('disabled')).toEqual('disabled');
+
+ done();
+ });
+ });
+
+ it('triggers onRemoveRequest when clicked', () => {
+ removeBtn.trigger('click');
+
+ const { relatedIssueRemoveRequest } = wrapper.emitted();
+
+ expect(relatedIssueRemoveRequest.length).toBe(1);
+ expect(relatedIssueRemoveRequest[0]).toEqual([props.idKey]);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/issue/related_issuable_mock_data.js b/spec/javascripts/vue_shared/components/issue/related_issuable_mock_data.js
new file mode 100644
index 00000000000..26bfdd7551e
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/issue/related_issuable_mock_data.js
@@ -0,0 +1,111 @@
+export const defaultProps = {
+ endpoint: '/foo/bar/issues/1/related_issues',
+ currentNamespacePath: 'foo',
+ currentProjectPath: 'bar',
+};
+
+export const issuable1 = {
+ id: 200,
+ epic_issue_id: 1,
+ confidential: false,
+ reference: 'foo/bar#123',
+ displayReference: '#123',
+ title: 'some title',
+ path: '/foo/bar/issues/123',
+ state: 'opened',
+};
+
+export const issuable2 = {
+ id: 201,
+ epic_issue_id: 2,
+ confidential: false,
+ reference: 'foo/bar#124',
+ displayReference: '#124',
+ title: 'some other thing',
+ path: '/foo/bar/issues/124',
+ state: 'opened',
+};
+
+export const issuable3 = {
+ id: 202,
+ epic_issue_id: 3,
+ confidential: false,
+ reference: 'foo/bar#125',
+ displayReference: '#125',
+ title: 'some other other thing',
+ path: '/foo/bar/issues/125',
+ state: 'opened',
+};
+
+export const issuable4 = {
+ id: 203,
+ epic_issue_id: 4,
+ confidential: false,
+ reference: 'foo/bar#126',
+ displayReference: '#126',
+ title: 'some other other other thing',
+ path: '/foo/bar/issues/126',
+ state: 'opened',
+};
+
+export const issuable5 = {
+ id: 204,
+ epic_issue_id: 5,
+ confidential: false,
+ reference: 'foo/bar#127',
+ displayReference: '#127',
+ title: 'some other other other thing',
+ path: '/foo/bar/issues/127',
+ state: 'opened',
+};
+
+export const defaultMilestone = {
+ id: 1,
+ state: 'active',
+ title: 'Milestone title',
+ start_date: '2018-01-01',
+ due_date: '2019-12-31',
+};
+
+export const defaultAssignees = [
+ {
+ id: 1,
+ name: 'Administrator',
+ username: 'root',
+ state: 'active',
+ avatar_url: `${gl.TEST_HOST}`,
+ web_url: `${gl.TEST_HOST}/root`,
+ status_tooltip_html: null,
+ path: '/root',
+ },
+ {
+ id: 13,
+ name: 'Brooks Beatty',
+ username: 'brynn_champlin',
+ state: 'active',
+ avatar_url: `${gl.TEST_HOST}`,
+ web_url: `${gl.TEST_HOST}/brynn_champlin`,
+ status_tooltip_html: null,
+ path: '/brynn_champlin',
+ },
+ {
+ id: 6,
+ name: 'Bryce Turcotte',
+ username: 'melynda',
+ state: 'active',
+ avatar_url: `${gl.TEST_HOST}`,
+ web_url: `${gl.TEST_HOST}/melynda`,
+ status_tooltip_html: null,
+ path: '/melynda',
+ },
+ {
+ id: 20,
+ name: 'Conchita Eichmann',
+ username: 'juliana_gulgowski',
+ state: 'active',
+ avatar_url: `${gl.TEST_HOST}`,
+ web_url: `${gl.TEST_HOST}/juliana_gulgowski`,
+ status_tooltip_html: null,
+ path: '/juliana_gulgowski',
+ },
+];
diff --git a/spec/javascripts/vue_shared/components/markdown/field_spec.js b/spec/javascripts/vue_shared/components/markdown/field_spec.js
index 79e0e756a7a..02d73e1849a 100644
--- a/spec/javascripts/vue_shared/components/markdown/field_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/field_spec.js
@@ -5,7 +5,7 @@ import fieldComponent from '~/vue_shared/components/markdown/field.vue';
function assertMarkdownTabs(isWrite, writeLink, previewLink, vm) {
expect(writeLink.parentNode.classList.contains('active')).toEqual(isWrite);
expect(previewLink.parentNode.classList.contains('active')).toEqual(!isWrite);
- expect(vm.$el.querySelector('.md-preview').style.display).toEqual(isWrite ? 'none' : '');
+ expect(vm.$el.querySelector('.md-preview-holder').style.display).toEqual(isWrite ? 'none' : '');
}
describe('Markdown field component', () => {
@@ -80,7 +80,9 @@ describe('Markdown field component', () => {
previewLink.click();
Vue.nextTick(() => {
- expect(vm.$el.querySelector('.md-preview').textContent.trim()).toContain('Loading…');
+ expect(vm.$el.querySelector('.md-preview-holder').textContent.trim()).toContain(
+ 'Loading…',
+ );
done();
});
@@ -90,7 +92,7 @@ describe('Markdown field component', () => {
previewLink.click();
setTimeout(() => {
- expect(vm.$el.querySelector('.md-preview').innerHTML).toContain(
+ expect(vm.$el.querySelector('.md-preview-holder').innerHTML).toContain(
'<p>markdown preview</p>',
);
diff --git a/spec/javascripts/vue_shared/components/table_pagination_spec.js b/spec/javascripts/vue_shared/components/table_pagination_spec.js
index 0dcb712e720..42cd41381dc 100644
--- a/spec/javascripts/vue_shared/components/table_pagination_spec.js
+++ b/spec/javascripts/vue_shared/components/table_pagination_spec.js
@@ -22,10 +22,10 @@ describe('Pagination component', () => {
it('should not render anything', () => {
component = mountComponent({
pageInfo: {
- nextPage: 1,
+ nextPage: NaN,
page: 1,
perPage: 20,
- previousPage: null,
+ previousPage: NaN,
total: 15,
totalPages: 1,
},
@@ -53,7 +53,29 @@ describe('Pagination component', () => {
component.$el.querySelector('.js-previous-button').classList.contains('disabled'),
).toEqual(true);
- component.$el.querySelector('.js-previous-button a').click();
+ component.$el.querySelector('.js-previous-button .page-link').click();
+
+ expect(spy).not.toHaveBeenCalled();
+ });
+
+ it('should be disabled and non clickable when total and totalPages are NaN', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: 2,
+ page: 1,
+ perPage: 20,
+ previousPage: NaN,
+ total: NaN,
+ totalPages: NaN,
+ },
+ change: spy,
+ });
+
+ expect(
+ component.$el.querySelector('.js-previous-button').classList.contains('disabled'),
+ ).toEqual(true);
+
+ component.$el.querySelector('.js-previous-button .page-link').click();
expect(spy).not.toHaveBeenCalled();
});
@@ -71,7 +93,25 @@ describe('Pagination component', () => {
change: spy,
});
- component.$el.querySelector('.js-previous-button a').click();
+ component.$el.querySelector('.js-previous-button .page-link').click();
+
+ expect(spy).toHaveBeenCalledWith(1);
+ });
+
+ it('should be enabled and clickable when total and totalPages are NaN', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: 3,
+ page: 2,
+ perPage: 20,
+ previousPage: 1,
+ total: NaN,
+ totalPages: NaN,
+ },
+ change: spy,
+ });
+
+ component.$el.querySelector('.js-previous-button .page-link').click();
expect(spy).toHaveBeenCalledWith(1);
});
@@ -91,7 +131,29 @@ describe('Pagination component', () => {
change: spy,
});
- const button = component.$el.querySelector('.js-first-button a');
+ const button = component.$el.querySelector('.js-first-button .page-link');
+
+ expect(button.textContent.trim()).toEqual('« First');
+
+ button.click();
+
+ expect(spy).toHaveBeenCalledWith(1);
+ });
+
+ it('should call the change callback with the first page when total and totalPages are NaN', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: 3,
+ page: 2,
+ perPage: 20,
+ previousPage: 1,
+ total: NaN,
+ totalPages: NaN,
+ },
+ change: spy,
+ });
+
+ const button = component.$el.querySelector('.js-first-button .page-link');
expect(button.textContent.trim()).toEqual('« First');
@@ -115,7 +177,7 @@ describe('Pagination component', () => {
change: spy,
});
- const button = component.$el.querySelector('.js-last-button a');
+ const button = component.$el.querySelector('.js-last-button .page-link');
expect(button.textContent.trim()).toEqual('Last »');
@@ -123,16 +185,32 @@ describe('Pagination component', () => {
expect(spy).toHaveBeenCalledWith(5);
});
+
+ it('should not render', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: 3,
+ page: 2,
+ perPage: 20,
+ previousPage: 1,
+ total: NaN,
+ totalPages: NaN,
+ },
+ change: spy,
+ });
+
+ expect(component.$el.querySelector('.js-last-button .page-link')).toBeNull();
+ });
});
describe('next button', () => {
it('should be disabled and non clickable', () => {
component = mountComponent({
pageInfo: {
- nextPage: 5,
+ nextPage: NaN,
page: 5,
perPage: 20,
- previousPage: 1,
+ previousPage: 4,
total: 84,
totalPages: 5,
},
@@ -141,7 +219,27 @@ describe('Pagination component', () => {
expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next');
- component.$el.querySelector('.js-next-button a').click();
+ component.$el.querySelector('.js-next-button .page-link').click();
+
+ expect(spy).not.toHaveBeenCalled();
+ });
+
+ it('should be disabled and non clickable when total and totalPages are NaN', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: NaN,
+ page: 5,
+ perPage: 20,
+ previousPage: 4,
+ total: NaN,
+ totalPages: NaN,
+ },
+ change: spy,
+ });
+
+ expect(component.$el.querySelector('.js-next-button').textContent.trim()).toEqual('Next');
+
+ component.$el.querySelector('.js-next-button .page-link').click();
expect(spy).not.toHaveBeenCalled();
});
@@ -159,7 +257,25 @@ describe('Pagination component', () => {
change: spy,
});
- component.$el.querySelector('.js-next-button a').click();
+ component.$el.querySelector('.js-next-button .page-link').click();
+
+ expect(spy).toHaveBeenCalledWith(4);
+ });
+
+ it('should be enabled and clickable when total and totalPages are NaN', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: 4,
+ page: 3,
+ perPage: 20,
+ previousPage: 2,
+ total: NaN,
+ totalPages: NaN,
+ },
+ change: spy,
+ });
+
+ component.$el.querySelector('.js-next-button .page-link').click();
expect(spy).toHaveBeenCalledWith(4);
});
@@ -181,22 +297,56 @@ describe('Pagination component', () => {
expect(component.$el.querySelectorAll('.page').length).toEqual(5);
});
+
+ it('should not render any page', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: 4,
+ page: 3,
+ perPage: 20,
+ previousPage: 2,
+ total: NaN,
+ totalPages: NaN,
+ },
+ change: spy,
+ });
+
+ expect(component.$el.querySelectorAll('.page').length).toEqual(0);
+ });
});
- it('should render the spread operator', () => {
- component = mountComponent({
- pageInfo: {
- nextPage: 4,
- page: 3,
- perPage: 20,
- previousPage: 2,
- total: 84,
- totalPages: 10,
- },
- change: spy,
+ describe('spread operator', () => {
+ it('should render', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: 4,
+ page: 3,
+ perPage: 20,
+ previousPage: 2,
+ total: 84,
+ totalPages: 10,
+ },
+ change: spy,
+ });
+
+ expect(component.$el.querySelector('.separator').textContent.trim()).toEqual('...');
});
- expect(component.$el.querySelector('.separator').textContent.trim()).toEqual('...');
+ it('should not render', () => {
+ component = mountComponent({
+ pageInfo: {
+ nextPage: 4,
+ page: 3,
+ perPage: 20,
+ previousPage: 2,
+ total: NaN,
+ totalPages: NaN,
+ },
+ change: spy,
+ });
+
+ expect(component.$el.querySelector('.separator')).toBeNull();
+ });
});
});
});
diff --git a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
index e8b41e8eeff..852558a83bc 100644
--- a/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
+++ b/spec/javascripts/vue_shared/components/user_popover/user_popover_spec.js
@@ -17,7 +17,7 @@ const DEFAULT_PROPS = {
const UserPopover = Vue.extend(userPopover);
describe('User Popover Component', () => {
- const fixtureTemplate = 'merge_requests/diff_comment.html.raw';
+ const fixtureTemplate = 'merge_requests/diff_comment.html';
preloadFixtures(fixtureTemplate);
let vm;
diff --git a/spec/javascripts/zen_mode_spec.js b/spec/javascripts/zen_mode_spec.js
index e5f1e6ae937..8f662c71c7a 100644
--- a/spec/javascripts/zen_mode_spec.js
+++ b/spec/javascripts/zen_mode_spec.js
@@ -6,7 +6,7 @@ import ZenMode from '~/zen_mode';
describe('ZenMode', () => {
let zen;
let dropzoneForElementSpy;
- const fixtureName = 'snippets/show.html.raw';
+ const fixtureName = 'snippets/show.html';
preloadFixtures(fixtureName);
diff --git a/spec/lib/api/helpers/custom_validators_spec.rb b/spec/lib/api/helpers/custom_validators_spec.rb
index 41e6fb47b11..9945d598a14 100644
--- a/spec/lib/api/helpers/custom_validators_spec.rb
+++ b/spec/lib/api/helpers/custom_validators_spec.rb
@@ -50,6 +50,29 @@ describe API::Helpers::CustomValidators do
end
end
+ describe API::Helpers::CustomValidators::ArrayNoneAny do
+ subject do
+ described_class.new(['test'], {}, false, scope.new)
+ end
+
+ context 'valid parameters' do
+ it 'does not raise a validation error' do
+ expect_no_validation_error({ 'test' => [] })
+ expect_no_validation_error({ 'test' => [1, 2, 3] })
+ expect_no_validation_error({ 'test' => 'None' })
+ expect_no_validation_error({ 'test' => 'Any' })
+ expect_no_validation_error({ 'test' => 'none' })
+ expect_no_validation_error({ 'test' => 'any' })
+ end
+ end
+
+ context 'invalid parameters' do
+ it 'should raise a validation error' do
+ expect_validation_error({ 'test' => 'some_other_string' })
+ end
+ end
+ end
+
def expect_no_validation_error(params)
expect { validate_test_param!(params) }.not_to raise_error
end
diff --git a/spec/lib/api/helpers/pagination_spec.rb b/spec/lib/api/helpers/pagination_spec.rb
index 2890aa4ae38..6e215ea1561 100644
--- a/spec/lib/api/helpers/pagination_spec.rb
+++ b/spec/lib/api/helpers/pagination_spec.rb
@@ -2,6 +2,8 @@ require 'spec_helper'
describe API::Helpers::Pagination do
let(:resource) { Project.all }
+ let(:incoming_api_projects_url) { "#{Gitlab.config.gitlab.url}:8080/api/v4/projects" }
+ let(:canonical_api_projects_url) { "#{Gitlab.config.gitlab.url}/api/v4/projects" }
subject do
Class.new.include(described_class).new
@@ -9,13 +11,19 @@ describe API::Helpers::Pagination do
describe '#paginate (keyset pagination)' do
let(:value) { spy('return value') }
+ let(:base_query) do
+ {
+ pagination: 'keyset',
+ foo: 'bar',
+ bar: 'baz'
+ }
+ end
+ let(:query) { base_query }
before do
- allow(value).to receive(:to_query).and_return(value)
-
allow(subject).to receive(:header).and_return(value)
- allow(subject).to receive(:params).and_return(value)
- allow(subject).to receive(:request).and_return(value)
+ allow(subject).to receive(:params).and_return(query)
+ allow(subject).to receive(:request).and_return(double(url: "#{incoming_api_projects_url}?#{query.to_query}"))
end
context 'when resource can be paginated' do
@@ -28,10 +36,7 @@ describe API::Helpers::Pagination do
end
describe 'first page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2 })
- end
+ let(:query) { base_query.merge(per_page: 2) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 2
@@ -43,7 +48,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{value}?ks_prev_id=#{projects[1].id}&pagination=keyset&per_page=2")
+ expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[1].id).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
@@ -54,10 +59,7 @@ describe API::Helpers::Pagination do
end
describe 'second page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2, ks_prev_id: projects[1].id })
- end
+ let(:query) { base_query.merge(per_page: 2, ks_prev_id: projects[1].id) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 1
@@ -69,7 +71,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{value}?ks_prev_id=#{projects[2].id}&pagination=keyset&per_page=2")
+ expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[2].id).to_query}")
expect_header('Link', anything) do |_key, val|
expect(val).to include('rel="next"')
@@ -80,10 +82,7 @@ describe API::Helpers::Pagination do
end
describe 'third page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2, ks_prev_id: projects[2].id })
- end
+ let(:query) { base_query.merge(per_page: 2, ks_prev_id: projects[2].id) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 0
@@ -91,6 +90,7 @@ describe API::Helpers::Pagination do
it 'adds appropriate headers' do
expect_header('X-Per-Page', '2')
+ expect_no_header('X-Next-Page')
expect(subject).not_to receive(:header).with('Link')
subject.paginate(resource)
@@ -99,10 +99,7 @@ describe API::Helpers::Pagination do
context 'if order' do
context 'is not present' do
- before do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2 })
- end
+ let(:query) { base_query.merge(per_page: 2) }
it 'is not present it adds default order(:id) desc' do
resource.order_values = []
@@ -144,9 +141,7 @@ describe API::Helpers::Pagination do
# (key is the id)
end
- it 'it also orders by primary key' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2 })
+ it 'also orders by primary key' do
paginated_relation = subject.paginate(resource)
expect(paginated_relation.order_values).to be_present
@@ -157,46 +152,45 @@ describe API::Helpers::Pagination do
expect(paginated_relation.order_values.second.expr.name).to eq :id
end
- it 'it returns the right records (first page)' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', per_page: 2 })
+ it 'returns the right records (first page)' do
result = subject.paginate(resource)
expect(result.first).to eq(projects[1])
expect(result.second).to eq(projects[3])
end
- it 'it returns the right records (second page)' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', ks_prev_id: projects[3].id, ks_prev_name: projects[3].name, per_page: 2 })
- result = subject.paginate(resource)
+ describe 'second page' do
+ let(:query) { base_query.merge(ks_prev_id: projects[3].id, ks_prev_name: projects[3].name, per_page: 2) }
- expect(result.first).to eq(projects[2])
- expect(result.second).to eq(projects[6])
- end
+ it 'returns the right records (second page)' do
+ result = subject.paginate(resource)
- it 'it returns the right records (third page), note increased per_page' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', ks_prev_id: projects[6].id, ks_prev_name: projects[6].name, per_page: 5 })
- result = subject.paginate(resource)
+ expect(result.first).to eq(projects[2])
+ expect(result.second).to eq(projects[6])
+ end
- expect(result.size).to eq(3)
- expect(result.first).to eq(projects[0])
- expect(result.second).to eq(projects[4])
- expect(result.last).to eq(projects[5])
+ it 'returns the right link to the next page' do
+ expect_header('X-Per-Page', '2')
+ expect_header('X-Next-Page', "#{canonical_api_projects_url}?#{query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name).to_query}")
+ expect_header('Link', anything) do |_key, val|
+ expect(val).to include('rel="next"')
+ end
+
+ subject.paginate(resource)
+ end
end
- it 'it returns the right link to the next page' do
- allow(subject).to receive(:params)
- .and_return({ pagination: 'keyset', ks_prev_id: projects[3].id, ks_prev_name: projects[3].name, per_page: 2 })
+ describe 'third page' do
+ let(:query) { base_query.merge(ks_prev_id: projects[6].id, ks_prev_name: projects[6].name, per_page: 5) }
- expect_header('X-Per-Page', '2')
- expect_header('X-Next-Page', "#{value}?ks_prev_id=#{projects[6].id}&ks_prev_name=#{projects[6].name}&pagination=keyset&per_page=2")
- expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="next"')
- end
+ it 'returns the right records (third page), note increased per_page' do
+ result = subject.paginate(resource)
- subject.paginate(resource)
+ expect(result.size).to eq(3)
+ expect(result.first).to eq(projects[0])
+ expect(result.second).to eq(projects[4])
+ expect(result.last).to eq(projects[5])
+ end
end
end
end
@@ -205,25 +199,13 @@ describe API::Helpers::Pagination do
describe '#paginate (default offset-based pagination)' do
let(:value) { spy('return value') }
+ let(:base_query) { { foo: 'bar', bar: 'baz' } }
+ let(:query) { base_query }
before do
- allow(value).to receive(:to_query).and_return(value)
-
allow(subject).to receive(:header).and_return(value)
- allow(subject).to receive(:params).and_return(value)
- allow(subject).to receive(:request).and_return(value)
- end
-
- describe 'required instance methods' do
- let(:return_spy) { spy }
-
- it 'requires some instance methods' do
- expect_message(:header)
- expect_message(:params)
- expect_message(:request)
-
- subject.paginate(resource)
- end
+ allow(subject).to receive(:params).and_return(query)
+ allow(subject).to receive(:request).and_return(double(url: "#{incoming_api_projects_url}?#{query.to_query}"))
end
context 'when resource can be paginated' do
@@ -232,11 +214,6 @@ describe API::Helpers::Pagination do
end
describe 'first page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ page: 1, per_page: 2 })
- end
-
shared_examples 'response with pagination headers' do
it 'adds appropriate headers' do
expect_header('X-Total', '3')
@@ -247,9 +224,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="first"')
- expect(val).to include('rel="last"')
- expect(val).to include('rel="next"')
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).not_to include('rel="prev"')
end
@@ -267,6 +244,8 @@ describe API::Helpers::Pagination do
end
end
+ let(:query) { base_query.merge(page: 1, per_page: 2) }
+
context 'when the api_kaminari_count_with_limit feature flag is unset' do
it_behaves_like 'paginated response'
it_behaves_like 'response with pagination headers'
@@ -311,9 +290,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="first"')
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="next"))
expect(val).not_to include('rel="last"')
- expect(val).to include('rel="next"')
expect(val).not_to include('rel="prev"')
end
@@ -324,10 +303,7 @@ describe API::Helpers::Pagination do
end
describe 'second page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ page: 2, per_page: 2 })
- end
+ let(:query) { base_query.merge(page: 2, per_page: 2) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 1
@@ -342,9 +318,9 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '1')
expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="first"')
- expect(val).to include('rel="last"')
- expect(val).to include('rel="prev"')
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 2).to_query}>; rel="last"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="prev"))
expect(val).not_to include('rel="next"')
end
@@ -376,10 +352,7 @@ describe API::Helpers::Pagination do
context 'when resource empty' do
describe 'first page' do
- before do
- allow(subject).to receive(:params)
- .and_return({ page: 1, per_page: 2 })
- end
+ let(:query) { base_query.merge(page: 1, per_page: 2) }
it 'returns appropriate amount of resources' do
expect(subject.paginate(resource).count).to eq 0
@@ -394,8 +367,8 @@ describe API::Helpers::Pagination do
expect_header('X-Prev-Page', '')
expect_header('Link', anything) do |_key, val|
- expect(val).to include('rel="first"')
- expect(val).to include('rel="last"')
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="first"))
+ expect(val).to include(%Q(<#{canonical_api_projects_url}?#{query.merge(page: 1).to_query}>; rel="last"))
expect(val).not_to include('rel="prev"')
expect(val).not_to include('rel="next"')
expect(val).not_to include('page=0')
diff --git a/spec/lib/backup/uploads_spec.rb b/spec/lib/backup/uploads_spec.rb
new file mode 100644
index 00000000000..544d3754c0f
--- /dev/null
+++ b/spec/lib/backup/uploads_spec.rb
@@ -0,0 +1,18 @@
+require 'spec_helper'
+
+describe Backup::Uploads do
+ let(:progress) { StringIO.new }
+ subject(:backup) { described_class.new(progress) }
+
+ describe '#initialize' do
+ it 'uses the correct upload dir' do
+ Dir.mktmpdir do |tmpdir|
+ FileUtils.mkdir_p("#{tmpdir}/uploads")
+
+ allow(Gitlab.config.uploads).to receive(:storage_path) { tmpdir }
+
+ expect(backup.app_files_dir).to eq("#{tmpdir}/uploads")
+ end
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
index a0270d93d50..43222ddb5e2 100644
--- a/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/external_issue_reference_filter_spec.rb
@@ -121,6 +121,42 @@ describe Banzai::Filter::ExternalIssueReferenceFilter do
end
end
+ context "youtrack project" do
+ let(:project) { create(:youtrack_project) }
+
+ before do
+ project.update!(issues_enabled: false)
+ end
+
+ context "with right markdown" do
+ let(:issue) { ExternalIssue.new("YT-123", project) }
+ let(:reference) { issue.to_reference }
+
+ it_behaves_like "external issue tracker"
+ end
+
+ context "with underscores in the prefix" do
+ let(:issue) { ExternalIssue.new("PRJ_1-123", project) }
+ let(:reference) { issue.to_reference }
+
+ it_behaves_like "external issue tracker"
+ end
+
+ context "with lowercase letters in the prefix" do
+ let(:issue) { ExternalIssue.new("YTkPrj-123", project) }
+ let(:reference) { issue.to_reference }
+
+ it_behaves_like "external issue tracker"
+ end
+
+ context "with a single-letter prefix" do
+ let(:issue) { ExternalIssue.new("T-123", project) }
+ let(:reference) { issue.to_reference }
+
+ it_behaves_like "external issue tracker"
+ end
+ end
+
context "jira project" do
let(:project) { create(:jira_project) }
let(:reference) { issue.to_reference }
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 55c41e55437..72dfd6ff9ea 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -30,6 +30,23 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
end
end
+ describe 'all references' do
+ let(:doc) { reference_filter(merge.to_reference) }
+ let(:tag_el) { doc.css('a').first }
+
+ it 'adds merge request iid' do
+ expect(tag_el["data-iid"]).to eq(merge.iid.to_s)
+ end
+
+ it 'adds project data attribute with project id' do
+ expect(tag_el["data-project-path"]).to eq(project.full_path)
+ end
+
+ it 'does not add `has-tooltip` class' do
+ expect(tag_el["class"]).not_to include('has-tooltip')
+ end
+ end
+
context 'internal reference' do
let(:reference) { merge.to_reference }
@@ -57,9 +74,9 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
expect(reference_filter(act).to_html).to eq exp
end
- it 'includes a title attribute' do
+ it 'has no title' do
doc = reference_filter("Merge #{reference}")
- expect(doc.css('a').first.attr('title')).to eq merge.title
+ expect(doc.css('a').first.attr('title')).to eq ""
end
it 'escapes the title attribute' do
@@ -69,9 +86,9 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
expect(doc.text).to eq "Merge #{reference}"
end
- it 'includes default classes' do
+ it 'includes default classes, without tooltip' do
doc = reference_filter("Merge #{reference}")
- expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request has-tooltip'
+ expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-merge_request'
end
it 'includes a data-project attribute' do
diff --git a/spec/lib/banzai/filter/output_safety_spec.rb b/spec/lib/banzai/filter/output_safety_spec.rb
new file mode 100644
index 00000000000..5ffe591c9a4
--- /dev/null
+++ b/spec/lib/banzai/filter/output_safety_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Banzai::Filter::OutputSafety do
+ subject do
+ Class.new do
+ include Banzai::Filter::OutputSafety
+ end.new
+ end
+
+ let(:content) { '<pre><code>foo</code></pre>' }
+
+ context 'when given HTML is safe' do
+ let(:html) { content.html_safe }
+
+ it 'returns safe HTML' do
+ expect(subject.escape_once(html)).to eq(html)
+ end
+ end
+
+ context 'when given HTML is not safe' do
+ let(:html) { content }
+
+ it 'returns escaped HTML' do
+ expect(subject.escape_once(html)).to eq(ERB::Util.html_escape_once(html))
+ end
+ end
+end
diff --git a/spec/lib/banzai/filter/suggestion_filter_spec.rb b/spec/lib/banzai/filter/suggestion_filter_spec.rb
index b13c90b54bd..af6f002fa30 100644
--- a/spec/lib/banzai/filter/suggestion_filter_spec.rb
+++ b/spec/lib/banzai/filter/suggestion_filter_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Banzai::Filter::SuggestionFilter do
include FilterSpecHelper
- let(:input) { "<pre class='code highlight js-syntax-highlight suggestion'><code>foo\n</code></pre>" }
+ let(:input) { %(<pre class="code highlight js-syntax-highlight suggestion"><code>foo\n</code></pre>) }
let(:default_context) do
{ suggestions_filter_enabled: true }
end
@@ -23,4 +23,35 @@ describe Banzai::Filter::SuggestionFilter do
expect(result[:class]).to be_nil
end
+
+ context 'multi-line suggestions' do
+ let(:data_attr) { Banzai::Filter::SyntaxHighlightFilter::LANG_PARAMS_ATTR }
+ let(:input) { %(<pre class="code highlight js-syntax-highlight suggestion" #{data_attr}="-3+2"><code>foo\n</code></pre>) }
+
+ context 'feature disabled' do
+ before do
+ stub_feature_flags(multi_line_suggestions: false)
+ end
+
+ it 'removes data-lang-params if it matches a multi-line suggestion param' do
+ doc = filter(input, default_context)
+ pre = doc.css('pre').first
+
+ expect(pre[data_attr]).to be_nil
+ end
+ end
+
+ context 'feature enabled' do
+ before do
+ stub_feature_flags(multi_line_suggestions: true)
+ end
+
+ it 'keeps data-lang-params' do
+ doc = filter(input, default_context)
+ pre = doc.css('pre').first
+
+ expect(pre[data_attr]).to eq('-3+2')
+ end
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
index ef52c572898..05057789cc1 100644
--- a/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
+++ b/spec/lib/banzai/filter/syntax_highlight_filter_spec.rb
@@ -45,7 +45,10 @@ describe Banzai::Filter::SyntaxHighlightFilter do
end
context "languages that should be passed through" do
- %w(math mermaid plantuml).each do |lang|
+ let(:delimiter) { described_class::PARAMS_DELIMITER }
+ let(:data_attr) { described_class::LANG_PARAMS_ATTR }
+
+ %w(math mermaid plantuml suggestion).each do |lang|
context "when #{lang} is specified" do
it "highlights as plaintext but with the correct language attribute and class" do
result = filter(%{<pre><code lang="#{lang}">This is a test</code></pre>})
@@ -55,6 +58,33 @@ describe Banzai::Filter::SyntaxHighlightFilter do
include_examples "XSS prevention", lang
end
+
+ context "when #{lang} has extra params" do
+ let(:lang_params) { 'foo-bar-kux' }
+
+ it "includes data-lang-params tag with extra information" do
+ result = filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}">This is a test</code></pre>})
+
+ expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight #{lang}" lang="#{lang}" #{data_attr}="#{lang_params}" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>})
+ end
+
+ include_examples "XSS prevention", lang
+ include_examples "XSS prevention",
+ "#{lang}#{described_class::PARAMS_DELIMITER}&lt;script&gt;alert(1)&lt;/script&gt;"
+ include_examples "XSS prevention",
+ "#{lang}#{described_class::PARAMS_DELIMITER}<script>alert(1)</script>"
+ end
+ end
+
+ context 'when multiple param delimiters are used' do
+ let(:lang) { 'suggestion' }
+ let(:lang_params) { '-1+10' }
+
+ it "delimits on the first appearence" do
+ result = filter(%{<pre><code lang="#{lang}#{delimiter}#{lang_params}#{delimiter}more-things">This is a test</code></pre>})
+
+ expect(result.to_html).to eq(%{<pre class="code highlight js-syntax-highlight #{lang}" lang="#{lang}" #{data_attr}="#{lang_params}#{delimiter}more-things" v-pre="true"><code><span id="LC1" class="line" lang="#{lang}">This is a test</span></code></pre>})
+ end
end
end
diff --git a/spec/lib/bitbucket_server/client_spec.rb b/spec/lib/bitbucket_server/client_spec.rb
index b021e69800a..4f0d57ca8a6 100644
--- a/spec/lib/bitbucket_server/client_spec.rb
+++ b/spec/lib/bitbucket_server/client_spec.rb
@@ -64,7 +64,7 @@ describe BitbucketServer::Client do
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: '{}')
+ stub_request(:post, url).to_return(status: 204, headers: headers, body: nil)
subject.create_branch(project, repo_slug, branch, sha)
@@ -78,7 +78,7 @@ describe BitbucketServer::Client do
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: '{}')
+ stub_request(:delete, url).to_return(status: 204, headers: headers, body: nil)
subject.delete_branch(project, repo_slug, branch, sha)
diff --git a/spec/lib/constraints/project_url_constrainer_spec.rb b/spec/lib/constraints/project_url_constrainer_spec.rb
index c96e7ab8495..3496b01ebcc 100644
--- a/spec/lib/constraints/project_url_constrainer_spec.rb
+++ b/spec/lib/constraints/project_url_constrainer_spec.rb
@@ -16,6 +16,10 @@ describe Constraints::ProjectUrlConstrainer do
let(:request) { build_request('foo', 'bar') }
it { expect(subject.matches?(request)).to be_falsey }
+
+ context 'existence_check is false' do
+ it { expect(subject.matches?(request, existence_check: false)).to be_truthy }
+ end
end
context "project id ending with .git" do
diff --git a/spec/lib/event_filter_spec.rb b/spec/lib/event_filter_spec.rb
index 30016da6828..6648e141b7a 100644
--- a/spec/lib/event_filter_spec.rb
+++ b/spec/lib/event_filter_spec.rb
@@ -26,10 +26,10 @@ describe EventFilter do
set(:push_event) { create(:push_event, project: public_project) }
set(:merged_event) { create(:event, :merged, project: public_project, target: public_project) }
- set(:created_event) { create(:event, :created, project: public_project, target: public_project) }
- set(:updated_event) { create(:event, :updated, project: public_project, target: public_project) }
- set(:closed_event) { create(:event, :closed, project: public_project, target: public_project) }
- set(:reopened_event) { create(:event, :reopened, project: public_project, target: public_project) }
+ set(:created_event) { create(:event, :created, project: public_project, target: create(:issue, project: public_project)) }
+ set(:updated_event) { create(:event, :updated, project: public_project, target: create(:issue, project: public_project)) }
+ set(:closed_event) { create(:event, :closed, project: public_project, target: create(:issue, project: public_project)) }
+ set(:reopened_event) { create(:event, :reopened, project: public_project, target: create(:issue, project: public_project)) }
set(:comments_event) { create(:event, :commented, project: public_project, target: public_project) }
set(:joined_event) { create(:event, :joined, project: public_project, target: public_project) }
set(:left_event) { create(:event, :left, project: public_project, target: public_project) }
diff --git a/spec/lib/gitlab/auth/ldap/config_spec.rb b/spec/lib/gitlab/auth/ldap/config_spec.rb
index d3ab599d5a0..b91a09e3137 100644
--- a/spec/lib/gitlab/auth/ldap/config_spec.rb
+++ b/spec/lib/gitlab/auth/ldap/config_spec.rb
@@ -5,6 +5,65 @@ describe Gitlab::Auth::LDAP::Config do
let(:config) { described_class.new('ldapmain') }
+ def raw_cert
+ <<-EOS
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk4CCQDX+u/9fICksDANBgkqhkiG9w0BAQsFADB1MQswCQYDVQQGEwJV
+UzEMMAoGA1UECAwDRm9vMQwwCgYDVQQHDANCYXIxDDAKBgNVBAoMA0JhejEMMAoG
+A1UECwwDUXV4MQ0wCwYDVQQDDARsZGFwMR8wHQYJKoZIhvcNAQkBFhBsZGFwQGV4
+YW1wbGUuY29tMB4XDTE5MDIyNzE1NTUxNFoXDTE5MDMyOTE1NTUxNFowdTELMAkG
+A1UEBhMCVVMxDDAKBgNVBAgMA0ZvbzEMMAoGA1UEBwwDQmFyMQwwCgYDVQQKDANC
+YXoxDDAKBgNVBAsMA1F1eDENMAsGA1UEAwwEbGRhcDEfMB0GCSqGSIb3DQEJARYQ
+bGRhcEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+APuDB/4/AUmTEmhYzN13no4Kt8hkRbLQuENRHlOeQw05/MVdoB1AWLOPzIXn4kex
+GD9tHkoJl8S0QPmAAcPHn5O97e+gd0ze5dRQZl/cSd2/j5zeaMvZ1mCrPN/dOluM
+94Oj+wQU4bEcOlrqIMSh0ezJw10R3IHXCQFeGtIZU57WmKcrryQX4kP7KTOgRw/t
+CYp+NivQHtLbBEj1MU0l10qMS2+w8Qpqov4MdW4gx4wTgId2j1ZZ56+n6Jsc9qoI
+wBWBNL4XU5a3kwhYZDOJoOvI9po33KLdT1dXS81uOFXClp3LGmKDgLTwQ1w+RmQG
++JG4EvTfDIShdcTDXEaOfCECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAJM9Btu5g
+k8qDiz5TilvpyoGuI4viCwusARFAFmOB/my/cHlVvkuq4bbfV1KJoWWGJg8GcklL
+cnIdxc35uYM5icr6xXQyrW0GqAO+LEXyUxVQqYETxrQ/LJ03xhBnuF7hvZJIBiky
+GwUy0clJxGfaCeEM8zXwePawLgGjuUawDDQOwigysoWqoMu3VFW8zl8UPa84bow9
+Kn2QmPAkLw4EcqYSCNSSvnyzu5SM64jwLWRXFsmlqD7773oT29vTkqM1EQANFEfT
+7gQomLyPqoPBoFph5oSNn6Rf31QX1Sie92EAKVnZ1XmD68hKzjv6ChCtzTv4jABg
+XrDwnLkORIAF/Q==
+-----END CERTIFICATE-----
+ EOS
+ end
+
+ def raw_key
+ <<-EOS
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQD7gwf+PwFJkxJo
+WMzdd56OCrfIZEWy0LhDUR5TnkMNOfzFXaAdQFizj8yF5+JHsRg/bR5KCZfEtED5
+gAHDx5+Tve3voHdM3uXUUGZf3Endv4+c3mjL2dZgqzzf3TpbjPeDo/sEFOGxHDpa
+6iDEodHsycNdEdyB1wkBXhrSGVOe1pinK68kF+JD+ykzoEcP7QmKfjYr0B7S2wRI
+9TFNJddKjEtvsPEKaqL+DHVuIMeME4CHdo9WWeevp+ibHPaqCMAVgTS+F1OWt5MI
+WGQziaDryPaaN9yi3U9XV0vNbjhVwpadyxpig4C08ENcPkZkBviRuBL03wyEoXXE
+w1xGjnwhAgMBAAECggEAbw82GVui6uUpjLAhjm3CssAi1TcJ2+L0aq1IMe5Bd3ay
+mkg0apY+VNPboQl6zuNxbJh3doPz42UhB8sxfE0Ktwd4KIb4Bxap7+2stwmkCGoN
+NVy0c8d2NWuHzuZ2XXTK2vMu5Wd/HWD0l66o14sJEoEpZlB7yU216UevmjSayxjh
+aBTSaYyyrf24haTaCuqwph/V73ZlMpFdSALGny0uiP/5inxciMCkMpHfX6BflSb4
+EGKsIYt9BJ0kY4GNG5bCP7971UCxp2eEJhU2fV8HuFGCOD12IqSpUqPxHxjsWpfx
+T7FZ3V2kM/58Ca+5LB2y3atcPIdY0/g7/43V4VD+7QKBgQD/PO4/0cmZuuLU1LPT
+C/C596kPK0JLlvvRqhbz4byRAkW/n7uQFG7TMtFNle3UmT7rk7pjtbHnByqzEd+9
+jMhBysjHOMg0+DWm7fEtSg/tJ3qLVO3nbdA4qmXYobLcLoG+PCYRLskEHHqTG/Bv
+QZLbavOU6rrTqckNr1TMpNBmXwKBgQD8Q0C2YTOpwgjRUe8i6Chnc3o4x8a1i98y
+9la6c7y7acWHSbEczMkNfEBrbM73rTb+bBA0Zqw+Z1gkv8bGpvGxX8kbSfJJ2YKW
+9koxpLNTVNVapqBa9ImiaozV285dz9Ukx8bnMOJlTELpOl7RRV7iF0smYjfHIl3D
+Yxyda/MtfwKBgHb9l/Dmw77IkqE4PFFimqqIHCe3OiP1UpavXh36midcUNoCBLYp
+4HTTlyI9iG/5tYysBVQgy7xx6eUrqww6Ss3pVOsTvLp9EL4u5aYAhiZApm+4e2TO
+HCmevvZcg/8EK3Zdoj2Wex5QjJBykQe9IVLrrH07ZTfySon3uGfjWkivAoGAGvqS
+VC8HGHOw/7n0ilYr5Ax8mM/813OzFj80PVKdb6m7P2HJOFxKcE/Gj/aeF+0FgaZL
+AV+tsirZSWzdNGesV5z35Bw/dlh11/FVNAP6TcI34y8I3VFj2uPsVf7hDjVpBTr8
+ccNPoyfJzCm69ESoBiQZnGxKrNhnELtr1wYxhr8CgYApWwf4hVrTWV1zs+pEJenh
+AtlErSqafbECNDSwS5BX8yDpu5yRBJ4xegO/rNlmb8ICRYkuJapD1xXicFOsmfUK
+0Ff8afd2Q/OfBeUdq9KA4JO9fNqzEwOWvv8Ryn4ZSYcAuLP7IVJKjjI6R7rYaO/G
+3OWJdizbykGOi0BFDu+3dw==
+-----END PRIVATE KEY-----
+ EOS
+ end
+
describe '.servers' do
it 'returns empty array if no server information is available' do
allow(Gitlab.config).to receive(:ldap).and_return('enabled' => false)
@@ -89,6 +148,42 @@ describe Gitlab::Auth::LDAP::Config do
expect(config.adapter_options[:encryption]).to include({ method: :start_tls })
end
+ it 'transforms SSL cert and key to OpenSSL objects' do
+ stub_ldap_config(
+ options: {
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => raw_cert,
+ 'key' => raw_key
+ }
+ }
+ )
+
+ expect(config.adapter_options[:encryption][:tls_options][:cert]).to be_a(OpenSSL::X509::Certificate)
+ expect(config.adapter_options[:encryption][:tls_options][:key]).to be_a(OpenSSL::PKey::RSA)
+ end
+
+ it 'logs an error when an invalid key or cert are configured' do
+ allow(Rails.logger).to receive(:error)
+ stub_ldap_config(
+ options: {
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => 'invalid cert',
+ 'key' => 'invalid_key'
+ }
+ }
+ )
+
+ config.adapter_options
+
+ expect(Rails.logger).to have_received(:error).with(/LDAP TLS Options/).twice
+ end
+
context 'when verify_certificates is enabled' do
it 'sets tls_options to OpenSSL defaults' do
stub_ldap_config(
@@ -130,7 +225,9 @@ describe Gitlab::Auth::LDAP::Config do
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
- 'ca_file' => '/etc/ca.pem'
+ 'tls_options' => {
+ 'ca_file' => '/etc/ca.pem'
+ }
}
)
@@ -145,7 +242,9 @@ describe Gitlab::Auth::LDAP::Config do
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
- 'ca_file' => ' '
+ 'tls_options' => {
+ 'ca_file' => ' '
+ }
}
)
@@ -160,7 +259,9 @@ describe Gitlab::Auth::LDAP::Config do
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
- 'ssl_version' => 'TLSv1_2'
+ 'tls_options' => {
+ 'ssl_version' => 'TLSv1_2'
+ }
}
)
@@ -175,7 +276,9 @@ describe Gitlab::Auth::LDAP::Config do
'host' => 'ldap.example.com',
'port' => 686,
'encryption' => 'simple_tls',
- 'ssl_version' => ' '
+ 'tls_options' => {
+ 'ssl_version' => ' '
+ }
}
)
@@ -223,6 +326,23 @@ describe Gitlab::Auth::LDAP::Config do
)
end
+ it 'transforms SSL cert and key to OpenSSL objects' do
+ stub_ldap_config(
+ options: {
+ 'host' => 'ldap.example.com',
+ 'port' => 686,
+ 'encryption' => 'start_tls',
+ 'tls_options' => {
+ 'cert' => raw_cert,
+ 'key' => raw_key
+ }
+ }
+ )
+
+ expect(config.omniauth_options[:tls_options][:cert]).to be_a(OpenSSL::X509::Certificate)
+ expect(config.omniauth_options[:tls_options][:key]).to be_a(OpenSSL::PKey::RSA)
+ end
+
context 'when verify_certificates is enabled' do
it 'specifies disable_verify_certificates as false' do
stub_ldap_config(
@@ -261,11 +381,13 @@ describe Gitlab::Auth::LDAP::Config do
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'ca_file' => '/etc/ca.pem'
+ 'tls_options' => {
+ 'ca_file' => '/etc/ca.pem'
+ }
}
)
- expect(config.omniauth_options).to include({ ca_file: '/etc/ca.pem' })
+ expect(config.omniauth_options[:tls_options]).to include({ ca_file: '/etc/ca.pem' })
end
end
@@ -277,11 +399,13 @@ describe Gitlab::Auth::LDAP::Config do
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'ca_file' => ' '
+ 'tls_options' => {
+ 'ca_file' => ' '
+ }
}
)
- expect(config.omniauth_options).not_to have_key(:ca_file)
+ expect(config.omniauth_options[:tls_options]).not_to have_key(:ca_file)
end
end
@@ -293,11 +417,13 @@ describe Gitlab::Auth::LDAP::Config do
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'ssl_version' => 'TLSv1_2'
+ 'tls_options' => {
+ 'ssl_version' => 'TLSv1_2'
+ }
}
)
- expect(config.omniauth_options).to include({ ssl_version: 'TLSv1_2' })
+ expect(config.omniauth_options[:tls_options]).to include({ ssl_version: 'TLSv1_2' })
end
end
@@ -309,11 +435,14 @@ describe Gitlab::Auth::LDAP::Config do
'port' => 686,
'encryption' => 'simple_tls',
'verify_certificates' => true,
- 'ssl_version' => ' '
+ 'tls_options' => {
+ 'ssl_version' => ' '
+ }
}
)
- expect(config.omniauth_options).not_to have_key(:ssl_version)
+ # OpenSSL default params includes `ssl_version` so we just check that it's not blank
+ expect(config.omniauth_options[:tls_options]).not_to include({ ssl_version: ' ' })
end
end
end
diff --git a/spec/lib/gitlab/auth/o_auth/user_spec.rb b/spec/lib/gitlab/auth/o_auth/user_spec.rb
index dcbd12fe190..b765c265e69 100644
--- a/spec/lib/gitlab/auth/o_auth/user_spec.rb
+++ b/spec/lib/gitlab/auth/o_auth/user_spec.rb
@@ -207,6 +207,7 @@ describe Gitlab::Auth::OAuth::User do
before do
allow(ldap_user).to receive(:uid) { uid }
allow(ldap_user).to receive(:username) { uid }
+ allow(ldap_user).to receive(:name) { 'John Doe' }
allow(ldap_user).to receive(:email) { ['johndoe@example.com', 'john2@example.com'] }
allow(ldap_user).to receive(:dn) { dn }
end
@@ -221,6 +222,7 @@ describe Gitlab::Auth::OAuth::User do
it "creates a user with dual LDAP and omniauth identities" do
expect(gl_user).to be_valid
expect(gl_user.username).to eql uid
+ expect(gl_user.name).to eql 'John Doe'
expect(gl_user.email).to eql 'johndoe@example.com'
expect(gl_user.identities.length).to be 2
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
@@ -232,11 +234,13 @@ describe Gitlab::Auth::OAuth::User do
)
end
- it "has email set as synced" do
+ it "has name and email set as synced" do
+ expect(gl_user.user_synced_attributes_metadata.name_synced).to be_truthy
expect(gl_user.user_synced_attributes_metadata.email_synced).to be_truthy
end
- it "has email set as read-only" do
+ it "has name and email set as read-only" do
+ expect(gl_user.read_only_attribute?(:name)).to be_truthy
expect(gl_user.read_only_attribute?(:email)).to be_truthy
end
@@ -246,7 +250,7 @@ describe Gitlab::Auth::OAuth::User do
end
context "and LDAP user has an account already" do
- let!(:existing_user) { create(:omniauth_user, email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') }
+ let!(:existing_user) { create(:omniauth_user, name: 'John Doe', email: 'john@example.com', extern_uid: dn, provider: 'ldapmain', username: 'john') }
it "adds the omniauth identity to the LDAP account" do
allow(Gitlab::Auth::LDAP::Person).to receive(:find_by_uid).and_return(ldap_user)
@@ -254,6 +258,7 @@ describe Gitlab::Auth::OAuth::User do
expect(gl_user).to be_valid
expect(gl_user.username).to eql 'john'
+ expect(gl_user.name).to eql 'John Doe'
expect(gl_user.email).to eql 'john@example.com'
expect(gl_user.identities.length).to be 2
identities_as_hash = gl_user.identities.map { |id| { provider: id.provider, extern_uid: id.extern_uid } }
diff --git a/spec/lib/gitlab/authorized_keys_spec.rb b/spec/lib/gitlab/authorized_keys_spec.rb
new file mode 100644
index 00000000000..42bc509eeef
--- /dev/null
+++ b/spec/lib/gitlab/authorized_keys_spec.rb
@@ -0,0 +1,194 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::AuthorizedKeys do
+ let(:logger) { double('logger').as_null_object }
+
+ subject { described_class.new(logger) }
+
+ describe '#add_key' do
+ context 'authorized_keys file exists' do
+ before do
+ create_authorized_keys_fixture
+ end
+
+ after do
+ delete_authorized_keys_file
+ end
+
+ it "adds a line at the end of the file and strips trailing garbage" do
+ auth_line = "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-741\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa AAAAB3NzaDAxx2E"
+
+ expect(logger).to receive(:info).with('Adding key (key-741): ssh-rsa AAAAB3NzaDAxx2E')
+ expect(subject.add_key('key-741', 'ssh-rsa AAAAB3NzaDAxx2E trailing garbage'))
+ .to be_truthy
+ expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{auth_line}\n")
+ end
+ end
+
+ context 'authorized_keys file does not exist' do
+ before do
+ delete_authorized_keys_file
+ end
+
+ it 'creates the file' do
+ expect(subject.add_key('key-741', 'ssh-rsa AAAAB3NzaDAxx2E')).to be_truthy
+ expect(File.exist?(tmp_authorized_keys_path)).to be_truthy
+ end
+ end
+ end
+
+ describe '#batch_add_keys' do
+ let(:keys) do
+ [
+ double(shell_id: 'key-12', key: 'ssh-dsa ASDFASGADG trailing garbage'),
+ double(shell_id: 'key-123', key: 'ssh-rsa GFDGDFSGSDFG')
+ ]
+ end
+
+ context 'authorized_keys file exists' do
+ before do
+ create_authorized_keys_fixture
+ end
+
+ after do
+ delete_authorized_keys_file
+ end
+
+ it "adds lines at the end of the file" do
+ auth_line1 = "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-12\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-dsa ASDFASGADG"
+ auth_line2 = "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-123\",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty ssh-rsa GFDGDFSGSDFG"
+
+ expect(logger).to receive(:info).with('Adding key (key-12): ssh-dsa ASDFASGADG')
+ expect(logger).to receive(:info).with('Adding key (key-123): ssh-rsa GFDGDFSGSDFG')
+ expect(subject.batch_add_keys(keys)).to be_truthy
+ expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{auth_line1}\n#{auth_line2}\n")
+ end
+
+ context "invalid key" do
+ let(:keys) { [double(shell_id: 'key-123', key: "ssh-rsa A\tSDFA\nSGADG")] }
+
+ it "doesn't add keys" do
+ expect(subject.batch_add_keys(keys)).to be_falsey
+ expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n")
+ end
+ end
+ end
+
+ context 'authorized_keys file does not exist' do
+ before do
+ delete_authorized_keys_file
+ end
+
+ it 'creates the file' do
+ expect(subject.batch_add_keys(keys)).to be_truthy
+ expect(File.exist?(tmp_authorized_keys_path)).to be_truthy
+ end
+ end
+ end
+
+ describe '#rm_key' do
+ context 'authorized_keys file exists' do
+ before do
+ create_authorized_keys_fixture
+ end
+
+ after do
+ delete_authorized_keys_file
+ end
+
+ it "removes the right line" do
+ other_line = "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-742\",options ssh-rsa AAAAB3NzaDAxx2E"
+ delete_line = "command=\"#{Gitlab.config.gitlab_shell.path}/bin/gitlab-shell key-741\",options ssh-rsa AAAAB3NzaDAxx2E"
+ erased_line = delete_line.gsub(/./, '#')
+ File.open(tmp_authorized_keys_path, 'a') do |auth_file|
+ auth_file.puts delete_line
+ auth_file.puts other_line
+ end
+
+ expect(logger).to receive(:info).with('Removing key (key-741)')
+ expect(subject.rm_key('key-741')).to be_truthy
+ expect(File.read(tmp_authorized_keys_path)).to eq("existing content\n#{erased_line}\n#{other_line}\n")
+ end
+ end
+
+ context 'authorized_keys file does not exist' do
+ before do
+ delete_authorized_keys_file
+ end
+
+ it 'returns false' do
+ expect(subject.rm_key('key-741')).to be_falsey
+ end
+ end
+ end
+
+ describe '#clear' do
+ context 'authorized_keys file exists' do
+ before do
+ create_authorized_keys_fixture
+ end
+
+ after do
+ delete_authorized_keys_file
+ end
+
+ it "returns true" do
+ expect(subject.clear).to be_truthy
+ end
+ end
+
+ context 'authorized_keys file does not exist' do
+ before do
+ delete_authorized_keys_file
+ end
+
+ it "still returns true" do
+ expect(subject.clear).to be_truthy
+ end
+ end
+ end
+
+ describe '#list_key_ids' do
+ context 'authorized_keys file exists' do
+ before do
+ create_authorized_keys_fixture(
+ existing_content:
+ "key-1\tssh-dsa AAA\nkey-2\tssh-rsa BBB\nkey-3\tssh-rsa CCC\nkey-9000\tssh-rsa DDD\n"
+ )
+ end
+
+ after do
+ delete_authorized_keys_file
+ end
+
+ it 'returns array of key IDs' do
+ expect(subject.list_key_ids).to eq([1, 2, 3, 9000])
+ end
+ end
+
+ context 'authorized_keys file does not exist' do
+ before do
+ delete_authorized_keys_file
+ end
+
+ it 'returns an empty array' do
+ expect(subject.list_key_ids).to be_empty
+ end
+ end
+ end
+
+ def create_authorized_keys_fixture(existing_content: 'existing content')
+ FileUtils.mkdir_p(File.dirname(tmp_authorized_keys_path))
+ File.open(tmp_authorized_keys_path, 'w') { |file| file.puts(existing_content) }
+ end
+
+ def delete_authorized_keys_file
+ File.delete(tmp_authorized_keys_path) if File.exist?(tmp_authorized_keys_path)
+ end
+
+ def tmp_authorized_keys_path
+ Gitlab.config.gitlab_shell.authorized_keys_file
+ 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 bc71a90605a..e7ff9169f1b 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
@@ -172,10 +172,8 @@ describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :m
let(:exception) { ActiveRecord::RecordNotFound }
let(:perform_ignoring_exceptions) do
- begin
- subject.perform(start_id, stop_id)
- rescue described_class::Error
- end
+ subject.perform(start_id, stop_id)
+ rescue described_class::Error
end
before do
diff --git a/spec/lib/gitlab/background_migration/digest_column_spec.rb b/spec/lib/gitlab/background_migration/digest_column_spec.rb
index 3e107ac3027..a25dcb06005 100644
--- a/spec/lib/gitlab/background_migration/digest_column_spec.rb
+++ b/spec/lib/gitlab/background_migration/digest_column_spec.rb
@@ -22,7 +22,7 @@ describe Gitlab::BackgroundMigration::DigestColumn, :migration, schema: 20180913
it 'erases token' do
expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.to(
- change { PersonalAccessToken.find(1).token }.from('token-01').to(nil))
+ change { PersonalAccessToken.find(1).read_attribute(:token) }.from('token-01').to(nil))
end
end
@@ -39,7 +39,7 @@ describe Gitlab::BackgroundMigration::DigestColumn, :migration, schema: 20180913
it 'leaves token empty' do
expect { subject.perform(PersonalAccessToken, :token, :token_digest, 1, 2) }.not_to(
- change { PersonalAccessToken.find(1).token }.from(nil))
+ change { PersonalAccessToken.find(1).read_attribute(:token) }.from(nil))
end
end
end
diff --git a/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb b/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb
new file mode 100644
index 00000000000..4a81a37d341
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_merge_request_assignees_table_spec.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Gitlab::BackgroundMigration::PopulateMergeRequestAssigneesTable, :migration, schema: 20190315191339 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:users) { table(:users) }
+
+ let(:user) { users.create!(email: 'test@example.com', projects_limit: 100, username: 'test') }
+ let(:user_2) { users.create!(email: 'test2@example.com', projects_limit: 100, username: 'test') }
+ let(:user_3) { users.create!(email: 'test3@example.com', projects_limit: 100, username: 'test') }
+
+ let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
+ let(:merge_requests) { table(:merge_requests) }
+ let(:merge_request_assignees) { table(:merge_request_assignees) }
+
+ def create_merge_request(id, params = {})
+ params.merge!(id: id,
+ target_project_id: project.id,
+ target_branch: 'master',
+ source_project_id: project.id,
+ source_branch: 'mr name',
+ title: "mr name#{id}")
+
+ merge_requests.create(params)
+ end
+
+ describe '#perform' do
+ it 'creates merge_request_assignees rows according to merge_requests' do
+ create_merge_request(2, assignee_id: user.id)
+ create_merge_request(3, assignee_id: user_2.id)
+ create_merge_request(4, assignee_id: user_3.id)
+ # Test filtering already migrated row
+ merge_request_assignees.create!(merge_request_id: 2, user_id: user_3.id)
+
+ subject.perform(1, 4)
+
+ rows = merge_request_assignees.order(:id).map { |row| row.attributes.slice('merge_request_id', 'user_id') }
+ existing_rows = [
+ { 'merge_request_id' => 2, 'user_id' => user_3.id }
+ ]
+ created_rows = [
+ { 'merge_request_id' => 3, 'user_id' => user_2.id },
+ { 'merge_request_id' => 4, 'user_id' => user_3.id }
+ ]
+ expected_rows = existing_rows + created_rows
+
+ expect(rows.size).to eq(expected_rows.size)
+ expected_rows.each do |expected_row|
+ expect(rows).to include(expected_row)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/badge/pipeline/template_spec.rb b/spec/lib/gitlab/badge/pipeline/template_spec.rb
index 20fa4f879c3..bcef0b7e120 100644
--- a/spec/lib/gitlab/badge/pipeline/template_spec.rb
+++ b/spec/lib/gitlab/badge/pipeline/template_spec.rb
@@ -59,6 +59,16 @@ describe Gitlab::Badge::Pipeline::Template do
end
end
+ context 'when status is preparing' do
+ before do
+ allow(badge).to receive(:status).and_return('preparing')
+ end
+
+ it 'has expected color' do
+ expect(template.value_color).to eq '#dfb317'
+ end
+ end
+
context 'when status is unknown' do
before do
allow(badge).to receive(:status).and_return('unknown')
diff --git a/spec/lib/gitlab/bitbucket_import/importer_spec.rb b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
index c432cc223b9..e1a2bae5fe8 100644
--- a/spec/lib/gitlab/bitbucket_import/importer_spec.rb
+++ b/spec/lib/gitlab/bitbucket_import/importer_spec.rb
@@ -95,6 +95,9 @@ describe Gitlab::BitbucketImport::Importer do
subject { described_class.new(project) }
describe '#import_pull_requests' do
+ let(:source_branch_sha) { sample.commits.last }
+ let(:target_branch_sha) { sample.commits.first }
+
before do
allow(subject).to receive(:import_wiki)
allow(subject).to receive(:import_issues)
@@ -102,9 +105,9 @@ describe Gitlab::BitbucketImport::Importer do
pull_request = instance_double(
Bitbucket::Representation::PullRequest,
iid: 10,
- source_branch_sha: sample.commits.last,
+ source_branch_sha: source_branch_sha,
source_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.source_branch,
- target_branch_sha: sample.commits.first,
+ target_branch_sha: target_branch_sha,
target_branch_name: Gitlab::Git::BRANCH_REF_PREFIX + sample.target_branch,
title: 'This is a title',
description: 'This is a test pull request',
@@ -162,6 +165,19 @@ describe Gitlab::BitbucketImport::Importer do
expect(reply_note).to be_a(DiffNote)
expect(reply_note.note).to eq(@reply.note)
end
+
+ context "when branches' sha is not found in the repository" do
+ let(:source_branch_sha) { 'a' * Commit::MIN_SHA_LENGTH }
+ let(:target_branch_sha) { 'b' * Commit::MIN_SHA_LENGTH }
+
+ it 'uses the pull request sha references' do
+ expect { subject.execute }.to change { MergeRequest.count }.by(1)
+
+ merge_request_diff = MergeRequest.first.merge_request_diff
+ expect(merge_request_diff.head_commit_sha).to eq source_branch_sha
+ expect(merge_request_diff.start_commit_sha).to eq target_branch_sha
+ end
+ end
end
context 'issues statuses' do
diff --git a/spec/lib/gitlab/chat/command_spec.rb b/spec/lib/gitlab/chat/command_spec.rb
new file mode 100644
index 00000000000..46d23ab2b62
--- /dev/null
+++ b/spec/lib/gitlab/chat/command_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Command do
+ let(:chat_name) { create(:chat_name) }
+
+ let(:command) do
+ described_class.new(
+ project: project,
+ chat_name: chat_name,
+ name: 'spinach',
+ arguments: 'foo',
+ channel: '123',
+ response_url: 'http://example.com'
+ )
+ end
+
+ describe '#try_create_pipeline' do
+ let(:project) { create(:project) }
+
+ it 'returns nil when the command is not valid' do
+ expect(command)
+ .to receive(:valid?)
+ .and_return(false)
+
+ expect(command.try_create_pipeline).to be_nil
+ end
+
+ it 'tries to create the pipeline when a command is valid' do
+ expect(command)
+ .to receive(:valid?)
+ .and_return(true)
+
+ expect(command)
+ .to receive(:create_pipeline)
+
+ command.try_create_pipeline
+ end
+ end
+
+ describe '#create_pipeline' do
+ let(:project) { create(:project, :test_repo) }
+ let(:pipeline) { command.create_pipeline }
+
+ before do
+ stub_repository_ci_yaml_file(sha: project.commit.id)
+
+ project.add_developer(chat_name.user)
+ end
+
+ it 'creates the pipeline' do
+ expect(pipeline).to be_persisted
+ end
+
+ it 'creates the chat data for the pipeline' do
+ expect(pipeline.chat_data).to be_an_instance_of(Ci::PipelineChatData)
+ end
+
+ it 'stores the chat name ID in the chat data' do
+ expect(pipeline.chat_data.chat_name_id).to eq(chat_name.id)
+ end
+
+ it 'stores the response URL in the chat data' do
+ expect(pipeline.chat_data.response_url).to eq('http://example.com')
+ end
+
+ it 'creates the environment variables for the pipeline' do
+ vars = pipeline.variables.each_with_object({}) do |row, hash|
+ hash[row.key] = row.value
+ end
+
+ expect(vars['CHAT_INPUT']).to eq('foo')
+ expect(vars['CHAT_CHANNEL']).to eq('123')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat/output_spec.rb b/spec/lib/gitlab/chat/output_spec.rb
new file mode 100644
index 00000000000..b179f9e9d0a
--- /dev/null
+++ b/spec/lib/gitlab/chat/output_spec.rb
@@ -0,0 +1,101 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Output do
+ let(:build) do
+ create(:ci_build, pipeline: create(:ci_pipeline, source: :chat))
+ end
+
+ let(:output) { described_class.new(build) }
+
+ describe '#to_s' do
+ it 'returns the build output as a String' do
+ trace = Gitlab::Ci::Trace.new(build)
+
+ trace.set("echo hello\nhello")
+
+ allow(build)
+ .to receive(:trace)
+ .and_return(trace)
+
+ allow(output)
+ .to receive(:read_offset_and_length)
+ .and_return([0, 13])
+
+ expect(output.to_s).to eq('he')
+ end
+ end
+
+ describe '#read_offset_and_length' do
+ context 'without the chat_reply trace section' do
+ it 'falls back to using the build_script trace section' do
+ expect(output)
+ .to receive(:find_build_trace_section)
+ .with('chat_reply')
+ .and_return(nil)
+
+ expect(output)
+ .to receive(:find_build_trace_section)
+ .with('build_script')
+ .and_return({ name: 'build_script', byte_start: 1, byte_end: 4 })
+
+ expect(output.read_offset_and_length).to eq([1, 3])
+ end
+ end
+
+ context 'without the build_script trace section' do
+ it 'raises MissingBuildSectionError' do
+ expect { output.read_offset_and_length }
+ .to raise_error(described_class::MissingBuildSectionError)
+ end
+ end
+
+ context 'with the chat_reply trace section' do
+ it 'returns the read offset and length as an Array' do
+ trace = Gitlab::Ci::Trace.new(build)
+
+ allow(build)
+ .to receive(:trace)
+ .and_return(trace)
+
+ allow(trace)
+ .to receive(:extract_sections)
+ .and_return([{ name: 'chat_reply', byte_start: 1, byte_end: 4 }])
+
+ expect(output.read_offset_and_length).to eq([1, 3])
+ end
+ end
+ end
+
+ describe '#without_executed_command_line' do
+ it 'returns the input without the first line' do
+ expect(output.without_executed_command_line("hello\nworld"))
+ .to eq('world')
+ end
+
+ it 'returns an empty String when the input is empty' do
+ expect(output.without_executed_command_line('')).to eq('')
+ end
+
+ it 'returns an empty String when the input consits of a single newline' do
+ expect(output.without_executed_command_line("\n")).to eq('')
+ end
+ end
+
+ describe '#find_build_trace_section' do
+ it 'returns nil when no section could be found' do
+ expect(output.find_build_trace_section('foo')).to be_nil
+ end
+
+ it 'returns the trace section when it could be found' do
+ section = { name: 'chat_reply', byte_start: 1, byte_end: 4 }
+
+ allow(output)
+ .to receive(:trace_sections)
+ .and_return([section])
+
+ expect(output.find_build_trace_section('chat_reply')).to eq(section)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat/responder/base_spec.rb b/spec/lib/gitlab/chat/responder/base_spec.rb
new file mode 100644
index 00000000000..7fa9bad9d38
--- /dev/null
+++ b/spec/lib/gitlab/chat/responder/base_spec.rb
@@ -0,0 +1,48 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Responder::Base do
+ let(:project) { double(:project) }
+ let(:pipeline) { double(:pipeline, project: project) }
+ let(:build) { double(:build, pipeline: pipeline) }
+ let(:responder) { described_class.new(build) }
+
+ describe '#pipeline' do
+ it 'returns the pipeline' do
+ expect(responder.pipeline).to eq(pipeline)
+ end
+ end
+
+ describe '#project' do
+ it 'returns the project' do
+ expect(responder.project).to eq(project)
+ end
+ end
+
+ describe '#success' do
+ it 'raises NotImplementedError' do
+ expect { responder.success }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#failure' do
+ it 'raises NotImplementedError' do
+ expect { responder.failure }.to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#send_response' do
+ it 'raises NotImplementedError' do
+ expect { responder.send_response('hello') }
+ .to raise_error(NotImplementedError)
+ end
+ end
+
+ describe '#scheduled_output' do
+ it 'raises NotImplementedError' do
+ expect { responder.scheduled_output }
+ .to raise_error(NotImplementedError)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat/responder/slack_spec.rb b/spec/lib/gitlab/chat/responder/slack_spec.rb
new file mode 100644
index 00000000000..a1553232b32
--- /dev/null
+++ b/spec/lib/gitlab/chat/responder/slack_spec.rb
@@ -0,0 +1,77 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Responder::Slack do
+ let(:chat_name) { create(:chat_name, chat_id: 'U123') }
+
+ let(:pipeline) do
+ pipeline = create(:ci_pipeline)
+
+ pipeline.create_chat_data!(
+ response_url: 'http://example.com',
+ chat_name_id: chat_name.id
+ )
+
+ pipeline
+ end
+
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+ let(:responder) { described_class.new(build) }
+
+ describe '#send_response' do
+ it 'sends a response back to Slack' do
+ expect(Gitlab::HTTP).to receive(:post).with(
+ 'http://example.com',
+ { headers: { Accept: 'application/json' }, body: 'hello'.to_json }
+ )
+
+ responder.send_response('hello')
+ end
+ end
+
+ describe '#success' do
+ it 'returns the output for a successful build' do
+ expect(responder)
+ .to receive(:send_response)
+ .with(hash_including(text: /<@U123>:.+hello/, response_type: :in_channel))
+
+ responder.success('hello')
+ end
+
+ it 'limits the output to a fixed size' do
+ expect(responder)
+ .to receive(:send_response)
+ .with(hash_including(text: /The output is too large/))
+
+ responder.success('a' * 4000)
+ end
+
+ it 'does not send a response if the output is empty' do
+ expect(responder).not_to receive(:send_response)
+
+ responder.success('')
+ end
+ end
+
+ describe '#failure' do
+ it 'returns the output for a failed build' do
+ expect(responder).to receive(:send_response).with(
+ hash_including(
+ text: /<@U123>:.+Sorry, the build failed!/,
+ response_type: :in_channel
+ )
+ )
+
+ responder.failure
+ end
+ end
+
+ describe '#scheduled_output' do
+ it 'returns the output for a scheduled build' do
+ output = responder.scheduled_output
+
+ expect(output).to eq({ text: '' })
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat/responder_spec.rb b/spec/lib/gitlab/chat/responder_spec.rb
new file mode 100644
index 00000000000..9893689cba9
--- /dev/null
+++ b/spec/lib/gitlab/chat/responder_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Chat::Responder do
+ describe '.responder_for' do
+ context 'using a regular build' do
+ it 'returns nil' do
+ build = create(:ci_build)
+
+ expect(described_class.responder_for(build)).to be_nil
+ end
+ end
+
+ context 'using a chat build' do
+ it 'returns the responder for the build' do
+ pipeline = create(:ci_pipeline)
+ build = create(:ci_build, pipeline: pipeline)
+ service = double(:service, chat_responder: Gitlab::Chat::Responder::Slack)
+ chat_name = double(:chat_name, service: service)
+ chat_data = double(:chat_data, chat_name: chat_name)
+
+ allow(pipeline)
+ .to receive(:chat_data)
+ .and_return(chat_data)
+
+ expect(described_class.responder_for(build))
+ .to be_an_instance_of(Gitlab::Chat::Responder::Slack)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/chat_spec.rb b/spec/lib/gitlab/chat_spec.rb
new file mode 100644
index 00000000000..d61c4b36668
--- /dev/null
+++ b/spec/lib/gitlab/chat_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Gitlab::Chat, :use_clean_rails_memory_store_caching do
+ describe '.available?' do
+ it 'returns true when the chatops feature is available' do
+ allow(Feature)
+ .to receive(:enabled?)
+ .with(:chatops, default_enabled: true)
+ .and_return(true)
+
+ expect(described_class).to be_available
+ end
+
+ it 'returns false when the chatops feature is not available' do
+ allow(Feature)
+ .to receive(:enabled?)
+ .with(:chatops, default_enabled: true)
+ .and_return(false)
+
+ expect(described_class).not_to be_available
+ end
+ end
+end
diff --git a/spec/lib/gitlab/checks/branch_check_spec.rb b/spec/lib/gitlab/checks/branch_check_spec.rb
index 77366e91dca..8d5ab27a17c 100644
--- a/spec/lib/gitlab/checks/branch_check_spec.rb
+++ b/spec/lib/gitlab/checks/branch_check_spec.rb
@@ -48,10 +48,150 @@ describe Gitlab::Checks::BranchCheck do
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)
+ context 'user is not allowed to push to protected branches' do
+ before do
+ allow(user_access)
+ .to receive(:can_push_to_branch?)
+ .and_return(false)
+ end
+
+ it 'raises an error' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /Ask a project Owner or Maintainer to create a default branch/)
+ end
+ end
+
+ context 'user is allowed to push to protected branches' do
+ before do
+ allow(user_access)
+ .to receive(:can_push_to_branch?)
+ .and_return(true)
+ end
+
+ it 'allows branch creation' do
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+ end
+
+ context 'branch creation' do
+ let(:oldrev) { '0000000000000000000000000000000000000000' }
+ let(:ref) { 'refs/heads/feature' }
- expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, /Ask a project Owner or Maintainer to create a default branch/)
+ context 'protected branch creation feature is disabled' do
+ before do
+ stub_feature_flags(protected_branch_creation: false)
+ end
+
+ context 'user is not allowed to push to protected branch' do
+ before do
+ allow(user_access)
+ .to receive(:can_push_to_branch?)
+ .and_return(false)
+ end
+
+ it 'raises an error' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to push code to protected branches on this project.')
+ end
+ end
+
+ context 'user is allowed to push to protected branch' do
+ before do
+ allow(user_access)
+ .to receive(:can_push_to_branch?)
+ .and_return(true)
+ end
+
+ it 'does not raise an error' do
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+ end
+
+ context 'protected branch creation feature is enabled' do
+ context 'user can push to branch' do
+ before do
+ allow(user_access)
+ .to receive(:can_push_to_branch?)
+ .with('feature')
+ .and_return(true)
+ end
+
+ it 'does not raise an error' do
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+
+ context 'user cannot push to branch' do
+ before do
+ allow(user_access)
+ .to receive(:can_push_to_branch?)
+ .with('feature')
+ .and_return(false)
+ end
+
+ context 'user cannot merge to branch' do
+ before do
+ allow(user_access)
+ .to receive(:can_merge_to_branch?)
+ .with('feature')
+ .and_return(false)
+ end
+
+ it 'raises an error' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You are not allowed to create protected branches on this project.')
+ end
+ end
+
+ context 'user can merge to branch' do
+ before do
+ allow(user_access)
+ .to receive(:can_merge_to_branch?)
+ .with('feature')
+ .and_return(true)
+
+ allow(project.repository)
+ .to receive(:branch_names_contains_sha)
+ .with(newrev)
+ .and_return(['branch'])
+ end
+
+ context "newrev isn't in any protected branches" do
+ before do
+ allow(ProtectedBranch)
+ .to receive(:any_protected?)
+ .with(project, ['branch'])
+ .and_return(false)
+ end
+
+ it 'raises an error' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only use an existing protected branch ref as the basis of a new protected branch.')
+ end
+ end
+
+ context 'newrev is included in a protected branch' do
+ before do
+ allow(ProtectedBranch)
+ .to receive(:any_protected?)
+ .with(project, ['branch'])
+ .and_return(true)
+ end
+
+ context 'via web interface' do
+ let(:protocol) { 'web' }
+
+ it 'allows branch creation' do
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+
+ context 'via SSH' do
+ it 'raises an error' do
+ expect { subject.validate! }.to raise_error(Gitlab::GitAccess::UnauthorizedError, 'You can only create protected branches using the web interface and API.')
+ end
+ end
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/build/policy/changes_spec.rb b/spec/lib/gitlab/ci/build/policy/changes_spec.rb
index dc3329061d1..92cf0376c02 100644
--- a/spec/lib/gitlab/ci/build/policy/changes_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/changes_spec.rb
@@ -133,7 +133,7 @@ describe Gitlab::Ci::Build::Policy::Changes do
let(:seed) { double('build seed', to_resource: ci_build) }
context 'when source is merge request' do
- let(:source) { :merge_request }
+ let(:source) { :merge_request_event }
let(:merge_request) do
create(:merge_request,
diff --git a/spec/lib/gitlab/ci/build/policy/refs_spec.rb b/spec/lib/gitlab/ci/build/policy/refs_spec.rb
index 553fc0fb9bf..b4ddbf89b70 100644
--- a/spec/lib/gitlab/ci/build/policy/refs_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/refs_spec.rb
@@ -68,6 +68,20 @@ describe Gitlab::Ci::Build::Policy::Refs do
expect(described_class.new(%w[triggers]))
.not_to be_satisfied_by(pipeline)
end
+
+ context 'when source is merge_request_event' do
+ let(:pipeline) { build_stubbed(:ci_pipeline, source: :merge_request_event) }
+
+ it 'is satisfied with only: merge_request' do
+ expect(described_class.new(%w[merge_requests]))
+ .to be_satisfied_by(pipeline)
+ end
+
+ it 'is not satisfied with only: merge_request_event' do
+ expect(described_class.new(%w[merge_request_events]))
+ .not_to be_satisfied_by(pipeline)
+ end
+ end
end
context 'when matching a ref by a regular expression' do
diff --git a/spec/lib/gitlab/ci/build/policy/variables_spec.rb b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
index c2c0742efc3..9b016901a20 100644
--- a/spec/lib/gitlab/ci/build/policy/variables_spec.rb
+++ b/spec/lib/gitlab/ci/build/policy/variables_spec.rb
@@ -15,6 +15,7 @@ describe Gitlab::Ci::Build::Policy::Variables do
before do
pipeline.variables.build(key: 'CI_PROJECT_NAME', value: '')
+ pipeline.variables.build(key: 'MY_VARIABLE', value: 'my-var')
end
describe '#satisfied_by?' do
@@ -24,6 +25,12 @@ describe Gitlab::Ci::Build::Policy::Variables do
expect(policy).to be_satisfied_by(pipeline, seed)
end
+ it 'is satisfied by a matching pipeline variable' do
+ policy = described_class.new(['$MY_VARIABLE'])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+
it 'is not satisfied by an overridden empty variable' do
policy = described_class.new(['$CI_PROJECT_NAME'])
@@ -68,5 +75,19 @@ describe Gitlab::Ci::Build::Policy::Variables do
expect(pipeline).not_to be_persisted
expect(seed.to_resource).not_to be_persisted
end
+
+ context 'when a bridge job is used' do
+ let(:bridge) do
+ build(:ci_bridge, pipeline: pipeline, project: project, ref: 'master')
+ end
+
+ let(:seed) { double('bridge seed', to_resource: bridge) }
+
+ it 'is satisfied by a matching expression for a bridge job' do
+ policy = described_class.new(['$MY_VARIABLE'])
+
+ expect(policy).to be_satisfied_by(pipeline, seed)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb
new file mode 100644
index 00000000000..5187f99a441
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/prerequisite/factory_spec.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Prerequisite::Factory do
+ let(:build) { create(:ci_build) }
+
+ describe '.for_build' do
+ let(:kubernetes_namespace) do
+ instance_double(
+ Gitlab::Ci::Build::Prerequisite::KubernetesNamespace,
+ unmet?: unmet)
+ end
+
+ subject { described_class.new(build).unmet }
+
+ before do
+ expect(Gitlab::Ci::Build::Prerequisite::KubernetesNamespace)
+ .to receive(:new).with(build).and_return(kubernetes_namespace)
+ end
+
+ context 'prerequisite is unmet' do
+ let(:unmet) { true }
+
+ it { is_expected.to eq [kubernetes_namespace] }
+ end
+
+ context 'prerequisite is met' do
+ let(:unmet) { false }
+
+ it { is_expected.to be_empty }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
new file mode 100644
index 00000000000..62dcd80fad7
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/prerequisite/kubernetes_namespace_spec.rb
@@ -0,0 +1,85 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Prerequisite::KubernetesNamespace do
+ let(:build) { create(:ci_build) }
+
+ describe '#unmet?' do
+ subject { described_class.new(build).unmet? }
+
+ context 'build has no deployment' do
+ before do
+ expect(build.deployment).to be_nil
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
+ context 'build has a deployment' do
+ let!(:deployment) { create(:deployment, deployable: build) }
+
+ context 'and a cluster to deploy to' do
+ let(:cluster) { create(:cluster, projects: [build.project]) }
+
+ before do
+ allow(build.deployment).to receive(:cluster).and_return(cluster)
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'and a namespace is already created for this project' do
+ let!(:kubernetes_namespace) { create(:cluster_kubernetes_namespace, cluster: cluster, project: build.project) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'and no cluster to deploy to' do
+ before do
+ expect(deployment.cluster).to be_nil
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+ end
+
+ describe '#complete!' do
+ let!(:deployment) { create(:deployment, deployable: build) }
+ let(:service) { double(execute: true) }
+
+ subject { described_class.new(build).complete! }
+
+ context 'completion is required' do
+ let(:cluster) { create(:cluster, projects: [build.project]) }
+
+ before do
+ allow(build.deployment).to receive(:cluster).and_return(cluster)
+ end
+
+ it 'creates a kubernetes namespace' do
+ expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService)
+ .to receive(:new)
+ .with(cluster: cluster, kubernetes_namespace: instance_of(Clusters::KubernetesNamespace))
+ .and_return(service)
+
+ expect(service).to receive(:execute).once
+
+ subject
+ end
+ end
+
+ context 'completion is not required' do
+ before do
+ expect(deployment.cluster).to be_nil
+ end
+
+ it 'does not create a namespace' do
+ expect(Clusters::Gcp::Kubernetes::CreateOrUpdateNamespaceService).not_to receive(:new)
+
+ subject
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/config/entry/global_spec.rb b/spec/lib/gitlab/ci/config/entry/global_spec.rb
index 7651f594a4c..e23efff18d5 100644
--- a/spec/lib/gitlab/ci/config/entry/global_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/global_spec.rb
@@ -13,7 +13,7 @@ describe Gitlab::Ci::Config::Entry::Global do
expect(described_class.nodes.keys)
.to match_array(%i[before_script image services
after_script variables stages
- types cache])
+ types cache include])
end
end
end
@@ -42,7 +42,7 @@ describe Gitlab::Ci::Config::Entry::Global do
end
it 'creates node object for each entry' do
- expect(global.descendants.count).to eq 8
+ expect(global.descendants.count).to eq 9
end
it 'creates node object using valid class' do
@@ -189,7 +189,7 @@ describe Gitlab::Ci::Config::Entry::Global do
describe '#nodes' do
it 'instantizes all nodes' do
- expect(global.descendants.count).to eq 8
+ expect(global.descendants.count).to eq 9
end
it 'contains unspecified nodes' do
diff --git a/spec/lib/gitlab/ci/config/external/file/base_spec.rb b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
index 1a6b3587599..fa39b32d7ab 100644
--- a/spec/lib/gitlab/ci/config/external/file/base_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/base_spec.rb
@@ -3,7 +3,7 @@
require 'fast_spec_helper'
describe Gitlab::Ci::Config::External::File::Base do
- let(:context) { described_class::Context.new(nil, 'HEAD', nil) }
+ let(:context) { described_class::Context.new(nil, 'HEAD', nil, Set.new) }
let(:test_class) do
Class.new(described_class) do
@@ -79,4 +79,20 @@ describe Gitlab::Ci::Config::External::File::Base do
end
end
end
+
+ describe '#to_hash' do
+ context 'with includes' do
+ let(:location) { 'some/file/config.yml' }
+ let(:content) { 'include: { template: Bash.gitlab-ci.yml }'}
+
+ before do
+ allow_any_instance_of(test_class)
+ .to receive(:content).and_return(content)
+ end
+
+ it 'does expand hash to include the template' do
+ expect(subject.to_hash).to include(:before_script)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/local_spec.rb b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
index ff67a765da0..dc14b07287e 100644
--- a/spec/lib/gitlab/ci/config/external/file/local_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/local_spec.rb
@@ -4,8 +4,10 @@ require 'spec_helper'
describe Gitlab::Ci::Config::External::File::Local do
set(:project) { create(:project, :repository) }
+ set(:user) { create(:user) }
- let(:context) { described_class::Context.new(project, '12345', nil) }
+ let(:sha) { '12345' }
+ let(:context) { described_class::Context.new(project, sha, user, Set.new) }
let(:params) { { local: location } }
let(:local_file) { described_class.new(params, context) }
@@ -103,4 +105,36 @@ describe Gitlab::Ci::Config::External::File::Local do
expect(local_file.error_message).to eq("Local file `#{location}` does not exist!")
end
end
+
+ describe '#expand_context' do
+ let(:location) { 'location.yml' }
+
+ subject { local_file.send(:expand_context) }
+
+ it 'inherits project, user and sha' do
+ is_expected.to include(user: user, project: project, sha: sha)
+ end
+ end
+
+ describe '#to_hash' do
+ context 'properly includes another local file in the same repository' do
+ let(:location) { 'some/file/config.yml' }
+ let(:content) { 'include: { local: another-config.yml }'}
+
+ let(:another_location) { 'another-config.yml' }
+ let(:another_content) { 'rspec: JOB' }
+
+ before do
+ allow(project.repository).to receive(:blob_data_at).with(sha, location)
+ .and_return(content)
+
+ allow(project.repository).to receive(:blob_data_at).with(sha, another_location)
+ .and_return(another_content)
+ end
+
+ it 'does expand hash to include the template' do
+ expect(local_file.to_hash).to include(:rspec)
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/project_spec.rb b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
index 11809adcaf6..6e89bb1b30f 100644
--- a/spec/lib/gitlab/ci/config/external/file/project_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/project_spec.rb
@@ -3,12 +3,13 @@
require 'spec_helper'
describe Gitlab::Ci::Config::External::File::Project do
+ set(:context_project) { create(:project) }
set(:project) { create(:project, :repository) }
set(:user) { create(:user) }
let(:context_user) { user }
- let(:context) { described_class::Context.new(nil, '12345', context_user) }
- let(:subject) { described_class.new(params, context) }
+ let(:context) { described_class::Context.new(context_project, '12345', context_user, Set.new) }
+ let(:project_file) { described_class.new(params, context) }
before do
project.add_developer(user)
@@ -19,7 +20,7 @@ describe Gitlab::Ci::Config::External::File::Project do
let(:params) { { file: 'file.yml', project: 'project' } }
it 'should return true' do
- expect(subject).to be_matching
+ expect(project_file).to be_matching
end
end
@@ -27,7 +28,7 @@ describe Gitlab::Ci::Config::External::File::Project do
let(:params) { { file: 'file.yml' } }
it 'should return false' do
- expect(subject).not_to be_matching
+ expect(project_file).not_to be_matching
end
end
@@ -35,7 +36,7 @@ describe Gitlab::Ci::Config::External::File::Project do
let(:params) { { project: 'project' } }
it 'should return false' do
- expect(subject).not_to be_matching
+ expect(project_file).not_to be_matching
end
end
@@ -43,7 +44,7 @@ describe Gitlab::Ci::Config::External::File::Project do
let(:params) { {} }
it 'should return false' do
- expect(subject).not_to be_matching
+ expect(project_file).not_to be_matching
end
end
end
@@ -61,15 +62,15 @@ describe Gitlab::Ci::Config::External::File::Project do
end
it 'should return true' do
- expect(subject).to be_valid
+ expect(project_file).to be_valid
end
context 'when user does not have permission to access file' do
let(:context_user) { create(:user) }
it 'should return false' do
- expect(subject).not_to be_valid
- expect(subject.error_message).to include("Project `#{project.full_path}` not found or access denied!")
+ expect(project_file).not_to be_valid
+ expect(project_file.error_message).to include("Project `#{project.full_path}` not found or access denied!")
end
end
end
@@ -86,7 +87,7 @@ describe Gitlab::Ci::Config::External::File::Project do
end
it 'should return true' do
- expect(subject).to be_valid
+ expect(project_file).to be_valid
end
end
@@ -102,8 +103,8 @@ describe Gitlab::Ci::Config::External::File::Project do
end
it 'should return false' do
- expect(subject).not_to be_valid
- expect(subject.error_message).to include("Project `#{project.full_path}` file `/file.yml` is empty!")
+ expect(project_file).not_to be_valid
+ expect(project_file.error_message).to include("Project `#{project.full_path}` file `/file.yml` is empty!")
end
end
@@ -113,8 +114,8 @@ describe Gitlab::Ci::Config::External::File::Project do
end
it 'should return false' do
- expect(subject).not_to be_valid
- expect(subject.error_message).to include("Project `#{project.full_path}` reference `I-Do-Not-Exist` does not exist!")
+ expect(project_file).not_to be_valid
+ expect(project_file.error_message).to include("Project `#{project.full_path}` reference `I-Do-Not-Exist` does not exist!")
end
end
@@ -124,8 +125,8 @@ describe Gitlab::Ci::Config::External::File::Project do
end
it 'should return false' do
- expect(subject).not_to be_valid
- expect(subject.error_message).to include("Project `#{project.full_path}` file `/invalid-file.yml` does not exist!")
+ expect(project_file).not_to be_valid
+ expect(project_file.error_message).to include("Project `#{project.full_path}` file `/invalid-file.yml` does not exist!")
end
end
@@ -135,12 +136,22 @@ describe Gitlab::Ci::Config::External::File::Project do
end
it 'should return false' do
- expect(subject).not_to be_valid
- expect(subject.error_message).to include('Included file `/invalid-file` does not have YAML extension!')
+ expect(project_file).not_to be_valid
+ expect(project_file.error_message).to include('Included file `/invalid-file` does not have YAML extension!')
end
end
end
+ describe '#expand_context' do
+ let(:params) { { file: 'file.yml', project: project.full_path, ref: 'master' } }
+
+ subject { project_file.send(:expand_context) }
+
+ it 'inherits user, and target project and sha' do
+ is_expected.to include(user: user, project: project, sha: project.commit('master').id)
+ end
+ end
+
private
def stub_project_blob(ref, path)
diff --git a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
index 3e0fda9c308..c5b32c29759 100644
--- a/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/remote_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
describe Gitlab::Ci::Config::External::File::Remote do
- let(:context) { described_class::Context.new(nil, '12345', nil) }
+ let(:context) { described_class::Context.new(nil, '12345', nil, Set.new) }
let(:params) { { remote: location } }
let(:remote_file) { described_class.new(params, context) }
let(:location) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
@@ -181,4 +181,14 @@ describe Gitlab::Ci::Config::External::File::Remote do
end
end
end
+
+ describe '#expand_context' do
+ let(:params) { { remote: 'http://remote' } }
+
+ subject { remote_file.send(:expand_context) }
+
+ it 'drops all parameters' do
+ is_expected.to include(user: nil, project: nil, sha: nil)
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/config/external/file/template_spec.rb b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
index 1fb5655309a..8ecaf4800f8 100644
--- a/spec/lib/gitlab/ci/config/external/file/template_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/file/template_spec.rb
@@ -3,18 +3,21 @@
require 'spec_helper'
describe Gitlab::Ci::Config::External::File::Template do
- let(:context) { described_class::Context.new(nil, '12345') }
+ set(:project) { create(:project) }
+ set(:user) { create(:user) }
+
+ let(:context) { described_class::Context.new(project, '12345', user, Set.new) }
let(:template) { 'Auto-DevOps.gitlab-ci.yml' }
let(:params) { { template: template } }
- subject { described_class.new(params, context) }
+ let(:template_file) { described_class.new(params, context) }
describe '#matching?' do
context 'when a template is specified' do
let(:params) { { template: 'some-template' } }
it 'should return true' do
- expect(subject).to be_matching
+ expect(template_file).to be_matching
end
end
@@ -22,7 +25,7 @@ describe Gitlab::Ci::Config::External::File::Template do
let(:params) { { template: nil } }
it 'should return false' do
- expect(subject).not_to be_matching
+ expect(template_file).not_to be_matching
end
end
@@ -30,7 +33,7 @@ describe Gitlab::Ci::Config::External::File::Template do
let(:params) { {} }
it 'should return false' do
- expect(subject).not_to be_matching
+ expect(template_file).not_to be_matching
end
end
end
@@ -40,7 +43,7 @@ describe Gitlab::Ci::Config::External::File::Template do
let(:template) { 'Auto-DevOps.gitlab-ci.yml' }
it 'should return true' do
- expect(subject).to be_valid
+ expect(template_file).to be_valid
end
end
@@ -48,8 +51,8 @@ describe Gitlab::Ci::Config::External::File::Template do
let(:template) { 'Template.yml' }
it 'should return false' do
- expect(subject).not_to be_valid
- expect(subject.error_message).to include('Template file `Template.yml` is not a valid location!')
+ expect(template_file).not_to be_valid
+ expect(template_file.error_message).to include('Template file `Template.yml` is not a valid location!')
end
end
@@ -57,14 +60,14 @@ describe Gitlab::Ci::Config::External::File::Template do
let(:template) { 'I-Do-Not-Have-This-Template.gitlab-ci.yml' }
it 'should return false' do
- expect(subject).not_to be_valid
- expect(subject.error_message).to include('Included file `I-Do-Not-Have-This-Template.gitlab-ci.yml` is empty or does not exist!')
+ expect(template_file).not_to be_valid
+ expect(template_file.error_message).to include('Included file `I-Do-Not-Have-This-Template.gitlab-ci.yml` is empty or does not exist!')
end
end
end
describe '#template_name' do
- let(:template_name) { subject.send(:template_name) }
+ let(:template_name) { template_file.send(:template_name) }
context 'when template does end with .gitlab-ci.yml' do
let(:template) { 'my-template.gitlab-ci.yml' }
@@ -90,4 +93,14 @@ describe Gitlab::Ci::Config::External::File::Template do
end
end
end
+
+ describe '#expand_context' do
+ let(:location) { 'location.yml' }
+
+ subject { template_file.send(:expand_context) }
+
+ it 'drops all parameters' do
+ is_expected.to include(user: nil, project: nil, sha: nil)
+ end
+ end
end
diff --git a/spec/lib/gitlab/ci/config/external/mapper_spec.rb b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
index 4cab4961b0f..136974569de 100644
--- a/spec/lib/gitlab/ci/config/external/mapper_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/mapper_spec.rb
@@ -9,6 +9,7 @@ describe Gitlab::Ci::Config::External::Mapper do
let(:local_file) { '/lib/gitlab/ci/templates/non-existent-file.yml' }
let(:remote_url) { 'https://gitlab.com/gitlab-org/gitlab-ce/blob/1234/.gitlab-ci-1.yml' }
let(:template_file) { 'Auto-DevOps.gitlab-ci.yml' }
+ let(:expandset) { Set.new }
let(:file_content) do
<<~HEREDOC
@@ -21,7 +22,7 @@ describe Gitlab::Ci::Config::External::Mapper do
end
describe '#process' do
- subject { described_class.new(values, project: project, sha: '123456', user: user).process }
+ subject { described_class.new(values, project: project, sha: '123456', user: user, expandset: expandset).process }
context "when single 'include' keyword is defined" do
context 'when the string is a local file' do
@@ -141,5 +142,37 @@ describe Gitlab::Ci::Config::External::Mapper do
expect(subject).to be_empty
end
end
+
+ context "when duplicate 'include' is defined" do
+ let(:values) do
+ { include: [
+ { 'local' => local_file },
+ { 'local' => local_file }
+ ],
+ image: 'ruby:2.2' }
+ end
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(described_class::DuplicateIncludesError)
+ end
+ end
+
+ context "when too many 'includes' are defined" do
+ let(:values) do
+ { include: [
+ { 'local' => local_file },
+ { 'remote' => remote_url }
+ ],
+ image: 'ruby:2.2' }
+ end
+
+ before do
+ stub_const("#{described_class}::MAX_INCLUDES", 1)
+ end
+
+ it 'raises an exception' do
+ expect { subject }.to raise_error(described_class::TooManyIncludesError)
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/config/external/processor_spec.rb b/spec/lib/gitlab/ci/config/external/processor_spec.rb
index 1ac58139b25..3f6f6d7c5d9 100644
--- a/spec/lib/gitlab/ci/config/external/processor_spec.rb
+++ b/spec/lib/gitlab/ci/config/external/processor_spec.rb
@@ -4,15 +4,20 @@ require 'spec_helper'
describe Gitlab::Ci::Config::External::Processor do
set(:project) { create(:project, :repository) }
+ set(:another_project) { create(:project, :repository) }
set(:user) { create(:user) }
- let(:processor) { described_class.new(values, project: project, sha: '12345', user: user) }
+ let(:expandset) { Set.new }
+ let(:sha) { '12345' }
+ let(:processor) { described_class.new(values, project: project, sha: '12345', user: user, expandset: expandset) }
before do
project.add_developer(user)
end
describe "#perform" do
+ subject { processor.perform }
+
context 'when no external files defined' do
let(:values) { { image: 'ruby:2.2' } }
@@ -190,5 +195,80 @@ describe Gitlab::Ci::Config::External::Processor do
expect(processor.perform[:image]).to eq('ruby:2.2')
end
end
+
+ context "when a nested includes are defined" do
+ let(:values) do
+ {
+ include: [
+ { local: '/local/file.yml' }
+ ],
+ image: 'ruby:2.2'
+ }
+ end
+
+ before do
+ allow(project.repository).to receive(:blob_data_at).with('12345', '/local/file.yml') do
+ <<~HEREDOC
+ include:
+ - template: Ruby.gitlab-ci.yml
+ - remote: http://my.domain.com/config.yml
+ - project: #{another_project.full_path}
+ file: /templates/my-workflow.yml
+ HEREDOC
+ end
+
+ allow_any_instance_of(Repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-workflow.yml') do
+ <<~HEREDOC
+ include:
+ - local: /templates/my-build.yml
+ HEREDOC
+ end
+
+ allow_any_instance_of(Repository).to receive(:blob_data_at).with(another_project.commit.id, '/templates/my-build.yml') do
+ <<~HEREDOC
+ my_build:
+ script: echo Hello World
+ HEREDOC
+ end
+
+ WebMock.stub_request(:get, 'http://my.domain.com/config.yml').to_return(body: 'remote_build: { script: echo Hello World }')
+ end
+
+ context 'when project is public' do
+ before do
+ another_project.update!(visibility: 'public')
+ end
+
+ it 'properly expands all includes' do
+ is_expected.to include(:my_build, :remote_build, :rspec)
+ end
+ end
+
+ context 'when user is reporter of another project' do
+ before do
+ another_project.add_reporter(user)
+ end
+
+ it 'properly expands all includes' do
+ is_expected.to include(:my_build, :remote_build, :rspec)
+ end
+ end
+
+ context 'when user is not allowed' do
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Ci::Config::External::Processor::IncludeError, /not found or access denied/)
+ end
+ end
+
+ context 'when too many includes is included' do
+ before do
+ stub_const('Gitlab::Ci::Config::External::Mapper::MAX_INCLUDES', 1)
+ end
+
+ it 'raises an error' do
+ expect { subject }.to raise_error(Gitlab::Ci::Config::External::Processor::IncludeError, /Maximum of 1 nested/)
+ end
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
index fab071405df..3debd42ac65 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/build_spec.rb
@@ -96,11 +96,13 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
context 'when pipeline is running for a merge request' do
let(:command) do
Gitlab::Ci::Pipeline::Chain::Command.new(
- source: :merge_request,
+ source: :merge_request_event,
origin_ref: 'feature',
checkout_sha: project.commit.id,
after_sha: nil,
before_sha: nil,
+ source_sha: merge_request.diff_head_sha,
+ target_sha: merge_request.target_branch_sha,
trigger_request: nil,
schedule: nil,
merge_request: merge_request,
@@ -115,8 +117,13 @@ describe Gitlab::Ci::Pipeline::Chain::Build do
end
it 'correctly indicated that this is a merge request pipeline' do
- expect(pipeline).to be_merge_request
+ expect(pipeline).to be_merge_request_event
expect(pipeline.merge_request).to eq(merge_request)
end
+
+ it 'correctly sets souce sha and target sha to pipeline' do
+ expect(pipeline.source_sha).to eq(merge_request.diff_head_sha)
+ expect(pipeline.target_sha).to eq(merge_request.target_branch_sha)
+ end
end
end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
index 6aa802ce6fd..dab0fb51bcc 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/command_spec.rb
@@ -161,6 +161,54 @@ describe Gitlab::Ci::Pipeline::Chain::Command do
end
end
+ describe '#source_sha' do
+ subject { command.source_sha }
+
+ let(:command) do
+ described_class.new(project: project,
+ source_sha: source_sha,
+ merge_request: merge_request)
+ end
+
+ let(:merge_request) do
+ create(:merge_request, target_project: project, source_project: project)
+ end
+
+ let(:source_sha) { nil }
+
+ context 'when source_sha is specified' do
+ let(:source_sha) { 'abc' }
+
+ it 'returns the specified value' do
+ is_expected.to eq('abc')
+ end
+ end
+ end
+
+ describe '#target_sha' do
+ subject { command.target_sha }
+
+ let(:command) do
+ described_class.new(project: project,
+ target_sha: target_sha,
+ merge_request: merge_request)
+ end
+
+ let(:merge_request) do
+ create(:merge_request, target_project: project, source_project: project)
+ end
+
+ let(:target_sha) { nil }
+
+ context 'when target_sha is specified' do
+ let(:target_sha) { 'abc' }
+
+ it 'returns the specified value' do
+ is_expected.to eq('abc')
+ end
+ end
+ end
+
describe '#protected_ref?' do
let(:command) { described_class.new(project: project, origin_ref: 'my-branch') }
diff --git a/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb
new file mode 100644
index 00000000000..7c1c016b4bb
--- /dev/null
+++ b/spec/lib/gitlab/ci/pipeline/chain/remove_unwanted_chat_jobs_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Pipeline::Chain::RemoveUnwantedChatJobs do
+ let(:project) { create(:project, :repository) }
+
+ let(:pipeline) do
+ build(:ci_pipeline_with_one_job, project: project, ref: 'master')
+ end
+
+ let(:command) do
+ double(:command, project: project, chat_data: { command: 'echo' })
+ end
+
+ describe '#perform!' do
+ it 'removes unwanted jobs for chat pipelines' do
+ allow(pipeline).to receive(:chat?).and_return(true)
+
+ pipeline.config_processor.jobs[:echo] = double(:job)
+
+ described_class.new(pipeline, command).perform!
+
+ expect(pipeline.config_processor.jobs.keys).to eq([:echo])
+ end
+ end
+
+ it 'does not remove any jobs for non-chat pipelines' do
+ described_class.new(pipeline, command).perform!
+
+ expect(pipeline.config_processor.jobs.keys).to eq([:rspec])
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
index 053bc421649..e6c6a82b463 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/validate/config_spec.rb
@@ -115,7 +115,7 @@ describe Gitlab::Ci::Pipeline::Chain::Validate::Config do
let(:pipeline) { build_stubbed(:ci_pipeline, project: project) }
let(:merge_request_pipeline) do
- build(:ci_pipeline, source: :merge_request, project: project)
+ build(:ci_pipeline, source: :merge_request_event, project: project)
end
let(:chain) { described_class.new(merge_request_pipeline, command).tap(&:perform!) }
diff --git a/spec/lib/gitlab/ci/status/build/preparing_spec.rb b/spec/lib/gitlab/ci/status/build/preparing_spec.rb
new file mode 100644
index 00000000000..4d8945845ba
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/build/preparing_spec.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Build::Preparing do
+ subject do
+ described_class.new(double('subject'))
+ end
+
+ describe '#illustration' do
+ it { expect(subject.illustration).to include(:image, :size, :title, :content) }
+ end
+
+ describe '.matches?' do
+ subject { described_class.matches?(build, nil) }
+
+ context 'when build is preparing' do
+ let(:build) { create(:ci_build, :preparing) }
+
+ it 'is a correct match' do
+ expect(subject).to be true
+ end
+ end
+
+ context 'when build is not preparing' do
+ let(:build) { create(:ci_build, :success) }
+
+ it 'does not match' do
+ expect(subject).to be false
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/status/preparing_spec.rb b/spec/lib/gitlab/ci/status/preparing_spec.rb
new file mode 100644
index 00000000000..7211c0e506d
--- /dev/null
+++ b/spec/lib/gitlab/ci/status/preparing_spec.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Ci::Status::Preparing do
+ subject do
+ described_class.new(double('subject'), nil)
+ end
+
+ describe '#text' do
+ it { expect(subject.text).to eq 'preparing' }
+ end
+
+ describe '#label' do
+ it { expect(subject.label).to eq 'preparing' }
+ end
+
+ describe '#icon' do
+ it { expect(subject.icon).to eq 'status_created' }
+ end
+
+ describe '#favicon' do
+ it { expect(subject.favicon).to eq 'favicon_status_created' }
+ end
+
+ describe '#group' do
+ it { expect(subject.group).to eq 'preparing' }
+ end
+end
diff --git a/spec/lib/gitlab/ci/templates/templates_spec.rb b/spec/lib/gitlab/ci/templates/templates_spec.rb
index 0dd74399a47..fbbd58280a9 100644
--- a/spec/lib/gitlab/ci/templates/templates_spec.rb
+++ b/spec/lib/gitlab/ci/templates/templates_spec.rb
@@ -3,9 +3,40 @@
require 'spec_helper'
describe "CI YML Templates" do
- Gitlab::Template::GitlabCiYmlTemplate.all.each do |template|
- it "#{template.name} should be valid" do
- expect { Gitlab::Ci::YamlProcessor.new(template.content) }.not_to raise_error
+ ABSTRACT_TEMPLATES = %w[Serverless].freeze
+
+ def self.concrete_templates
+ Gitlab::Template::GitlabCiYmlTemplate.all.reject do |template|
+ ABSTRACT_TEMPLATES.include?(template.name)
+ end
+ end
+
+ def self.abstract_templates
+ Gitlab::Template::GitlabCiYmlTemplate.all.select do |template|
+ ABSTRACT_TEMPLATES.include?(template.name)
+ end
+ end
+
+ describe 'concrete templates with CI/CD jobs' do
+ concrete_templates.each do |template|
+ it "#{template.name} template should be valid" do
+ expect { Gitlab::Ci::YamlProcessor.new(template.content) }
+ .not_to raise_error
+ end
+ end
+ end
+
+ describe 'abstract templates without concrete jobs defined' do
+ abstract_templates.each do |template|
+ it "#{template.name} template should be valid after being implemented" do
+ content = template.content + <<~EOS
+ concrete_build_implemented_by_a_user:
+ stage: build
+ script: do something
+ EOS
+
+ expect { Gitlab::Ci::YamlProcessor.new(content) }.not_to raise_error
+ end
end
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 8bf44acb228..3ff2fe18c15 100644
--- a/spec/lib/gitlab/ci/variables/collection/item_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection/item_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::Ci::Variables::Collection::Item do
let(:expected_value) { variable_value }
let(:variable) do
- { key: variable_key, value: variable_value, public: true }
+ { key: variable_key, value: variable_value, public: true, masked: false }
end
describe '.new' do
@@ -88,7 +88,7 @@ describe Gitlab::Ci::Variables::Collection::Item do
resource = described_class.fabricate(variable)
expect(resource).to be_a(described_class)
- expect(resource).to eq(key: 'CI_VAR', value: '123', public: false)
+ expect(resource).to eq(key: 'CI_VAR', value: '123', public: false, masked: false)
end
it 'supports using another collection item' do
@@ -134,7 +134,21 @@ describe Gitlab::Ci::Variables::Collection::Item do
.to_runner_variable
expect(runner_variable)
- .to eq(key: 'VAR', value: 'value', public: true, file: true)
+ .to eq(key: 'VAR', value: 'value', public: true, file: true, masked: false)
+ end
+ end
+
+ context 'when variable masking is disabled' do
+ before do
+ stub_feature_flags(variable_masking: false)
+ end
+
+ it 'does not expose the masked field to the runner' do
+ runner_variable = described_class
+ .new(key: 'VAR', value: 'value', masked: true)
+ .to_runner_variable
+
+ expect(runner_variable).to eq(key: 'VAR', value: 'value', public: true)
end
end
end
diff --git a/spec/lib/gitlab/ci/variables/collection_spec.rb b/spec/lib/gitlab/ci/variables/collection_spec.rb
index 5c91816a586..8e732d44d5d 100644
--- a/spec/lib/gitlab/ci/variables/collection_spec.rb
+++ b/spec/lib/gitlab/ci/variables/collection_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Gitlab::Ci::Variables::Collection do
describe '.new' do
it 'can be initialized with an array' do
- variable = { key: 'VAR', value: 'value', public: true }
+ variable = { key: 'VAR', value: 'value', public: true, masked: false }
collection = described_class.new([variable])
@@ -66,6 +66,14 @@ describe Gitlab::Ci::Variables::Collection do
expect(collection).to include(key: 'VAR_3', value: '3', public: true)
end
+ it 'does not concatenate resource if it undefined' do
+ collection = described_class.new([{ key: 'VAR_1', value: '1' }])
+
+ collection.concat(nil)
+
+ expect(collection).to be_one
+ end
+
it 'returns self' do
expect(subject.concat([key: 'VAR', value: 'test']))
.to eq subject
@@ -93,7 +101,7 @@ describe Gitlab::Ci::Variables::Collection do
collection = described_class.new([{ key: 'TEST', value: '1' }])
expect(collection.to_runner_variables)
- .to eq [{ key: 'TEST', value: '1', public: true }]
+ .to eq [{ key: 'TEST', value: '1', public: true, masked: false }]
end
end
diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb
index 91139d421f5..29638ef47c5 100644
--- a/spec/lib/gitlab/ci/yaml_processor_spec.rb
+++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb
@@ -602,6 +602,85 @@ module Gitlab
end
end
+ describe "Include" do
+ let(:opts) { {} }
+
+ let(:config) do
+ {
+ include: include_content,
+ rspec: { script: "test" }
+ }
+ end
+
+ subject { Gitlab::Ci::YamlProcessor.new(YAML.dump(config), opts) }
+
+ context "when validating a ci config file with no project context" do
+ context "when an array is provided" do
+ let(:include_content) { ["/local.gitlab-ci.yml"] }
+
+ it "does not return any error" do
+ expect { subject }.not_to raise_error
+ end
+ end
+
+ context "when an array of wrong keyed object is provided" do
+ let(:include_content) { [{ yolo: "/local.gitlab-ci.yml" }] }
+
+ it "returns a validation error" do
+ expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError)
+ end
+ end
+
+ context "when an array of mixed typed objects is provided" do
+ let(:include_content) do
+ [
+ 'https://gitlab.com/awesome-project/raw/master/.before-script-template.yml',
+ '/templates/.after-script-template.yml',
+ { template: 'Auto-DevOps.gitlab-ci.yml' }
+ ]
+ end
+
+ it "does not return any error" do
+ expect { subject }.not_to raise_error
+ end
+ end
+
+ context "when the include type is incorrect" do
+ let(:include_content) { { name: "/local.gitlab-ci.yml" } }
+
+ it "returns an invalid configuration error" do
+ expect { subject }.to raise_error(Gitlab::Ci::YamlProcessor::ValidationError)
+ end
+ end
+ end
+
+ context "when validating a ci config file within a project" do
+ let(:include_content) { "/local.gitlab-ci.yml" }
+ let(:project) { create(:project, :repository) }
+ let(:opts) { { project: project, sha: project.commit.sha } }
+
+ context "when the included internal file is present" do
+ before do
+ expect(project.repository).to receive(:blob_data_at)
+ .and_return(YAML.dump({ job1: { script: 'hello' } }))
+ end
+
+ it "does not return an error" do
+ expect { subject }.not_to raise_error
+ end
+ end
+
+ context "when the included internal file is not present" do
+ it "returns an error with missing file details" do
+ expect { subject }.to raise_error(
+ Gitlab::Ci::YamlProcessor::ValidationError,
+ "Local file `#{include_content}` does not exist!"
+ )
+ end
+ end
+ end
+ end
+
describe "When" do
%w(on_success on_failure always).each do |when_state|
it "returns #{when_state} when defined" do
diff --git a/spec/lib/gitlab/current_settings_spec.rb b/spec/lib/gitlab/current_settings_spec.rb
index caf9fc5442c..17d5eae24f5 100644
--- a/spec/lib/gitlab/current_settings_spec.rb
+++ b/spec/lib/gitlab/current_settings_spec.rb
@@ -143,7 +143,7 @@ describe Gitlab::CurrentSettings do
it_behaves_like 'a non-persisted ApplicationSetting object'
- it 'uses the value from the DB attribute if present and not overriden by an accessor' do
+ it 'uses the value from the DB attribute if present and not overridden by an accessor' do
expect(current_settings.home_page_url).to eq(db_settings.home_page_url)
end
end
diff --git a/spec/lib/gitlab/danger/helper_spec.rb b/spec/lib/gitlab/danger/helper_spec.rb
index 793cac593a2..66cd8171c12 100644
--- a/spec/lib/gitlab/danger/helper_spec.rb
+++ b/spec/lib/gitlab/danger/helper_spec.rb
@@ -217,6 +217,7 @@ describe Gitlab::Danger::Helper do
'app/views/foo' | :frontend
'public/foo' | :frontend
'spec/javascripts/foo' | :frontend
+ 'spec/frontend/bar' | :frontend
'vendor/assets/foo' | :frontend
'jest.config.js' | :frontend
'package.json' | :frontend
@@ -225,6 +226,7 @@ describe Gitlab::Danger::Helper do
'ee/app/assets/foo' | :frontend
'ee/app/views/foo' | :frontend
'ee/spec/javascripts/foo' | :frontend
+ 'ee/spec/frontend/bar' | :frontend
'app/models/foo' | :backend
'bin/foo' | :backend
@@ -263,6 +265,7 @@ describe Gitlab::Danger::Helper do
'changelogs/foo' | :none
'ee/changelogs/foo' | :none
+ 'locale/gitlab.pot' | :none
'FOO' | :unknown
'foo' | :unknown
diff --git a/spec/lib/gitlab/danger/teammate_spec.rb b/spec/lib/gitlab/danger/teammate_spec.rb
new file mode 100644
index 00000000000..4bc0a4c1398
--- /dev/null
+++ b/spec/lib/gitlab/danger/teammate_spec.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+describe Gitlab::Danger::Teammate do
+ subject { described_class.new({ 'projects' => projects }) }
+ let(:projects) { { project => capabilities } }
+ let(:project) { double }
+
+ describe 'multiple roles project project' do
+ let(:capabilities) { ['reviewer backend', 'maintainer frontend', 'trainee_maintainer database'] }
+
+ it '#reviewer? supports multiple roles per project' do
+ expect(subject.reviewer?(project, 'backend')).to be_truthy
+ end
+
+ it '#traintainer? supports multiple roles per project' do
+ expect(subject.traintainer?(project, 'database')).to be_truthy
+ end
+
+ it '#maintainer? supports multiple roles per project' do
+ expect(subject.maintainer?(project, 'frontend')).to be_truthy
+ end
+ end
+
+ describe 'one role project project' do
+ let(:capabilities) { 'reviewer backend' }
+
+ it '#reviewer? supports one role per project' do
+ expect(subject.reviewer?(project, 'backend')).to be_truthy
+ end
+
+ it '#traintainer? supports one role per project' do
+ expect(subject.traintainer?(project, 'database')).to be_falsey
+ end
+
+ it '#maintainer? supports one role per project' do
+ expect(subject.maintainer?(project, 'frontend')).to be_falsey
+ end
+ end
+end
diff --git a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb
index b44e8c5a110..bd3c66d0548 100644
--- a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb
+++ b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb
@@ -6,10 +6,11 @@ describe Gitlab::Database::Count::ReltuplesCountStrategy do
create(:identity)
end
- let(:models) { [Project, Identity] }
subject { described_class.new(models).count }
describe '#count', :postgresql do
+ let(:models) { [Project, Identity] }
+
context 'when reltuples is up to date' do
before do
ActiveRecord::Base.connection.execute('ANALYZE projects')
@@ -23,6 +24,22 @@ describe Gitlab::Database::Count::ReltuplesCountStrategy do
end
end
+ context 'when models using single-type inheritance are used' do
+ let(:models) { [Group, CiService, Namespace] }
+
+ before do
+ models.each do |model|
+ ActiveRecord::Base.connection.execute("ANALYZE #{model.table_name}")
+ end
+ end
+
+ it 'returns nil counts for inherited tables' do
+ models.each { |model| expect(model).not_to receive(:count) }
+
+ expect(subject).to eq({ Namespace => 3 })
+ end
+ end
+
context 'insufficient permissions' do
it 'returns an empty hash' do
allow(ActiveRecord::Base).to receive(:transaction).and_raise(PG::InsufficientPrivilege)
diff --git a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb
index 203f9344a41..40d810b195b 100644
--- a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb
+++ b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb
@@ -4,15 +4,23 @@ describe Gitlab::Database::Count::TablesampleCountStrategy do
before do
create_list(:project, 3)
create(:identity)
+ create(:group)
end
- let(:models) { [Project, Identity] }
+ let(:models) { [Project, Identity, Group, Namespace] }
let(:strategy) { described_class.new(models) }
subject { strategy.count }
describe '#count', :postgresql do
- let(:estimates) { { Project => threshold + 1, Identity => threshold - 1 } }
+ let(:estimates) do
+ {
+ Project => threshold + 1,
+ Identity => threshold - 1,
+ Group => threshold + 1,
+ Namespace => threshold + 1
+ }
+ end
let(:threshold) { Gitlab::Database::Count::TablesampleCountStrategy::EXACT_COUNT_THRESHOLD }
before do
@@ -30,9 +38,13 @@ describe Gitlab::Database::Count::TablesampleCountStrategy do
context 'for tables with an estimated large size' do
it 'performs a tablesample count' do
expect(Project).not_to receive(:count)
+ expect(Group).not_to receive(:count)
+ expect(Namespace).not_to receive(:count)
result = subject
expect(result[Project]).to eq(3)
+ expect(result[Group]).to eq(1)
+ expect(result[Namespace]).to eq(4)
end
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
index 248cca25a2c..81419e51635 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
@@ -19,7 +19,7 @@ describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :delete
Project.find(project.id)
end
- describe "#remove_last_ocurrence" do
+ describe "#remove_last_occurrence" do
it "removes only the last occurrence of a string" do
input = "this/is/a-word-to-replace/namespace/with/a-word-to-replace"
diff --git a/spec/lib/gitlab/database_spec.rb b/spec/lib/gitlab/database_spec.rb
index 60106ee3c0b..5f57cd6b825 100644
--- a/spec/lib/gitlab/database_spec.rb
+++ b/spec/lib/gitlab/database_spec.rb
@@ -17,6 +17,20 @@ describe Gitlab::Database do
end
end
+ describe '.human_adapter_name' do
+ it 'returns PostgreSQL when using PostgreSQL' do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+
+ expect(described_class.human_adapter_name).to eq('PostgreSQL')
+ end
+
+ it 'returns MySQL when using MySQL' do
+ allow(described_class).to receive(:postgresql?).and_return(false)
+
+ expect(described_class.human_adapter_name).to eq('MySQL')
+ end
+ end
+
# These are just simple smoke tests to check if the methods work (regardless
# of what they may return).
describe '.mysql?' do
@@ -87,6 +101,38 @@ describe Gitlab::Database do
end
end
+ describe '.postgresql_minimum_supported_version?' do
+ it 'returns false when not using PostgreSQL' do
+ allow(described_class).to receive(:postgresql?).and_return(false)
+
+ expect(described_class.postgresql_minimum_supported_version?).to eq(false)
+ end
+
+ context 'when using PostgreSQL' do
+ before do
+ allow(described_class).to receive(:postgresql?).and_return(true)
+ end
+
+ it 'returns false when using PostgreSQL 9.5' do
+ allow(described_class).to receive(:version).and_return('9.5')
+
+ expect(described_class.postgresql_minimum_supported_version?).to eq(false)
+ end
+
+ it 'returns true when using PostgreSQL 9.6' do
+ allow(described_class).to receive(:version).and_return('9.6')
+
+ expect(described_class.postgresql_minimum_supported_version?).to eq(true)
+ end
+
+ it 'returns true when using PostgreSQL 10 or newer' do
+ allow(described_class).to receive(:version).and_return('10')
+
+ expect(described_class.postgresql_minimum_supported_version?).to eq(true)
+ end
+ end
+ end
+
describe '.join_lateral_supported?' do
it 'returns false when using MySQL' do
allow(described_class).to receive(:postgresql?).and_return(false)
@@ -195,6 +241,12 @@ describe Gitlab::Database do
end
end
+ describe '.pg_last_xact_replay_timestamp' do
+ it 'returns pg_last_xact_replay_timestamp' do
+ expect(described_class.pg_last_xact_replay_timestamp).to eq('pg_last_xact_replay_timestamp')
+ end
+ end
+
describe '.nulls_last_order' do
context 'when using PostgreSQL' do
before do
diff --git a/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb
index 4d222564fd0..0ebd8994636 100644
--- a/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/composer_json_linker_spec.rb
@@ -50,8 +50,8 @@ describe Gitlab::DependencyLinker::ComposerJsonLinker do
%{<a href="#{url}" rel="nofollow noreferrer noopener" target="_blank">#{name}</a>}
end
- it 'links the module name' do
- expect(subject).to include(link('laravel/laravel', 'https://packagist.org/packages/laravel/laravel'))
+ it 'does not link the module name' do
+ expect(subject).not_to include(link('laravel/laravel', 'https://packagist.org/packages/laravel/laravel'))
end
it 'links the homepage' do
diff --git a/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb b/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb
index a97803b119e..f00f6b47b94 100644
--- a/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/gemfile_linker_spec.rb
@@ -41,13 +41,16 @@ describe Gitlab::DependencyLinker::GemfileLinker do
end
it 'links dependencies' do
- expect(subject).to include(link('rails', 'https://rubygems.org/gems/rails'))
expect(subject).to include(link('rails-deprecated_sanitizer', 'https://rubygems.org/gems/rails-deprecated_sanitizer'))
- expect(subject).to include(link('responders', 'https://rubygems.org/gems/responders'))
- expect(subject).to include(link('sprockets', 'https://rubygems.org/gems/sprockets'))
expect(subject).to include(link('default_value_for', 'https://rubygems.org/gems/default_value_for'))
end
+ it 'links to external dependencies' do
+ expect(subject).to include(link('rails', 'https://github.com/rails/rails'))
+ expect(subject).to include(link('responders', 'https://github.com/rails/responders'))
+ expect(subject).to include(link('sprockets', 'https://gitlab.example.com/gems/sprockets'))
+ end
+
it 'links GitHub repos' do
expect(subject).to include(link('rails/rails', 'https://github.com/rails/rails'))
expect(subject).to include(link('rails/responders', 'https://github.com/rails/responders'))
diff --git a/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb b/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb
index 24ad7d12f4c..6c6a5d70576 100644
--- a/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/gemspec_linker_spec.rb
@@ -43,8 +43,8 @@ describe Gitlab::DependencyLinker::GemspecLinker do
%{<a href="#{url}" rel="nofollow noreferrer noopener" target="_blank">#{name}</a>}
end
- it 'links the gem name' do
- expect(subject).to include(link('gitlab_git', 'https://rubygems.org/gems/gitlab_git'))
+ it 'does not link the gem name' do
+ expect(subject).not_to include(link('gitlab_git', 'https://rubygems.org/gems/gitlab_git'))
end
it 'links the license' do
diff --git a/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb b/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb
index 1e8b72afb7b..9050127af7f 100644
--- a/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/package_json_linker_spec.rb
@@ -33,7 +33,8 @@ describe Gitlab::DependencyLinker::PackageJsonLinker do
"express": "4.2.x",
"bigpipe": "bigpipe/pagelet",
"plates": "https://github.com/flatiron/plates/tarball/master",
- "karma": "^1.4.1"
+ "karma": "^1.4.1",
+ "random": "git+https://EdOverflow@github.com/example/example.git"
},
"devDependencies": {
"vows": "^0.7.0",
@@ -51,8 +52,8 @@ describe Gitlab::DependencyLinker::PackageJsonLinker do
%{<a href="#{url}" rel="nofollow noreferrer noopener" target="_blank">#{name}</a>}
end
- it 'links the module name' do
- expect(subject).to include(link('module-name', 'https://npmjs.com/package/module-name'))
+ it 'does not link the module name' do
+ expect(subject).not_to include(link('module-name', 'https://npmjs.com/package/module-name'))
end
it 'links the homepage' do
@@ -71,14 +72,21 @@ describe Gitlab::DependencyLinker::PackageJsonLinker do
expect(subject).to include(link('primus', 'https://npmjs.com/package/primus'))
expect(subject).to include(link('async', 'https://npmjs.com/package/async'))
expect(subject).to include(link('express', 'https://npmjs.com/package/express'))
- expect(subject).to include(link('bigpipe', 'https://npmjs.com/package/bigpipe'))
- expect(subject).to include(link('plates', 'https://npmjs.com/package/plates'))
expect(subject).to include(link('karma', 'https://npmjs.com/package/karma'))
expect(subject).to include(link('vows', 'https://npmjs.com/package/vows'))
expect(subject).to include(link('assume', 'https://npmjs.com/package/assume'))
expect(subject).to include(link('pre-commit', 'https://npmjs.com/package/pre-commit'))
end
+ it 'links dependencies to URL detected on value' do
+ expect(subject).to include(link('bigpipe', 'https://github.com/bigpipe/pagelet'))
+ expect(subject).to include(link('plates', 'https://github.com/flatiron/plates/tarball/master'))
+ end
+
+ it 'does not link to NPM when invalid git URL' do
+ expect(subject).not_to include(link('random', 'https://npmjs.com/package/random'))
+ end
+
it 'links GitHub repos' do
expect(subject).to include(link('bigpipe/pagelet', 'https://github.com/bigpipe/pagelet'))
end
diff --git a/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb b/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb
new file mode 100644
index 00000000000..9bfb1b13a2b
--- /dev/null
+++ b/spec/lib/gitlab/dependency_linker/parser/gemfile_spec.rb
@@ -0,0 +1,44 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe Gitlab::DependencyLinker::Parser::Gemfile do
+ describe '#parse' do
+ let(:file_content) do
+ <<-CONTENT.strip_heredoc
+ source 'https://rubygems.org'
+
+ gem "rails", '4.2.6', github: "rails/rails"
+ gem 'rails-deprecated_sanitizer', '~> 1.0.3'
+ gem 'responders', '~> 2.0', :github => 'rails/responders'
+ gem 'sprockets', '~> 3.6.0', git: 'https://gitlab.example.com/gems/sprockets'
+ gem 'default_value_for', '~> 3.0.0'
+ CONTENT
+ end
+
+ subject { described_class.new(file_content).parse(keyword: 'gem') }
+
+ def fetch_package(name)
+ subject.find { |package| package.name == name }
+ end
+
+ it 'returns parsed packages' do
+ expect(subject.size).to eq(5)
+ expect(subject).to all(be_a(Gitlab::DependencyLinker::Package))
+ end
+
+ it 'packages respond to name and external_ref accordingly' do
+ expect(fetch_package('rails')).to have_attributes(name: 'rails',
+ github_ref: 'rails/rails',
+ git_ref: nil)
+
+ expect(fetch_package('sprockets')).to have_attributes(name: 'sprockets',
+ github_ref: nil,
+ git_ref: 'https://gitlab.example.com/gems/sprockets')
+
+ expect(fetch_package('default_value_for')).to have_attributes(name: 'default_value_for',
+ github_ref: nil,
+ git_ref: nil)
+ end
+ end
+end
diff --git a/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb b/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb
index cdfd7ad9826..8f1b523653e 100644
--- a/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/podfile_linker_spec.rb
@@ -43,7 +43,10 @@ describe Gitlab::DependencyLinker::PodfileLinker do
it 'links packages' do
expect(subject).to include(link('AFNetworking', 'https://cocoapods.org/pods/AFNetworking'))
- expect(subject).to include(link('Interstellar/Core', 'https://cocoapods.org/pods/Interstellar'))
+ end
+
+ it 'links external packages' do
+ expect(subject).to include(link('Interstellar/Core', 'https://github.com/ashfurrow/Interstellar.git'))
end
it 'links Git repos' do
diff --git a/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb b/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb
index ed60ab45955..bacec830103 100644
--- a/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb
+++ b/spec/lib/gitlab/dependency_linker/podspec_linker_spec.rb
@@ -42,8 +42,8 @@ describe Gitlab::DependencyLinker::PodspecLinker do
%{<a href="#{url}" rel="nofollow noreferrer noopener" target="_blank">#{name}</a>}
end
- it 'links the gem name' do
- expect(subject).to include(link('Reachability', 'https://cocoapods.org/pods/Reachability'))
+ it 'does not link the pod name' do
+ expect(subject).not_to include(link('Reachability', 'https://cocoapods.org/pods/Reachability'))
end
it 'links the license' do
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index 862590268ca..cc36060f864 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -8,6 +8,47 @@ describe Gitlab::Diff::File do
let(:diff) { commit.raw_diffs.first }
let(:diff_file) { described_class.new(diff, diff_refs: commit.diff_refs, repository: project.repository) }
+ def create_file(file_name, content)
+ Files::CreateService.new(
+ project,
+ project.owner,
+ commit_message: 'Update',
+ start_branch: branch_name,
+ branch_name: branch_name,
+ file_path: file_name,
+ file_content: content
+ ).execute
+
+ project.commit(branch_name).diffs.diff_files.first
+ end
+
+ def update_file(file_name, content)
+ Files::UpdateService.new(
+ project,
+ project.owner,
+ commit_message: 'Update',
+ start_branch: branch_name,
+ branch_name: branch_name,
+ file_path: file_name,
+ file_content: content
+ ).execute
+
+ project.commit(branch_name).diffs.diff_files.first
+ end
+
+ def delete_file(file_name)
+ Files::DeleteService.new(
+ project,
+ project.owner,
+ commit_message: 'Update',
+ start_branch: branch_name,
+ branch_name: branch_name,
+ file_path: file_name
+ ).execute
+
+ project.commit(branch_name).diffs.diff_files.first
+ end
+
describe '#diff_lines' do
let(:diff_lines) { diff_file.diff_lines }
@@ -31,6 +72,13 @@ describe Gitlab::Diff::File do
expect(diff_file.diff_lines_for_serializer.last.type).to eq('match')
end
+ context 'when called multiple times' do
+ it 'only adds bottom match line once' do
+ expect(diff_file.diff_lines_for_serializer.size).to eq(31)
+ expect(diff_file.diff_lines_for_serializer.size).to eq(31)
+ end
+ 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') }
@@ -675,47 +723,6 @@ describe Gitlab::Diff::File do
end
let(:branch_name) { 'master' }
- def create_file(file_name, content)
- Files::CreateService.new(
- project,
- project.owner,
- commit_message: 'Update',
- start_branch: branch_name,
- branch_name: branch_name,
- file_path: file_name,
- file_content: content
- ).execute
-
- project.commit(branch_name).diffs.diff_files.first
- end
-
- def update_file(file_name, content)
- Files::UpdateService.new(
- project,
- project.owner,
- commit_message: 'Update',
- start_branch: branch_name,
- branch_name: branch_name,
- file_path: file_name,
- file_content: content
- ).execute
-
- project.commit(branch_name).diffs.diff_files.first
- end
-
- def delete_file(file_name)
- Files::DeleteService.new(
- project,
- project.owner,
- commit_message: 'Update',
- start_branch: branch_name,
- branch_name: branch_name,
- file_path: file_name
- ).execute
-
- project.commit(branch_name).diffs.diff_files.first
- end
-
context 'when empty file is created' do
it 'returns true' do
diff_file = create_file('empty.md', '')
@@ -751,4 +758,123 @@ describe Gitlab::Diff::File do
end
end
end
+
+ describe '#fully_expanded?' do
+ let(:project) do
+ create(:project, :custom_repo, files: {})
+ end
+ let(:branch_name) { 'master' }
+
+ context 'when empty file is created' do
+ it 'returns true' do
+ diff_file = create_file('empty.md', '')
+
+ expect(diff_file.fully_expanded?).to be_truthy
+ end
+ end
+
+ context 'when empty file is deleted' do
+ it 'returns true' do
+ create_file('empty.md', '')
+ diff_file = delete_file('empty.md')
+
+ expect(diff_file.fully_expanded?).to be_truthy
+ end
+ end
+
+ context 'when short file with last line removed' do
+ it 'returns true' do
+ create_file('with-content.md', (1..3).to_a.join("\n"))
+ diff_file = update_file('with-content.md', (1..2).to_a.join("\n"))
+
+ expect(diff_file.fully_expanded?).to be_truthy
+ end
+ end
+
+ context 'when a single line is added to empty file' do
+ it 'returns true' do
+ create_file('empty.md', '')
+ diff_file = update_file('empty.md', 'new content')
+
+ expect(diff_file.fully_expanded?).to be_truthy
+ end
+ end
+
+ context 'when single line file is changed' do
+ it 'returns true' do
+ create_file('file.md', 'foo')
+ diff_file = update_file('file.md', 'bar')
+
+ expect(diff_file.fully_expanded?).to be_truthy
+ end
+ end
+
+ context 'when long file is changed' do
+ before do
+ create_file('file.md', (1..999).to_a.join("\n"))
+ end
+
+ context 'when first line is removed' do
+ it 'returns true' do
+ diff_file = update_file('file.md', (2..999).to_a.join("\n"))
+
+ expect(diff_file.fully_expanded?).to be_falsey
+ end
+ end
+
+ context 'when last line is removed' do
+ it 'returns true' do
+ diff_file = update_file('file.md', (1..998).to_a.join("\n"))
+
+ expect(diff_file.fully_expanded?).to be_falsey
+ end
+ end
+
+ context 'when first and last lines are removed' do
+ it 'returns false' do
+ diff_file = update_file('file.md', (2..998).to_a.join("\n"))
+
+ expect(diff_file.fully_expanded?).to be_falsey
+ end
+ end
+
+ context 'when first and last lines are changed' do
+ it 'returns false' do
+ content = (2..998).to_a
+ content.prepend('a')
+ content.append('z')
+ content = content.join("\n")
+
+ diff_file = update_file('file.md', content)
+
+ expect(diff_file.fully_expanded?).to be_falsey
+ end
+ end
+
+ context 'when every line are changed' do
+ it 'returns true' do
+ diff_file = update_file('file.md', "hi\n" * 999)
+
+ expect(diff_file.fully_expanded?).to be_truthy
+ end
+ end
+
+ context 'when all contents are cleared' do
+ it 'returns true' do
+ diff_file = update_file('file.md', "")
+
+ expect(diff_file.fully_expanded?).to be_truthy
+ end
+ end
+
+ context 'when file is binary' do
+ it 'returns true' do
+ diff_file = update_file('file.md', (1..998).to_a.join("\n"))
+ allow(diff_file).to receive(:binary?).and_return(true)
+
+ expect(diff_file.fully_expanded?).to be_truthy
+ end
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/diff/suggestion_diff_spec.rb b/spec/lib/gitlab/diff/suggestion_diff_spec.rb
new file mode 100644
index 00000000000..5a32c2bea37
--- /dev/null
+++ b/spec/lib/gitlab/diff/suggestion_diff_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::SuggestionDiff do
+ describe '#diff_lines' do
+ let(:from_content) do
+ <<-BLOB.strip_heredoc
+ "tags": ["devel", "development", "nightly"],
+ "desktop-file-name-prefix": "(Development) ",
+ "finish-args": "foo",
+ BLOB
+ end
+
+ let(:to_content) do
+ <<-BLOB.strip_heredoc
+ "buildsystem": "meson",
+ "builddir": true,
+ "name": "nautilus",
+ "bar": "bar",
+ BLOB
+ end
+
+ let(:suggestion) do
+ instance_double(Suggestion, from_line: 12,
+ from_content: from_content,
+ to_content: to_content)
+ end
+
+ subject { described_class.new(suggestion).diff_lines }
+
+ let(:expected_diff_lines) do
+ [
+ { old_pos: 12, new_pos: 12, type: "match", text: "@@ -12 +12" },
+ { old_pos: 12, new_pos: 12, type: "old", text: "-\"tags\": [\"devel\", \"development\", \"nightly\"]," },
+ { old_pos: 13, new_pos: 12, type: "old", text: "-\"desktop-file-name-prefix\": \"(Development) \"," },
+ { old_pos: 14, new_pos: 12, type: "old", text: "-\"finish-args\": \"foo\"," },
+ { old_pos: 15, new_pos: 12, type: "new", text: "+\"buildsystem\": \"meson\"," },
+ { old_pos: 15, new_pos: 13, type: "new", text: "+\"builddir\": true," },
+ { old_pos: 15, new_pos: 14, type: "new", text: "+\"name\": \"nautilus\"," },
+ { old_pos: 15, new_pos: 15, type: "new", text: "+\"bar\": \"bar\"," }
+ ]
+ end
+
+ it 'returns diff lines with correct line numbers' do
+ diff_lines = subject
+
+ expect(diff_lines).to all(be_a(Gitlab::Diff::Line))
+
+ expected_diff_lines.each_with_index do |expected_line, index|
+ expect(diff_lines[index].to_hash).to include(expected_line)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/suggestion_spec.rb b/spec/lib/gitlab/diff/suggestion_spec.rb
new file mode 100644
index 00000000000..71fd25df698
--- /dev/null
+++ b/spec/lib/gitlab/diff/suggestion_spec.rb
@@ -0,0 +1,88 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::Suggestion do
+ shared_examples 'correct suggestion raw content' do
+ it 'returns correct raw data' do
+ expect(suggestion.to_hash).to include(from_content: expected_lines.join,
+ to_content: "#{text}\n",
+ lines_above: above,
+ lines_below: below)
+ end
+ end
+
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+ let(:position) do
+ Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 9,
+ diff_refs: merge_request.diff_refs)
+ end
+ let(:diff_file) do
+ position.diff_file(project.repository)
+ end
+ let(:text) { "# parsed suggestion content\n# with comments" }
+
+ def blob_lines_data(from_line, to_line)
+ diff_file.new_blob_lines_between(from_line, to_line)
+ end
+
+ def blob_data
+ blob = diff_file.new_blob
+ blob.load_all_data!
+ blob.data
+ end
+
+ let(:suggestion) do
+ described_class.new(text, line: line, above: above, below: below, diff_file: diff_file)
+ end
+
+ describe '#to_hash' do
+ context 'when changing content surpasses the top limit' do
+ let(:line) { 4 }
+ let(:above) { 5 }
+ let(:below) { 2 }
+ let(:expected_above) { line - 1 }
+ let(:expected_below) { below }
+ let(:expected_lines) { blob_lines_data(line - expected_above, line + expected_below) }
+
+ it_behaves_like 'correct suggestion raw content'
+ end
+
+ context 'when changing content surpasses the amount of lines in the blob (bottom)' do
+ let(:line) { 5 }
+ let(:above) { 1 }
+ let(:below) { blob_data.lines.size + 10 }
+ let(:expected_below) { below }
+ let(:expected_above) { above }
+ let(:expected_lines) { blob_lines_data(line - expected_above, line + expected_below) }
+
+ it_behaves_like 'correct suggestion raw content'
+ end
+
+ context 'when lines are within blob lines boundary' do
+ let(:line) { 5 }
+ let(:above) { 2 }
+ let(:below) { 3 }
+ let(:expected_below) { below }
+ let(:expected_above) { above }
+ let(:expected_lines) { blob_lines_data(line - expected_above, line + expected_below) }
+
+ it_behaves_like 'correct suggestion raw content'
+ end
+
+ context 'when no extra lines (single-line suggestion)' do
+ let(:line) { 5 }
+ let(:above) { 0 }
+ let(:below) { 0 }
+ let(:expected_below) { below }
+ let(:expected_above) { above }
+ let(:expected_lines) { blob_lines_data(line - expected_above, line + expected_below) }
+
+ it_behaves_like 'correct suggestion raw content'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/diff/suggestions_parser_spec.rb b/spec/lib/gitlab/diff/suggestions_parser_spec.rb
new file mode 100644
index 00000000000..1119ea04995
--- /dev/null
+++ b/spec/lib/gitlab/diff/suggestions_parser_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::Diff::SuggestionsParser do
+ describe '.parse' do
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.project }
+ let(:position) do
+ Gitlab::Diff::Position.new(old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 9,
+ diff_refs: merge_request.diff_refs)
+ end
+
+ let(:diff_file) do
+ position.diff_file(project.repository)
+ end
+
+ subject do
+ described_class.parse(markdown, project: merge_request.project,
+ position: position)
+ end
+
+ def blob_lines_data(from_line, to_line)
+ diff_file.new_blob_lines_between(from_line, to_line).join
+ end
+
+ context 'single-line suggestions' do
+ let(:markdown) do
+ <<-MARKDOWN.strip_heredoc
+ ```suggestion
+ foo
+ bar
+ ```
+
+ ```
+ nothing
+ ```
+
+ ```suggestion
+ xpto
+ baz
+ ```
+
+ ```thing
+ this is not a suggestion, it's a thing
+ ```
+ MARKDOWN
+ end
+
+ it 'returns a list of Gitlab::Diff::Suggestion' do
+ expect(subject).to all(be_a(Gitlab::Diff::Suggestion))
+ expect(subject.size).to eq(2)
+ end
+
+ it 'parsed suggestion has correct data' do
+ from_line, to_line = position.new_line, position.new_line
+
+ expect(subject.first.to_hash).to include(from_content: blob_lines_data(from_line, to_line),
+ to_content: " foo\n bar\n",
+ lines_above: 0,
+ lines_below: 0)
+
+ expect(subject.second.to_hash).to include(from_content: blob_lines_data(from_line, to_line),
+ to_content: " xpto\n baz\n",
+ lines_above: 0,
+ lines_below: 0)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/fake_application_settings_spec.rb b/spec/lib/gitlab/fake_application_settings_spec.rb
index af12e13d36d..c81cb83d9f4 100644
--- a/spec/lib/gitlab/fake_application_settings_spec.rb
+++ b/spec/lib/gitlab/fake_application_settings_spec.rb
@@ -1,32 +1,33 @@
require 'spec_helper'
describe Gitlab::FakeApplicationSettings do
- let(:defaults) { { password_authentication_enabled_for_web: false, foobar: 'asdf', signup_enabled: true, 'test?' => 123 } }
+ let(:defaults) do
+ described_class.defaults.merge(
+ foobar: 'asdf',
+ 'test?' => 123
+ )
+ end
- subject { described_class.new(defaults) }
+ let(:setting) { described_class.new(defaults) }
it 'wraps OpenStruct variables properly' do
- expect(subject.password_authentication_enabled_for_web).to be_falsey
- expect(subject.signup_enabled).to be_truthy
- expect(subject.foobar).to eq('asdf')
+ expect(setting.password_authentication_enabled_for_web).to be_truthy
+ expect(setting.signup_enabled).to be_truthy
+ expect(setting.foobar).to eq('asdf')
end
it 'defines predicate methods' do
- expect(subject.password_authentication_enabled_for_web?).to be_falsey
- expect(subject.signup_enabled?).to be_truthy
- end
-
- it 'predicate method changes when value is updated' do
- subject.password_authentication_enabled_for_web = true
-
- expect(subject.password_authentication_enabled_for_web?).to be_truthy
+ expect(setting.password_authentication_enabled_for_web?).to be_truthy
+ expect(setting.signup_enabled?).to be_truthy
end
it 'does not define a predicate method' do
- expect(subject.foobar?).to be_nil
+ expect(setting.foobar?).to be_nil
end
it 'does not override an existing predicate method' do
- expect(subject.test?).to eq(123)
+ expect(setting.test?).to eq(123)
end
+
+ it_behaves_like 'application settings examples'
end
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index a1b5cea88c0..10bc82e24d1 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::Git::Blob, :seed_helper do
end
end
- describe '.find' do
+ shared_examples '.find' do
context 'nil path' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, nil) }
@@ -128,6 +128,20 @@ describe Gitlab::Git::Blob, :seed_helper do
end
end
+ describe '.find with Gitaly enabled' do
+ it_behaves_like '.find'
+ end
+
+ describe '.find with Rugged enabled', :enable_rugged do
+ it 'calls out to the Rugged implementation' do
+ allow_any_instance_of(Rugged).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+
+ described_class.find(repository, SeedRepo::Commit::ID, 'files/images/6049019_460s.jpg')
+ end
+
+ it_behaves_like '.find'
+ end
+
describe '.raw' do
let(:raw_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::RubyBlob::ID) }
let(:bad_blob) { Gitlab::Git::Blob.raw(repository, SeedRepo::BigCommit::ID) }
diff --git a/spec/lib/gitlab/git/commit_spec.rb b/spec/lib/gitlab/git/commit_spec.rb
index 2611ebed25b..4a4ac833e39 100644
--- a/spec/lib/gitlab/git/commit_spec.rb
+++ b/spec/lib/gitlab/git/commit_spec.rb
@@ -112,7 +112,7 @@ describe Gitlab::Git::Commit, :seed_helper do
end
context 'Class methods' do
- describe '.find' do
+ shared_examples '.find' do
it "should return first head commit if without params" do
expect(described_class.last(repository).id).to eq(
rugged_repo.head.target.oid
@@ -154,6 +154,20 @@ describe Gitlab::Git::Commit, :seed_helper do
end
end
+ describe '.find with Gitaly enabled' do
+ it_should_behave_like '.find'
+ end
+
+ describe '.find with Rugged enabled', :enable_rugged do
+ it 'calls out to the Rugged implementation' do
+ allow_any_instance_of(Rugged).to receive(:rev_parse).with(SeedRepo::Commit::ID).and_call_original
+
+ described_class.find(repository, SeedRepo::Commit::ID)
+ end
+
+ it_should_behave_like '.find'
+ end
+
describe '.last_for_path' do
context 'no path' do
subject { described_class.last_for_path(repository, 'master') }
@@ -523,6 +537,18 @@ describe Gitlab::Git::Commit, :seed_helper do
end
end
+ describe '#gitaly_commit?' do
+ context 'when the commit data comes from gitaly' do
+ it { expect(commit.gitaly_commit?).to eq(true) }
+ end
+
+ context 'when the commit data comes from a Hash' do
+ let(:commit) { described_class.new(repository, sample_commit_hash) }
+
+ it { expect(commit.gitaly_commit?).to eq(false) }
+ end
+ end
+
describe '#has_zero_stats?' do
it { expect(commit.has_zero_stats?).to eq(false) }
end
diff --git a/spec/lib/gitlab/git/pre_receive_error_spec.rb b/spec/lib/gitlab/git/pre_receive_error_spec.rb
index 1b8be62dec6..cb030e38032 100644
--- a/spec/lib/gitlab/git/pre_receive_error_spec.rb
+++ b/spec/lib/gitlab/git/pre_receive_error_spec.rb
@@ -1,9 +1,19 @@
require 'spec_helper'
describe Gitlab::Git::PreReceiveError do
- it 'makes its message HTML-friendly' do
- ex = described_class.new("hello\nworld\n")
+ Gitlab::Git::PreReceiveError::SAFE_MESSAGE_PREFIXES.each do |prefix|
+ context "error messages prefixed with #{prefix}" do
+ it 'accepts only errors lines with the prefix' do
+ ex = described_class.new("#{prefix} Hello,\nworld!")
- expect(ex.message).to eq('hello<br>world<br>')
+ expect(ex.message).to eq('Hello,')
+ end
+
+ it 'makes its message HTML-friendly' do
+ ex = described_class.new("#{prefix} Hello,\n#{prefix} world!\n")
+
+ expect(ex.message).to eq('Hello,<br>world!')
+ end
+ end
end
end
diff --git a/spec/lib/gitlab/git/repository_cleaner_spec.rb b/spec/lib/gitlab/git/repository_cleaner_spec.rb
index 7f9cc2bc9ec..6602f22843f 100644
--- a/spec/lib/gitlab/git/repository_cleaner_spec.rb
+++ b/spec/lib/gitlab/git/repository_cleaner_spec.rb
@@ -37,14 +37,12 @@ describe Gitlab::Git::RepositoryCleaner do
let(:object_map) { Gitlab::HttpIO.new(url, object_map_data.size) }
around do |example|
- begin
- tempfile.write(object_map_data)
- tempfile.close
+ tempfile.write(object_map_data)
+ tempfile.close
- example.run
- ensure
- tempfile.unlink
- end
+ example.run
+ ensure
+ tempfile.unlink
end
it 'removes internal references' do
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 8a9e78ba3c3..8ba6862392c 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -31,7 +31,7 @@ describe Gitlab::Git::Repository, :seed_helper do
describe '.create_hooks' do
let(:repo_path) { File.join(storage_path, 'hook-test.git') }
let(:hooks_dir) { File.join(repo_path, 'hooks') }
- let(:target_hooks_dir) { Gitlab.config.gitlab_shell.hooks_path }
+ let(:target_hooks_dir) { Gitlab::Shell.new.hooks_path }
let(:existing_target) { File.join(repo_path, 'foobar') }
before do
@@ -619,16 +619,6 @@ describe Gitlab::Git::Repository, :seed_helper do
repository.search_files_by_content('search-files-by-content', 'search-files-by-content-branch')
end
end
-
- it_should_behave_like 'search files by content' do
- let(:search_results) do
- repository.gitaly_repository_client.search_files_by_content(
- 'search-files-by-content-branch',
- 'search-files-by-content',
- chunked_response: false
- )
- end
- end
end
describe '#find_remote_root_ref' do
@@ -1698,12 +1688,48 @@ describe Gitlab::Git::Repository, :seed_helper do
expect(repository.delete_config(*%w[does.not.exist test.foo1 test.foo2])).to be_nil
+ # Workaround for https://github.com/libgit2/rugged/issues/785: If
+ # Gitaly changes .gitconfig while Rugged has the file loaded
+ # Rugged::Repository#each_key will report stale values unless a
+ # lookup is done first.
+ expect(repository_rugged.config['test.foo1']).to be_nil
config_keys = repository_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_to_ref' do
+ let(:repository) { mutable_repository }
+ let(:branch_head) { '6d394385cf567f80a8fd85055db1ab4c5295806f' }
+ let(:left_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
+ let(:right_branch) { 'test-master' }
+ let(:target_ref) { 'refs/merge-requests/999/merge' }
+
+ before do
+ repository.create_branch(right_branch, branch_head) unless repository.branch_exists?(right_branch)
+ end
+
+ def merge_to_ref
+ repository.merge_to_ref(user, left_sha, right_branch, target_ref, 'Merge message')
+ end
+
+ it 'generates a commit in the target_ref' do
+ expect(repository.ref_exists?(target_ref)).to be(false)
+
+ commit_sha = merge_to_ref
+ ref_head = repository.commit(target_ref)
+
+ expect(commit_sha).to be_present
+ expect(repository.ref_exists?(target_ref)).to be(true)
+ expect(ref_head.id).to eq(commit_sha)
+ end
+
+ it 'does not change the right branch HEAD' do
+ expect { merge_to_ref }.not_to change { repository.find_branch(right_branch).target }
+ end
+ end
+
describe '#merge' do
let(:repository) { mutable_repository }
let(:source_sha) { '913c66a37b4a45b9769037c55c2d238bd0942d2e' }
@@ -1914,7 +1940,7 @@ describe Gitlab::Git::Repository, :seed_helper do
imported_repo.create_from_bundle(valid_bundle_path)
hooks_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access { File.join(imported_repo.path, 'hooks') }
- expect(File.readlink(hooks_path)).to eq(Gitlab.config.gitlab_shell.hooks_path)
+ expect(File.readlink(hooks_path)).to eq(Gitlab::Shell.new.hooks_path)
end
it 'raises an error if the bundle is an attempted malicious payload' do
diff --git a/spec/lib/gitlab/git/tree_spec.rb b/spec/lib/gitlab/git/tree_spec.rb
index 4a4d69490a3..60060c41616 100644
--- a/spec/lib/gitlab/git/tree_spec.rb
+++ b/spec/lib/gitlab/git/tree_spec.rb
@@ -3,7 +3,7 @@ require "spec_helper"
describe Gitlab::Git::Tree, :seed_helper do
let(:repository) { Gitlab::Git::Repository.new('default', TEST_REPO_PATH, '', 'group/project') }
- context :repo do
+ shared_examples :repo do
let(:tree) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID) }
it { expect(tree).to be_kind_of Array }
@@ -12,6 +12,17 @@ describe Gitlab::Git::Tree, :seed_helper do
it { expect(tree.select(&:file?).size).to eq(10) }
it { expect(tree.select(&:submodule?).size).to eq(2) }
+ it 'returns an empty array when called with an invalid ref' do
+ expect(described_class.where(repository, 'foobar-does-not-exist')).to eq([])
+ end
+
+ it 'returns a list of tree objects' do
+ entries = described_class.where(repository, SeedRepo::Commit::ID, 'files', true)
+
+ expect(entries.count).to be > 10
+ expect(entries).to all(be_a(Gitlab::Git::Tree))
+ end
+
describe '#dir?' do
let(:dir) { tree.select(&:dir?).first }
@@ -20,8 +31,8 @@ describe Gitlab::Git::Tree, :seed_helper do
it { expect(dir.commit_id).to eq(SeedRepo::Commit::ID) }
it { expect(dir.name).to eq('encoding') }
it { expect(dir.path).to eq('encoding') }
- it { expect(dir.flat_path).to eq('encoding') }
it { expect(dir.mode).to eq('40000') }
+ it { expect(dir.flat_path).to eq('encoding') }
context :subdir do
let(:subdir) { Gitlab::Git::Tree.where(repository, SeedRepo::Commit::ID, 'files').first }
@@ -44,6 +55,51 @@ describe Gitlab::Git::Tree, :seed_helper do
it { expect(subdir_file.path).to eq('files/ruby/popen.rb') }
it { expect(subdir_file.flat_path).to eq('files/ruby/popen.rb') }
end
+
+ context :flat_path do
+ let(:filename) { 'files/flat/path/correct/content.txt' }
+ let(:oid) { create_file(filename) }
+ let(:subdir_file) { Gitlab::Git::Tree.where(repository, oid, 'files/flat').first }
+ let(:repository_rugged) { Rugged::Repository.new(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH)) }
+
+ it { expect(subdir_file.flat_path).to eq('files/flat/path/correct') }
+ end
+
+ def create_file(path)
+ oid = repository_rugged.write('test', :blob)
+ index = repository_rugged.index
+ index.add(path: filename, oid: oid, mode: 0100644)
+
+ options = commit_options(
+ repository_rugged,
+ index,
+ repository_rugged.head.target,
+ 'HEAD',
+ 'Add new file')
+
+ Rugged::Commit.create(repository_rugged, options)
+ end
+
+ # Build the options hash that's passed to Rugged::Commit#create
+ def commit_options(repo, index, target, ref, message)
+ options = {}
+ options[:tree] = index.write_tree(repo)
+ options[:author] = {
+ email: "test@example.com",
+ name: "Test Author",
+ time: Time.gm(2014, "mar", 3, 20, 15, 1)
+ }
+ options[:committer] = {
+ email: "test@example.com",
+ name: "Test Author",
+ time: Time.gm(2014, "mar", 3, 20, 15, 1)
+ }
+ options[:message] ||= message
+ options[:parents] = repo.empty? ? [] : [target].compact
+ options[:update_ref] = ref
+
+ options
+ end
end
describe '#file?' do
@@ -79,9 +135,17 @@ describe Gitlab::Git::Tree, :seed_helper do
end
end
- describe '#where' do
- it 'returns an empty array when called with an invalid ref' do
- expect(described_class.where(repository, 'foobar-does-not-exist')).to eq([])
+ describe '.where with Gitaly enabled' do
+ it_behaves_like :repo
+ end
+
+ describe '.where with Rugged enabled', :enable_rugged do
+ it 'calls out to the Rugged implementation' do
+ allow_any_instance_of(Rugged).to receive(:lookup).with(SeedRepo::Commit::ID)
+
+ described_class.where(repository, SeedRepo::Commit::ID, 'files', false)
end
+
+ it_behaves_like :repo
end
end
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index b37fe2686b6..7579a6577b9 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -39,7 +39,7 @@ describe Gitlab::GitalyClient::OperationService do
context "when pre_receive_error is present" do
let(:response) do
- Gitaly::UserCreateBranchResponse.new(pre_receive_error: "something failed")
+ Gitaly::UserCreateBranchResponse.new(pre_receive_error: "GitLab: something failed")
end
it "throws a PreReceive exception" do
@@ -80,7 +80,7 @@ describe Gitlab::GitalyClient::OperationService do
context "when pre_receive_error is present" do
let(:response) do
- Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "something failed")
+ Gitaly::UserUpdateBranchResponse.new(pre_receive_error: "GitLab: something failed")
end
it "throws a PreReceive exception" do
@@ -117,7 +117,7 @@ describe Gitlab::GitalyClient::OperationService do
context "when pre_receive_error is present" do
let(:response) do
- Gitaly::UserDeleteBranchResponse.new(pre_receive_error: "something failed")
+ Gitaly::UserDeleteBranchResponse.new(pre_receive_error: "GitLab: something failed")
end
it "throws a PreReceive exception" do
@@ -175,7 +175,7 @@ describe Gitlab::GitalyClient::OperationService do
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") }
+ let(:response) { response_class.new(pre_receive_error: "GitLab: something failed") }
it 'raises a PreReceiveError' do
expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "something failed")
@@ -313,7 +313,7 @@ describe Gitlab::GitalyClient::OperationService do
end
context 'when a pre_receive_error is present' do
- let(:response) { Gitaly::UserCommitFilesResponse.new(pre_receive_error: "something failed") }
+ let(:response) { Gitaly::UserCommitFilesResponse.new(pre_receive_error: "GitLab: something failed") }
it 'raises a PreReceiveError' do
expect { subject }.to raise_error(Gitlab::Git::PreReceiveError, "something failed")
diff --git a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
index 400d426c949..0dab39575b9 100644
--- a/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/ref_service_spec.rb
@@ -89,6 +89,16 @@ describe Gitlab::GitalyClient::RefService do
end
end
+ describe '#list_new_blobs' do
+ it 'raises DeadlineExceeded when timeout is too small' do
+ newrev = '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51'
+
+ expect do
+ client.list_new_blobs(newrev, dynamic_timeout: 0.001)
+ end.to raise_error(GRPC::DeadlineExceeded)
+ end
+ end
+
describe '#local_branches' do
it 'sends a find_local_branches message' do
expect_any_instance_of(Gitaly::RefService::Stub)
diff --git a/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb b/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb
index c89913ec8e9..bb10be2a4dc 100644
--- a/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/storage_settings_spec.rb
@@ -26,4 +26,14 @@ describe Gitlab::GitalyClient::StorageSettings do
end
end
end
+
+ describe '.disk_access_denied?' do
+ it 'return false when Rugged is enabled', :enable_rugged do
+ expect(described_class.disk_access_denied?).to be_falsey
+ end
+
+ it 'returns true' do
+ expect(described_class.disk_access_denied?).to be_truthy
+ end
+ end
end
diff --git a/spec/lib/gitlab/gitaly_client_spec.rb b/spec/lib/gitlab/gitaly_client_spec.rb
index cf12baf1a93..f1acb1d9bc4 100644
--- a/spec/lib/gitlab/gitaly_client_spec.rb
+++ b/spec/lib/gitlab/gitaly_client_spec.rb
@@ -149,11 +149,21 @@ describe Gitlab::GitalyClient do
end
end
- context 'when RequestStore is enabled', :request_store do
+ context 'when RequestStore is enabled and the maximum number of calls is not enforced by a feature flag', :request_store do
+ before do
+ stub_feature_flags(gitaly_enforce_requests_limits: false)
+ end
+
it 'allows up the maximum number of allowed calls' do
expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS) }.not_to raise_error
end
+ it 'allows the maximum number of calls to be exceeded if GITALY_DISABLE_REQUEST_LIMITS is set' do
+ stub_env('GITALY_DISABLE_REQUEST_LIMITS', 'true')
+
+ expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) }.not_to raise_error
+ end
+
context 'when the maximum number of calls has been reached' do
before do
call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS)
@@ -189,6 +199,32 @@ describe Gitlab::GitalyClient do
end
end
+ context 'in production and when RequestStore is enabled', :request_store do
+ before do
+ allow(Rails.env).to receive(:production?).and_return(true)
+ end
+
+ context 'when the maximum number of calls is enforced by a feature flag' do
+ before do
+ stub_feature_flags(gitaly_enforce_requests_limits: true)
+ end
+
+ it 'does not allow the maximum number of calls to be exceeded' do
+ expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) }.to raise_error(Gitlab::GitalyClient::TooManyInvocationsError)
+ end
+ end
+
+ context 'when the maximum number of calls is not enforced by a feature flag' do
+ before do
+ stub_feature_flags(gitaly_enforce_requests_limits: false)
+ end
+
+ it 'allows the maximum number of calls to be exceeded' do
+ expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 1) }.not_to raise_error
+ end
+ end
+ end
+
context 'when RequestStore is not active' do
it 'does not raise errors when the maximum number of allowed calls is exceeded' do
expect { call_gitaly(Gitlab::GitalyClient::MAXIMUM_GITALY_CALLS + 2) }.not_to raise_error
diff --git a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
index b1cac3b6e46..120a07ff2b3 100644
--- a/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/milestones_importer_spec.rb
@@ -4,6 +4,7 @@ describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis
let(:project) { create(:project, import_source: 'foo/bar') }
let(:client) { double(:client) }
let(:importer) { described_class.new(project, client) }
+ let(:due_on) { Time.new(2017, 2, 1, 12, 00) }
let(:created_at) { Time.new(2017, 1, 1, 12, 00) }
let(:updated_at) { Time.new(2017, 1, 1, 12, 15) }
@@ -14,6 +15,20 @@ describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis
title: '1.0',
description: 'The first release',
state: 'open',
+ due_on: due_on,
+ created_at: created_at,
+ updated_at: updated_at
+ )
+ end
+
+ let(:milestone2) do
+ double(
+ :milestone,
+ number: 1,
+ title: '1.0',
+ description: 'The first release',
+ state: 'open',
+ due_on: nil,
created_at: created_at,
updated_at: updated_at
)
@@ -72,6 +87,7 @@ describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis
describe '#build' do
let(:milestone_hash) { importer.build(milestone) }
+ let(:milestone_hash2) { importer.build(milestone2) }
it 'returns the attributes of the milestone as a Hash' do
expect(milestone_hash).to be_an_instance_of(Hash)
@@ -98,6 +114,14 @@ describe Gitlab::GithubImport::Importer::MilestonesImporter, :clean_gitlab_redis
expect(milestone_hash[:state]).to eq(:active)
end
+ it 'includes the due date' do
+ expect(milestone_hash[:due_date]).to eq(due_on.to_date)
+ end
+
+ it 'responds correctly to no due date value' do
+ expect(milestone_hash2[:due_date]).to be nil
+ end
+
it 'includes the created timestamp' do
expect(milestone_hash[:created_at]).to eq(created_at)
end
diff --git a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
index 15e59718dce..2e4a7c36fb8 100644
--- a/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/pull_request_importer_spec.rb
@@ -11,6 +11,7 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
let(:source_commit) { project.repository.commit('feature') }
let(:target_commit) { project.repository.commit('master') }
let(:milestone) { create(:milestone, project: project) }
+ let(:state) { :closed }
let(:pull_request) do
alice = Gitlab::GithubImport::Representation::User.new(id: 4, login: 'alice')
@@ -26,13 +27,13 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
source_repository_id: 400,
target_repository_id: 200,
source_repository_owner: 'alice',
- state: :closed,
+ state: state,
milestone_number: milestone.iid,
author: alice,
assignee: alice,
created_at: created_at,
updated_at: updated_at,
- merged_at: merged_at
+ merged_at: state == :closed && merged_at
)
end
@@ -260,53 +261,63 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
end
it 'does not create the source branch if merge request is merged' do
- mr, exists = importer.create_merge_request
-
- importer.insert_git_data(mr, exists)
+ mr = insert_git_data
expect(project.repository.branch_exists?(mr.source_branch)).to be_falsey
expect(project.repository.branch_exists?(mr.target_branch)).to be_truthy
end
- it 'creates the source branch if merge request is open' do
- mr, exists = importer.create_merge_request
- mr.state = 'opened'
- mr.save
+ context 'when merge request is open' do
+ let(:state) { :opened }
- importer.insert_git_data(mr, exists)
+ it 'creates the source branch' do
+ # Ensure the project creator is creating the branches because the
+ # merge request author may not have access to push to this
+ # repository. The project owner may also be a group.
+ allow(project.repository).to receive(:add_branch).with(project.creator, anything, anything).and_call_original
- expect(project.repository.branch_exists?(mr.source_branch)).to be_truthy
- expect(project.repository.branch_exists?(mr.target_branch)).to be_truthy
- end
+ mr = insert_git_data
- it 'ignores Git errors when creating a branch' do
- mr, exists = importer.create_merge_request
- mr.state = 'opened'
- mr.save
+ expect(project.repository.branch_exists?(mr.source_branch)).to be_truthy
+ expect(project.repository.branch_exists?(mr.target_branch)).to be_truthy
+ end
- expect(project.repository).to receive(:add_branch).and_raise(Gitlab::Git::CommandError)
- expect(Gitlab::Sentry).to receive(:track_acceptable_exception).and_call_original
+ it 'is able to retry on pre-receive errors' do
+ expect(importer).to receive(:insert_or_replace_git_data).twice.and_call_original
+ expect(project.repository).to receive(:add_branch).and_raise('exception')
- importer.insert_git_data(mr, exists)
+ expect { insert_git_data }.to raise_error('exception')
- expect(project.repository.branch_exists?(mr.source_branch)).to be_falsey
- expect(project.repository.branch_exists?(mr.target_branch)).to be_truthy
+ expect(project.repository).to receive(:add_branch).with(project.creator, anything, anything).and_call_original
+
+ mr = insert_git_data
+
+ expect(project.repository.branch_exists?(mr.source_branch)).to be_truthy
+ expect(project.repository.branch_exists?(mr.target_branch)).to be_truthy
+ expect(mr.merge_request_diffs).to be_one
+ end
+
+ it 'ignores Git command errors when creating a branch' do
+ expect(project.repository).to receive(:add_branch).and_raise(Gitlab::Git::CommandError)
+ expect(Gitlab::Sentry).to receive(:track_acceptable_exception).and_call_original
+
+ mr = insert_git_data
+
+ expect(project.repository.branch_exists?(mr.source_branch)).to be_falsey
+ expect(project.repository.branch_exists?(mr.target_branch)).to be_truthy
+ end
end
it 'creates the merge request diffs' do
- mr, exists = importer.create_merge_request
-
- importer.insert_git_data(mr, exists)
+ mr = insert_git_data
expect(mr.merge_request_diffs.exists?).to eq(true)
end
it 'creates the merge request diff commits' do
- mr, exists = importer.create_merge_request
-
- importer.insert_git_data(mr, exists)
+ mr = insert_git_data
- diff = mr.merge_request_diffs.take
+ diff = mr.merge_request_diffs.reload.first
expect(diff.merge_request_diff_commits.exists?).to eq(true)
end
@@ -322,5 +333,11 @@ describe Gitlab::GithubImport::Importer::PullRequestImporter, :clean_gitlab_redi
expect(mr.merge_request_diffs.exists?).to eq(true)
end
end
+
+ def insert_git_data
+ mr, exists = importer.create_merge_request
+ importer.insert_git_data(mr, exists)
+ mr
+ end
end
end
diff --git a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
index 47233ea6ee2..41810a8ec03 100644
--- a/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
+++ b/spec/lib/gitlab/github_import/importer/repository_importer_spec.rb
@@ -179,6 +179,17 @@ describe Gitlab::GithubImport::Importer::RepositoryImporter do
describe '#import_repository' do
it 'imports the repository' do
+ repo = double(:repo, default_branch: 'develop')
+
+ expect(client)
+ .to receive(:repository)
+ .with('foo/bar')
+ .and_return(repo)
+
+ expect(project)
+ .to receive(:change_head)
+ .with('develop')
+
expect(project)
.to receive(:ensure_repository)
diff --git a/spec/lib/gitlab/gl_repository/repo_type_spec.rb b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
new file mode 100644
index 00000000000..f06a2448ff7
--- /dev/null
+++ b/spec/lib/gitlab/gl_repository/repo_type_spec.rb
@@ -0,0 +1,64 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Gitlab::GlRepository::RepoType do
+ set(:project) { create(:project) }
+
+ shared_examples 'a repo type' do
+ describe "#identifier_for_subject" do
+ subject { described_class.identifier_for_subject(project) }
+
+ it { is_expected.to eq(expected_identifier) }
+ end
+
+ describe "#fetch_id" do
+ it "finds an id match in the identifier" do
+ expect(described_class.fetch_id(expected_identifier)).to eq(expected_id)
+ end
+
+ it 'does not break on other identifiers' do
+ expect(described_class.fetch_id("wiki-noid")).to eq(nil)
+ end
+ end
+
+ describe "#path_suffix" do
+ subject { described_class.path_suffix }
+
+ it { is_expected.to eq(expected_suffix) }
+ end
+
+ describe "#repository_for" do
+ it "finds the repository for the repo type" do
+ expect(described_class.repository_for(project)).to eq(expected_repository)
+ end
+ end
+ end
+
+ describe Gitlab::GlRepository::PROJECT do
+ it_behaves_like 'a repo type' do
+ let(:expected_identifier) { "project-#{project.id}" }
+ let(:expected_id) { project.id.to_s }
+ let(:expected_suffix) { "" }
+ let(:expected_repository) { project.repository }
+ end
+
+ it "knows its type" do
+ expect(described_class).not_to be_wiki
+ expect(described_class).to be_project
+ end
+ end
+
+ describe Gitlab::GlRepository::WIKI do
+ it_behaves_like 'a repo type' do
+ let(:expected_identifier) { "wiki-#{project.id}" }
+ let(:expected_id) { project.id.to_s }
+ let(:expected_suffix) { ".wiki" }
+ let(:expected_repository) { project.wiki.repository }
+ end
+
+ it "knows its type" do
+ expect(described_class).to be_wiki
+ expect(described_class).not_to be_project
+ end
+ end
+end
diff --git a/spec/lib/gitlab/gl_repository_spec.rb b/spec/lib/gitlab/gl_repository_spec.rb
index 4e09020471b..d4b6c629659 100644
--- a/spec/lib/gitlab/gl_repository_spec.rb
+++ b/spec/lib/gitlab/gl_repository_spec.rb
@@ -5,11 +5,11 @@ describe ::Gitlab::GlRepository do
set(:project) { create(:project, :repository) }
it 'parses a project gl_repository' do
- expect(described_class.parse("project-#{project.id}")).to eq([project, false])
+ expect(described_class.parse("project-#{project.id}")).to eq([project, Gitlab::GlRepository::PROJECT])
end
it 'parses a wiki gl_repository' do
- expect(described_class.parse("wiki-#{project.id}")).to eq([project, true])
+ expect(described_class.parse("wiki-#{project.id}")).to eq([project, Gitlab::GlRepository::WIKI])
end
it 'throws an argument error on an invalid gl_repository' do
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
index 95bf7685ade..13cf52fd795 100644
--- a/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
+++ b/spec/lib/gitlab/graphql/authorize/authorize_resource_spec.rb
@@ -100,4 +100,22 @@ describe Gitlab::Graphql::Authorize::AuthorizeResource do
expect { fake_class.new.find_object }.to raise_error(/Implement #find_object in #{fake_class.name}/)
end
end
+
+ describe '#authorize' do
+ it 'adds permissions from subclasses to those of superclasses when used on classes' do
+ base_class = Class.new do
+ include Gitlab::Graphql::Authorize::AuthorizeResource
+
+ 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/authorize_spec.rb b/spec/lib/gitlab/graphql/authorize_spec.rb
deleted file mode 100644
index 9c17a3b0e4b..00000000000
--- a/spec/lib/gitlab/graphql/authorize_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-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/group_search_results_spec.rb b/spec/lib/gitlab/group_search_results_spec.rb
new file mode 100644
index 00000000000..2734fcef0a0
--- /dev/null
+++ b/spec/lib/gitlab/group_search_results_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe Gitlab::GroupSearchResults do
+ let(:user) { create(:user) }
+
+ describe 'user search' do
+ let(:group) { create(:group) }
+
+ it 'returns the users belonging to the group matching the search query' do
+ user1 = create(:user, username: 'gob_bluth')
+ create(:group_member, :developer, user: user1, group: group)
+
+ user2 = create(:user, username: 'michael_bluth')
+ create(:group_member, :developer, user: user2, group: group)
+
+ create(:user, username: 'gob_2018')
+
+ result = described_class.new(user, anything, group, 'gob').objects('users')
+
+ expect(result).to eq [user1]
+ end
+
+ it 'returns the user belonging to the subgroup matching the search query', :nested_groups do
+ user1 = create(:user, username: 'gob_bluth')
+ subgroup = create(:group, parent: group)
+ create(:group_member, :developer, user: user1, group: subgroup)
+
+ create(:user, username: 'gob_2018')
+
+ result = described_class.new(user, anything, group, 'gob').objects('users')
+
+ expect(result).to eq [user1]
+ end
+
+ it 'returns the user belonging to the parent group matching the search query', :nested_groups do
+ user1 = create(:user, username: 'gob_bluth')
+ parent_group = create(:group, children: [group])
+ create(:group_member, :developer, user: user1, group: parent_group)
+
+ create(:user, username: 'gob_2018')
+
+ result = described_class.new(user, anything, group, 'gob').objects('users')
+
+ expect(result).to eq [user1]
+ end
+
+ it 'does not return the user belonging to the private subgroup', :nested_groups do
+ user1 = create(:user, username: 'gob_bluth')
+ subgroup = create(:group, :private, parent: group)
+ create(:group_member, :developer, user: user1, group: subgroup)
+
+ create(:user, username: 'gob_2018')
+
+ result = described_class.new(user, anything, group, 'gob').objects('users')
+
+ expect(result).to eq []
+ end
+
+ it 'does not return the user belonging to an unrelated group' do
+ user = create(:user, username: 'gob_bluth')
+ unrelated_group = create(:group)
+ create(:group_member, :developer, user: user, group: unrelated_group)
+
+ result = described_class.new(user, anything, group, 'gob').objects('users')
+
+ expect(result).to eq []
+ end
+ end
+end
diff --git a/spec/lib/gitlab/hashed_storage/migrator_spec.rb b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
index 3942f168ceb..8e253b51597 100644
--- a/spec/lib/gitlab/hashed_storage/migrator_spec.rb
+++ b/spec/lib/gitlab/hashed_storage/migrator_spec.rb
@@ -1,21 +1,31 @@
+# frozen_string_literal: true
+
require 'spec_helper'
-describe Gitlab::HashedStorage::Migrator do
- describe '#bulk_schedule' do
- it 'schedules job to StorageMigratorWorker' do
+describe Gitlab::HashedStorage::Migrator, :sidekiq, :redis do
+ describe '#bulk_schedule_migration' do
+ it 'schedules job to HashedStorage::MigratorWorker' do
+ Sidekiq::Testing.fake! do
+ expect { subject.bulk_schedule_migration(start: 1, finish: 5) }.to change(HashedStorage::MigratorWorker.jobs, :size).by(1)
+ end
+ end
+ end
+
+ describe '#bulk_schedule_rollback' do
+ it 'schedules job to HashedStorage::RollbackerWorker' do
Sidekiq::Testing.fake! do
- expect { subject.bulk_schedule(start: 1, finish: 5) }.to change(HashedStorage::MigratorWorker.jobs, :size).by(1)
+ expect { subject.bulk_schedule_rollback(start: 1, finish: 5) }.to change(HashedStorage::RollbackerWorker.jobs, :size).by(1)
end
end
end
describe '#bulk_migrate' do
- let(:projects) { create_list(:project, 2, :legacy_storage) }
+ let(:projects) { create_list(:project, 2, :legacy_storage, :empty_repo) }
let(:ids) { projects.map(&:id) }
- it 'enqueue jobs to ProjectMigrateHashedStorageWorker' do
+ it 'enqueue jobs to HashedStorage::ProjectMigrateWorker' do
Sidekiq::Testing.fake! do
- expect { subject.bulk_migrate(start: ids.min, finish: ids.max) }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(2)
+ expect { subject.bulk_migrate(start: ids.min, finish: ids.max) }.to change(HashedStorage::ProjectMigrateWorker.jobs, :size).by(2)
end
end
@@ -32,13 +42,53 @@ describe Gitlab::HashedStorage::Migrator do
subject.bulk_migrate(start: ids.min, finish: ids.max)
end
- it 'has migrated projects set as writable' do
+ it 'has all projects migrated and set as writable' do
perform_enqueued_jobs do
subject.bulk_migrate(start: ids.min, finish: ids.max)
end
projects.each do |project|
- expect(project.reload.repository_read_only?).to be_falsey
+ project.reload
+
+ expect(project.hashed_storage?(:repository)).to be_truthy
+ expect(project.repository_read_only?).to be_falsey
+ end
+ end
+ end
+
+ describe '#bulk_rollback' do
+ let(:projects) { create_list(:project, 2, :empty_repo) }
+ let(:ids) { projects.map(&:id) }
+
+ it 'enqueue jobs to HashedStorage::ProjectRollbackWorker' do
+ Sidekiq::Testing.fake! do
+ expect { subject.bulk_rollback(start: ids.min, finish: ids.max) }.to change(HashedStorage::ProjectRollbackWorker.jobs, :size).by(2)
+ end
+ end
+
+ it 'rescues and log exceptions' do
+ allow_any_instance_of(Project).to receive(:rollback_to_legacy_storage!).and_raise(StandardError)
+ expect { subject.bulk_rollback(start: ids.min, finish: ids.max) }.not_to raise_error
+ end
+
+ it 'delegates each project in specified range to #rollback' do
+ projects.each do |project|
+ expect(subject).to receive(:rollback).with(project)
+ end
+
+ subject.bulk_rollback(start: ids.min, finish: ids.max)
+ end
+
+ it 'has all projects rolledback and set as writable' do
+ perform_enqueued_jobs do
+ subject.bulk_rollback(start: ids.min, finish: ids.max)
+ end
+
+ projects.each do |project|
+ project.reload
+
+ expect(project.legacy_storage?).to be_truthy
+ expect(project.repository_read_only?).to be_falsey
end
end
end
@@ -48,7 +98,7 @@ describe Gitlab::HashedStorage::Migrator do
it 'enqueues project migration job' do
Sidekiq::Testing.fake! do
- expect { subject.migrate(project) }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(1)
+ expect { subject.migrate(project) }.to change(HashedStorage::ProjectMigrateWorker.jobs, :size).by(1)
end
end
@@ -79,7 +129,7 @@ describe Gitlab::HashedStorage::Migrator do
it 'doesnt enqueue any migration job' do
Sidekiq::Testing.fake! do
- expect { subject.migrate(project) }.not_to change(ProjectMigrateHashedStorageWorker.jobs, :size)
+ expect { subject.migrate(project) }.not_to change(HashedStorage::ProjectMigrateWorker.jobs, :size)
end
end
@@ -88,4 +138,98 @@ describe Gitlab::HashedStorage::Migrator do
end
end
end
+
+ describe '#rollback' do
+ let(:project) { create(:project, :empty_repo) }
+
+ it 'enqueues project rollback job' do
+ Sidekiq::Testing.fake! do
+ expect { subject.rollback(project) }.to change(HashedStorage::ProjectRollbackWorker.jobs, :size).by(1)
+ end
+ end
+
+ it 'rescues and log exceptions' do
+ allow(project).to receive(:rollback_to_hashed_storage!).and_raise(StandardError)
+
+ expect { subject.rollback(project) }.not_to raise_error
+ end
+
+ it 'rolls-back project storage' do
+ perform_enqueued_jobs do
+ subject.rollback(project)
+ end
+
+ expect(project.reload.legacy_storage?).to be_truthy
+ end
+
+ it 'has rolled-back project set as writable' do
+ perform_enqueued_jobs do
+ subject.rollback(project)
+ end
+
+ expect(project.reload.repository_read_only?).to be_falsey
+ end
+
+ context 'when project is already on legacy storage' do
+ let(:project) { create(:project, :legacy_storage, :empty_repo) }
+
+ it 'doesnt enqueue any rollback job' do
+ Sidekiq::Testing.fake! do
+ expect { subject.rollback(project) }.not_to change(HashedStorage::ProjectRollbackWorker.jobs, :size)
+ end
+ end
+
+ it 'returns false' do
+ expect(subject.rollback(project)).to be_falsey
+ end
+ end
+ end
+
+ describe 'migration_pending?' do
+ set(:project) { create(:project, :empty_repo) }
+
+ it 'returns true when there are MigratorWorker jobs scheduled' do
+ Sidekiq::Testing.disable! do
+ ::HashedStorage::MigratorWorker.perform_async(1, 5)
+
+ expect(subject.migration_pending?).to be_truthy
+ end
+ end
+
+ it 'returns true when there are ProjectMigrateWorker jobs scheduled' do
+ Sidekiq::Testing.disable! do
+ ::HashedStorage::ProjectMigrateWorker.perform_async(1)
+
+ expect(subject.migration_pending?).to be_truthy
+ end
+ end
+
+ it 'returns false when queues are empty' do
+ expect(subject.migration_pending?).to be_falsey
+ end
+ end
+
+ describe 'rollback_pending?' do
+ set(:project) { create(:project, :empty_repo) }
+
+ it 'returns true when there are RollbackerWorker jobs scheduled' do
+ Sidekiq::Testing.disable! do
+ ::HashedStorage::RollbackerWorker.perform_async(1, 5)
+
+ expect(subject.rollback_pending?).to be_truthy
+ end
+ end
+
+ it 'returns true when there are jobs scheduled' do
+ Sidekiq::Testing.disable! do
+ ::HashedStorage::ProjectRollbackWorker.perform_async(1)
+
+ expect(subject.rollback_pending?).to be_truthy
+ end
+ end
+
+ it 'returns false when queues are empty' do
+ expect(subject.rollback_pending?).to be_falsey
+ end
+ end
end
diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml
index c15b360b563..e418516569a 100644
--- a/spec/lib/gitlab/import_export/all_models.yml
+++ b/spec/lib/gitlab/import_export/all_models.yml
@@ -100,6 +100,8 @@ merge_requests:
- head_pipeline
- latest_merge_request_diff
- merge_request_pipelines
+- merge_request_assignees
+- suggestions
merge_request_diff:
- merge_request
- merge_request_diff_commits
@@ -127,10 +129,11 @@ ci_pipelines:
- scheduled_actions
- artifacts
- pipeline_schedule
-- merge_requests
+- merge_requests_as_head_pipeline
- merge_request
- deployments
- environments
+- chat_data
pipeline_variables:
- pipeline
stages:
@@ -232,6 +235,7 @@ project:
- pushover_service
- jira_service
- redmine_service
+- youtrack_service
- custom_issue_tracker_service
- bugzilla_service
- gitlab_issue_tracker_service
@@ -349,3 +353,5 @@ resource_label_events:
- label
error_tracking_setting:
- project
+suggestions:
+- note
diff --git a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
index 68eaa70e6b6..4b234411a44 100644
--- a/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
+++ b/spec/lib/gitlab/import_export/merge_request_parser_spec.rb
@@ -41,4 +41,20 @@ describe Gitlab::ImportExport::MergeRequestParser do
expect(parsed_merge_request).to eq(merge_request)
end
+
+ context 'when the merge request has diffs' do
+ let(:merge_request) do
+ build(:merge_request, source_project: forked_project, target_project: project)
+ end
+
+ context 'when the diff is invalid' do
+ let(:merge_request_diff) { build(:merge_request_diff, merge_request: merge_request, base_commit_sha: 'foobar') }
+
+ it 'sets the diff to nil' do
+ expect(merge_request_diff).to be_invalid
+ expect(merge_request_diff.merge_request).to eq merge_request
+ expect(parsed_merge_request.merge_request_diff).to be_nil
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/import_export/project.json b/spec/lib/gitlab/import_export/project.json
index 1327f414498..773651dd226 100644
--- a/spec/lib/gitlab/import_export/project.json
+++ b/spec/lib/gitlab/import_export/project.json
@@ -6630,6 +6630,26 @@
"deploy_keys": [],
"services": [
{
+ "id": 101,
+ "title": "YouTrack",
+ "project_id": 5,
+ "created_at": "2016-06-14T15:01:51.327Z",
+ "updated_at": "2016-06-14T15:01:51.327Z",
+ "active": false,
+ "properties": {},
+ "template": false,
+ "push_events": true,
+ "issues_events": true,
+ "merge_requests_events": true,
+ "tag_push_events": true,
+ "note_events": true,
+ "job_events": true,
+ "type": "YoutrackService",
+ "category": "issue_tracker",
+ "default": false,
+ "wiki_page_events": true
+ },
+ {
"id": 100,
"title": "JetBrains TeamCity CI",
"project_id": 5,
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 46fdfba953b..cfc3e0ce926 100644
--- a/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
+++ b/spec/lib/gitlab/import_export/project_tree_saver_spec.rb
@@ -78,6 +78,14 @@ describe Gitlab::ImportExport::ProjectTreeSaver do
expect(saved_project_json['releases']).not_to be_empty
end
+ it 'has no author on releases' do
+ expect(saved_project_json['releases'].first['author']).to be_nil
+ end
+
+ it 'has the author ID on releases' do
+ expect(saved_project_json['releases'].first['author_id']).not_to be_nil
+ end
+
it 'has issues' do
expect(saved_project_json['issues']).not_to be_empty
end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 9cfa29ca7d9..d0ed588f05f 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -237,6 +237,8 @@ Ci::Pipeline:
- ref
- sha
- before_sha
+- source_sha
+- target_sha
- push_data
- created_at
- updated_at
@@ -608,3 +610,14 @@ ErrorTracking::ProjectErrorTrackingSetting:
- project_id
- project_name
- organization_name
+Suggestion:
+- id
+- note_id
+- relative_order
+- applied
+- commit_id
+- from_content
+- to_content
+- outdated
+- lines_above
+- lines_below
diff --git a/spec/lib/gitlab/issuable_metadata_spec.rb b/spec/lib/gitlab/issuable_metadata_spec.rb
index 42635a68ee1..6ec86163233 100644
--- a/spec/lib/gitlab/issuable_metadata_spec.rb
+++ b/spec/lib/gitlab/issuable_metadata_spec.rb
@@ -28,12 +28,12 @@ describe Gitlab::IssuableMetadata do
expect(data.count).to eq(2)
expect(data[issue.id].upvotes).to eq(1)
expect(data[issue.id].downvotes).to eq(0)
- expect(data[issue.id].notes_count).to eq(0)
+ expect(data[issue.id].user_notes_count).to eq(0)
expect(data[issue.id].merge_requests_count).to eq(1)
expect(data[closed_issue.id].upvotes).to eq(0)
expect(data[closed_issue.id].downvotes).to eq(1)
- expect(data[closed_issue.id].notes_count).to eq(0)
+ expect(data[closed_issue.id].user_notes_count).to eq(0)
expect(data[closed_issue.id].merge_requests_count).to eq(0)
end
end
@@ -51,12 +51,12 @@ describe Gitlab::IssuableMetadata do
expect(data.count).to eq(2)
expect(data[merge_request.id].upvotes).to eq(1)
expect(data[merge_request.id].downvotes).to eq(1)
- expect(data[merge_request.id].notes_count).to eq(1)
+ expect(data[merge_request.id].user_notes_count).to eq(1)
expect(data[merge_request.id].merge_requests_count).to eq(0)
expect(data[merge_request_closed.id].upvotes).to eq(0)
expect(data[merge_request_closed.id].downvotes).to eq(0)
- expect(data[merge_request_closed.id].notes_count).to eq(0)
+ expect(data[merge_request_closed.id].user_notes_count).to eq(0)
expect(data[merge_request_closed.id].merge_requests_count).to eq(0)
end
end
diff --git a/spec/lib/gitlab/json_cache_spec.rb b/spec/lib/gitlab/json_cache_spec.rb
index b52078e8556..b82c09af306 100644
--- a/spec/lib/gitlab/json_cache_spec.rb
+++ b/spec/lib/gitlab/json_cache_spec.rb
@@ -7,7 +7,7 @@ describe Gitlab::JsonCache do
let(:namespace) { 'geo' }
let(:key) { 'foo' }
let(:expanded_key) { "#{namespace}:#{key}:#{Rails.version}" }
- let(:broadcast_message) { create(:broadcast_message) }
+ set(:broadcast_message) { create(:broadcast_message) }
subject(:cache) { described_class.new(namespace: namespace, backend: backend) }
@@ -146,6 +146,18 @@ describe Gitlab::JsonCache do
expect(cache.read(key, BroadcastMessage)).to be_nil
end
+
+ it 'gracefully handles excluded fields from attributes during serialization' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return(broadcast_message.attributes.except("message_html").to_json)
+
+ result = cache.read(key, BroadcastMessage)
+
+ BroadcastMessage.cached_markdown_fields.html_fields.each do |field|
+ expect(result.public_send(field)).to be_nil
+ end
+ end
end
context 'when the cached value is an array' do
@@ -297,13 +309,79 @@ describe Gitlab::JsonCache do
expect(result).to eq(broadcast_message)
end
+ context 'when the cached value is an instance of ActiveRecord::Base' do
+ it 'returns a persisted record when id is set' do
+ backend.write(expanded_key, broadcast_message.to_json)
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ expect(result).to be_persisted
+ end
+
+ it 'returns a new record when id is nil' do
+ backend.write(expanded_key, build(:broadcast_message).to_json)
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ expect(result).to be_new_record
+ end
+
+ it 'returns a new record when id is missing' do
+ backend.write(expanded_key, build(:broadcast_message).attributes.except('id').to_json)
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ expect(result).to be_new_record
+ end
+
+ it 'gracefully handles bad cached entry' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return('{')
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ expect(result).to eq 'block result'
+ end
+
+ it 'gracefully handles an empty hash' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return('{}')
+
+ expect(cache.fetch(key, as: BroadcastMessage)).to be_a(BroadcastMessage)
+ end
+
+ it 'gracefully handles unknown attributes' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return(broadcast_message.attributes.merge(unknown_attribute: 1).to_json)
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ expect(result).to eq 'block result'
+ end
+
+ it 'gracefully handles excluded fields from attributes during serialization' do
+ allow(backend).to receive(:read)
+ .with(expanded_key)
+ .and_return(broadcast_message.attributes.except("message_html").to_json)
+
+ result = cache.fetch(key, as: BroadcastMessage) { 'block result' }
+
+ BroadcastMessage.cached_markdown_fields.html_fields.each do |field|
+ expect(result.public_send(field)).to be_nil
+ end
+ end
+ end
+
it "returns the result of the block when 'as' option is nil" do
result = cache.fetch(key, as: nil) { 'block result' }
expect(result).to eq('block result')
end
- it "returns the result of the block when 'as' option is not informed" do
+ it "returns the result of the block when 'as' option is missing" do
result = cache.fetch(key) { 'block result' }
expect(result).to eq('block result')
diff --git a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
index 27c802f34ec..95b6b3fd953 100644
--- a/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/pod_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Kubernetes::Helm::Pod do
it 'should generate the appropriate specifications for the container' do
container = subject.generate.spec.containers.first
expect(container.name).to eq('helm')
- expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.12.2-kube-1.11.0')
+ expect(container.image).to eq('registry.gitlab.com/gitlab-org/cluster-integration/helm-install-image/releases/2.12.3-kube-1.11.7')
expect(container.env.count).to eq(3)
expect(container.env.map(&:name)).to match_array([:HELM_VERSION, :TILLER_NAMESPACE, :COMMAND_SCRIPT])
expect(container.command).to match_array(["/bin/sh"])
diff --git a/spec/lib/gitlab/kubernetes/kube_client_spec.rb b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
index 02364e92149..978e64c4407 100644
--- a/spec/lib/gitlab/kubernetes/kube_client_spec.rb
+++ b/spec/lib/gitlab/kubernetes/kube_client_spec.rb
@@ -50,6 +50,36 @@ describe Gitlab::Kubernetes::KubeClient do
end
end
+ describe '#initialize' do
+ shared_examples 'local address' do
+ it 'blocks local addresses' do
+ expect { client }.to raise_error(Gitlab::UrlBlocker::BlockedUrlError)
+ end
+
+ context 'when local requests are allowed' do
+ before do
+ stub_application_setting(allow_local_requests_from_hooks_and_services: true)
+ end
+
+ it 'allows local addresses' do
+ expect { client }.not_to raise_error
+ end
+ end
+ end
+
+ context 'localhost address' do
+ let(:api_url) { 'http://localhost:22' }
+
+ it_behaves_like 'local address'
+ end
+
+ context 'private network address' do
+ let(:api_url) { 'http://192.168.1.2:3003' }
+
+ it_behaves_like 'local address'
+ end
+ end
+
describe '#core_client' do
subject { client.core_client }
diff --git a/spec/lib/gitlab/kubernetes_spec.rb b/spec/lib/gitlab/kubernetes_spec.rb
index f326d57e9c6..57b570a9166 100644
--- a/spec/lib/gitlab/kubernetes_spec.rb
+++ b/spec/lib/gitlab/kubernetes_spec.rb
@@ -40,10 +40,40 @@ describe Gitlab::Kubernetes do
describe '#filter_by_label' do
it 'returns matching labels' do
- matching_items = [kube_pod(app: 'foo')]
+ matching_items = [kube_pod(track: 'foo'), kube_deployment(track: 'foo')]
+ items = matching_items + [kube_pod, kube_deployment]
+
+ expect(filter_by_label(items, 'track' => 'foo')).to eq(matching_items)
+ end
+ end
+
+ describe '#filter_by_annotation' do
+ it 'returns matching labels' do
+ matching_items = [kube_pod(environment_slug: 'foo'), kube_deployment(environment_slug: 'foo')]
+ items = matching_items + [kube_pod, kube_deployment]
+
+ expect(filter_by_annotation(items, 'app.gitlab.com/env' => 'foo')).to eq(matching_items)
+ end
+ end
+
+ describe '#filter_by_project_environment' do
+ let(:matching_pod) { kube_pod(environment_slug: 'production', project_slug: 'my-cool-app') }
+
+ it 'returns matching legacy env label' do
+ matching_pod['metadata']['annotations'].delete('app.gitlab.com/app')
+ matching_pod['metadata']['annotations'].delete('app.gitlab.com/env')
+ matching_pod['metadata']['labels']['app'] = 'production'
+ matching_items = [matching_pod]
+ items = matching_items + [kube_pod]
+
+ expect(filter_by_project_environment(items, 'my-cool-app', 'production')).to eq(matching_items)
+ end
+
+ it 'returns matching env label' do
+ matching_items = [matching_pod]
items = matching_items + [kube_pod]
- expect(filter_by_label(items, app: 'foo')).to eq(matching_items)
+ expect(filter_by_project_environment(items, 'my-cool-app', 'production')).to eq(matching_items)
end
end
diff --git a/spec/lib/gitlab/middleware/basic_health_check_spec.rb b/spec/lib/gitlab/middleware/basic_health_check_spec.rb
index 187d903a5e1..86bdc479b66 100644
--- a/spec/lib/gitlab/middleware/basic_health_check_spec.rb
+++ b/spec/lib/gitlab/middleware/basic_health_check_spec.rb
@@ -28,6 +28,35 @@ describe Gitlab::Middleware::BasicHealthCheck do
end
end
+ context 'with X-Forwarded-For headers' do
+ let(:load_balancer_ip) { '1.2.3.4' }
+
+ before do
+ env['HTTP_X_FORWARDED_FOR'] = "#{load_balancer_ip}, 127.0.0.1"
+ env['REMOTE_ADDR'] = '127.0.0.1'
+ env['PATH_INFO'] = described_class::HEALTH_PATH
+ end
+
+ it 'returns 200 response when endpoint is allowed' do
+ allow(Settings.monitoring).to receive(:ip_whitelist).and_return([load_balancer_ip])
+ 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 'returns 404 when whitelist is not configured' do
+ allow(Settings.monitoring).to receive(:ip_whitelist).and_return([])
+
+ response = middleware.call(env)
+
+ expect(response[0]).to eq(404)
+ end
+ end
+
context 'whitelisted IP' do
before do
env['REMOTE_ADDR'] = '127.0.0.1'
diff --git a/spec/lib/gitlab/path_regex_spec.rb b/spec/lib/gitlab/path_regex_spec.rb
index e90e0aba0a4..312e5e55af8 100644
--- a/spec/lib/gitlab/path_regex_spec.rb
+++ b/spec/lib/gitlab/path_regex_spec.rb
@@ -107,7 +107,7 @@ describe Gitlab::PathRegex do
git = Gitlab.config.git.bin_path
tracked = `cd #{Rails.root} && #{git} ls-files public`
.split("\n")
- .map { |entry| entry.gsub('public/', '') }
+ .map { |entry| entry.start_with?('public/-/') ? '-' : entry.gsub('public/', '') }
.uniq
tracked + %w(assets uploads)
end
diff --git a/spec/lib/gitlab/profiler_spec.rb b/spec/lib/gitlab/profiler_spec.rb
index 8bb0c1a0b8a..9f2214f7ce7 100644
--- a/spec/lib/gitlab/profiler_spec.rb
+++ b/spec/lib/gitlab/profiler_spec.rb
@@ -1,8 +1,6 @@
require 'spec_helper'
describe Gitlab::Profiler do
- RSpec::Matchers.define_negated_matcher :not_change, :change
-
let(:null_logger) { Logger.new('/dev/null') }
let(:private_token) { 'private' }
@@ -187,7 +185,7 @@ describe Gitlab::Profiler do
end
it 'does not modify the standard Rails loggers' do
- expect { described_class.with_custom_logger(nil) { } }
+ expect { described_class.with_custom_logger(nil) {} }
.to not_change { ActiveRecord::Base.logger }
.and not_change { ActionController::Base.logger }
.and not_change { ActiveSupport::LogSubscriber.colorize_logging }
@@ -204,7 +202,7 @@ describe Gitlab::Profiler do
end
it 'cleans up ApplicationController afterwards' do
- expect { described_class.with_user(user) { } }
+ expect { described_class.with_user(user) {} }
.to not_change { ActionController.instance_methods(false) }
end
end
@@ -213,7 +211,7 @@ describe Gitlab::Profiler do
it 'does not define methods on ApplicationController' do
expect(ApplicationController).not_to receive(:define_method)
- described_class.with_user(nil) { }
+ described_class.with_user(nil) {}
end
end
end
diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb
index 6831274d37c..4a41d5cf51e 100644
--- a/spec/lib/gitlab/project_search_results_spec.rb
+++ b/spec/lib/gitlab/project_search_results_spec.rb
@@ -412,4 +412,36 @@ describe Gitlab::ProjectSearchResults do
end
end
end
+
+ describe 'user search' do
+ it 'returns the user belonging to the project matching the search query' do
+ project = create(:project)
+
+ user1 = create(:user, username: 'gob_bluth')
+ create(:project_member, :developer, user: user1, project: project)
+
+ user2 = create(:user, username: 'michael_bluth')
+ create(:project_member, :developer, user: user2, project: project)
+
+ create(:user, username: 'gob_2018')
+
+ result = described_class.new(user, project, 'gob').objects('users')
+
+ expect(result).to eq [user1]
+ end
+
+ it 'returns the user belonging to the group matching the search query' do
+ group = create(:group)
+ project = create(:project, namespace: group)
+
+ user1 = create(:user, username: 'gob_bluth')
+ create(:group_member, :developer, user: user1, group: group)
+
+ create(:user, username: 'gob_2018')
+
+ result = described_class.new(user, project, 'gob').objects('users')
+
+ expect(result).to eq [user1]
+ end
+ end
end
diff --git a/spec/lib/gitlab/project_template_spec.rb b/spec/lib/gitlab/project_template_spec.rb
index 1cc2bde50e9..8c2fc048a54 100644
--- a/spec/lib/gitlab/project_template_spec.rb
+++ b/spec/lib/gitlab/project_template_spec.rb
@@ -7,11 +7,20 @@ describe Gitlab::ProjectTemplate do
described_class.new('rails', 'Ruby on Rails', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/rails'),
described_class.new('spring', 'Spring', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/spring'),
described_class.new('express', 'NodeJS Express', 'Includes an MVC structure, .gitignore, Gemfile, and more great stuff', 'https://gitlab.com/gitlab-org/project-templates/express'),
+ described_class.new('iosswift', 'iOS (Swift)', 'A ready-to-go template for use with iOS Swift apps.', 'https://gitlab.com/gitlab-org/project-templates/iosswift'),
+ described_class.new('dotnetcore', '.NET Core', 'A .NET Core console application template, customizable for any .NET Core project', 'https://gitlab.com/gitlab-org/project-templates/dotnetcore'),
+ described_class.new('android', 'Android', 'A ready-to-go template for use with Android apps.', 'https://gitlab.com/gitlab-org/project-templates/android'),
+ described_class.new('gomicro', 'Go Micro', 'Go Micro is a framework for micro service development.', 'https://gitlab.com/gitlab-org/project-templates/go-micro'),
described_class.new('hugo', 'Pages/Hugo', 'Everything you need to get started using a Hugo Pages site.', 'https://gitlab.com/pages/hugo'),
described_class.new('jekyll', 'Pages/Jekyll', 'Everything you need to get started using a Jekyll Pages site.', 'https://gitlab.com/pages/jekyll'),
described_class.new('plainhtml', 'Pages/Plain HTML', 'Everything you need to get started using a plain HTML Pages site.', 'https://gitlab.com/pages/plain-html'),
described_class.new('gitbook', 'Pages/GitBook', 'Everything you need to get started using a GitBook Pages site.', 'https://gitlab.com/pages/gitbook'),
- described_class.new('hexo', 'Pages/Hexo', 'Everything you need to get started using a plan Hexo Pages site.', 'https://gitlab.com/pages/hexo')
+ described_class.new('hexo', 'Pages/Hexo', 'Everything you need to get started using a Hexo Pages site.', 'https://gitlab.com/pages/hexo'),
+ described_class.new('nfhugo', 'Netlify/Hugo', _('A Hugo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhugo'),
+ described_class.new('nfjekyll', 'Netlify/Jekyll', _('A Jekyll site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfjekyll'),
+ described_class.new('nfplainhtml', 'Netlify/Plain HTML', _('A plain HTML site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfplain-html'),
+ described_class.new('nfgitbook', 'Netlify/GitBook', _('A GitBook site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfgitbook'),
+ described_class.new('nfhexo', 'Netlify/Hexo', _('A Hexo site that uses Netlify for CI/CD instead of GitLab, but still with all the other great GitLab features.'), 'https://gitlab.com/pages/nfhexo')
]
expect(described_class.all).to be_an(Array)
diff --git a/spec/lib/gitlab/prometheus/queries/matched_metric_query_spec.rb b/spec/lib/gitlab/prometheus/queries/matched_metric_query_spec.rb
index 420218a695a..936447b8474 100644
--- a/spec/lib/gitlab/prometheus/queries/matched_metric_query_spec.rb
+++ b/spec/lib/gitlab/prometheus/queries/matched_metric_query_spec.rb
@@ -15,7 +15,7 @@ describe Gitlab::Prometheus::Queries::MatchedMetricQuery do
[{ '__name__' => 'metric_a' },
{ '__name__' => 'metric_b' }]
end
- let(:partialy_empty_series_info) { [{ '__name__' => 'metric_a', 'environment' => '' }] }
+ let(:partially_empty_series_info) { [{ '__name__' => 'metric_a', 'environment' => '' }] }
let(:empty_series_info) { [] }
let(:client) { double('prometheus_client') }
@@ -60,7 +60,7 @@ describe Gitlab::Prometheus::Queries::MatchedMetricQuery do
context 'one of the series info was not found' do
before do
- allow(client).to receive(:series).and_return(partialy_empty_series_info)
+ allow(client).to receive(:series).and_return(partially_empty_series_info)
end
it 'responds with one active and one missing metric' do
expect(subject.query).to eq([{ group: 'name', priority: 1, active_metrics: 1, metrics_missing_requirements: 1 }])
diff --git a/spec/lib/gitlab/quick_actions/command_definition_spec.rb b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
index 5dae82a63b4..136cfb5bcc5 100644
--- a/spec/lib/gitlab/quick_actions/command_definition_spec.rb
+++ b/spec/lib/gitlab/quick_actions/command_definition_spec.rb
@@ -72,7 +72,7 @@ describe Gitlab::QuickActions::CommandDefinition do
end
describe "#execute" do
- let(:context) { OpenStruct.new(run: false) }
+ let(:context) { OpenStruct.new(run: false, commands_executed_count: nil) }
context "when the command is a noop" do
it "doesn't execute the command" do
@@ -80,6 +80,7 @@ describe Gitlab::QuickActions::CommandDefinition do
subject.execute(context, nil)
+ expect(context.commands_executed_count).to be_nil
expect(context.run).to be false
end
end
@@ -97,6 +98,7 @@ describe Gitlab::QuickActions::CommandDefinition do
it "doesn't execute the command" do
subject.execute(context, nil)
+ expect(context.commands_executed_count).to be_nil
expect(context.run).to be false
end
end
@@ -112,6 +114,7 @@ describe Gitlab::QuickActions::CommandDefinition do
subject.execute(context, true)
expect(context.run).to be true
+ expect(context.commands_executed_count).to eq(1)
end
end
@@ -120,6 +123,7 @@ describe Gitlab::QuickActions::CommandDefinition do
subject.execute(context, nil)
expect(context.run).to be true
+ expect(context.commands_executed_count).to eq(1)
end
end
end
@@ -134,6 +138,7 @@ describe Gitlab::QuickActions::CommandDefinition do
subject.execute(context, true)
expect(context.run).to be true
+ expect(context.commands_executed_count).to eq(1)
end
end
diff --git a/spec/lib/gitlab/reference_extractor_spec.rb b/spec/lib/gitlab/reference_extractor_spec.rb
index 4139d1c650c..d982053d92e 100644
--- a/spec/lib/gitlab/reference_extractor_spec.rb
+++ b/spec/lib/gitlab/reference_extractor_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Gitlab::ReferenceExtractor do
diff --git a/spec/lib/gitlab/repo_path_spec.rb b/spec/lib/gitlab/repo_path_spec.rb
index 13940713dfc..4c7ca4e2b57 100644
--- a/spec/lib/gitlab/repo_path_spec.rb
+++ b/spec/lib/gitlab/repo_path_spec.rb
@@ -6,43 +6,47 @@ describe ::Gitlab::RepoPath do
context 'a repository storage path' do
it 'parses a full repository path' do
- expect(described_class.parse(project.repository.full_path)).to eq([project, false, nil])
+ expect(described_class.parse(project.repository.full_path)).to eq([project, Gitlab::GlRepository::PROJECT, nil])
end
it 'parses a full wiki path' do
- expect(described_class.parse(project.wiki.repository.full_path)).to eq([project, true, nil])
+ expect(described_class.parse(project.wiki.repository.full_path)).to eq([project, Gitlab::GlRepository::WIKI, nil])
end
end
context 'a relative path' do
it 'parses a relative repository path' do
- expect(described_class.parse(project.full_path + '.git')).to eq([project, false, nil])
+ expect(described_class.parse(project.full_path + '.git')).to eq([project, Gitlab::GlRepository::PROJECT, nil])
end
it 'parses a relative wiki path' do
- expect(described_class.parse(project.full_path + '.wiki.git')).to eq([project, true, nil])
+ expect(described_class.parse(project.full_path + '.wiki.git')).to eq([project, Gitlab::GlRepository::WIKI, nil])
end
it 'parses a relative path starting with /' do
- expect(described_class.parse('/' + project.full_path + '.git')).to eq([project, false, nil])
+ expect(described_class.parse('/' + project.full_path + '.git')).to eq([project, Gitlab::GlRepository::PROJECT, nil])
end
context 'of a redirected project' do
let(:redirect) { project.route.create_redirect('foo/bar') }
it 'parses a relative repository path' do
- expect(described_class.parse(redirect.path + '.git')).to eq([project, false, 'foo/bar'])
+ expect(described_class.parse(redirect.path + '.git')).to eq([project, Gitlab::GlRepository::PROJECT, 'foo/bar'])
end
it 'parses a relative wiki path' do
- expect(described_class.parse(redirect.path + '.wiki.git')).to eq([project, true, 'foo/bar.wiki'])
+ expect(described_class.parse(redirect.path + '.wiki.git')).to eq([project, Gitlab::GlRepository::WIKI, 'foo/bar.wiki'])
end
it 'parses a relative path starting with /' do
- expect(described_class.parse('/' + redirect.path + '.git')).to eq([project, false, 'foo/bar'])
+ expect(described_class.parse('/' + redirect.path + '.git')).to eq([project, Gitlab::GlRepository::PROJECT, 'foo/bar'])
end
end
end
+
+ it "returns nil for non existent paths" do
+ expect(described_class.parse("path/non-existent.git")).to eq(nil)
+ end
end
describe '.find_project' do
diff --git a/spec/lib/gitlab/request_context_spec.rb b/spec/lib/gitlab/request_context_spec.rb
index fd443cc1f71..23e45aff1c5 100644
--- a/spec/lib/gitlab/request_context_spec.rb
+++ b/spec/lib/gitlab/request_context_spec.rb
@@ -6,6 +6,31 @@ describe Gitlab::RequestContext do
let(:app) { -> (env) {} }
let(:env) { Hash.new }
+ context 'with X-Forwarded-For headers', :request_store do
+ let(:load_balancer_ip) { '1.2.3.4' }
+ let(:headers) do
+ {
+ 'HTTP_X_FORWARDED_FOR' => "#{load_balancer_ip}, 127.0.0.1",
+ 'REMOTE_ADDR' => '127.0.0.1'
+ }
+ end
+
+ let(:env) { Rack::MockRequest.env_for("/").merge(headers) }
+
+ it 'returns the load balancer IP' do
+ client_ip = nil
+
+ endpoint = proc do
+ client_ip = Gitlab::SafeRequestStore[:client_ip]
+ [200, {}, ["Hello"]]
+ end
+
+ described_class.new(endpoint).call(env)
+
+ expect(client_ip).to eq(load_balancer_ip)
+ end
+ end
+
context 'when RequestStore::Middleware is used' do
around do |example|
RequestStore::Middleware.new(-> (env) { example.run }).call({})
@@ -15,7 +40,7 @@ describe Gitlab::RequestContext do
let(:ip) { '192.168.1.11' }
before do
- allow_any_instance_of(ActionDispatch::Request).to receive(:ip).and_return(ip)
+ allow_any_instance_of(Rack::Request).to receive(:ip).and_return(ip)
described_class.new(app).call(env)
end
diff --git a/spec/lib/gitlab/search_results_spec.rb b/spec/lib/gitlab/search_results_spec.rb
index 87288baedb0..4b57eecff93 100644
--- a/spec/lib/gitlab/search_results_spec.rb
+++ b/spec/lib/gitlab/search_results_spec.rb
@@ -121,6 +121,22 @@ describe Gitlab::SearchResults do
results.objects('issues')
end
end
+
+ describe '#users' do
+ it 'does not call the UsersFinder when the current_user is not allowed to read users list' do
+ allow(Ability).to receive(:allowed?).and_return(false)
+
+ expect(UsersFinder).not_to receive(:new).with(user, search: 'foo').and_call_original
+
+ results.objects('users')
+ end
+
+ it 'calls the UsersFinder' do
+ expect(UsersFinder).to receive(:new).with(user, search: 'foo').and_call_original
+
+ results.objects('users')
+ end
+ end
end
it 'does not list issues on private projects' do
diff --git a/spec/lib/gitlab/serializer/pagination_spec.rb b/spec/lib/gitlab/serializer/pagination_spec.rb
index 1bc6536439e..c54be78f050 100644
--- a/spec/lib/gitlab/serializer/pagination_spec.rb
+++ b/spec/lib/gitlab/serializer/pagination_spec.rb
@@ -1,16 +1,12 @@
require 'spec_helper'
describe Gitlab::Serializer::Pagination do
- let(:request) { spy('request') }
+ let(:request) { double(url: "#{Gitlab.config.gitlab.url}:8080/api/v4/projects?#{query.to_query}", query_parameters: query) }
let(:response) { spy('response') }
let(:headers) { spy('headers') }
before do
- allow(request).to receive(:query_parameters)
- .and_return(params)
-
- allow(response).to receive(:headers)
- .and_return(headers)
+ allow(response).to receive(:headers).and_return(headers)
end
let(:pagination) { described_class.new(request, response) }
@@ -19,7 +15,7 @@ describe Gitlab::Serializer::Pagination do
subject { pagination.paginate(resource) }
let(:resource) { User.all }
- let(:params) { { page: 1, per_page: 2 } }
+ let(:query) { { page: 1, per_page: 2 } }
context 'when a multiple resources are present in relation' do
before do
diff --git a/spec/lib/gitlab/shell_spec.rb b/spec/lib/gitlab/shell_spec.rb
index 033e1bf81a1..e2f09de2808 100644
--- a/spec/lib/gitlab/shell_spec.rb
+++ b/spec/lib/gitlab/shell_spec.rb
@@ -8,6 +8,7 @@ describe Gitlab::Shell do
let(:gitlab_shell) { described_class.new }
let(:popen_vars) { { 'GIT_TERMINAL_PROMPT' => ENV['GIT_TERMINAL_PROMPT'] } }
let(:timeout) { Gitlab.config.gitlab_shell.git_timeout }
+ let(:gitlab_authorized_keys) { double }
before do
allow(Project).to receive(:find).and_return(project)
@@ -49,13 +50,38 @@ describe Gitlab::Shell do
describe '#add_key' do
context 'when authorized_keys_enabled is true' do
- it 'removes trailing garbage' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar']
- )
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ allow(gitlab_shell)
+ .to receive(:gitlab_shell_keys_path)
+ .and_return(:gitlab_shell_keys_path)
+ end
+
+ it 'calls #gitlab_shell_fast_execute with add-key command' do
+ expect(gitlab_shell)
+ .to receive(:gitlab_shell_fast_execute)
+ .with([
+ :gitlab_shell_keys_path,
+ 'add-key',
+ 'key-123',
+ 'ssh-rsa foobar'
+ ])
+
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ end
+ end
+
+ context 'authorized_keys_file set' do
+ it 'calls Gitlab::AuthorizedKeys#add_key with id and key' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ expect(gitlab_authorized_keys)
+ .to receive(:add_key)
+ .with('key-123', 'ssh-rsa foobar')
+
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar')
+ end
end
end
@@ -64,10 +90,24 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: false)
end
- it 'does nothing' do
- expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ end
+
+ it 'does nothing' do
+ expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
+
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ end
+ end
+
+ context 'authorized_keys_file set' do
+ it 'does nothing' do
+ expect(Gitlab::AuthorizedKeys).not_to receive(:new)
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ end
end
end
@@ -76,24 +116,89 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: nil)
end
- it 'removes trailing garbage' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [:gitlab_shell_keys_path, 'add-key', 'key-123', 'ssh-rsa foobar']
- )
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ allow(gitlab_shell)
+ .to receive(:gitlab_shell_keys_path)
+ .and_return(:gitlab_shell_keys_path)
+ end
+
+ it 'calls #gitlab_shell_fast_execute with add-key command' do
+ expect(gitlab_shell)
+ .to receive(:gitlab_shell_fast_execute)
+ .with([
+ :gitlab_shell_keys_path,
+ 'add-key',
+ 'key-123',
+ 'ssh-rsa foobar'
+ ])
+
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ end
+ end
+
+ context 'authorized_keys_file set' do
+ it 'calls Gitlab::AuthorizedKeys#add_key with id and key' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+
+ expect(gitlab_authorized_keys)
+ .to receive(:add_key)
+ .with('key-123', 'ssh-rsa foobar')
- gitlab_shell.add_key('key-123', 'ssh-rsa foobar trailing garbage')
+ gitlab_shell.add_key('key-123', 'ssh-rsa foobar')
+ end
end
end
end
describe '#batch_add_keys' do
+ let(:keys) { [double(shell_id: 'key-123', key: 'ssh-rsa foobar')] }
+
context 'when authorized_keys_enabled is true' do
- it 'instantiates KeyAdder' do
- expect_any_instance_of(Gitlab::Shell::KeyAdder).to receive(:add_key).with('key-123', 'ssh-rsa foobar')
+ context 'authorized_keys_file not set' do
+ let(:io) { double }
+
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ end
+
+ context 'valid keys' do
+ before do
+ allow(gitlab_shell)
+ .to receive(:gitlab_shell_keys_path)
+ .and_return(:gitlab_shell_keys_path)
+ end
+
+ it 'calls gitlab-keys with batch-add-keys command' do
+ expect(IO)
+ .to receive(:popen)
+ .with("gitlab_shell_keys_path batch-add-keys", 'w')
+ .and_yield(io)
+
+ expect(io).to receive(:puts).with("key-123\tssh-rsa foobar")
+ expect(gitlab_shell.batch_add_keys(keys)).to be_truthy
+ end
+ end
+
+ context 'invalid keys' do
+ let(:keys) { [double(shell_id: 'key-123', key: "ssh-rsa A\tSDFA\nSGADG")] }
+
+ it 'catches failure and returns false' do
+ expect(gitlab_shell.batch_add_keys(keys)).to be_falsey
+ end
+ end
+ end
- gitlab_shell.batch_add_keys do |adder|
- adder.add_key('key-123', 'ssh-rsa foobar')
+ context 'authorized_keys_file set' do
+ it 'calls Gitlab::AuthorizedKeys#batch_add_keys with keys to be added' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+
+ expect(gitlab_authorized_keys)
+ .to receive(:batch_add_keys)
+ .with(keys)
+
+ gitlab_shell.batch_add_keys(keys)
end
end
end
@@ -103,11 +208,23 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: false)
end
- it 'does nothing' do
- expect_any_instance_of(Gitlab::Shell::KeyAdder).not_to receive(:add_key)
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ end
+
+ it 'does nothing' do
+ expect(IO).not_to receive(:popen)
+
+ gitlab_shell.batch_add_keys(keys)
+ end
+ end
+
+ context 'authorized_keys_file set' do
+ it 'does nothing' do
+ expect(Gitlab::AuthorizedKeys).not_to receive(:new)
- gitlab_shell.batch_add_keys do |adder|
- adder.add_key('key-123', 'ssh-rsa foobar')
+ gitlab_shell.batch_add_keys(keys)
end
end
end
@@ -117,11 +234,37 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: nil)
end
- it 'instantiates KeyAdder' do
- expect_any_instance_of(Gitlab::Shell::KeyAdder).to receive(:add_key).with('key-123', 'ssh-rsa foobar')
+ context 'authorized_keys_file not set' do
+ let(:io) { double }
- gitlab_shell.batch_add_keys do |adder|
- adder.add_key('key-123', 'ssh-rsa foobar')
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ allow(gitlab_shell)
+ .to receive(:gitlab_shell_keys_path)
+ .and_return(:gitlab_shell_keys_path)
+ end
+
+ it 'calls gitlab-keys with batch-add-keys command' do
+ expect(IO)
+ .to receive(:popen)
+ .with("gitlab_shell_keys_path batch-add-keys", 'w')
+ .and_yield(io)
+
+ expect(io).to receive(:puts).with("key-123\tssh-rsa foobar")
+
+ gitlab_shell.batch_add_keys(keys)
+ end
+ end
+
+ context 'authorized_keys_file set' do
+ it 'calls Gitlab::AuthorizedKeys#batch_add_keys with keys to be added' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+
+ expect(gitlab_authorized_keys)
+ .to receive(:batch_add_keys)
+ .with(keys)
+
+ gitlab_shell.batch_add_keys(keys)
end
end
end
@@ -129,13 +272,34 @@ describe Gitlab::Shell do
describe '#remove_key' do
context 'when authorized_keys_enabled is true' do
- it 'removes trailing garbage' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [:gitlab_shell_keys_path, 'rm-key', 'key-123', 'ssh-rsa foobar']
- )
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ allow(gitlab_shell)
+ .to receive(:gitlab_shell_keys_path)
+ .and_return(:gitlab_shell_keys_path)
+ end
- gitlab_shell.remove_key('key-123', 'ssh-rsa foobar')
+ it 'calls #gitlab_shell_fast_execute with rm-key command' do
+ expect(gitlab_shell)
+ .to receive(:gitlab_shell_fast_execute)
+ .with([
+ :gitlab_shell_keys_path,
+ 'rm-key',
+ 'key-123'
+ ])
+
+ gitlab_shell.remove_key('key-123')
+ end
+ end
+
+ context 'authorized_keys_file not set' do
+ it 'calls Gitlab::AuthorizedKeys#rm_key with the key to be removed' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys).to receive(:rm_key).with('key-123')
+
+ gitlab_shell.remove_key('key-123')
+ end
end
end
@@ -144,10 +308,24 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: false)
end
- it 'does nothing' do
- expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ end
+
+ it 'does nothing' do
+ expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
- gitlab_shell.remove_key('key-123', 'ssh-rsa foobar')
+ gitlab_shell.remove_key('key-123')
+ end
+ end
+
+ context 'authorized_keys_file set' do
+ it 'does nothing' do
+ expect(Gitlab::AuthorizedKeys).not_to receive(:new)
+
+ gitlab_shell.remove_key('key-123')
+ end
end
end
@@ -156,232 +334,256 @@ describe Gitlab::Shell do
stub_application_setting(authorized_keys_enabled: nil)
end
- it 'removes trailing garbage' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [:gitlab_shell_keys_path, 'rm-key', 'key-123', 'ssh-rsa foobar']
- )
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ allow(gitlab_shell)
+ .to receive(:gitlab_shell_keys_path)
+ .and_return(:gitlab_shell_keys_path)
+ end
+
+ it 'calls #gitlab_shell_fast_execute with rm-key command' do
+ expect(gitlab_shell)
+ .to receive(:gitlab_shell_fast_execute)
+ .with([
+ :gitlab_shell_keys_path,
+ 'rm-key',
+ 'key-123'
+ ])
- gitlab_shell.remove_key('key-123', 'ssh-rsa foobar')
+ gitlab_shell.remove_key('key-123')
+ end
end
- end
- context 'when key content is not given' do
- it 'calls rm-key with only one argument' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [:gitlab_shell_keys_path, 'rm-key', 'key-123']
- )
+ context 'authorized_keys_file not set' do
+ it 'calls Gitlab::AuthorizedKeys#rm_key with the key to be removed' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys).to receive(:rm_key).with('key-123')
- gitlab_shell.remove_key('key-123')
+ gitlab_shell.remove_key('key-123')
+ end
end
end
end
describe '#remove_all_keys' do
context 'when authorized_keys_enabled is true' do
- it 'removes trailing garbage' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with([:gitlab_shell_keys_path, 'clear'])
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ allow(gitlab_shell)
+ .to receive(:gitlab_shell_keys_path)
+ .and_return(:gitlab_shell_keys_path)
+ end
- gitlab_shell.remove_all_keys
- end
- end
+ it 'calls #gitlab_shell_fast_execute with clear command' do
+ expect(gitlab_shell)
+ .to receive(:gitlab_shell_fast_execute)
+ .with([:gitlab_shell_keys_path, 'clear'])
- context 'when authorized_keys_enabled is false' do
- before do
- stub_application_setting(authorized_keys_enabled: false)
+ gitlab_shell.remove_all_keys
+ end
end
- it 'does nothing' do
- expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
+ context 'authorized_keys_file set' do
+ it 'calls Gitlab::AuthorizedKeys#clear' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys).to receive(:clear)
- gitlab_shell.remove_all_keys
+ gitlab_shell.remove_all_keys
+ end
end
end
- context 'when authorized_keys_enabled is nil' do
+ context 'when authorized_keys_enabled is false' do
before do
- stub_application_setting(authorized_keys_enabled: nil)
+ stub_application_setting(authorized_keys_enabled: false)
end
- it 'removes trailing garbage' do
- allow(gitlab_shell).to receive(:gitlab_shell_keys_path).and_return(:gitlab_shell_keys_path)
- expect(gitlab_shell).to receive(:gitlab_shell_fast_execute).with(
- [:gitlab_shell_keys_path, 'clear']
- )
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ end
- gitlab_shell.remove_all_keys
- end
- end
- end
+ it 'does nothing' do
+ expect(gitlab_shell).not_to receive(:gitlab_shell_fast_execute)
- describe '#remove_keys_not_found_in_db' do
- context 'when keys are in the file that are not in the DB' do
- before do
- gitlab_shell.remove_all_keys
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- gitlab_shell.add_key('key-9876', 'ssh-rsa ASDFASDF')
- @another_key = create(:key) # this one IS in the DB
+ gitlab_shell.remove_all_keys
+ end
end
- it 'removes the keys' do
- expect(find_in_authorized_keys_file(1234)).to be_truthy
- expect(find_in_authorized_keys_file(9876)).to be_truthy
- expect(find_in_authorized_keys_file(@another_key.id)).to be_truthy
- gitlab_shell.remove_keys_not_found_in_db
- expect(find_in_authorized_keys_file(1234)).to be_falsey
- expect(find_in_authorized_keys_file(9876)).to be_falsey
- expect(find_in_authorized_keys_file(@another_key.id)).to be_truthy
+ context 'authorized_keys_file set' do
+ it 'does nothing' do
+ expect(Gitlab::AuthorizedKeys).not_to receive(:new)
+
+ gitlab_shell.remove_all_keys
+ end
end
end
- context 'when keys there are duplicate keys in the file that are not in the DB' do
+ context 'when authorized_keys_enabled is nil' do
before do
- gitlab_shell.remove_all_keys
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
- gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ stub_application_setting(authorized_keys_enabled: nil)
end
- it 'removes the keys' do
- expect(find_in_authorized_keys_file(1234)).to be_truthy
- gitlab_shell.remove_keys_not_found_in_db
- expect(find_in_authorized_keys_file(1234)).to be_falsey
- end
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ allow(gitlab_shell)
+ .to receive(:gitlab_shell_keys_path)
+ .and_return(:gitlab_shell_keys_path)
+ end
- it 'does not run remove more than once per key (in a batch)' do
- expect(gitlab_shell).to receive(:remove_key).with('key-1234').once
- gitlab_shell.remove_keys_not_found_in_db
- end
- end
+ it 'calls #gitlab_shell_fast_execute with clear command' do
+ expect(gitlab_shell)
+ .to receive(:gitlab_shell_fast_execute)
+ .with([:gitlab_shell_keys_path, 'clear'])
- context 'when keys there are duplicate keys in the file that ARE in the DB' do
- before do
- gitlab_shell.remove_all_keys
- @key = create(:key)
- gitlab_shell.add_key(@key.shell_id, @key.key)
+ gitlab_shell.remove_all_keys
+ end
end
- it 'does not remove the key' do
- gitlab_shell.remove_keys_not_found_in_db
- expect(find_in_authorized_keys_file(@key.id)).to be_truthy
- end
+ context 'authorized_keys_file set' do
+ it 'calls Gitlab::AuthorizedKeys#clear' do
+ expect(Gitlab::AuthorizedKeys).to receive(:new).and_return(gitlab_authorized_keys)
+ expect(gitlab_authorized_keys).to receive(:clear)
- it 'does not need to run a SELECT query for that batch, on account of that key' do
- expect_any_instance_of(ActiveRecord::Relation).not_to receive(:pluck)
- gitlab_shell.remove_keys_not_found_in_db
+ gitlab_shell.remove_all_keys
+ end
end
end
+ end
- unless ENV['CI'] # Skip in CI, it takes 1 minute
- context 'when the first batch can be skipped, but the next batch has keys that are not in the DB' do
+ describe '#remove_keys_not_found_in_db' do
+ context 'when keys are in the file that are not in the DB' do
+ context 'authorized_keys_file not set' do
before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
gitlab_shell.remove_all_keys
- 100.times { |i| create(:key) } # first batch is all in the DB
gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ gitlab_shell.add_key('key-9876', 'ssh-rsa ASDFASDF')
+ @another_key = create(:key) # this one IS in the DB
end
- it 'removes the keys not in the DB' do
- expect(find_in_authorized_keys_file(1234)).to be_truthy
+ it 'removes the keys' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
+ expect(gitlab_shell).to receive(:remove_key).with('key-9876')
+ expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@another_key.id}")
+
gitlab_shell.remove_keys_not_found_in_db
- expect(find_in_authorized_keys_file(1234)).to be_falsey
end
end
- end
- end
- describe '#batch_read_key_ids' do
- context 'when there are keys in the authorized_keys file' do
- before do
- gitlab_shell.remove_all_keys
- (1..4).each do |i|
- gitlab_shell.add_key("key-#{i}", "ssh-rsa ASDFASDF#{i}")
+ context 'authorized_keys_file set' do
+ before do
+ gitlab_shell.remove_all_keys
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ gitlab_shell.add_key('key-9876', 'ssh-rsa ASDFASDF')
+ @another_key = create(:key) # this one IS in the DB
end
- end
- it 'iterates over the key IDs in the file, in batches' do
- loop_count = 0
- first_batch = [1, 2]
- second_batch = [3, 4]
+ it 'removes the keys' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
+ expect(gitlab_shell).to receive(:remove_key).with('key-9876')
+ expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@another_key.id}")
- gitlab_shell.batch_read_key_ids(batch_size: 2) do |batch|
- expected = (loop_count == 0 ? first_batch : second_batch)
- expect(batch).to eq(expected)
- loop_count += 1
+ gitlab_shell.remove_keys_not_found_in_db
end
end
end
- end
- describe '#list_key_ids' do
- context 'when there are keys in the authorized_keys file' do
- before do
- gitlab_shell.remove_all_keys
- (1..4).each do |i|
- gitlab_shell.add_key("key-#{i}", "ssh-rsa ASDFASDF#{i}")
+ context 'when keys there are duplicate keys in the file that are not in the DB' do
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ gitlab_shell.remove_all_keys
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ end
+
+ it 'removes the keys' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
+
+ gitlab_shell.remove_keys_not_found_in_db
end
end
- it 'outputs the key IDs in the file, separated by newlines' do
- ids = []
- gitlab_shell.list_key_ids do |io|
- io.each do |line|
- ids << line
- end
+ context 'authorized_keys_file set' do
+ before do
+ gitlab_shell.remove_all_keys
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
end
- expect(ids).to eq(%W{1\n 2\n 3\n 4\n})
- end
- end
+ it 'removes the keys' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
- context 'when there are no keys in the authorized_keys file' do
- before do
- gitlab_shell.remove_all_keys
+ gitlab_shell.remove_keys_not_found_in_db
+ end
end
+ end
- it 'outputs nothing, not even an empty string' do
- ids = []
- gitlab_shell.list_key_ids do |io|
- io.each do |line|
- ids << line
- end
+ context 'when keys there are duplicate keys in the file that ARE in the DB' do
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ gitlab_shell.remove_all_keys
+ @key = create(:key)
+ gitlab_shell.add_key(@key.shell_id, @key.key)
end
- expect(ids).to eq([])
+ it 'does not remove the key' do
+ expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@key.id}")
+
+ gitlab_shell.remove_keys_not_found_in_db
+ end
end
- end
- end
- describe Gitlab::Shell::KeyAdder do
- describe '#add_key' do
- it 'removes trailing garbage' do
- io = spy(:io)
- adder = described_class.new(io)
+ context 'authorized_keys_file set' do
+ before do
+ gitlab_shell.remove_all_keys
+ @key = create(:key)
+ gitlab_shell.add_key(@key.shell_id, @key.key)
+ end
- adder.add_key('key-42', "ssh-rsa foo bar\tbaz")
+ it 'does not remove the key' do
+ expect(gitlab_shell).not_to receive(:remove_key).with("key-#{@key.id}")
- expect(io).to have_received(:puts).with("key-42\tssh-rsa foo")
+ gitlab_shell.remove_keys_not_found_in_db
+ end
end
+ end
- it 'handles multiple spaces in the key' do
- io = spy(:io)
- adder = described_class.new(io)
+ unless ENV['CI'] # Skip in CI, it takes 1 minute
+ context 'when the first batch can be skipped, but the next batch has keys that are not in the DB' do
+ context 'authorized_keys_file not set' do
+ before do
+ stub_gitlab_shell_setting(authorized_keys_file: nil)
+ gitlab_shell.remove_all_keys
+ 100.times { |i| create(:key) } # first batch is all in the DB
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ end
- adder.add_key('key-42', "ssh-rsa foo")
+ it 'removes the keys not in the DB' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
- expect(io).to have_received(:puts).with("key-42\tssh-rsa foo")
- end
+ gitlab_shell.remove_keys_not_found_in_db
+ end
+ end
- it 'raises an exception if the key contains a tab' do
- expect do
- described_class.new(StringIO.new).add_key('key-42', "ssh-rsa\tfoobar")
- end.to raise_error(Gitlab::Shell::Error)
- end
+ context 'authorized_keys_file set' do
+ before do
+ gitlab_shell.remove_all_keys
+ 100.times { |i| create(:key) } # first batch is all in the DB
+ gitlab_shell.add_key('key-1234', 'ssh-rsa ASDFASDF')
+ end
+
+ it 'removes the keys not in the DB' do
+ expect(gitlab_shell).to receive(:remove_key).with('key-1234')
- it 'raises an exception if the key contains a newline' do
- expect do
- described_class.new(StringIO.new).add_key('key-42', "ssh-rsa foobar\nssh-rsa pawned")
- end.to raise_error(Gitlab::Shell::Error)
+ gitlab_shell.remove_keys_not_found_in_db
+ end
+ end
end
end
end
@@ -393,7 +595,6 @@ describe Gitlab::Shell do
before do
allow(Gitlab.config.gitlab_shell).to receive(:path).and_return(gitlab_shell_path)
- allow(Gitlab.config.gitlab_shell).to receive(:hooks_path).and_return(gitlab_shell_hooks_path)
allow(Gitlab.config.gitlab_shell).to receive(:git_timeout).and_return(800)
end
@@ -567,12 +768,4 @@ describe Gitlab::Shell do
end
end
end
-
- def find_in_authorized_keys_file(key_id)
- gitlab_shell.batch_read_key_ids do |ids|
- return true if ids.include?(key_id) # rubocop:disable Cop/AvoidReturnFromBlocks
- end
-
- false
- end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
new file mode 100644
index 00000000000..1a5a38b5d99
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqMiddleware::MemoryKiller do
+ subject { described_class.new }
+ let(:pid) { 999 }
+
+ let(:worker) { double(:worker, class: 'TestWorker') }
+ let(:job) { { 'jid' => 123 } }
+ let(:queue) { 'test_queue' }
+
+ def run
+ thread = subject.call(worker, job, queue) { nil }
+ thread&.join
+ end
+
+ before do
+ allow(subject).to receive(:get_rss).and_return(10.kilobytes)
+ allow(subject).to receive(:pid).and_return(pid)
+ end
+
+ context 'when MAX_RSS is set to 0' do
+ before do
+ stub_const("#{described_class}::MAX_RSS", 0)
+ end
+
+ it 'does nothing' do
+ expect(subject).not_to receive(:sleep)
+
+ run
+ end
+ end
+
+ context 'when MAX_RSS is exceeded' do
+ before do
+ stub_const("#{described_class}::MAX_RSS", 5.kilobytes)
+ end
+
+ it 'sends the TSTP, TERM and KILL signals at expected times' do
+ expect(subject).to receive(:sleep).with(15 * 60).ordered
+ expect(Process).to receive(:kill).with('SIGTSTP', pid).ordered
+
+ expect(subject).to receive(:sleep).with(30).ordered
+ expect(Process).to receive(:kill).with('SIGTERM', pid).ordered
+
+ expect(subject).to receive(:sleep).with(10).ordered
+ expect(Process).to receive(:kill).with('SIGKILL', pid).ordered
+
+ run
+ end
+
+ it 'sends TSTP and TERM to the pid, but KILL to the pgroup, when running as process leader' do
+ allow(Process).to receive(:getpgrp) { pid }
+ allow(subject).to receive(:sleep)
+
+ expect(Process).to receive(:kill).with('SIGTSTP', pid).ordered
+ expect(Process).to receive(:kill).with('SIGTERM', pid).ordered
+ expect(Process).to receive(:kill).with('SIGKILL', 0).ordered
+
+ run
+ end
+ end
+
+ context 'when MAX_RSS is not exceeded' do
+ before do
+ stub_const("#{described_class}::MAX_RSS", 15.kilobytes)
+ end
+
+ it 'does nothing' do
+ expect(subject).not_to receive(:sleep)
+
+ run
+ end
+ end
+end
diff --git a/spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb b/spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb
deleted file mode 100644
index 0001795c3f0..00000000000
--- a/spec/lib/gitlab/sidekiq_middleware/shutdown_spec.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::SidekiqMiddleware::Shutdown do
- subject { described_class.new }
-
- let(:pid) { Process.pid }
- let(:worker) { double(:worker, class: 'TestWorker') }
- let(:job) { { 'jid' => 123 } }
- let(:queue) { 'test_queue' }
- let(:block) { proc { nil } }
-
- def run
- subject.call(worker, job, queue) { block.call }
- described_class.shutdown_thread&.join
- end
-
- def pop_trace
- subject.trace.pop(true)
- end
-
- before do
- allow(subject).to receive(:get_rss).and_return(10.kilobytes)
- described_class.clear_shutdown_thread
- end
-
- context 'when MAX_RSS is set to 0' do
- before do
- stub_const("#{described_class}::MAX_RSS", 0)
- end
-
- it 'does nothing' do
- expect(subject).not_to receive(:sleep)
-
- run
- end
- end
-
- def expect_shutdown_sequence
- expect(pop_trace).to eq([:sleep, 15 * 60])
- expect(pop_trace).to eq([:kill, 'SIGTSTP', pid])
-
- expect(pop_trace).to eq([:sleep, 30])
- expect(pop_trace).to eq([:kill, 'SIGTERM', pid])
-
- expect(pop_trace).to eq([:sleep, 10])
- expect(pop_trace).to eq([:kill, 'SIGKILL', pid])
- end
-
- context 'when MAX_RSS is exceeded' do
- before do
- stub_const("#{described_class}::MAX_RSS", 5.kilobytes)
- end
-
- it 'sends the TSTP, TERM and KILL signals at expected times' do
- run
-
- expect_shutdown_sequence
- end
- end
-
- context 'when MAX_RSS is not exceeded' do
- before do
- stub_const("#{described_class}::MAX_RSS", 15.kilobytes)
- end
-
- it 'does nothing' do
- expect(subject).not_to receive(:sleep)
-
- run
- end
- end
-
- context 'when WantShutdown is raised' do
- let(:block) { proc { raise described_class::WantShutdown } }
-
- it 'starts the shutdown sequence and re-raises the exception' do
- expect { run }.to raise_exception(described_class::WantShutdown)
-
- # We can't expect 'run' to have joined on the shutdown thread, because
- # it hit an exception.
- shutdown_thread = described_class.shutdown_thread
- expect(shutdown_thread).not_to be_nil
- shutdown_thread.join
-
- expect_shutdown_sequence
- end
- end
-end
diff --git a/spec/lib/gitlab/sidekiq_signals_spec.rb b/spec/lib/gitlab/sidekiq_signals_spec.rb
new file mode 100644
index 00000000000..77ecd1840d2
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_signals_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqSignals do
+ describe '.install' do
+ let(:result) { Hash.new { |h, k| h[k] = 0 } }
+ let(:int_handler) { -> (_) { result['INT'] += 1 } }
+ let(:term_handler) { -> (_) { result['TERM'] += 1 } }
+ let(:other_handler) { -> (_) { result['OTHER'] += 1 } }
+ let(:signals) { { 'INT' => int_handler, 'TERM' => term_handler, 'OTHER' => other_handler } }
+
+ context 'not a process group leader' do
+ before do
+ allow(Process).to receive(:getpgrp) { Process.pid * 2 }
+ end
+
+ it 'does nothing' do
+ expect { described_class.install!(signals) }
+ .not_to change { signals }
+ end
+ end
+
+ context 'as a process group leader' do
+ before do
+ allow(Process).to receive(:getpgrp) { Process.pid }
+ end
+
+ it 'installs its own signal handlers for TERM and INT only' do
+ described_class.install!(signals)
+
+ expect(signals['INT']).not_to eq(int_handler)
+ expect(signals['TERM']).not_to eq(term_handler)
+ expect(signals['OTHER']).to eq(other_handler)
+ end
+
+ %w[INT TERM].each do |signal|
+ it "installs a forwarding signal handler for #{signal}" do
+ described_class.install!(signals)
+
+ expect(described_class)
+ .to receive(:trap)
+ .with(signal, 'IGNORE')
+ .and_return(:original_trap)
+ .ordered
+
+ expect(Process)
+ .to receive(:kill)
+ .with(signal, 0)
+ .ordered
+
+ expect(described_class)
+ .to receive(:trap)
+ .with(signal, :original_trap)
+ .ordered
+
+ signals[signal].call(:cli)
+
+ expect(result[signal]).to eq(1)
+ end
+
+ it "raises if sidekiq no longer traps SIG#{signal}" do
+ signals.delete(signal)
+
+ expect { described_class.install!(signals) }
+ .to raise_error(/Sidekiq should have registered/)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/application_help_spec.rb b/spec/lib/gitlab/slash_commands/application_help_spec.rb
new file mode 100644
index 00000000000..b203a1ee79c
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/application_help_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::ApplicationHelp do
+ let(:params) { { command: '/gitlab', text: 'help' } }
+
+ describe '#execute' do
+ subject do
+ described_class.new(params).execute
+ end
+
+ it 'displays the help section' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:text]).to include('Available commands')
+ expect(subject[:text]).to include('/gitlab [project name or alias] issue show')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/presenters/error_spec.rb b/spec/lib/gitlab/slash_commands/presenters/error_spec.rb
new file mode 100644
index 00000000000..30ff81510c1
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/presenters/error_spec.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::Presenters::Error do
+ subject { described_class.new('Error').message }
+
+ it { is_expected.to be_a(Hash) }
+
+ it 'shows the error message' do
+ expect(subject[:response_type]).to be(:ephemeral)
+ expect(subject[:status]).to eq(200)
+ expect(subject[:text]).to eq('Error')
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/presenters/run_spec.rb b/spec/lib/gitlab/slash_commands/presenters/run_spec.rb
new file mode 100644
index 00000000000..f3ab01ef6bb
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/presenters/run_spec.rb
@@ -0,0 +1,79 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::Presenters::Run do
+ let(:presenter) { described_class.new }
+
+ describe '#present' do
+ context 'when no builds are present' do
+ it 'returns an error' do
+ builds = double(:builds, take: nil)
+ pipeline = double(:pipeline, builds: builds)
+
+ expect(presenter)
+ .to receive(:unsupported_chat_service)
+
+ presenter.present(pipeline)
+ end
+ end
+
+ context 'when a responder could be found' do
+ it 'returns the output for a scheduled pipeline' do
+ responder = double(:responder, scheduled_output: 'hello')
+ build = double(:build)
+ builds = double(:builds, take: build)
+ pipeline = double(:pipeline, builds: builds)
+
+ allow(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(build)
+ .and_return(responder)
+
+ expect(presenter)
+ .to receive(:in_channel_response)
+ .with('hello')
+
+ presenter.present(pipeline)
+ end
+ end
+
+ context 'when a responder could not be found' do
+ it 'returns an error' do
+ build = double(:build)
+ builds = double(:builds, take: build)
+ pipeline = double(:pipeline, builds: builds)
+
+ allow(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(build)
+ .and_return(nil)
+
+ expect(presenter)
+ .to receive(:unsupported_chat_service)
+
+ presenter.present(pipeline)
+ end
+ end
+ end
+
+ describe '#unsupported_chat_service' do
+ it 'returns an ephemeral response' do
+ expect(presenter)
+ .to receive(:ephemeral_response)
+ .with(text: /Sorry, this chat service is currently not supported/)
+
+ presenter.unsupported_chat_service
+ end
+ end
+
+ describe '#failed_to_schedule' do
+ it 'returns an ephemeral response' do
+ expect(presenter)
+ .to receive(:ephemeral_response)
+ .with(text: /The command could not be scheduled/)
+
+ presenter.failed_to_schedule('foo')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/slash_commands/run_spec.rb b/spec/lib/gitlab/slash_commands/run_spec.rb
new file mode 100644
index 00000000000..900fae05719
--- /dev/null
+++ b/spec/lib/gitlab/slash_commands/run_spec.rb
@@ -0,0 +1,123 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Gitlab::SlashCommands::Run do
+ describe '.available?' do
+ it 'returns true when builds are enabled for the project' do
+ project = double(:project, builds_enabled?: true)
+
+ allow(Gitlab::Chat)
+ .to receive(:available?)
+ .and_return(true)
+
+ expect(described_class.available?(project)).to eq(true)
+ end
+
+ it 'returns false when builds are disabled for the project' do
+ project = double(:project, builds_enabled?: false)
+
+ expect(described_class.available?(project)).to eq(false)
+ end
+
+ it 'returns false when chatops is not available' do
+ allow(Gitlab::Chat)
+ .to receive(:available?)
+ .and_return(false)
+
+ project = double(:project, builds_enabled?: true)
+
+ expect(described_class.available?(project)).to eq(false)
+ end
+ end
+
+ describe '.allowed?' do
+ it 'returns true when the user can create a pipeline' do
+ project = create(:project)
+
+ expect(described_class.allowed?(project, project.creator)).to eq(true)
+ end
+
+ it 'returns false when the user can not create a pipeline' do
+ project = create(:project)
+ user = create(:user)
+
+ expect(described_class.allowed?(project, user)).to eq(false)
+ end
+ end
+
+ describe '#execute' do
+ let(:chat_name) { create(:chat_name) }
+ let(:project) { create(:project) }
+
+ let(:command) do
+ described_class.new(project, chat_name, response_url: 'http://example.com')
+ end
+
+ context 'when a pipeline could not be scheduled' do
+ it 'returns an error' do
+ expect_any_instance_of(Gitlab::Chat::Command)
+ .to receive(:try_create_pipeline)
+ .and_return(nil)
+
+ expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
+ .to receive(:failed_to_schedule)
+ .with('foo')
+
+ command.execute(command: 'foo', arguments: '')
+ end
+ end
+
+ context 'when a pipeline could be created but the chat service was not supported' do
+ it 'returns an error' do
+ build = double(:build)
+ pipeline = double(
+ :pipeline,
+ builds: double(:relation, take: build),
+ persisted?: true
+ )
+
+ expect_any_instance_of(Gitlab::Chat::Command)
+ .to receive(:try_create_pipeline)
+ .and_return(pipeline)
+
+ expect(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(build)
+ .and_return(nil)
+
+ expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
+ .to receive(:unsupported_chat_service)
+
+ command.execute(command: 'foo', arguments: '')
+ end
+ end
+
+ context 'using a valid pipeline' do
+ it 'schedules the pipeline' do
+ responder = double(:responder, scheduled_output: 'hello')
+ build = double(:build)
+ pipeline = double(
+ :pipeline,
+ builds: double(:relation, take: build),
+ persisted?: true
+ )
+
+ expect_any_instance_of(Gitlab::Chat::Command)
+ .to receive(:try_create_pipeline)
+ .and_return(pipeline)
+
+ expect(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(build)
+ .and_return(responder)
+
+ expect_any_instance_of(Gitlab::SlashCommands::Presenters::Run)
+ .to receive(:in_channel_response)
+ .with(responder.scheduled_output)
+
+ command.execute(command: 'foo', arguments: '')
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/tracing_spec.rb b/spec/lib/gitlab/tracing_spec.rb
index 2cddc895026..566b5050e47 100644
--- a/spec/lib/gitlab/tracing_spec.rb
+++ b/spec/lib/gitlab/tracing_spec.rb
@@ -44,12 +44,15 @@ describe Gitlab::Tracing do
describe '.tracing_url' do
where(:tracing_url_enabled?, :tracing_url_template, :correlation_id, :process_name, :tracing_url) do
- false | "https://localhost" | "123" | "web" | nil
- true | "https://localhost" | "123" | "web" | "https://localhost"
- true | "https://localhost?service=%{service}" | "123" | "web" | "https://localhost?service=web"
- true | "https://localhost?c=%{correlation_id}" | "123" | "web" | "https://localhost?c=123"
- true | "https://localhost?c=%{correlation_id}&s=%{service}" | "123" | "web" | "https://localhost?c=123&s=web"
- true | "https://localhost?c=%{correlation_id}" | nil | "web" | "https://localhost?c="
+ false | "https://localhost" | "123" | "web" | nil
+ true | "https://localhost" | "123" | "web" | "https://localhost"
+ true | "https://localhost?service={{ service }}" | "123" | "web" | "https://localhost?service=web"
+ true | "https://localhost?c={{ correlation_id }}" | "123" | "web" | "https://localhost?c=123"
+ true | "https://localhost?c={{ correlation_id }}&s={{ service }}" | "123" | "web" | "https://localhost?c=123&s=web"
+ true | "https://localhost?c={{ correlation_id }}" | nil | "web" | "https://localhost?c="
+ true | "https://localhost?c={{ correlation_id }}&s=%22{{ service }}%22" | "123" | "web" | "https://localhost?c=123&s=%22web%22"
+ true | "https://localhost?c={{correlation_id}}&s={{service}}" | "123" | "web" | "https://localhost?c=123&s=web"
+ true | "https://localhost?c={{correlation_id }}&s={{ service}}" | "123" | "web" | "https://localhost?c=123&s=web"
end
with_them do
diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb
index d3eae80cc56..549cc5ac057 100644
--- a/spec/lib/gitlab/usage_data_spec.rb
+++ b/spec/lib/gitlab/usage_data_spec.rb
@@ -13,6 +13,8 @@ describe Gitlab::UsageData do
create(:service, project: projects[0], type: 'SlackSlashCommandsService', active: true)
create(:service, project: projects[1], type: 'SlackService', active: true)
create(:service, project: projects[2], type: 'SlackService', active: true)
+ create(:project_error_tracking_setting, project: projects[0])
+ create(:project_error_tracking_setting, project: projects[1], enabled: false)
gcp_cluster = create(:cluster, :provided_by_gcp)
create(:cluster, :provided_by_user)
@@ -79,6 +81,8 @@ describe Gitlab::UsageData do
auto_devops_disabled
deploy_keys
deployments
+ successful_deployments
+ failed_deployments
environments
clusters
clusters_enabled
@@ -115,6 +119,7 @@ describe Gitlab::UsageData do
projects_slack_slash_active
projects_prometheus_active
projects_with_repositories_enabled
+ projects_with_error_tracking_enabled
pages_domains
protected_branches
releases
@@ -144,6 +149,7 @@ describe Gitlab::UsageData do
expect(count_data[:projects_slack_notifications_active]).to eq(2)
expect(count_data[:projects_slack_slash_active]).to eq(1)
expect(count_data[:projects_with_repositories_enabled]).to eq(2)
+ expect(count_data[:projects_with_error_tracking_enabled]).to eq(1)
expect(count_data[:clusters_enabled]).to eq(7)
expect(count_data[:project_clusters_enabled]).to eq(6)
diff --git a/spec/lib/gitlab/user_extractor_spec.rb b/spec/lib/gitlab/user_extractor_spec.rb
index fcc05ab3a0c..b86ec5445b8 100644
--- a/spec/lib/gitlab/user_extractor_spec.rb
+++ b/spec/lib/gitlab/user_extractor_spec.rb
@@ -38,6 +38,18 @@ describe Gitlab::UserExtractor do
expect(extractor.users).to include(user)
end
+
+ context 'input as array of strings' do
+ it 'is treated as one string' do
+ extractor = described_class.new(text.lines)
+
+ user_1 = create(:user, username: "USER-1")
+ user_4 = create(:user, username: "USER-4")
+ user_email = create(:user, email: 'user@gitlab.org')
+
+ expect(extractor.users).to contain_exactly(user_1, user_4, user_email)
+ end
+ end
end
describe '#matches' do
@@ -48,6 +60,14 @@ describe Gitlab::UserExtractor do
it 'includes all mentioned usernames' do
expect(extractor.matches[:usernames]).to contain_exactly('user-1', 'user-2', 'user-4')
end
+
+ context 'input has no matching e-mail or usernames' do
+ it 'returns an empty list of users' do
+ extractor = described_class.new('My test')
+
+ expect(extractor.users).to be_empty
+ end
+ end
end
describe '#references' do
diff --git a/spec/lib/gitlab/utils_spec.rb b/spec/lib/gitlab/utils_spec.rb
index 8f5029b3565..4645339f439 100644
--- a/spec/lib/gitlab/utils_spec.rb
+++ b/spec/lib/gitlab/utils_spec.rb
@@ -213,4 +213,22 @@ describe Gitlab::Utils do
expect(subject[:variables].first[:key]).to eq('VAR1')
end
end
+
+ describe '.try_megabytes_to_bytes' do
+ context 'when the size can be converted to megabytes' do
+ it 'returns the size in megabytes' do
+ size = described_class.try_megabytes_to_bytes(1)
+
+ expect(size).to eq(1.megabytes)
+ end
+ end
+
+ context 'when the size can not be converted to megabytes' do
+ it 'returns the input size' do
+ size = described_class.try_megabytes_to_bytes('foo')
+
+ expect(size).to eq('foo')
+ end
+ end
+ end
end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 7213eee5675..d88086b01b1 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -250,11 +250,11 @@ describe Gitlab::Workhorse do
}
end
- subject { described_class.git_http_ok(repository, false, user, action) }
+ subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action) }
it { expect(subject).to include(params) }
- context 'when is_wiki' do
+ context 'when the repo_type is a wiki' do
let(:params) do
{
GL_ID: "user-#{user.id}",
@@ -264,7 +264,7 @@ describe Gitlab::Workhorse do
}
end
- subject { described_class.git_http_ok(repository, true, user, action) }
+ subject { described_class.git_http_ok(repository, Gitlab::GlRepository::WIKI, user, action) }
it { expect(subject).to include(params) }
end
@@ -304,7 +304,7 @@ describe Gitlab::Workhorse do
end
context 'show_all_refs enabled' do
- subject { described_class.git_http_ok(repository, false, user, action, show_all_refs: true) }
+ subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
it { is_expected.to include(ShowAllRefs: true) }
end
@@ -322,7 +322,7 @@ describe Gitlab::Workhorse do
it { expect(subject).to include(gitaly_params) }
context 'show_all_refs enabled' do
- subject { described_class.git_http_ok(repository, false, user, action, show_all_refs: true) }
+ subject { described_class.git_http_ok(repository, Gitlab::GlRepository::PROJECT, user, action, show_all_refs: true) }
it { is_expected.to include(ShowAllRefs: true) }
end
diff --git a/spec/lib/gitlab_spec.rb b/spec/lib/gitlab_spec.rb
index 5f7a0cca351..8232715d00e 100644
--- a/spec/lib/gitlab_spec.rb
+++ b/spec/lib/gitlab_spec.rb
@@ -95,4 +95,18 @@ describe Gitlab do
expect(described_class.com?).to eq false
end
end
+
+ describe '.ee?' do
+ it 'returns true when using Enterprise Edition' do
+ stub_const('License', Class.new)
+
+ expect(described_class.ee?).to eq(true)
+ end
+
+ it 'returns false when using Community Edition' do
+ hide_const('License')
+
+ expect(described_class.ee?).to eq(false)
+ end
+ end
end
diff --git a/spec/lib/google_api/cloud_platform/client_spec.rb b/spec/lib/google_api/cloud_platform/client_spec.rb
index e2134dc279c..1fefc947636 100644
--- a/spec/lib/google_api/cloud_platform/client_spec.rb
+++ b/spec/lib/google_api/cloud_platform/client_spec.rb
@@ -97,6 +97,12 @@ describe GoogleApi::CloudPlatform::Client do
"node_config": {
"machine_type": machine_type
},
+ "master_auth": {
+ "username": "admin",
+ "client_certificate_config": {
+ issue_client_certificate: true
+ }
+ },
"legacy_abac": {
"enabled": true
}
@@ -122,6 +128,12 @@ describe GoogleApi::CloudPlatform::Client do
"node_config": {
"machine_type": machine_type
},
+ "master_auth": {
+ "username": "admin",
+ "client_certificate_config": {
+ issue_client_certificate: true
+ }
+ },
"legacy_abac": {
"enabled": false
}
diff --git a/spec/lib/object_storage/direct_upload_spec.rb b/spec/lib/object_storage/direct_upload_spec.rb
index 1024e1a25ea..8ccbd90ddb8 100644
--- a/spec/lib/object_storage/direct_upload_spec.rb
+++ b/spec/lib/object_storage/direct_upload_spec.rb
@@ -121,7 +121,7 @@ describe ObjectStorage::DirectUpload do
expect(subject[:MultipartUpload][:PartURLs].length).to eq(2)
end
- it 'part size is mimimum, 5MB' do
+ it 'part size is minimum, 5MB' do
expect(subject[:MultipartUpload][:PartSize]).to eq(5.megabyte)
end
end
diff --git a/spec/lib/sentry/client_spec.rb b/spec/lib/sentry/client_spec.rb
index 6fbf60a6222..88e7e2e5ebb 100644
--- a/spec/lib/sentry/client_spec.rb
+++ b/spec/lib/sentry/client_spec.rb
@@ -36,7 +36,7 @@ describe Sentry::Client do
end
it 'does not follow redirects' do
- expect { subject }.to raise_exception(Sentry::Client::Error, 'Sentry response error: 302')
+ expect { subject }.to raise_exception(Sentry::Client::Error, 'Sentry response status code: 302')
expect(redirect_req_stub).to have_been_requested
expect(redirected_req_stub).not_to have_been_requested
end
diff --git a/spec/mailers/abuse_report_mailer_spec.rb b/spec/mailers/abuse_report_mailer_spec.rb
index bda892083b3..f96870cc112 100644
--- a/spec/mailers/abuse_report_mailer_spec.rb
+++ b/spec/mailers/abuse_report_mailer_spec.rb
@@ -4,25 +4,24 @@ describe AbuseReportMailer do
include EmailSpec::Matchers
describe '.notify' do
- context 'with admin_notification_email set' do
- before do
- stub_application_setting(admin_notification_email: 'admin@example.com')
- end
+ before do
+ stub_application_setting(admin_notification_email: 'admin@example.com')
+ end
- it 'sends to the admin_notification_email' do
- report = create(:abuse_report)
+ let(:report) { create(:abuse_report) }
+
+ subject { described_class.notify(report.id) }
- mail = described_class.notify(report.id)
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
- expect(mail).to deliver_to 'admin@example.com'
+ context 'with admin_notification_email set' do
+ it 'sends to the admin_notification_email' do
+ is_expected.to deliver_to 'admin@example.com'
end
it 'includes the user in the subject' do
- report = create(:abuse_report)
-
- mail = described_class.notify(report.id)
-
- expect(mail).to have_subject "#{report.user.name} (#{report.user.username}) was reported for abuse"
+ is_expected.to have_subject "#{report.user.name} (#{report.user.username}) was reported for abuse"
end
end
diff --git a/spec/mailers/email_rejection_mailer_spec.rb b/spec/mailers/email_rejection_mailer_spec.rb
new file mode 100644
index 00000000000..bbe0a50ae8e
--- /dev/null
+++ b/spec/mailers/email_rejection_mailer_spec.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe EmailRejectionMailer do
+ include EmailSpec::Matchers
+
+ describe '#rejection' do
+ let(:raw_email) { 'From: someone@example.com\nraw email here' }
+
+ subject { described_class.rejection('some rejection reason', raw_email) }
+
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+ end
+end
diff --git a/spec/mailers/emails/auto_devops_spec.rb b/spec/mailers/emails/auto_devops_spec.rb
index 839caf3f50e..dd7c12c3143 100644
--- a/spec/mailers/emails/auto_devops_spec.rb
+++ b/spec/mailers/emails/auto_devops_spec.rb
@@ -13,6 +13,9 @@ describe Emails::AutoDevops do
subject { Notify.autodevops_disabled_email(pipeline, owner.email) }
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
it 'sents email with correct subject' do
is_expected.to have_subject("#{project.name} | Auto DevOps pipeline was disabled for #{project.name}")
end
diff --git a/spec/mailers/emails/issues_spec.rb b/spec/mailers/emails/issues_spec.rb
index 09253cf8003..5b5bd6f4308 100644
--- a/spec/mailers/emails/issues_spec.rb
+++ b/spec/mailers/emails/issues_spec.rb
@@ -29,5 +29,14 @@ describe Emails::Issues do
expect(subject).to have_body_text "23, 34, 58"
end
+
+ context 'with header and footer' do
+ let(:results) { { success: 165, error_lines: [], parse_error: false } }
+
+ subject { Notify.import_issues_csv_email(user.id, project.id, results) }
+
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+ end
end
end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 4f578c48d5b..3c8897ed37c 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -53,6 +53,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject and body' do
aggregate_failures do
@@ -72,6 +74,9 @@ describe Notify do
context 'when sent with a reason' do
subject { described_class.new_issue_email(issue.assignees.first.id, issue.id, NotificationReason::ASSIGNED) }
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
it 'includes the reason in a header' do
is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
end
@@ -99,6 +104,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -118,6 +125,9 @@ describe Notify do
context 'when sent with a reason' do
subject { described_class.reassigned_issue_email(recipient.id, issue.id, [previous_assignee.id], current_user.id, NotificationReason::ASSIGNED) }
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
it 'includes the reason in a header' do
is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
end
@@ -134,6 +144,8 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with a labels subscriptions link in its footer'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -173,6 +185,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -194,23 +208,53 @@ describe Notify do
let(:new_issue) { create(:issue) }
subject { described_class.issue_moved_email(recipient, issue, new_issue, current_user) }
- it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
- let(:model) { issue }
- end
- it_behaves_like 'it should show Gmail Actions View Issue link'
- it_behaves_like 'an unsubscribeable thread'
+ context 'when a user has permissions to access the new issue' do
+ before do
+ new_issue.project.add_developer(recipient)
+ end
+
+ it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
+ let(:model) { issue }
+ end
+ it_behaves_like 'it should show Gmail Actions View Issue link'
+ it_behaves_like 'an unsubscribeable thread'
+
+ it 'contains description about action taken' do
+ is_expected.to have_body_text 'Issue was moved to another project'
+ end
+
+ it 'has the correct subject and body' do
+ new_issue_url = project_issue_path(new_issue.project, new_issue)
+
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.to have_body_text(new_issue_url)
+ is_expected.to have_body_text(project_issue_path(project, issue))
+ end
+ end
- it 'contains description about action taken' do
- is_expected.to have_body_text 'Issue was moved to another project'
+ it 'contains the issue title' do
+ is_expected.to have_body_text new_issue.title
+ end
end
- it 'has the correct subject and body' do
- new_issue_url = project_issue_path(new_issue.project, new_issue)
+ context 'when a user does not permissions to access the new issue' do
+ it 'has the correct subject and body' do
+ new_issue_url = project_issue_path(new_issue.project, new_issue)
- aggregate_failures do
- is_expected.to have_referable_subject(issue, reply: true)
- is_expected.to have_body_text(new_issue_url)
- is_expected.to have_body_text(project_issue_path(project, issue))
+ aggregate_failures do
+ is_expected.to have_referable_subject(issue, reply: true)
+ is_expected.not_to have_body_text(new_issue_url)
+ is_expected.to have_body_text(project_issue_path(project, issue))
+ end
+ end
+
+ it 'does not contain the issue title' do
+ is_expected.not_to have_body_text new_issue.title
+ end
+
+ it 'contains information about missing permissions' do
+ is_expected.to have_body_text "You don't have access to the project."
end
end
end
@@ -226,6 +270,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject and body' do
aggregate_failures do
@@ -243,6 +289,9 @@ describe Notify do
context 'when sent with a reason' do
subject { described_class.new_merge_request_email(merge_request.assignee_id, merge_request.id, NotificationReason::ASSIGNED) }
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
it 'includes the reason in a header' do
is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
end
@@ -270,6 +319,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -289,6 +340,9 @@ describe Notify do
context 'when sent with a reason' do
subject { described_class.reassigned_merge_request_email(recipient.id, merge_request.id, previous_assignee.id, current_user.id, NotificationReason::ASSIGNED) }
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
it 'includes the reason in a header' do
is_expected.to have_header('X-GitLab-NotificationReason', NotificationReason::ASSIGNED)
end
@@ -313,6 +367,8 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like "an unsubscribeable thread"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains the description' do
is_expected.to have_body_text(merge_request.description)
@@ -329,6 +385,8 @@ describe Notify do
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with a labels subscriptions link in its footer'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -352,6 +410,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -379,6 +439,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the merge author' do
sender = subject.header[:from].addrs[0]
@@ -413,6 +475,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the merge request author' do
sender = subject.header[:from].addrs[0]
@@ -442,6 +506,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the push user' do
sender = subject.header[:from].addrs[0]
@@ -482,6 +548,9 @@ describe Notify do
subject { described_class.note_issue_email(recipient.id, third_note.id) }
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
it 'has In-Reply-To header pointing to previous note in discussion' do
expect(subject.header['In-Reply-To'].message_ids).to eq(["note_#{second_note.id}@#{host}"])
end
@@ -502,6 +571,9 @@ describe Notify do
subject { described_class.note_issue_email(recipient.id, note.id) }
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
it 'has In-Reply-To header pointing to the issue' do
expect(subject.header['In-Reply-To'].message_ids).to eq(["issue_#{note.noteable.id}@#{host}"])
end
@@ -518,6 +590,9 @@ describe Notify do
subject { described_class.note_project_snippet_email(project_snippet_note.author_id, project_snippet_note.id) }
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
it_behaves_like 'an answer to an existing thread with reply-by-email enabled' do
let(:model) { project_snippet }
end
@@ -535,6 +610,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject and body' do
is_expected.to have_subject("#{project.name} | Project was moved")
@@ -559,6 +636,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
to_emails = subject.header[:to].addrs.map(&:address)
@@ -582,6 +661,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject "Access to the #{project.full_name} project was denied"
@@ -599,6 +680,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject "Access to the #{project.full_name} project was granted"
@@ -629,6 +712,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject "Invitation to join the #{project.full_name} project"
@@ -653,6 +738,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject 'Invitation accepted'
@@ -676,6 +763,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject 'Invitation declined'
@@ -708,6 +797,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Commit link'
it_behaves_like 'a user cannot unsubscribe through footer link'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject and body' do
aggregate_failures do
@@ -732,6 +823,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject and body' do
aggregate_failures do
@@ -756,6 +849,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject and body' do
aggregate_failures do
@@ -819,6 +914,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Commit link'
it_behaves_like 'a user cannot unsubscribe through footer link'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject' do
is_expected.to have_subject "Re: #{project.name} | #{commit.title} (#{commit.short_id})"
@@ -845,6 +942,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject' do
is_expected.to have_referable_subject(merge_request, reply: true)
@@ -871,6 +970,8 @@ describe Notify do
end
it_behaves_like 'it should show Gmail Actions View Issue link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject' do
is_expected.to have_referable_subject(issue, reply: true)
@@ -948,6 +1049,8 @@ describe Notify do
it_behaves_like 'an email for a note on a diff discussion', :diff_note_on_commit
it_behaves_like 'it should show Gmail Actions View Commit link'
it_behaves_like 'a user cannot unsubscribe through footer link'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
end
describe 'on a merge request' do
@@ -958,6 +1061,8 @@ describe Notify do
it_behaves_like 'an email for a note on a diff discussion', :diff_note_on_merge_request
it_behaves_like 'it should show Gmail Actions View Merge request link'
it_behaves_like 'an unsubscribeable thread'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
end
end
end
@@ -976,6 +1081,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
to_emails = subject.header[:to].addrs.map(&:address)
@@ -998,6 +1105,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject "Access to the #{group.name} group was denied"
@@ -1014,6 +1123,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject "Access to the #{group.name} group was granted"
@@ -1044,6 +1155,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject "Invitation to join the #{group.name} group"
@@ -1068,6 +1181,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject 'Invitation accepted'
@@ -1091,6 +1206,8 @@ describe Notify do
it_behaves_like 'an email sent from GitLab'
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'contains all the useful information' do
is_expected.to have_subject 'Invitation declined'
@@ -1140,6 +1257,8 @@ describe Notify do
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with X-GitLab headers containing project details'
it_behaves_like 'an email that contains a header with author username'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -1165,6 +1284,8 @@ describe Notify do
it_behaves_like "a user cannot unsubscribe through footer link"
it_behaves_like 'an email with X-GitLab headers containing project details'
it_behaves_like 'an email that contains a header with author username'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -1189,6 +1310,8 @@ describe Notify do
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with X-GitLab headers containing project details'
it_behaves_like 'an email that contains a header with author username'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -1210,6 +1333,8 @@ describe Notify do
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with X-GitLab headers containing project details'
it_behaves_like 'an email that contains a header with author username'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -1237,6 +1362,8 @@ describe Notify do
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with X-GitLab headers containing project details'
it_behaves_like 'an email that contains a header with author username'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -1328,6 +1455,8 @@ describe Notify do
it_behaves_like 'a user cannot unsubscribe through footer link'
it_behaves_like 'an email with X-GitLab headers containing project details'
it_behaves_like 'an email that contains a header with author username'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'is sent as the author' do
sender = subject.header[:from].addrs[0]
@@ -1348,6 +1477,11 @@ describe Notify do
describe 'HTML emails setting' do
let(:multipart_mail) { described_class.project_was_moved_email(project.id, user.id, "gitlab/gitlab") }
+ subject { multipart_mail }
+
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+
context 'when disabled' do
it 'only sends the text template' do
stub_application_setting(html_emails_enabled: false)
@@ -1386,6 +1520,8 @@ describe Notify do
subject { described_class.note_personal_snippet_email(personal_snippet_note.author_id, personal_snippet_note.id) }
it_behaves_like 'a user cannot unsubscribe through footer link'
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
it 'has the correct subject and body' do
is_expected.to have_referable_subject(personal_snippet, reply: true)
diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb
index 00613c7b671..384660f7221 100644
--- a/spec/mailers/repository_check_mailer_spec.rb
+++ b/spec/mailers/repository_check_mailer_spec.rb
@@ -17,5 +17,12 @@ describe RepositoryCheckMailer do
expect(mail).to have_subject 'GitLab Admin | 3 projects failed their last repository check'
end
+
+ context 'with footer and header' do
+ subject { described_class.notify(1) }
+
+ it_behaves_like 'appearance header and footer enabled'
+ it_behaves_like 'appearance header and footer not enabled'
+ end
end
end
diff --git a/spec/migrations/add_foreign_keys_to_todos_spec.rb b/spec/migrations/add_foreign_keys_to_todos_spec.rb
index efd87173b9c..2500e2f8333 100644
--- a/spec/migrations/add_foreign_keys_to_todos_spec.rb
+++ b/spec/migrations/add_foreign_keys_to_todos_spec.rb
@@ -36,7 +36,7 @@ describe AddForeignKeysToTodos, :migration do
end
context 'add foreign key on note_id' do
- let(:note) { create(:note) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ let(:note) { table(:notes).create! }
let!(:todo_with_note) { create_todo(note_id: note.id) }
let!(:todo_with_invalid_note) { create_todo(note_id: 4711) }
let!(:todo_without_note) { create_todo(note_id: nil) }
diff --git a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
index d8dd7a2fb83..13dc62595b5 100644
--- a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
+++ b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
@@ -1,23 +1,21 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170508170547_add_head_pipeline_for_each_merge_request.rb')
-describe AddHeadPipelineForEachMergeRequest, :delete do
- include ProjectForksHelper
-
+describe AddHeadPipelineForEachMergeRequest, :migration do
let(:migration) { described_class.new }
- let!(:project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:other_project) { fork_project(project) }
+ let!(:project) { table(:projects).create! }
+ let!(:other_project) { table(:projects).create! }
- let!(:pipeline_1) { create(:ci_pipeline, project: project, ref: "branch_1") } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:pipeline_2) { create(:ci_pipeline, project: other_project, ref: "branch_1") } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:pipeline_3) { create(:ci_pipeline, project: other_project, ref: "branch_1") } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:pipeline_4) { create(:ci_pipeline, project: project, ref: "branch_2") } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ let!(:pipeline_1) { table(:ci_pipelines).create!(project_id: project.id, ref: "branch_1") }
+ let!(:pipeline_2) { table(:ci_pipelines).create!(project_id: other_project.id, ref: "branch_1") }
+ let!(:pipeline_3) { table(:ci_pipelines).create!(project_id: other_project.id, ref: "branch_1") }
+ let!(:pipeline_4) { table(:ci_pipelines).create!(project_id: project.id, ref: "branch_2") }
- let!(:mr_1) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_1", target_branch: "target_1") } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:mr_2) { create(:merge_request, source_project: other_project, target_project: project, source_branch: "branch_1", target_branch: "target_2") } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:mr_3) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_2", target_branch: "master") } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:mr_4) { create(:merge_request, source_project: project, target_project: project, source_branch: "branch_3", target_branch: "master") } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ let!(:mr_1) { table(:merge_requests).create!(source_project_id: project.id, target_project_id: project.id, source_branch: "branch_1", target_branch: "target_1") }
+ let!(:mr_2) { table(:merge_requests).create!(source_project_id: other_project.id, target_project_id: project.id, source_branch: "branch_1", target_branch: "target_2") }
+ let!(:mr_3) { table(:merge_requests).create!(source_project_id: project.id, target_project_id: project.id, source_branch: "branch_2", target_branch: "master") }
+ let!(:mr_4) { table(:merge_requests).create!(source_project_id: project.id, target_project_id: project.id, source_branch: "branch_3", target_branch: "master") }
context "#up" do
context "when source_project and source_branch of pipeline are the same of merge request" do
diff --git a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
index 19f06810e54..09c78d02890 100644
--- a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
+++ b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
@@ -3,12 +3,30 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170803090603_calculate_conv_dev_index_percentages.rb')
-describe CalculateConvDevIndexPercentages, :delete do
+describe CalculateConvDevIndexPercentages, :migration do
let(:migration) { described_class.new }
let!(:conv_dev_index) do
- create(:conversational_development_index_metric, # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ table(:conversational_development_index_metrics).create!(
+ leader_issues: 9.256,
leader_notes: 0,
+ leader_milestones: 16.2456,
+ leader_boards: 5.2123,
+ leader_merge_requests: 1.2,
+ leader_ci_pipelines: 12.1234,
+ leader_environments: 3.3333,
+ leader_deployments: 1.200,
+ leader_projects_prometheus_active: 0.111,
+ leader_service_desk_issues: 15.891,
+ instance_issues: 1.234,
+ instance_notes: 28.123,
instance_milestones: 0,
+ instance_boards: 3.254,
+ instance_merge_requests: 0.6,
+ instance_ci_pipelines: 2.344,
+ instance_environments: 2.2222,
+ instance_deployments: 0.771,
+ instance_projects_prometheus_active: 0.109,
+ instance_service_desk_issues: 13.345,
percentage_issues: 0,
percentage_notes: 0,
percentage_milestones: 0,
diff --git a/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb b/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb
index 8f40ac3e38b..0e6bded29b4 100644
--- a/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb
+++ b/spec/migrations/cleanup_nonexisting_namespace_pending_delete_projects_spec.rb
@@ -1,20 +1,17 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170816102555_cleanup_nonexisting_namespace_pending_delete_projects.rb')
-describe CleanupNonexistingNamespacePendingDeleteProjects do
- before do
- # Stub after_save callbacks that will fail when Project has invalid namespace
- allow_any_instance_of(Project).to receive(:ensure_storage_path_exist).and_return(nil)
- allow_any_instance_of(Project).to receive(:update_project_statistics).and_return(nil)
- end
+describe CleanupNonexistingNamespacePendingDeleteProjects, :migration do
+ let(:projects) { table(:projects) }
+ let(:namespaces) { table(:namespaces) }
describe '#up' do
- set(:some_project) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ let!(:some_project) { projects.create! }
+ let(:namespace) { namespaces.create!(name: 'test', path: 'test') }
it 'only cleans up when namespace does not exist' do
- create(:project, pending_delete: true) # rubocop:disable RSpec/FactoriesInMigrationSpecs
- project = build(:project, pending_delete: true, namespace: nil, namespace_id: Namespace.maximum(:id).to_i.succ) # rubocop:disable RSpec/FactoriesInMigrationSpecs
- project.save(validate: false)
+ projects.create!(pending_delete: true, namespace_id: namespace.id)
+ project = projects.create!(pending_delete: true, namespace_id: 0)
expect(NamespacelessProjectDestroyWorker).to receive(:bulk_perform_async).with([[project.id]])
@@ -22,7 +19,7 @@ describe CleanupNonexistingNamespacePendingDeleteProjects do
end
it 'does nothing when no pending delete projects without namespace found' do
- create(:project, pending_delete: true, namespace: create(:namespace)) # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ projects.create!(pending_delete: true, namespace_id: namespace.id)
expect(NamespacelessProjectDestroyWorker).not_to receive(:bulk_perform_async)
diff --git a/spec/migrations/delete_inconsistent_internal_id_records_spec.rb b/spec/migrations/delete_inconsistent_internal_id_records_spec.rb
index e2ce69a7bb1..58b8b4a16f0 100644
--- a/spec/migrations/delete_inconsistent_internal_id_records_spec.rb
+++ b/spec/migrations/delete_inconsistent_internal_id_records_spec.rb
@@ -1,25 +1,36 @@
# frozen_string_literal: true
-# rubocop:disable RSpec/FactoriesInMigrationSpecs
+
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20180723130817_delete_inconsistent_internal_id_records.rb')
describe DeleteInconsistentInternalIdRecords, :migration do
- let!(:project1) { create(:project) }
- let!(:project2) { create(:project) }
- let!(:project3) { create(:project) }
+ let!(:namespace) { table(:namespaces).create!(name: 'test', path: 'test') }
+ let!(:project1) { table(:projects).create!(namespace_id: namespace.id) }
+ let!(:project2) { table(:projects).create!(namespace_id: namespace.id) }
+ let!(:project3) { table(:projects).create!(namespace_id: namespace.id) }
- let(:internal_id_query) { ->(project) { InternalId.where(usage: InternalId.usages[scope.to_s.tableize], project: project) } }
+ let(:internal_ids) { table(:internal_ids) }
+ let(:internal_id_query) { ->(project) { InternalId.where(usage: InternalId.usages[scope.to_s.tableize], project_id: project.id) } }
let(:create_models) do
- 3.times { create(scope, project: project1) }
- 3.times { create(scope, project: project2) }
- 3.times { create(scope, project: project3) }
+ [project1, project2, project3].each do |project|
+ 3.times do |i|
+ attributes = required_attributes.merge(project_id: project.id,
+ iid: i.succ)
+
+ table(scope.to_s.pluralize).create!(attributes)
+ end
+ end
end
shared_examples_for 'deleting inconsistent internal_id records' do
before do
create_models
+ [project1, project2, project3].each do |project|
+ internal_ids.create!(project_id: project.id, usage: InternalId.usages[scope.to_s.tableize], last_value: 3)
+ end
+
internal_id_query.call(project1).first.tap do |iid|
iid.last_value = iid.last_value - 2
# This is an inconsistent record
@@ -33,11 +44,11 @@ describe DeleteInconsistentInternalIdRecords, :migration do
end
end
- it "deletes inconsistent issues" do
+ it "deletes inconsistent records" do
expect { migrate! }.to change { internal_id_query.call(project1).size }.from(1).to(0)
end
- it "retains consistent issues" do
+ it "retains consistent records" do
expect { migrate! }.not_to change { internal_id_query.call(project2).size }
end
@@ -48,6 +59,8 @@ describe DeleteInconsistentInternalIdRecords, :migration do
context 'for issues' do
let(:scope) { :issue }
+ let(:required_attributes) { {} }
+
it_behaves_like 'deleting inconsistent internal_id records'
end
@@ -55,9 +68,17 @@ describe DeleteInconsistentInternalIdRecords, :migration do
let(:scope) { :merge_request }
let(:create_models) do
- 3.times { |i| create(scope, target_project: project1, source_project: project1, source_branch: i.to_s) }
- 3.times { |i| create(scope, target_project: project2, source_project: project2, source_branch: i.to_s) }
- 3.times { |i| create(scope, target_project: project3, source_project: project3, source_branch: i.to_s) }
+ [project1, project2, project3].each do |project|
+ 3.times do |i|
+ table(:merge_requests).create!(
+ target_project_id: project.id,
+ source_project_id: project.id,
+ target_branch: 'master',
+ source_branch: j.to_s,
+ iid: i.succ
+ )
+ end
+ end
end
it_behaves_like 'deleting inconsistent internal_id records'
@@ -66,13 +87,6 @@ describe DeleteInconsistentInternalIdRecords, :migration do
context 'for deployments' do
let(:scope) { :deployment }
let(:deployments) { table(:deployments) }
- let(:internal_ids) { table(:internal_ids) }
-
- before do
- internal_ids.create!(project_id: project1.id, usage: 2, last_value: 2)
- internal_ids.create!(project_id: project2.id, usage: 2, last_value: 2)
- internal_ids.create!(project_id: project3.id, usage: 2, last_value: 2)
- end
let(:create_models) do
3.times { |i| deployments.create!(project_id: project1.id, iid: i, environment_id: 1, ref: 'master', sha: 'a', tag: false) }
@@ -85,17 +99,14 @@ describe DeleteInconsistentInternalIdRecords, :migration do
context 'for milestones (by project)' do
let(:scope) { :milestone }
+ let(:required_attributes) { { title: 'test' } }
+
it_behaves_like 'deleting inconsistent internal_id records'
end
context 'for ci_pipelines' do
let(:scope) { :ci_pipeline }
-
- let(:create_models) do
- create_list(:ci_empty_pipeline, 3, project: project1)
- create_list(:ci_empty_pipeline, 3, project: project2)
- create_list(:ci_empty_pipeline, 3, project: project3)
- end
+ let(:required_attributes) { { ref: 'test' } }
it_behaves_like 'deleting inconsistent internal_id records'
end
@@ -107,12 +118,20 @@ describe DeleteInconsistentInternalIdRecords, :migration do
let(:group2) { groups.create(name: 'Group 2', type: 'Group', path: 'group_2') }
let(:group3) { groups.create(name: 'Group 2', type: 'Group', path: 'group_3') }
- let(:internal_id_query) { ->(group) { InternalId.where(usage: InternalId.usages['milestones'], namespace: group) } }
+ let(:internal_id_query) { ->(group) { InternalId.where(usage: InternalId.usages['milestones'], namespace_id: group.id) } }
before do
- 3.times { create(:milestone, group_id: group1.id) }
- 3.times { create(:milestone, group_id: group2.id) }
- 3.times { create(:milestone, group_id: group3.id) }
+ [group1, group2, group3].each do |group|
+ 3.times do |i|
+ table(:milestones).create!(
+ group_id: group.id,
+ title: 'test',
+ iid: i.succ
+ )
+ end
+
+ internal_ids.create!(namespace_id: group.id, usage: InternalId.usages['milestones'], last_value: 3)
+ end
internal_id_query.call(group1).first.tap do |iid|
iid.last_value = iid.last_value - 2
@@ -127,11 +146,11 @@ describe DeleteInconsistentInternalIdRecords, :migration do
end
end
- it "deletes inconsistent issues" do
+ it "deletes inconsistent records" do
expect { migrate! }.to change { internal_id_query.call(group1).size }.from(1).to(0)
end
- it "retains consistent issues" do
+ it "retains consistent records" do
expect { migrate! }.not_to change { internal_id_query.call(group2).size }
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 495e86ee888..71a4e71ac8a 100644
--- a/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
+++ b/spec/migrations/issues_moved_to_id_foreign_key_spec.rb
@@ -1,20 +1,19 @@
require 'spec_helper'
require Rails.root.join('db', 'migrate', '20171106151218_issues_moved_to_id_foreign_key.rb')
-# The schema version has to be far enough in advance to have the
-# only_mirror_protected_branches column in the projects table to create a
-# project via FactoryBot.
-describe IssuesMovedToIdForeignKey, :migration, schema: 20171114150259 do
- let!(:issue_first) { create(:issue, moved_to_id: issue_second.id) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:issue_second) { create(:issue, moved_to_id: issue_third.id) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:issue_third) { create(:issue) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+describe IssuesMovedToIdForeignKey, :migration do
+ let(:issues) { table(:issues) }
+
+ let!(:issue_third) { issues.create! }
+ let!(:issue_second) { issues.create!(moved_to_id: issue_third.id) }
+ let!(:issue_first) { issues.create!(moved_to_id: issue_second.id) }
subject { described_class.new }
it 'removes the orphaned moved_to_id' do
subject.down
- issue_third.update(moved_to_id: 100000)
+ issue_third.update!(moved_to_id: 0)
subject.up
diff --git a/spec/migrations/migrate_old_artifacts_spec.rb b/spec/migrations/migrate_old_artifacts_spec.rb
index af77d64fdbf..79e21514506 100644
--- a/spec/migrations/migrate_old_artifacts_spec.rb
+++ b/spec/migrations/migrate_old_artifacts_spec.rb
@@ -3,7 +3,9 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170523083112_migrate_old_artifacts.rb')
-describe MigrateOldArtifacts do
+# Adding the ci_job_artifacts table (from the 20170918072948 schema)
+# makes the use of model code below easier.
+describe MigrateOldArtifacts, :migration, schema: 20170918072948 do
let(:migration) { described_class.new }
let!(:directory) { Dir.mktmpdir }
@@ -16,18 +18,22 @@ describe MigrateOldArtifacts do
end
context 'with migratable data' do
- set(:project1) { create(:project, ci_id: 2) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- set(:project2) { create(:project, ci_id: 3) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- set(:project3) { create(:project) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
-
- set(:pipeline1) { create(:ci_empty_pipeline, project: project1) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- set(:pipeline2) { create(:ci_empty_pipeline, project: project2) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- set(:pipeline3) { create(:ci_empty_pipeline, project: project3) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
-
- let!(:build_with_legacy_artifacts) { create(:ci_build, pipeline: pipeline1) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:build_without_artifacts) { create(:ci_build, pipeline: pipeline1) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:build2) { create(:ci_build, pipeline: pipeline2) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:build3) { create(:ci_build, pipeline: pipeline3) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ let(:projects) { table(:projects) }
+ let(:ci_pipelines) { table(:ci_pipelines) }
+ let(:ci_builds) { table(:ci_builds) }
+
+ let!(:project1) { projects.create!(ci_id: 2) }
+ let!(:project2) { projects.create!(ci_id: 3) }
+ let!(:project3) { projects.create! }
+
+ let!(:pipeline1) { ci_pipelines.create!(project_id: project1.id) }
+ let!(:pipeline2) { ci_pipelines.create!(project_id: project2.id) }
+ let!(:pipeline3) { ci_pipelines.create!(project_id: project3.id) }
+
+ let!(:build_with_legacy_artifacts) { ci_builds.create!(commit_id: pipeline1.id, project_id: project1.id, type: 'Ci::Build').becomes(Ci::Build) }
+ let!(:build_without_artifacts) { ci_builds.create!(commit_id: pipeline1.id, project_id: project1.id, type: 'Ci::Build').becomes(Ci::Build) }
+ let!(:build2) { ci_builds.create!(commit_id: pipeline2.id, project_id: project2.id, type: 'Ci::Build').becomes(Ci::Build) }
+ let!(:build3) { ci_builds.create!(commit_id: pipeline3.id, project_id: project3.id, type: 'Ci::Build').becomes(Ci::Build) }
before do
setup_builds(build2, build3)
diff --git a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
index 99173708190..88aef3b70b4 100644
--- a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
+++ b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
@@ -3,10 +3,10 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170324160416_migrate_user_activities_to_users_last_activity_on.rb')
-describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :delete do
+describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :migration do
let(:migration) { described_class.new }
- let!(:user_active_1) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
- let!(:user_active_2) { create(:user) } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ let!(:user_active_1) { table(:users).create!(email: 'test1', username: 'test1') }
+ let!(:user_active_2) { table(:users).create!(email: 'test2', username: 'test2') }
def record_activity(user, time)
Gitlab::Redis::SharedState.with do |redis|
diff --git a/spec/migrations/migrate_user_project_view_spec.rb b/spec/migrations/migrate_user_project_view_spec.rb
index 80468b9d01e..a0179ab3ceb 100644
--- a/spec/migrations/migrate_user_project_view_spec.rb
+++ b/spec/migrations/migrate_user_project_view_spec.rb
@@ -3,15 +3,15 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170406142253_migrate_user_project_view.rb')
-describe MigrateUserProjectView, :delete do
+describe MigrateUserProjectView, :migration do
let(:migration) { described_class.new }
- let!(:user) { create(:user, project_view: 'readme') } # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ let!(:user) { table(:users).create!(project_view: User.project_views['readme']) }
describe '#up' do
it 'updates project view setting with new value' do
migration.up
- expect(user.reload.project_view).to eq('files')
+ expect(user.reload.project_view).to eq(User.project_views['files'])
end
end
end
diff --git a/spec/migrations/move_personal_snippets_files_spec.rb b/spec/migrations/move_personal_snippets_files_spec.rb
index 1f39ad98fb8..d94ae1e52f5 100644
--- a/spec/migrations/move_personal_snippets_files_spec.rb
+++ b/spec/migrations/move_personal_snippets_files_spec.rb
@@ -1,12 +1,19 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170612071012_move_personal_snippets_files.rb')
-describe MovePersonalSnippetsFiles do
+describe MovePersonalSnippetsFiles, :migration do
let(:migration) { described_class.new }
let(:test_dir) { File.join(Rails.root, "tmp", "tests", "move_snippet_files_test") }
let(:uploads_dir) { File.join(test_dir, 'uploads') }
let(:new_uploads_dir) { File.join(uploads_dir, '-', 'system') }
+ let(:notes) { table(:notes) }
+ let(:snippets) { table(:snippets) }
+ let(:uploads) { table(:uploads) }
+
+ let(:user) { table(:users).create!(email: 'user@example.com', projects_limit: 10) }
+ let(:project) { table(:projects).create!(name: 'gitlab', namespace_id: 1) }
+
before do
allow(CarrierWave).to receive(:root).and_return(test_dir)
allow(migration).to receive(:base_directory).and_return(test_dir)
@@ -16,14 +23,14 @@ describe MovePersonalSnippetsFiles do
describe "#up" do
let(:snippet) do
- snippet = create(:personal_snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ snippet = snippets.create!(author_id: user.id)
create_upload('picture.jpg', snippet)
snippet.update(description: markdown_linking_file('picture.jpg', snippet))
snippet
end
let(:snippet_with_missing_file) do
- snippet = create(:snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ snippet = snippets.create!(author_id: user.id, project_id: project.id)
create_upload('picture.jpg', snippet, create_file: false)
snippet.update(description: markdown_linking_file('picture.jpg', snippet))
snippet
@@ -62,7 +69,10 @@ describe MovePersonalSnippetsFiles do
secret = "secret#{snippet.id}"
file_location = "/uploads/-/system/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
markdown = markdown_linking_file('picture.jpg', snippet)
- note = create(:note_on_personal_snippet, noteable: snippet, note: "with #{markdown}") # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ note = notes.create!(noteable_id: snippet.id,
+ noteable_type: Snippet,
+ note: "with #{markdown}",
+ author_id: user.id)
migration.up
@@ -73,14 +83,14 @@ describe MovePersonalSnippetsFiles do
describe "#down" do
let(:snippet) do
- snippet = create(:personal_snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ snippet = snippets.create!(author_id: user.id)
create_upload('picture.jpg', snippet, in_new_path: true)
snippet.update(description: markdown_linking_file('picture.jpg', snippet, in_new_path: true))
snippet
end
let(:snippet_with_missing_file) do
- snippet = create(:personal_snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ snippet = snippets.create!(author_id: user.id)
create_upload('picture.jpg', snippet, create_file: false, in_new_path: true)
snippet.update(description: markdown_linking_file('picture.jpg', snippet, in_new_path: true))
snippet
@@ -119,7 +129,10 @@ describe MovePersonalSnippetsFiles do
markdown = markdown_linking_file('picture.jpg', snippet, in_new_path: true)
secret = "secret#{snippet.id}"
file_location = "/uploads/personal_snippet/#{snippet.id}/#{secret}/picture.jpg"
- note = create(:note_on_personal_snippet, noteable: snippet, note: "with #{markdown}") # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ note = notes.create!(noteable_id: snippet.id,
+ noteable_type: Snippet,
+ note: "with #{markdown}",
+ author_id: user.id)
migration.down
@@ -135,7 +148,7 @@ describe MovePersonalSnippetsFiles do
secret = '123456789'
filename = 'hello.jpg'
- snippet = create(:personal_snippet) # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ snippet = snippets.create!(author_id: user.id)
path_before = "/uploads/personal_snippet/#{snippet.id}/#{secret}/#{filename}"
path_after = "/uploads/system/personal_snippet/#{snippet.id}/#{secret}/#{filename}"
@@ -161,7 +174,11 @@ describe MovePersonalSnippetsFiles do
FileUtils.touch(absolute_path)
end
- create(:upload, model: snippet, path: "#{secret}/#{filename}", uploader: PersonalFileUploader) # rubocop:disable RSpec/FactoriesInMigrationSpecs
+ uploads.create!(model_id: snippet.id,
+ model_type: snippet.class,
+ path: "#{secret}/#{filename}",
+ uploader: PersonalFileUploader,
+ size: 100.kilobytes)
end
def markdown_linking_file(filename, snippet, in_new_path: false)
diff --git a/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb b/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb
new file mode 100644
index 00000000000..e397fbb7138
--- /dev/null
+++ b/spec/migrations/schedule_populate_merge_request_assignees_table_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20190322132835_schedule_populate_merge_request_assignees_table.rb')
+
+describe SchedulePopulateMergeRequestAssigneesTable, :migration, :sidekiq do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:namespace) { namespaces.create(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create(namespace_id: namespace.id, name: 'foo') }
+ let(:merge_requests) { table(:merge_requests) }
+
+ def create_merge_request(id)
+ params = {
+ id: id,
+ target_project_id: project.id,
+ target_branch: 'master',
+ source_project_id: project.id,
+ source_branch: 'mr name',
+ title: "mr name#{id}"
+ }
+
+ merge_requests.create!(params)
+ end
+
+ it 'correctly schedules background migrations' do
+ create_merge_request(1)
+ create_merge_request(2)
+ create_merge_request(3)
+
+ stub_const("#{described_class.name}::BATCH_SIZE", 2)
+
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ migrate!
+
+ expect(described_class::MIGRATION)
+ .to be_scheduled_delayed_migration(8.minutes, 1, 2)
+
+ expect(described_class::MIGRATION)
+ .to be_scheduled_delayed_migration(16.minutes, 3, 3)
+
+ expect(BackgroundMigrationWorker.jobs.size).to eq(2)
+ end
+ end
+ end
+end
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb
index 129b2f92683..e128fe8a4b7 100644
--- a/spec/models/active_session_spec.rb
+++ b/spec/models/active_session_spec.rb
@@ -7,7 +7,10 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
end
end
- let(:session) { double(:session, id: '6919a6f1bb119dd7396fadc38fd18d0d') }
+ let(:session) do
+ double(:session, { id: '6919a6f1bb119dd7396fadc38fd18d0d',
+ '[]': {} })
+ end
let(:request) do
double(:request, {
diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb
index cc76a2019ec..3e95aa2b5dd 100644
--- a/spec/models/appearance_spec.rb
+++ b/spec/models/appearance_spec.rb
@@ -63,4 +63,37 @@ describe Appearance do
%i(logo header_logo favicon).each do |logo_type|
it_behaves_like 'logo paths', logo_type
end
+
+ describe 'validations' do
+ let(:triplet) { '#000' }
+ let(:hex) { '#AABBCC' }
+
+ it { is_expected.to allow_value(nil).for(:message_background_color) }
+ it { is_expected.to allow_value(triplet).for(:message_background_color) }
+ it { is_expected.to allow_value(hex).for(:message_background_color) }
+ it { is_expected.not_to allow_value('000').for(:message_background_color) }
+
+ it { is_expected.to allow_value(nil).for(:message_font_color) }
+ it { is_expected.to allow_value(triplet).for(:message_font_color) }
+ it { is_expected.to allow_value(hex).for(:message_font_color) }
+ it { is_expected.not_to allow_value('000').for(:message_font_color) }
+ end
+
+ describe 'email_header_and_footer_enabled' do
+ context 'default email_header_and_footer_enabled flag value' do
+ it 'returns email_header_and_footer_enabled as true' do
+ appearance = build(:appearance)
+
+ expect(appearance.email_header_and_footer_enabled?).to eq(false)
+ end
+ end
+
+ context 'when setting email_header_and_footer_enabled flag value' do
+ it 'returns email_header_and_footer_enabled as true' do
+ appearance = build(:appearance, email_header_and_footer_enabled: true)
+
+ expect(appearance.email_header_and_footer_enabled?).to eq(true)
+ end
+ end
+ end
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 789e14e8a20..314f0728b8e 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -117,14 +117,6 @@ describe ApplicationSetting do
it { expect(setting.repository_storages).to eq(['default']) }
end
- context '#commit_email_hostname' do
- it 'returns configured gitlab hostname if commit_email_hostname is not defined' do
- setting.update(commit_email_hostname: nil)
-
- expect(setting.commit_email_hostname).to eq("users.noreply.#{Gitlab.config.gitlab.host}")
- end
- end
-
context 'auto_devops_domain setting' do
context 'when auto_devops_enabled? is true' do
before do
@@ -182,15 +174,6 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value("").for(:repository_storages) }
it { is_expected.not_to allow_value(nil).for(:repository_storages) }
end
-
- describe '.pick_repository_storage' do
- it 'uses Array#sample to pick a random storage' do
- array = double('array', sample: 'random')
- expect(setting).to receive(:repository_storages).and_return(array)
-
- expect(setting.pick_repository_storage).to eq('random')
- end
- end
end
context 'housekeeping settings' do
@@ -367,65 +350,6 @@ describe ApplicationSetting do
end
end
- context 'restricted signup domains' do
- it 'sets single domain' do
- setting.domain_whitelist_raw = 'example.com'
- expect(setting.domain_whitelist).to eq(['example.com'])
- end
-
- it 'sets multiple domains with spaces' do
- setting.domain_whitelist_raw = 'example.com *.example.com'
- expect(setting.domain_whitelist).to eq(['example.com', '*.example.com'])
- end
-
- it 'sets multiple domains with newlines and a space' do
- setting.domain_whitelist_raw = "example.com\n *.example.com"
- expect(setting.domain_whitelist).to eq(['example.com', '*.example.com'])
- end
-
- it 'sets multiple domains with commas' do
- setting.domain_whitelist_raw = "example.com, *.example.com"
- expect(setting.domain_whitelist).to eq(['example.com', '*.example.com'])
- end
- end
-
- context 'blacklisted signup domains' do
- it 'sets single domain' do
- setting.domain_blacklist_raw = 'example.com'
- expect(setting.domain_blacklist).to contain_exactly('example.com')
- end
-
- it 'sets multiple domains with spaces' do
- setting.domain_blacklist_raw = 'example.com *.example.com'
- expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com')
- end
-
- it 'sets multiple domains with newlines and a space' do
- setting.domain_blacklist_raw = "example.com\n *.example.com"
- expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com')
- end
-
- it 'sets multiple domains with commas' do
- setting.domain_blacklist_raw = "example.com, *.example.com"
- expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com')
- end
-
- it 'sets multiple domains with semicolon' do
- setting.domain_blacklist_raw = "example.com; *.example.com"
- expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com')
- end
-
- it 'sets multiple domains with mixture of everything' do
- setting.domain_blacklist_raw = "example.com; *.example.com\n test.com\sblock.com yes.com"
- expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com', 'test.com', 'block.com', 'yes.com')
- end
-
- it 'sets multiple domain with file' do
- setting.domain_blacklist_file = File.open(Rails.root.join('spec/fixtures/', 'domain_blacklist.txt'))
- expect(setting.domain_blacklist).to contain_exactly('example.com', 'test.com', 'foo.bar')
- end
- end
-
describe 'performance bar settings' do
describe 'performance_bar_allowed_group' do
context 'with no performance_bar_allowed_group_id saved' do
@@ -462,142 +386,6 @@ describe ApplicationSetting do
end
end
- describe 'usage ping settings' do
- context 'when the usage ping is disabled in gitlab.yml' do
- before do
- allow(Settings.gitlab).to receive(:usage_ping_enabled).and_return(false)
- end
-
- it 'does not allow the usage ping to be configured' do
- expect(setting.usage_ping_can_be_configured?).to be_falsey
- end
-
- context 'when the usage ping is disabled in the DB' do
- before do
- setting.usage_ping_enabled = false
- end
-
- it 'returns false for usage_ping_enabled' do
- expect(setting.usage_ping_enabled).to be_falsey
- end
- end
-
- context 'when the usage ping is enabled in the DB' do
- before do
- setting.usage_ping_enabled = true
- end
-
- it 'returns false for usage_ping_enabled' do
- expect(setting.usage_ping_enabled).to be_falsey
- end
- end
- end
-
- context 'when the usage ping is enabled in gitlab.yml' do
- before do
- allow(Settings.gitlab).to receive(:usage_ping_enabled).and_return(true)
- end
-
- it 'allows the usage ping to be configured' do
- expect(setting.usage_ping_can_be_configured?).to be_truthy
- end
-
- context 'when the usage ping is disabled in the DB' do
- before do
- setting.usage_ping_enabled = false
- end
-
- it 'returns false for usage_ping_enabled' do
- expect(setting.usage_ping_enabled).to be_falsey
- end
- end
-
- context 'when the usage ping is enabled in the DB' do
- before do
- setting.usage_ping_enabled = true
- end
-
- it 'returns true for usage_ping_enabled' do
- expect(setting.usage_ping_enabled).to be_truthy
- end
- end
- end
- end
-
- describe '#allowed_key_types' do
- it 'includes all key types by default' do
- expect(setting.allowed_key_types).to contain_exactly(*described_class::SUPPORTED_KEY_TYPES)
- end
-
- it 'excludes disabled key types' do
- expect(setting.allowed_key_types).to include(:ed25519)
-
- setting.ed25519_key_restriction = described_class::FORBIDDEN_KEY_VALUE
-
- expect(setting.allowed_key_types).not_to include(:ed25519)
- end
- end
-
- describe '#key_restriction_for' do
- it 'returns the restriction value for recognised types' do
- setting.rsa_key_restriction = 1024
-
- expect(setting.key_restriction_for(:rsa)).to eq(1024)
- end
-
- it 'allows types to be passed as a string' do
- setting.rsa_key_restriction = 1024
-
- expect(setting.key_restriction_for('rsa')).to eq(1024)
- end
-
- it 'returns forbidden for unrecognised type' do
- expect(setting.key_restriction_for(:foo)).to eq(described_class::FORBIDDEN_KEY_VALUE)
- end
- end
-
- describe '#allow_signup?' do
- it 'returns true' do
- expect(setting.allow_signup?).to be_truthy
- end
-
- it 'returns false if signup is disabled' do
- allow(setting).to receive(:signup_enabled?).and_return(false)
-
- expect(setting.allow_signup?).to be_falsey
- end
-
- it 'returns false if password authentication is disabled for the web interface' do
- allow(setting).to receive(:password_authentication_enabled_for_web?).and_return(false)
-
- expect(setting.allow_signup?).to be_falsey
- end
- end
-
- describe '#user_default_internal_regex_enabled?' do
- using RSpec::Parameterized::TableSyntax
-
- where(:user_default_external, :user_default_internal_regex, :result) do
- false | nil | false
- false | '' | false
- false | '^(?:(?!\.ext@).)*$\r?\n?' | false
- true | '' | false
- true | nil | false
- true | '^(?:(?!\.ext@).)*$\r?\n?' | true
- end
-
- with_them do
- before do
- setting.update(user_default_external: user_default_external)
- setting.update(user_default_internal_regex: user_default_internal_regex)
- end
-
- subject { setting.user_default_internal_regex_enabled? }
-
- it { is_expected.to eq(result) }
- end
- end
-
context 'diff limit settings' do
describe '#diff_max_patch_bytes' do
context 'validations' do
@@ -613,23 +401,5 @@ describe ApplicationSetting do
end
end
- describe '#archive_builds_older_than' do
- subject { setting.archive_builds_older_than }
-
- context 'when the archive_builds_in_seconds is set' do
- before do
- setting.archive_builds_in_seconds = 3600
- end
-
- it { is_expected.to be_within(1.minute).of(1.hour.ago) }
- end
-
- context 'when the archive_builds_in_seconds is set' do
- before do
- setting.archive_builds_in_seconds = nil
- end
-
- it { is_expected.to be_nil }
- end
- end
+ it_behaves_like 'application settings examples'
end
diff --git a/spec/models/board_group_recent_visit_spec.rb b/spec/models/board_group_recent_visit_spec.rb
index 59ad4e5417e..558be61824f 100644
--- a/spec/models/board_group_recent_visit_spec.rb
+++ b/spec/models/board_group_recent_visit_spec.rb
@@ -50,15 +50,25 @@ describe BoardGroupRecentVisit do
end
describe '#latest' do
- it 'returns the most recent visited' do
- board2 = create(:board, group: group)
- board3 = create(:board, group: group)
+ def create_visit(time)
+ create :board_group_recent_visit, group: group, user: user, updated_at: time
+ end
- create :board_group_recent_visit, group: board.group, board: board, user: user, updated_at: 7.days.ago
- create :board_group_recent_visit, group: board2.group, board: board2, user: user, updated_at: 5.days.ago
- recent = create :board_group_recent_visit, group: board3.group, board: board3, user: user, updated_at: 1.day.ago
+ it 'returns the most recent visited' do
+ create_visit(7.days.ago)
+ create_visit(5.days.ago)
+ recent = create_visit(1.day.ago)
expect(described_class.latest(user, group)).to eq recent
end
+
+ it 'returns last 3 visited boards' do
+ create_visit(7.days.ago)
+ visit1 = create_visit(3.days.ago)
+ visit2 = create_visit(2.days.ago)
+ visit3 = create_visit(5.days.ago)
+
+ expect(described_class.latest(user, group, count: 3)).to eq([visit2, visit1, visit3])
+ end
end
end
diff --git a/spec/models/board_project_recent_visit_spec.rb b/spec/models/board_project_recent_visit_spec.rb
index 275581945fa..e404fb3bbdb 100644
--- a/spec/models/board_project_recent_visit_spec.rb
+++ b/spec/models/board_project_recent_visit_spec.rb
@@ -50,15 +50,25 @@ describe BoardProjectRecentVisit do
end
describe '#latest' do
- it 'returns the most recent visited' do
- board2 = create(:board, project: project)
- board3 = create(:board, project: project)
+ def create_visit(time)
+ create :board_project_recent_visit, project: project, user: user, updated_at: time
+ end
- create :board_project_recent_visit, project: board.project, board: board, user: user, updated_at: 7.days.ago
- create :board_project_recent_visit, project: board2.project, board: board2, user: user, updated_at: 5.days.ago
- recent = create :board_project_recent_visit, project: board3.project, board: board3, user: user, updated_at: 1.day.ago
+ it 'returns the most recent visited' do
+ create_visit(7.days.ago)
+ create_visit(5.days.ago)
+ recent = create_visit(1.day.ago)
expect(described_class.latest(user, project)).to eq recent
end
+
+ it 'returns last 3 visited boards' do
+ create_visit(7.days.ago)
+ visit1 = create_visit(3.days.ago)
+ visit2 = create_visit(2.days.ago)
+ visit3 = create_visit(5.days.ago)
+
+ expect(described_class.latest(user, project, count: 3)).to eq([visit2, visit1, visit3])
+ end
end
end
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
index 89839709131..30ca07d5d2c 100644
--- a/spec/models/broadcast_message_spec.rb
+++ b/spec/models/broadcast_message_spec.rb
@@ -95,6 +95,12 @@ describe BroadcastMessage do
end
end
+ describe '#attributes' do
+ it 'includes message_html field' do
+ expect(subject.attributes.keys).to include("cached_markdown_version", "message_html")
+ end
+ end
+
describe '#active?' do
it 'is truthy when started and not ended' do
message = build(:broadcast_message)
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index 741cdfef1a5..b5ec8991720 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -22,4 +22,19 @@ describe Ci::Bridge do
expect(status).to be_a Gitlab::Ci::Status::Success
end
end
+
+ describe '#scoped_variables_hash' do
+ it 'returns a hash representing variables' do
+ variables = %w[
+ CI_JOB_NAME CI_JOB_STAGE CI_COMMIT_SHA CI_COMMIT_SHORT_SHA
+ CI_COMMIT_BEFORE_SHA CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG
+ CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH
+ CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PIPELINE_IID
+ CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
+ CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION
+ ]
+
+ expect(bridge.scoped_variables_hash.keys).to include(*variables)
+ end
+ end
end
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 47865e4d08f..7500e6ae5b1 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -23,6 +23,7 @@ describe Ci::Build do
it { is_expected.to validate_presence_of(:ref) }
it { is_expected.to respond_to(:has_trace?) }
it { is_expected.to respond_to(:trace) }
+ it { is_expected.to delegate_method(:merge_request_event?).to(:pipeline) }
it { is_expected.to be_a(ArtifactMigratable) }
@@ -185,6 +186,37 @@ describe Ci::Build do
end
end
+ describe '#enqueue' do
+ let(:build) { create(:ci_build, :created) }
+
+ subject { build.enqueue }
+
+ before do
+ allow(build).to receive(:any_unmet_prerequisites?).and_return(has_prerequisites)
+ allow(Ci::PrepareBuildService).to receive(:perform_async)
+ end
+
+ context 'build has unmet prerequisites' do
+ let(:has_prerequisites) { true }
+
+ it 'transitions to preparing' do
+ subject
+
+ expect(build).to be_preparing
+ end
+ end
+
+ context 'build has no prerequisites' do
+ let(:has_prerequisites) { false }
+
+ it 'transitions to pending' do
+ subject
+
+ expect(build).to be_pending
+ end
+ end
+ end
+
describe '#actionize' do
context 'when build is a created' do
before do
@@ -343,6 +375,18 @@ describe Ci::Build do
expect(build).to be_pending
end
+
+ context 'build has unmet prerequisites' do
+ before do
+ allow(build).to receive(:prerequisites).and_return([double])
+ end
+
+ it 'transits to preparing' do
+ subject
+
+ expect(build).to be_preparing
+ end
+ end
end
end
@@ -2113,55 +2157,55 @@ describe Ci::Build do
context 'returns variables' do
let(:predefined_variables) do
[
- { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true },
- { key: 'CI_PIPELINE_URL', value: project.web_url + "/pipelines/#{pipeline.id}", public: true },
- { key: 'CI_JOB_ID', value: build.id.to_s, public: true },
- { key: 'CI_JOB_URL', value: project.web_url + "/-/jobs/#{build.id}", public: true },
- { key: 'CI_JOB_TOKEN', value: 'my-token', public: false },
- { key: 'CI_BUILD_ID', value: build.id.to_s, public: true },
- { key: 'CI_BUILD_TOKEN', value: 'my-token', public: false },
- { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true },
- { key: 'CI_REGISTRY_PASSWORD', value: 'my-token', public: false },
- { key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false },
- { key: 'CI', value: 'true', public: true },
- { key: 'GITLAB_CI', value: 'true', public: true },
- { key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true },
- { key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
- { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
- { key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s, public: true },
- { key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s, public: true },
- { key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s, public: true },
- { key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true },
- { 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_SHORT_SHA', value: build.short_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_NODE_TOTAL', value: '1', public: true },
- { key: 'CI_BUILD_REF', value: build.sha, public: true },
- { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true },
- { key: 'CI_BUILD_REF_NAME', value: build.ref, public: true },
- { key: 'CI_BUILD_REF_SLUG', value: build.ref_slug, public: true },
- { key: 'CI_BUILD_NAME', value: 'test', public: true },
- { key: 'CI_BUILD_STAGE', value: 'test', public: true },
- { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true },
- { key: 'CI_PROJECT_NAME', value: project.path, public: true },
- { key: 'CI_PROJECT_PATH', value: project.full_path, public: true },
- { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true },
- { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true },
- { key: 'CI_PROJECT_URL', value: project.web_url, public: true },
- { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true },
- { key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host, public: true },
- { key: 'CI_PAGES_URL', value: project.pages_url, public: true },
- { key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true },
- { key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true },
- { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true },
- { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true },
- { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true },
- { key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true },
- { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true }
+ { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true, masked: false },
+ { key: 'CI_PIPELINE_URL', value: project.web_url + "/pipelines/#{pipeline.id}", public: true, masked: false },
+ { key: 'CI_JOB_ID', value: build.id.to_s, public: true, masked: false },
+ { key: 'CI_JOB_URL', value: project.web_url + "/-/jobs/#{build.id}", public: true, masked: false },
+ { key: 'CI_JOB_TOKEN', value: 'my-token', public: false, masked: true },
+ { key: 'CI_BUILD_ID', value: build.id.to_s, public: true, masked: false },
+ { key: 'CI_BUILD_TOKEN', value: 'my-token', public: false, masked: true },
+ { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true, masked: false },
+ { key: 'CI_REGISTRY_PASSWORD', value: 'my-token', public: false, masked: true },
+ { key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false, masked: false },
+ { key: 'CI', value: 'true', public: true, masked: false },
+ { key: 'GITLAB_CI', value: 'true', public: true, masked: false },
+ { key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true, masked: false },
+ { key: 'CI_SERVER_NAME', value: 'GitLab', public: true, masked: false },
+ { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true, masked: false },
+ { key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s, public: true, masked: false },
+ { key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s, public: true, masked: false },
+ { key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s, public: true, masked: false },
+ { key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true, masked: false },
+ { key: 'CI_JOB_NAME', value: 'test', public: true, masked: false },
+ { key: 'CI_JOB_STAGE', value: 'test', public: true, masked: false },
+ { key: 'CI_COMMIT_SHA', value: build.sha, public: true, masked: false },
+ { key: 'CI_COMMIT_SHORT_SHA', value: build.short_sha, public: true, masked: false },
+ { key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true, masked: false },
+ { key: 'CI_COMMIT_REF_NAME', value: build.ref, public: true, masked: false },
+ { key: 'CI_COMMIT_REF_SLUG', value: build.ref_slug, public: true, masked: false },
+ { key: 'CI_NODE_TOTAL', value: '1', public: true, masked: false },
+ { key: 'CI_BUILD_REF', value: build.sha, public: true, masked: false },
+ { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true, masked: false },
+ { key: 'CI_BUILD_REF_NAME', value: build.ref, public: true, masked: false },
+ { key: 'CI_BUILD_REF_SLUG', value: build.ref_slug, public: true, masked: false },
+ { key: 'CI_BUILD_NAME', value: 'test', public: true, masked: false },
+ { key: 'CI_BUILD_STAGE', value: 'test', public: true, masked: false },
+ { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true, masked: false },
+ { key: 'CI_PROJECT_NAME', value: project.path, public: true, masked: false },
+ { key: 'CI_PROJECT_PATH', value: project.full_path, public: true, masked: false },
+ { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true, masked: false },
+ { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true, masked: false },
+ { key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false },
+ { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false },
+ { key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host, public: true, masked: false },
+ { key: 'CI_PAGES_URL', value: project.pages_url, public: true, masked: false },
+ { key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true, masked: false },
+ { key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true, masked: false },
+ { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true, masked: false },
+ { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false },
+ { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true, masked: false },
+ { key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true, masked: false },
+ { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true, masked: false }
]
end
@@ -2174,10 +2218,10 @@ describe Ci::Build do
describe 'variables ordering' do
context 'when variables hierarchy is stubbed' do
- let(:build_pre_var) { { key: 'build', value: 'value', public: true } }
- let(:project_pre_var) { { key: 'project', value: 'value', public: true } }
- let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true } }
- let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true } }
+ let(:build_pre_var) { { key: 'build', value: 'value', public: true, masked: false } }
+ let(:project_pre_var) { { key: 'project', value: 'value', public: true, masked: false } }
+ let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true, masked: false } }
+ let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true, masked: false } }
before do
allow(build).to receive(:predefined_variables) { [build_pre_var] }
@@ -2199,7 +2243,7 @@ describe Ci::Build do
project_pre_var,
pipeline_pre_var,
build_yaml_var,
- { key: 'secret', value: 'value', public: false }])
+ { key: 'secret', value: 'value', public: false, masked: false }])
end
end
@@ -2232,10 +2276,10 @@ describe Ci::Build do
context 'when build has user' do
let(:user_variables) do
[
- { key: 'GITLAB_USER_ID', value: user.id.to_s, public: true },
- { key: 'GITLAB_USER_EMAIL', value: user.email, public: true },
- { key: 'GITLAB_USER_LOGIN', value: user.username, public: true },
- { key: 'GITLAB_USER_NAME', value: user.name, public: true }
+ { key: 'GITLAB_USER_ID', value: user.id.to_s, public: true, masked: false },
+ { key: 'GITLAB_USER_EMAIL', value: user.email, public: true, masked: false },
+ { key: 'GITLAB_USER_LOGIN', value: user.username, public: true, masked: false },
+ { key: 'GITLAB_USER_NAME', value: user.name, public: true, masked: false }
]
end
@@ -2249,8 +2293,8 @@ describe Ci::Build do
context 'when build has an environment' do
let(:environment_variables) do
[
- { key: 'CI_ENVIRONMENT_NAME', value: 'production', public: true },
- { key: 'CI_ENVIRONMENT_SLUG', value: 'prod-slug', public: true }
+ { key: 'CI_ENVIRONMENT_NAME', value: 'production', public: true, masked: false },
+ { key: 'CI_ENVIRONMENT_SLUG', value: 'prod-slug', public: true, masked: false }
]
end
@@ -2285,7 +2329,7 @@ describe Ci::Build do
before do
environment_variables <<
- { key: 'CI_ENVIRONMENT_URL', value: url, public: true }
+ { key: 'CI_ENVIRONMENT_URL', value: url, public: true, masked: false }
end
context 'when the URL was set from the job' do
@@ -2322,7 +2366,7 @@ describe Ci::Build do
end
let(:manual_variable) do
- { key: 'CI_JOB_MANUAL', value: 'true', public: true }
+ { key: 'CI_JOB_MANUAL', value: 'true', public: true, masked: false }
end
it { is_expected.to include(manual_variable) }
@@ -2330,7 +2374,7 @@ describe Ci::Build do
context 'when build is for tag' do
let(:tag_variable) do
- { key: 'CI_COMMIT_TAG', value: 'master', public: true }
+ { key: 'CI_COMMIT_TAG', value: 'master', public: true, masked: false }
end
before do
@@ -2342,7 +2386,7 @@ describe Ci::Build do
context 'when CI variable is defined' do
let(:ci_variable) do
- { key: 'SECRET_KEY', value: 'secret_value', public: false }
+ { key: 'SECRET_KEY', value: 'secret_value', public: false, masked: false }
end
before do
@@ -2357,7 +2401,7 @@ describe Ci::Build do
let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + build.ref }
let(:protected_variable) do
- { key: 'PROTECTED_KEY', value: 'protected_value', public: false }
+ { key: 'PROTECTED_KEY', value: 'protected_value', public: false, masked: false }
end
before do
@@ -2389,7 +2433,7 @@ describe Ci::Build do
context 'when group CI variable is defined' do
let(:ci_variable) do
- { key: 'SECRET_KEY', value: 'secret_value', public: false }
+ { key: 'SECRET_KEY', value: 'secret_value', public: false, masked: false }
end
before do
@@ -2404,7 +2448,7 @@ describe Ci::Build do
let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + build.ref }
let(:protected_variable) do
- { key: 'PROTECTED_KEY', value: 'protected_value', public: false }
+ { key: 'PROTECTED_KEY', value: 'protected_value', public: false, masked: false }
end
before do
@@ -2443,11 +2487,11 @@ describe Ci::Build do
let(:trigger_request) { create(:ci_trigger_request, pipeline: pipeline, trigger: trigger) }
let(:user_trigger_variable) do
- { key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1', public: false }
+ { key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1', public: false, masked: false }
end
let(:predefined_trigger_variable) do
- { key: 'CI_PIPELINE_TRIGGERED', value: 'true', public: true }
+ { key: 'CI_PIPELINE_TRIGGERED', value: 'true', public: true, masked: false }
end
before do
@@ -2479,7 +2523,7 @@ describe Ci::Build do
context 'when pipeline has a variable' do
let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline) }
- it { is_expected.to include(pipeline_variable.to_runner_variable) }
+ it { is_expected.to include(key: pipeline_variable.key, value: pipeline_variable.value, public: false, masked: false) }
end
context 'when a job was triggered by a pipeline schedule' do
@@ -2496,16 +2540,16 @@ describe Ci::Build do
pipeline_schedule.reload
end
- it { is_expected.to include(pipeline_schedule_variable.to_runner_variable) }
+ it { is_expected.to include(key: pipeline_schedule_variable.key, value: pipeline_schedule_variable.value, public: false, masked: false) }
end
context 'when container registry is enabled' do
let(:container_registry_enabled) { true }
let(:ci_registry) do
- { key: 'CI_REGISTRY', value: 'registry.example.com', public: true }
+ { key: 'CI_REGISTRY', value: 'registry.example.com', public: true, masked: false }
end
let(:ci_registry_image) do
- { key: 'CI_REGISTRY_IMAGE', value: project.container_registry_url, public: true }
+ { key: 'CI_REGISTRY_IMAGE', value: project.container_registry_url, public: true, masked: false }
end
context 'and is disabled for project' do
@@ -2534,13 +2578,13 @@ describe Ci::Build do
build.update(runner: runner)
end
- it { is_expected.to include({ key: 'CI_RUNNER_ID', value: runner.id.to_s, public: true }) }
- it { is_expected.to include({ key: 'CI_RUNNER_DESCRIPTION', value: 'description', public: true }) }
- it { is_expected.to include({ key: 'CI_RUNNER_TAGS', value: 'docker, linux', public: true }) }
+ it { is_expected.to include({ key: 'CI_RUNNER_ID', value: runner.id.to_s, public: true, masked: false }) }
+ it { is_expected.to include({ key: 'CI_RUNNER_DESCRIPTION', value: 'description', public: true, masked: false }) }
+ it { is_expected.to include({ key: 'CI_RUNNER_TAGS', value: 'docker, linux', public: true, masked: false }) }
end
context 'when build is for a deployment' do
- let(:deployment_variable) { { key: 'KUBERNETES_TOKEN', value: 'TOKEN', public: false } }
+ let(:deployment_variable) { { key: 'KUBERNETES_TOKEN', value: 'TOKEN', public: false, masked: false } }
before do
build.environment = 'production'
@@ -2554,7 +2598,7 @@ describe Ci::Build do
end
context 'when project has custom CI config path' do
- let(:ci_config_path) { { key: 'CI_CONFIG_PATH', value: 'custom', public: true } }
+ let(:ci_config_path) { { key: 'CI_CONFIG_PATH', value: 'custom', public: true, masked: false } }
before do
project.update(ci_config_path: 'custom')
@@ -2571,7 +2615,7 @@ describe Ci::Build do
it "includes AUTO_DEVOPS_DOMAIN" do
is_expected.to include(
- { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true })
+ { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true, masked: false })
end
end
@@ -2582,7 +2626,7 @@ describe Ci::Build do
it "includes AUTO_DEVOPS_DOMAIN" do
is_expected.not_to include(
- { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true })
+ { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true, masked: false })
end
end
end
@@ -2597,9 +2641,9 @@ describe Ci::Build do
variables = subject.reverse.uniq { |variable| variable[:key] }.reverse
expect(variables)
- .not_to include(key: 'MYVAR', value: 'myvar', public: true)
+ .not_to include(key: 'MYVAR', value: 'myvar', public: true, masked: false)
expect(variables)
- .to include(key: 'MYVAR', value: 'pipeline value', public: false)
+ .to include(key: 'MYVAR', value: 'pipeline value', public: false, masked: false)
end
end
@@ -2615,13 +2659,13 @@ describe Ci::Build do
it 'includes CI_NODE_INDEX' do
is_expected.to include(
- { key: 'CI_NODE_INDEX', value: index.to_s, public: true }
+ { key: 'CI_NODE_INDEX', value: index.to_s, public: true, masked: false }
)
end
it 'includes correct CI_NODE_TOTAL' do
is_expected.to include(
- { key: 'CI_NODE_TOTAL', value: total.to_s, public: true }
+ { key: 'CI_NODE_TOTAL', value: total.to_s, public: true, masked: false }
)
end
end
@@ -2640,7 +2684,7 @@ describe Ci::Build do
it 'returns static predefined variables' do
expect(build.variables.size).to be >= 28
expect(build.variables)
- .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true)
+ .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true, masked: false)
expect(build).not_to be_persisted
end
end
@@ -2650,8 +2694,8 @@ describe Ci::Build do
let(:deploy_token_variables) do
[
- { key: 'CI_DEPLOY_USER', value: deploy_token.username, public: true },
- { key: 'CI_DEPLOY_PASSWORD', value: deploy_token.token, public: false }
+ { key: 'CI_DEPLOY_USER', value: deploy_token.username, public: true, masked: false },
+ { key: 'CI_DEPLOY_PASSWORD', value: deploy_token.token, public: false, masked: true }
]
end
@@ -2710,7 +2754,7 @@ describe Ci::Build do
end
expect(variables)
- .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true)
+ .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true, masked: false)
end
it 'does not return prohibited variables' do
@@ -2733,6 +2777,122 @@ describe Ci::Build do
end
end
+ describe '#secret_group_variables' do
+ subject { build.secret_group_variables }
+
+ let!(:variable) { create(:ci_group_variable, protected: true, group: group) }
+
+ context 'when ref is branch' do
+ let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) }
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_branch, :developers_can_merge, name: 'master', project: project)
+ end
+
+ it { is_expected.to include(variable) }
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.not_to include(variable) }
+ end
+ end
+
+ context 'when ref is tag' do
+ let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, project: project) }
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_tag, project: project, name: 'v*')
+ end
+
+ it { is_expected.to include(variable) }
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.not_to include(variable) }
+ end
+ end
+
+ context 'when ref is merge request' do
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:pipeline) { merge_request.merge_request_pipelines.first }
+ let(:build) { create(:ci_build, ref: merge_request.source_branch, tag: false, pipeline: pipeline, project: project) }
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_branch, :developers_can_merge, name: merge_request.source_branch, project: project)
+ end
+
+ it 'does not return protected variables as it is not supported for merge request pipelines' do
+ is_expected.not_to include(variable)
+ end
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.not_to include(variable) }
+ end
+ end
+ end
+
+ describe '#secret_project_variables' do
+ subject { build.secret_project_variables }
+
+ let!(:variable) { create(:ci_variable, protected: true, project: project) }
+
+ context 'when ref is branch' do
+ let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) }
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_branch, :developers_can_merge, name: 'master', project: project)
+ end
+
+ it { is_expected.to include(variable) }
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.not_to include(variable) }
+ end
+ end
+
+ context 'when ref is tag' do
+ let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, project: project) }
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_tag, project: project, name: 'v*')
+ end
+
+ it { is_expected.to include(variable) }
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.not_to include(variable) }
+ end
+ end
+
+ context 'when ref is merge request' do
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:pipeline) { merge_request.merge_request_pipelines.first }
+ let(:build) { create(:ci_build, ref: merge_request.source_branch, tag: false, pipeline: pipeline, project: project) }
+
+ context 'when ref is protected' do
+ before do
+ create(:protected_branch, :developers_can_merge, name: merge_request.source_branch, project: project)
+ end
+
+ it 'does not return protected variables as it is not supported for merge request pipelines' do
+ is_expected.not_to include(variable)
+ end
+ end
+
+ context 'when ref is not protected' do
+ it { is_expected.not_to include(variable) }
+ end
+ end
+ end
+
describe '#scoped_variables_hash' do
context 'when overriding CI variables' do
before do
@@ -2759,6 +2919,36 @@ describe Ci::Build do
end
end
+ describe '#any_unmet_prerequisites?' do
+ let(:build) { create(:ci_build, :created) }
+
+ subject { build.any_unmet_prerequisites? }
+
+ context 'build has prerequisites' do
+ before do
+ allow(build).to receive(:prerequisites).and_return([double])
+ end
+
+ it { is_expected.to be_truthy }
+
+ context 'and the ci_preparing_state feature is disabled' do
+ before do
+ stub_feature_flags(ci_preparing_state: false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'build does not have prerequisites' do
+ before do
+ allow(build).to receive(:prerequisites).and_return([])
+ end
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
describe '#yaml_variables' do
let(:build) { create(:ci_build, pipeline: pipeline, yaml_variables: variables) }
@@ -2811,6 +3001,20 @@ describe Ci::Build do
end
end
+ describe 'state transition: any => [:preparing]' do
+ let(:build) { create(:ci_build, :created) }
+
+ before do
+ allow(build).to receive(:prerequisites).and_return([double])
+ end
+
+ it 'queues BuildPrepareWorker' do
+ expect(Ci::BuildPrepareWorker).to receive(:perform_async).with(build.id)
+
+ build.enqueue
+ end
+ end
+
describe 'state transition: any => [:pending]' do
let(:build) { create(:ci_build, :created) }
diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb
index d214fdf369a..59db347582b 100644
--- a/spec/models/ci/build_trace_chunk_spec.rb
+++ b/spec/models/ci/build_trace_chunk_spec.rb
@@ -171,7 +171,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
shared_examples_for 'Scheduling sidekiq worker to flush data to persist store' do
- context 'when new data fullfilled chunk size' do
+ context 'when new data fulfilled chunk size' do
let(:new_data) { 'a' * described_class::CHUNK_SIZE }
it 'schedules trace chunk flush worker' do
@@ -193,7 +193,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do
end
shared_examples_for 'Scheduling no sidekiq worker' do
- context 'when new data fullfilled chunk size' do
+ context 'when new data fulfilled chunk size' do
let(:new_data) { 'a' * described_class::CHUNK_SIZE }
it 'does not schedule trace chunk flush worker' do
diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb
index 1b10501701c..21d96bf3454 100644
--- a/spec/models/ci/group_variable_spec.rb
+++ b/spec/models/ci/group_variable_spec.rb
@@ -5,6 +5,7 @@ describe Ci::GroupVariable do
it { is_expected.to include_module(HasVariable) }
it { is_expected.to include_module(Presentable) }
+ it { is_expected.to include_module(Maskable) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:group_id).with_message(/\(\w+\) has already been taken/) }
describe '.unprotected' do
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index 460b5c8cd31..5b8097621e0 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -1,6 +1,8 @@
require 'spec_helper'
describe Ci::Pipeline, :mailer do
+ include ProjectForksHelper
+
let(:user) { create(:user) }
set(:project) { create(:project) }
@@ -22,6 +24,7 @@ describe Ci::Pipeline, :mailer do
it { is_expected.to have_many(:builds) }
it { is_expected.to have_many(:auto_canceled_pipelines) }
it { is_expected.to have_many(:auto_canceled_jobs) }
+ it { is_expected.to have_one(:chat_data) }
it { is_expected.to validate_presence_of(:sha) }
it { is_expected.to validate_presence_of(:status) }
@@ -77,11 +80,11 @@ describe Ci::Pipeline, :mailer do
context 'when merge request pipelines exist' do
let!(:merge_request_pipeline_1) do
- create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
end
let!(:merge_request_pipeline_2) do
- create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
end
let(:merge_request) do
@@ -103,11 +106,11 @@ describe Ci::Pipeline, :mailer do
let!(:branch_pipeline_2) { create(:ci_pipeline, source: :push) }
let!(:merge_request_pipeline_1) do
- create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
end
let!(:merge_request_pipeline_2) do
- create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
end
let(:merge_request) do
@@ -127,11 +130,311 @@ describe Ci::Pipeline, :mailer do
end
end
- describe '.merge_request' do
- subject { described_class.merge_request }
+ describe '.for_sha' do
+ subject { described_class.for_sha(sha) }
+
+ let(:sha) { 'abc' }
+ let!(:pipeline) { create(:ci_pipeline, sha: 'abc') }
+
+ it 'returns the pipeline' do
+ is_expected.to contain_exactly(pipeline)
+ end
+
+ context 'when argument is array' do
+ let(:sha) { %w[abc def] }
+ let!(:pipeline_2) { create(:ci_pipeline, sha: 'def') }
+
+ it 'returns the pipelines' do
+ is_expected.to contain_exactly(pipeline, pipeline_2)
+ end
+ end
+
+ context 'when sha is empty' do
+ let(:sha) { nil }
+
+ it 'does not return anything' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '.for_source_sha' do
+ subject { described_class.for_source_sha(source_sha) }
+
+ let(:source_sha) { 'abc' }
+ let!(:pipeline) { create(:ci_pipeline, source_sha: 'abc') }
+
+ it 'returns the pipeline' do
+ is_expected.to contain_exactly(pipeline)
+ end
+
+ context 'when argument is array' do
+ let(:source_sha) { %w[abc def] }
+ let!(:pipeline_2) { create(:ci_pipeline, source_sha: 'def') }
+
+ it 'returns the pipelines' do
+ is_expected.to contain_exactly(pipeline, pipeline_2)
+ end
+ end
+
+ context 'when source_sha is empty' do
+ let(:source_sha) { nil }
+
+ it 'does not return anything' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '.for_sha_or_source_sha' do
+ subject { described_class.for_sha_or_source_sha(sha) }
+
+ let(:sha) { 'abc' }
+
+ context 'when sha is matched' do
+ let!(:pipeline) { create(:ci_pipeline, sha: sha) }
+
+ it 'returns the pipeline' do
+ is_expected.to contain_exactly(pipeline)
+ end
+ end
+
+ context 'when source sha is matched' do
+ let!(:pipeline) { create(:ci_pipeline, source_sha: sha) }
+
+ it 'returns the pipeline' do
+ is_expected.to contain_exactly(pipeline)
+ end
+ end
+
+ context 'when both sha and source sha are not matched' do
+ let!(:pipeline) { create(:ci_pipeline, sha: 'bcd', source_sha: 'bcd') }
+
+ it 'does not return anything' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '.detached_merge_request_pipelines' do
+ subject { described_class.detached_merge_request_pipelines(merge_request, sha) }
+
+ let!(:pipeline) do
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, sha: merge_request.diff_head_sha)
+ end
+
+ let(:merge_request) { create(:merge_request) }
+ let(:sha) { merge_request.diff_head_sha }
+
+ it 'returns detached merge request pipelines' do
+ is_expected.to eq([pipeline])
+ end
+
+ context 'when sha does not exist' do
+ let(:sha) { 'abc' }
+
+ it 'returns empty array' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let!(:pipeline) do
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, source_sha: merge_request.diff_head_sha)
+ end
+
+ it 'returns empty array' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '#detached_merge_request_pipeline?' do
+ subject { pipeline.detached_merge_request_pipeline? }
+
+ let!(:pipeline) do
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha)
+ end
+
+ let(:merge_request) { create(:merge_request) }
+ let(:target_sha) { nil }
+
+ it { is_expected.to be_truthy }
+
+ context 'when target sha exists' do
+ let(:target_sha) { merge_request.target_branch_sha }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '.merge_request_pipelines' do
+ subject { described_class.merge_request_pipelines(merge_request, source_sha) }
+
+ let!(:pipeline) do
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, source_sha: merge_request.diff_head_sha)
+ end
+
+ let(:merge_request) { create(:merge_request) }
+ let(:source_sha) { merge_request.diff_head_sha }
+
+ it 'returns merge pipelines' do
+ is_expected.to eq([pipeline])
+ end
+
+ context 'when source sha is empty' do
+ let(:source_sha) { nil }
+
+ it 'returns empty array' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when pipeline is detached merge request pipeline' do
+ let!(:pipeline) do
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, sha: merge_request.diff_head_sha)
+ end
+
+ it 'returns empty array' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '#merge_request_pipeline?' do
+ subject { pipeline.merge_request_pipeline? }
+
+ let!(:pipeline) do
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha)
+ end
+
+ let(:merge_request) { create(:merge_request) }
+ let(:target_sha) { merge_request.target_branch_sha }
+
+ it { is_expected.to be_truthy }
+
+ context 'when target sha is empty' do
+ let(:target_sha) { nil }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '.mergeable_merge_request_pipelines' do
+ subject { described_class.mergeable_merge_request_pipelines(merge_request) }
+
+ let!(:pipeline) do
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha)
+ end
+
+ let(:merge_request) { create(:merge_request) }
+ let(:target_sha) { merge_request.target_branch_sha }
+
+ it 'returns mergeable merge pipelines' do
+ is_expected.to eq([pipeline])
+ end
+
+ context 'when target sha does not point the head of the target branch' do
+ let(:target_sha) { merge_request.diff_head_sha }
+
+ it 'returns empty array' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '#mergeable_merge_request_pipeline?' do
+ subject { pipeline.mergeable_merge_request_pipeline? }
+
+ let!(:pipeline) do
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha)
+ end
+
+ let(:merge_request) { create(:merge_request) }
+ let(:target_sha) { merge_request.target_branch_sha }
+
+ it { is_expected.to be_truthy }
+
+ context 'when target sha does not point the head of the target branch' do
+ let(:target_sha) { merge_request.diff_head_sha }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '#matches_sha_or_source_sha?' do
+ subject { pipeline.matches_sha_or_source_sha?(sample_sha) }
+
+ let(:sample_sha) { Digest::SHA1.hexdigest(SecureRandom.hex) }
+
+ context 'when sha matches' do
+ let(:pipeline) { build(:ci_pipeline, sha: sample_sha) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when source_sha matches' do
+ let(:pipeline) { build(:ci_pipeline, source_sha: sample_sha) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when both sha and source_sha do not matche' do
+ let(:pipeline) { build(:ci_pipeline, sha: 'test', source_sha: 'test') }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ describe '.triggered_for_branch' do
+ subject { described_class.triggered_for_branch(ref) }
+
+ let(:project) { create(:project, :repository) }
+ let(:ref) { 'feature' }
+ let!(:pipeline) { create(:ci_pipeline, ref: ref) }
+
+ it 'returns the pipeline' do
+ is_expected.to eq([pipeline])
+ end
+
+ context 'when sha is not specified' do
+ it 'returns the pipeline' do
+ expect(described_class.triggered_for_branch(ref)).to eq([pipeline])
+ end
+ end
+
+ context 'when pipeline is triggered for tag' do
+ let(:ref) { 'v1.1.0' }
+ let!(:pipeline) { create(:ci_pipeline, ref: ref, tag: true) }
+
+ it 'does not return the pipeline' do
+ is_expected.to be_empty
+ end
+ end
+
+ context 'when pipeline is triggered for merge_request' do
+ let!(:merge_request) do
+ create(:merge_request,
+ :with_merge_request_pipeline,
+ source_project: project,
+ source_branch: ref,
+ target_project: project,
+ target_branch: 'master')
+ end
+
+ let(:pipeline) { merge_request.merge_request_pipelines.first }
+
+ it 'does not return the pipeline' do
+ is_expected.to be_empty
+ end
+ end
+ end
+
+ describe '.merge_request_event' do
+ subject { described_class.merge_request_event }
context 'when there is a merge request pipeline' do
- let!(:pipeline) { create(:ci_pipeline, source: :merge_request, merge_request: merge_request) }
+ let!(:pipeline) { create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) }
let(:merge_request) { create(:merge_request) }
it 'returns merge request pipeline first' do
@@ -152,7 +455,7 @@ describe Ci::Pipeline, :mailer do
let(:pipeline) { build(:ci_pipeline, source: source, merge_request: merge_request) }
context 'when source is merge request' do
- let(:source) { :merge_request }
+ let(:source) { :merge_request_event }
context 'when merge request is specified' do
let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_project: project, target_branch: 'master') }
@@ -376,7 +679,7 @@ describe Ci::Pipeline, :mailer do
context 'when source is merge request' do
let(:pipeline) do
- create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
end
let(:merge_request) do
@@ -384,9 +687,16 @@ describe Ci::Pipeline, :mailer do
source_project: project,
source_branch: 'feature',
target_project: project,
- target_branch: 'master')
+ target_branch: 'master',
+ assignee: assignee,
+ milestone: milestone,
+ labels: labels)
end
+ let(:assignee) { create(:user) }
+ let(:milestone) { create(:milestone, project: project) }
+ let(:labels) { create_list(:label, 2) }
+
it 'exposes merge request pipeline variables' do
expect(subject.to_hash)
.to include(
@@ -397,10 +707,16 @@ describe Ci::Pipeline, :mailer do
'CI_MERGE_REQUEST_PROJECT_PATH' => merge_request.project.full_path,
'CI_MERGE_REQUEST_PROJECT_URL' => merge_request.project.web_url,
'CI_MERGE_REQUEST_TARGET_BRANCH_NAME' => merge_request.target_branch.to_s,
+ 'CI_MERGE_REQUEST_TARGET_BRANCH_SHA' => pipeline.target_sha.to_s,
'CI_MERGE_REQUEST_SOURCE_PROJECT_ID' => merge_request.source_project.id.to_s,
'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path,
'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' => merge_request.source_project.web_url,
- 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s)
+ 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s,
+ 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => pipeline.source_sha.to_s,
+ 'CI_MERGE_REQUEST_TITLE' => merge_request.title,
+ 'CI_MERGE_REQUEST_ASSIGNEES' => assignee.username,
+ 'CI_MERGE_REQUEST_MILESTONE' => milestone.title,
+ 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).join(','))
end
context 'when source project does not exist' do
@@ -416,6 +732,30 @@ describe Ci::Pipeline, :mailer do
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME])
end
end
+
+ context 'without assignee' do
+ let(:assignee) { nil }
+
+ it 'does not expose assignee variable' do
+ expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_ASSIGNEES')
+ end
+ end
+
+ context 'without milestone' do
+ let(:milestone) { nil }
+
+ it 'does not expose milestone variable' do
+ expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_MILESTONE')
+ end
+ end
+
+ context 'without labels' do
+ let(:labels) { [] }
+
+ it 'does not expose labels variable' do
+ expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_LABELS')
+ end
+ end
end
end
@@ -885,16 +1225,28 @@ describe Ci::Pipeline, :mailer do
end
describe '#started_at' do
- it 'updates on transitioning to running' do
- build.run
+ let(:pipeline) { create(:ci_empty_pipeline, status: from_status) }
+
+ %i[created preparing pending].each do |status|
+ context "from #{status}" do
+ let(:from_status) { status }
+
+ it 'updates on transitioning to running' do
+ pipeline.run
- expect(pipeline.reload.started_at).not_to be_nil
+ expect(pipeline.started_at).not_to be_nil
+ end
+ end
end
- it 'does not update on transitioning to success' do
- build.success
+ context 'from created' do
+ let(:from_status) { :created }
- expect(pipeline.reload.started_at).to be_nil
+ it 'does not update on transitioning to success' do
+ pipeline.succeed
+
+ expect(pipeline.started_at).to be_nil
+ end
end
end
@@ -913,27 +1265,49 @@ describe Ci::Pipeline, :mailer do
end
describe 'merge request metrics' do
- let(:project) { create(:project, :repository) }
- let(:pipeline) { FactoryBot.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) }
- let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) }
+ let(:pipeline) { create(:ci_empty_pipeline, status: from_status) }
before do
expect(PipelineMetricsWorker).to receive(:perform_async).with(pipeline.id)
end
context 'when transitioning to running' do
- it 'schedules metrics workers' do
- pipeline.run
+ %i[created preparing pending].each do |status|
+ context "from #{status}" do
+ let(:from_status) { status }
+
+ it 'schedules metrics workers' do
+ pipeline.run
+ end
+ end
end
end
context 'when transitioning to success' do
+ let(:from_status) { 'created' }
+
it 'schedules metrics workers' do
pipeline.succeed
end
end
end
+ describe 'merge on success' do
+ let(:pipeline) { create(:ci_empty_pipeline, status: from_status) }
+
+ %i[created preparing pending running].each do |status|
+ context "from #{status}" do
+ let(:from_status) { status }
+
+ it 'schedules pipeline success worker' do
+ expect(PipelineSuccessWorker).to receive(:perform_async).with(pipeline.id)
+
+ pipeline.succeed
+ end
+ end
+ end
+ end
+
describe 'pipeline caching' do
it 'performs ExpirePipelinesCacheWorker' do
expect(ExpirePipelineCacheWorker).to receive(:perform_async).with(pipeline.id)
@@ -966,7 +1340,7 @@ describe Ci::Pipeline, :mailer do
context 'when source is merge request' do
let(:pipeline) do
- create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
end
let(:merge_request) do
@@ -1016,7 +1390,7 @@ describe Ci::Pipeline, :mailer do
context 'when ref is merge request' do
let(:pipeline) do
create(:ci_pipeline,
- source: :merge_request,
+ source: :merge_request_event,
merge_request: merge_request)
end
@@ -1179,7 +1553,7 @@ describe Ci::Pipeline, :mailer do
context 'when source is merge request' do
let(:pipeline) do
- create(:ci_pipeline, source: :merge_request, merge_request: merge_request)
+ create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request)
end
let(:merge_request) do
@@ -1452,6 +1826,18 @@ describe Ci::Pipeline, :mailer do
subject { pipeline.reload.status }
+ context 'on prepare' do
+ before do
+ # Prevent skipping directly to 'pending'
+ allow(build).to receive(:prerequisites).and_return([double])
+ allow(Ci::BuildPrepareWorker).to receive(:perform_async)
+
+ build.enqueue
+ end
+
+ it { is_expected.to eq('preparing') }
+ end
+
context 'on queuing' do
before do
build.enqueue
@@ -2086,7 +2472,7 @@ describe Ci::Pipeline, :mailer do
end
end
- describe "#merge_requests" do
+ describe "#merge_requests_as_head_pipeline" do
let(:project) { create(:project) }
let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: 'a288a022a53a5a944fae87bcec6efc87b7061808') }
@@ -2094,85 +2480,100 @@ describe Ci::Pipeline, :mailer do
allow_any_instance_of(MergeRequest).to receive(:diff_head_sha) { 'a288a022a53a5a944fae87bcec6efc87b7061808' }
merge_request = create(:merge_request, source_project: project, head_pipeline: pipeline, source_branch: pipeline.ref)
- expect(pipeline.merge_requests).to eq([merge_request])
+ expect(pipeline.merge_requests_as_head_pipeline).to eq([merge_request])
end
it "doesn't return merge requests whose source branch doesn't match the pipeline's ref" do
create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master')
- expect(pipeline.merge_requests).to be_empty
+ expect(pipeline.merge_requests_as_head_pipeline).to be_empty
end
it "doesn't return merge requests whose `diff_head_sha` doesn't match the pipeline's SHA" do
create(:merge_request, source_project: project, source_branch: pipeline.ref)
allow_any_instance_of(MergeRequest).to receive(:diff_head_sha) { '97de212e80737a608d939f648d959671fb0a0142b' }
- expect(pipeline.merge_requests).to be_empty
+ expect(pipeline.merge_requests_as_head_pipeline).to be_empty
end
end
describe "#all_merge_requests" do
let(:project) { create(:project) }
- let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master') }
- it "returns all merge requests having the same source branch" do
- merge_request = create(:merge_request, source_project: project, source_branch: pipeline.ref)
-
- expect(pipeline.all_merge_requests).to eq([merge_request])
- end
+ shared_examples 'a method that returns all merge requests for a given pipeline' do
+ let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: pipeline_project, ref: 'master') }
- it "doesn't return merge requests having a different source branch" do
- create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master')
-
- expect(pipeline.all_merge_requests).to be_empty
- end
+ it "returns all merge requests having the same source branch" do
+ merge_request = create(:merge_request, source_project: pipeline_project, target_project: project, source_branch: pipeline.ref)
- context 'when there is a merge request pipeline' do
- let(:source_branch) { 'feature' }
- let(:target_branch) { 'master' }
-
- let!(:pipeline) do
- create(:ci_pipeline,
- source: :merge_request,
- project: project,
- ref: source_branch,
- merge_request: merge_request)
+ expect(pipeline.all_merge_requests).to eq([merge_request])
end
- let(:merge_request) do
- create(:merge_request,
- source_project: project,
- source_branch: source_branch,
- target_project: project,
- target_branch: target_branch)
- end
+ it "doesn't return merge requests having a different source branch" do
+ create(:merge_request, source_project: pipeline_project, target_project: project, source_branch: 'feature', target_branch: 'master')
- it 'returns an associated merge request' do
- expect(pipeline.all_merge_requests).to eq([merge_request])
+ expect(pipeline.all_merge_requests).to be_empty
end
- context 'when there is another merge request pipeline that targets a different branch' do
- let(:target_branch_2) { 'merge-test' }
+ context 'when there is a merge request pipeline' do
+ let(:source_branch) { 'feature' }
+ let(:target_branch) { 'master' }
- let!(:pipeline_2) do
+ let!(:pipeline) do
create(:ci_pipeline,
- source: :merge_request,
- project: project,
+ source: :merge_request_event,
+ project: pipeline_project,
ref: source_branch,
- merge_request: merge_request_2)
+ merge_request: merge_request)
end
- let(:merge_request_2) do
+ let(:merge_request) do
create(:merge_request,
- source_project: project,
+ source_project: pipeline_project,
source_branch: source_branch,
target_project: project,
- target_branch: target_branch_2)
+ target_branch: target_branch)
end
- it 'does not return an associated merge request' do
- expect(pipeline.all_merge_requests).not_to include(merge_request_2)
+ it 'returns an associated merge request' do
+ expect(pipeline.all_merge_requests).to eq([merge_request])
end
+
+ context 'when there is another merge request pipeline that targets a different branch' do
+ let(:target_branch_2) { 'merge-test' }
+
+ let!(:pipeline_2) do
+ create(:ci_pipeline,
+ source: :merge_request_event,
+ project: pipeline_project,
+ ref: source_branch,
+ merge_request: merge_request_2)
+ end
+
+ let(:merge_request_2) do
+ create(:merge_request,
+ source_project: pipeline_project,
+ source_branch: source_branch,
+ target_project: project,
+ target_branch: target_branch_2)
+ end
+
+ it 'does not return an associated merge request' do
+ expect(pipeline.all_merge_requests).not_to include(merge_request_2)
+ end
+ end
+ end
+ end
+
+ it_behaves_like 'a method that returns all merge requests for a given pipeline' do
+ let(:pipeline_project) { project }
+ end
+
+ context 'for a fork' do
+ let(:fork) { fork_project(project) }
+
+ it_behaves_like 'a method that returns all merge requests for a given pipeline' do
+ let(:pipeline_project) { fork }
end
end
end
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index 875e8b2b682..02c07a2bd83 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -6,6 +6,7 @@ describe Ci::Variable do
describe 'validations' do
it { is_expected.to include_module(HasVariable) }
it { is_expected.to include_module(Presentable) }
+ it { is_expected.to include_module(Maskable) }
it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope).with_message(/\(\w+\) has already been taken/) }
end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index d5fd42509a3..a40fa988287 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -56,6 +56,14 @@ describe Clusters::Applications::Ingress do
expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in)
end
end
+
+ context 'when there is already an external_hostname' do
+ let(:application) { create(:clusters_applications_ingress, :installed, external_hostname: 'localhost.localdomain') }
+
+ it 'does not schedule a ClusterWaitForIngressIpAddressWorker' do
+ expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in)
+ end
+ end
end
describe '#install_command' do
diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb
index 2c22c24c498..2967c4076c6 100644
--- a/spec/models/clusters/applications/jupyter_spec.rb
+++ b/spec/models/clusters/applications/jupyter_spec.rb
@@ -26,6 +26,13 @@ describe Clusters::Applications::Jupyter do
it { expect(jupyter).to be_installable }
end
+
+ context 'when ingress is installed and external_hostname is assigned' do
+ let(:ingress) { create(:clusters_applications_ingress, :installed, external_hostname: 'localhost.localdomain') }
+ let(:jupyter) { create(:clusters_applications_jupyter, cluster: ingress.cluster) }
+
+ it { expect(jupyter).to be_installable }
+ end
end
describe '#install_command' do
@@ -39,7 +46,7 @@ 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 eq('v0.6')
+ expect(subject.version).to eq('0.9-174bbd5')
expect(subject).to be_rbac
expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/')
expect(subject.files).to eq(jupyter.files)
@@ -57,7 +64,7 @@ describe Clusters::Applications::Jupyter 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')
+ expect(subject.version).to eq('0.9-174bbd5')
end
end
end
@@ -77,6 +84,7 @@ describe Clusters::Applications::Jupyter do
expect(values).to include('singleuser')
expect(values).to match(/clientId: '?#{application.oauth_application.uid}/)
expect(values).to match(/callbackUrl: '?#{application.callback_url}/)
+ expect(values).to include("gitlabProjectIdWhitelist:\n - #{application.cluster.project.id}")
end
context 'when cluster belongs to a project' do
diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb
index 006b922ab27..bf425a2617c 100644
--- a/spec/models/clusters/applications/knative_spec.rb
+++ b/spec/models/clusters/applications/knative_spec.rb
@@ -64,11 +64,17 @@ describe Clusters::Applications::Knative do
expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in)
end
end
- end
- describe '#install_command' do
- subject { knative.install_command }
+ context 'when there is already an external_hostname' do
+ let(:application) { create(:clusters_applications_knative, :installed, external_hostname: 'localhost.localdomain') }
+
+ it 'does not schedule a ClusterWaitForIngressIpAddressWorker' do
+ expect(ClusterWaitForIngressIpAddressWorker).not_to have_received(:perform_in)
+ end
+ end
+ end
+ shared_examples 'a command' do
it 'should be an instance of Helm::InstallCommand' do
expect(subject).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand)
end
@@ -76,7 +82,6 @@ describe Clusters::Applications::Knative do
it 'should be initialized with knative arguments' do
expect(subject.name).to eq('knative')
expect(subject.chart).to eq('knative/knative')
- expect(subject.version).to eq('0.2.2')
expect(subject.files).to eq(knative.files)
end
@@ -98,6 +103,27 @@ describe Clusters::Applications::Knative do
end
end
+ describe '#install_command' do
+ subject { knative.install_command }
+
+ it 'should be initialized with latest version' do
+ expect(subject.version).to eq('0.2.2')
+ end
+
+ it_behaves_like 'a command'
+ end
+
+ describe '#update_command' do
+ let!(:current_installed_version) { knative.version = '0.1.0' }
+ subject { knative.update_command }
+
+ it 'should be initialized with current version' do
+ expect(subject.version).to eq(current_installed_version)
+ end
+
+ it_behaves_like 'a command'
+ end
+
describe '#files' do
let(:application) { knative }
let(:values) { subject[:'values.yaml'] }
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index 2250ef301aa..81708b0c2ed 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -27,65 +27,6 @@ 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
-
- it 'returns true when updating' do
- application = build(:clusters_applications_prometheus, :updating, cluster: cluster)
-
- expect(application).to be_ready
- end
-
- it 'returns true when updated' do
- application = build(:clusters_applications_prometheus, :updated, cluster: cluster)
-
- expect(application).to be_ready
- end
-
- it 'returns true when errored' do
- application = build(:clusters_applications_prometheus, :update_errored, cluster: cluster)
-
- expect(application).to be_ready
- end
- end
-
describe '#prometheus_client' do
context 'cluster is nil' do
it 'returns nil' do
diff --git a/spec/models/clusters/applications/runner_spec.rb b/spec/models/clusters/applications/runner_spec.rb
index 5e5b25cbf8a..3ce8aa1c7bc 100644
--- a/spec/models/clusters/applications/runner_spec.rb
+++ b/spec/models/clusters/applications/runner_spec.rb
@@ -22,7 +22,7 @@ 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 eq('0.1.45')
+ expect(subject.version).to eq('0.3.0')
expect(subject).to be_rbac
expect(subject.repository).to eq('https://charts.gitlab.io')
expect(subject.files).to eq(gitlab_runner.files)
@@ -40,7 +40,7 @@ describe Clusters::Applications::Runner 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.45')
+ expect(subject.version).to eq('0.3.0')
end
end
end
@@ -64,24 +64,45 @@ describe Clusters::Applications::Runner do
end
context 'without a runner' do
- let(:project) { create(:project) }
- let(:cluster) { create(:cluster, :with_installed_helm, projects: [project]) }
let(:application) { create(:clusters_applications_runner, runner: nil, cluster: cluster) }
+ let(:runner) { application.runner }
- it 'creates a runner' do
- expect do
- subject
- end.to change { Ci::Runner.count }.by(1)
+ shared_examples 'runner creation' do
+ it 'creates a runner' do
+ expect { subject }.to change { Ci::Runner.count }.by(1)
+ end
+
+ it 'uses the new runner token' do
+ expect(values).to match(/runnerToken: '?#{runner.token}/)
+ end
end
- it 'uses the new runner token' do
- expect(values).to match(/runnerToken: '?#{application.reload.runner.token}/)
+ context 'project cluster' do
+ let(:project) { create(:project) }
+ let(:cluster) { create(:cluster, :with_installed_helm, projects: [project]) }
+
+ include_examples 'runner creation'
+
+ it 'creates a project runner' do
+ subject
+
+ expect(runner).to be_project_type
+ expect(runner.projects).to eq [project]
+ end
end
- it 'assigns the new runner to runner' do
- subject
+ context 'group cluster' do
+ let(:group) { create(:group) }
+ let(:cluster) { create(:cluster, :with_installed_helm, cluster_type: :group_type, groups: [group]) }
+
+ include_examples 'runner creation'
+
+ it 'creates a group runner' do
+ subject
- expect(application.reload.runner).to be_project_type
+ expect(runner).to be_group_type
+ expect(runner.groups).to eq [group]
+ end
end
end
diff --git a/spec/models/clusters/cluster_spec.rb b/spec/models/clusters/cluster_spec.rb
index 3feed4e9718..acbcdc7d170 100644
--- a/spec/models/clusters/cluster_spec.rb
+++ b/spec/models/clusters/cluster_spec.rb
@@ -31,6 +31,7 @@ describe Clusters::Cluster do
it { is_expected.to delegate_method(:available?).to(:application_prometheus).with_prefix }
it { is_expected.to delegate_method(:available?).to(:application_knative).with_prefix }
it { is_expected.to delegate_method(:external_ip).to(:application_ingress).with_prefix }
+ it { is_expected.to delegate_method(:external_hostname).to(:application_ingress).with_prefix }
it { is_expected.to respond_to :project }
diff --git a/spec/models/clusters/kubernetes_namespace_spec.rb b/spec/models/clusters/kubernetes_namespace_spec.rb
index b865909c7fd..579f486f99f 100644
--- a/spec/models/clusters/kubernetes_namespace_spec.rb
+++ b/spec/models/clusters/kubernetes_namespace_spec.rb
@@ -115,7 +115,7 @@ RSpec.describe Clusters::KubernetesNamespace, type: :model do
expect(kubernetes_namespace.predefined_variables).to include(
{ key: 'KUBE_SERVICE_ACCOUNT', value: kubernetes_namespace.service_account_name, public: true },
{ key: 'KUBE_NAMESPACE', value: kubernetes_namespace.namespace, public: true },
- { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false },
+ { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true },
{ key: 'KUBECONFIG', value: kubeconfig, public: false, file: true }
)
end
diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb
index 4068d98d8f7..af65530e663 100644
--- a/spec/models/clusters/platforms/kubernetes_spec.rb
+++ b/spec/models/clusters/platforms/kubernetes_spec.rb
@@ -98,6 +98,22 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it { expect(kubernetes.save).to be_truthy }
end
+
+ context 'when api_url is localhost' do
+ let(:api_url) { 'http://localhost:22' }
+
+ it { expect(kubernetes.save).to be_falsey }
+
+ context 'Application settings allows local requests' do
+ before do
+ allow(ApplicationSetting)
+ .to receive(:current)
+ .and_return(ApplicationSetting.build_from_defaults(allow_local_requests_from_hooks_and_services: true))
+ end
+
+ it { expect(kubernetes.save).to be_truthy }
+ end
+ end
end
context 'when validates token' do
@@ -253,7 +269,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it 'sets KUBE_TOKEN' do
expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes.token, public: false }
+ { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: true }
)
end
end
@@ -265,7 +281,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it 'sets KUBE_TOKEN' do
expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false }
+ { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true }
)
end
end
@@ -281,7 +297,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it 'sets KUBE_TOKEN' do
expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes.token, public: false }
+ { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: true }
)
end
end
@@ -293,7 +309,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it 'sets KUBE_TOKEN' do
expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes.token, public: false }
+ { key: 'KUBE_TOKEN', value: kubernetes.token, public: false, masked: true }
)
end
end
@@ -322,7 +338,7 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
it 'sets KUBE_TOKEN' do
expect(subject).to include(
- { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false }
+ { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true }
)
end
end
@@ -359,14 +375,14 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching
end
context 'with valid pods' do
- let(:pod) { kube_pod(app: environment.slug) }
- let(:pod_with_no_terminal) { kube_pod(app: environment.slug, status: "Pending") }
+ let(:pod) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug) }
+ let(:pod_with_no_terminal) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: "Pending") }
let(:terminals) { kube_terminals(service, pod) }
before do
stub_reactive_cache(
service,
- pods: [pod, pod, pod_with_no_terminal, kube_pod(app: "should-be-filtered-out")]
+ pods: [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")]
)
end
diff --git a/spec/models/commit_collection_spec.rb b/spec/models/commit_collection_spec.rb
index 0f5d03ff458..30c504ebea8 100644
--- a/spec/models/commit_collection_spec.rb
+++ b/spec/models/commit_collection_spec.rb
@@ -37,12 +37,92 @@ describe CommitCollection do
describe '#without_merge_commits' do
it 'returns all commits except merge commits' do
+ merge_commit = project.commit("60ecb67744cb56576c30214ff52294f8ce2def98")
+ expect(merge_commit).to receive(:merge_commit?).and_return(true)
+
collection = described_class.new(project, [
- build(:commit),
- build(:commit, :merge_commit)
+ commit,
+ merge_commit
])
- expect(collection.without_merge_commits.size).to eq(1)
+ expect(collection.without_merge_commits).to contain_exactly(commit)
+ end
+ end
+
+ describe 'enrichment methods' do
+ let(:gitaly_commit) { commit }
+ let(:hash_commit) { Commit.from_hash(gitaly_commit.to_hash, project) }
+
+ describe '#unenriched' do
+ it 'returns all commits that are not backed by gitaly data' do
+ collection = described_class.new(project, [gitaly_commit, hash_commit])
+
+ expect(collection.unenriched).to contain_exactly(hash_commit)
+ end
+ end
+
+ describe '#fully_enriched?' do
+ it 'returns true when all commits are backed by gitaly data' do
+ collection = described_class.new(project, [gitaly_commit, gitaly_commit])
+
+ expect(collection.fully_enriched?).to eq(true)
+ end
+
+ it 'returns false when any commits are not backed by gitaly data' do
+ collection = described_class.new(project, [gitaly_commit, hash_commit])
+
+ expect(collection.fully_enriched?).to eq(false)
+ end
+
+ it 'returns true when the collection is empty' do
+ collection = described_class.new(project, [])
+
+ expect(collection.fully_enriched?).to eq(true)
+ end
+ end
+
+ describe '#enrich!' do
+ it 'replaces commits in the collection with those backed by gitaly data' do
+ collection = described_class.new(project, [hash_commit])
+
+ collection.enrich!
+
+ new_commit = collection.commits.first
+ expect(new_commit.id).to eq(hash_commit.id)
+ expect(hash_commit.gitaly_commit?).to eq(false)
+ expect(new_commit.gitaly_commit?).to eq(true)
+ end
+
+ it 'maintains the original order of the commits' do
+ gitaly_commits = [gitaly_commit] * 3
+ hash_commits = [hash_commit] * 3
+ # Interleave the gitaly and hash commits together
+ original_commits = gitaly_commits.zip(hash_commits).flatten
+ collection = described_class.new(project, original_commits)
+
+ collection.enrich!
+
+ original_commits.each_with_index do |original_commit, i|
+ new_commit = collection.commits[i]
+ expect(original_commit.id).to eq(new_commit.id)
+ end
+ end
+
+ it 'fetches data if there are unenriched commits' do
+ collection = described_class.new(project, [hash_commit])
+
+ expect(Commit).to receive(:lazy).exactly(:once)
+
+ collection.enrich!
+ end
+
+ it 'does not fetch data if all commits are enriched' do
+ collection = described_class.new(project, [gitaly_commit])
+
+ expect(Commit).not_to receive(:lazy)
+
+ collection.enrich!
+ end
end
end
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index baad8352185..9d4e18534ae 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -542,7 +542,7 @@ eos
end
end
- describe '#uri_type' do
+ shared_examples '#uri_type' do
it 'returns the URI type at the given path' do
expect(commit.uri_type('files/html')).to be(:tree)
expect(commit.uri_type('files/images/logo-black.png')).to be(:raw)
@@ -561,6 +561,20 @@ eos
end
end
+ describe '#uri_type with Gitaly enabled' do
+ it_behaves_like "#uri_type"
+ end
+
+ describe '#uri_type with Rugged enabled', :enable_rugged do
+ it 'calls out to the Rugged implementation' do
+ allow_any_instance_of(Rugged::Tree).to receive(:path).with('files/html').and_call_original
+
+ commit.uri_type('files/html')
+ end
+
+ it_behaves_like '#uri_type'
+ end
+
describe '.from_hash' do
let(:new_commit) { described_class.from_hash(commit.to_hash, project) }
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index 8b7c88805c1..e2b7f5c6ee2 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -49,6 +49,16 @@ describe CommitStatus do
commit_status.success!
end
+
+ describe 'transitioning to running' do
+ let(:commit_status) { create(:commit_status, :pending, started_at: nil) }
+
+ it 'records the started at time' do
+ commit_status.run!
+
+ expect(commit_status.started_at).to be_present
+ end
+ end
end
describe '#started?' do
@@ -479,6 +489,12 @@ describe CommitStatus do
it { is_expected.to be_script_failure }
end
+
+ context 'when failure_reason is unmet_prerequisites' do
+ let(:reason) { :unmet_prerequisites }
+
+ it { is_expected.to be_unmet_prerequisites }
+ end
end
describe 'ensure stage assignment' do
@@ -555,6 +571,7 @@ describe CommitStatus do
before do
allow(Time).to receive(:now).and_return(current_time)
+ expect(commit_status.any_unmet_prerequisites?).to eq false
end
shared_examples 'commit status enqueued' do
@@ -569,6 +586,12 @@ describe CommitStatus do
it_behaves_like 'commit status enqueued'
end
+ context 'when initial state is :preparing' do
+ let(:commit_status) { create(:commit_status, :preparing) }
+
+ it_behaves_like 'commit status enqueued'
+ end
+
context 'when initial state is :skipped' do
let(:commit_status) { create(:commit_status, :skipped) }
diff --git a/spec/models/concerns/cache_markdown_field_spec.rb b/spec/models/concerns/cache_markdown_field_spec.rb
index 447279f19a8..7d555f15e39 100644
--- a/spec/models/concerns/cache_markdown_field_spec.rb
+++ b/spec/models/concerns/cache_markdown_field_spec.rb
@@ -23,6 +23,7 @@ describe CacheMarkdownField do
include CacheMarkdownField
cache_markdown_field :foo
cache_markdown_field :baz, pipeline: :single_line
+ cache_markdown_field :zoo, whitelisted: true
def self.add_attr(name)
self.attribute_names += [name]
@@ -35,7 +36,7 @@ describe CacheMarkdownField do
add_attr :cached_markdown_version
- [:foo, :foo_html, :bar, :baz, :baz_html].each do |name|
+ [:foo, :foo_html, :bar, :baz, :baz_html, :zoo, :zoo_html].each do |name|
add_attr(name)
end
@@ -84,8 +85,8 @@ describe CacheMarkdownField do
end
describe '.attributes' do
- it 'excludes cache attributes' do
- expect(thing.attributes.keys.sort).to eq(%w[bar baz foo])
+ it 'excludes cache attributes that is blacklisted by default' do
+ expect(thing.attributes.keys.sort).to eq(%w[bar baz cached_markdown_version foo zoo zoo_html])
end
end
@@ -297,7 +298,12 @@ describe CacheMarkdownField do
it 'saves the changes using #update_columns' do
expect(thing).to receive(:persisted?).and_return(true)
expect(thing).to receive(:update_columns)
- .with("foo_html" => updated_html, "baz_html" => "", "cached_markdown_version" => cache_version)
+ .with(
+ "foo_html" => updated_html,
+ "baz_html" => "",
+ "zoo_html" => "",
+ "cached_markdown_version" => cache_version
+ )
thing.refresh_markdown_cache!
end
diff --git a/spec/models/concerns/has_ref_spec.rb b/spec/models/concerns/has_ref_spec.rb
index 8aed72d77a4..6805731fed3 100644
--- a/spec/models/concerns/has_ref_spec.rb
+++ b/spec/models/concerns/has_ref_spec.rb
@@ -16,6 +16,16 @@ describe HasRef do
it 'return true when tag is set to false' do
is_expected.to be_truthy
end
+
+ context 'when it was triggered by merge request' do
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:pipeline) { merge_request.merge_request_pipelines.first }
+ let(:build) { create(:ci_build, pipeline: pipeline) }
+
+ it 'returns false' do
+ is_expected.to be_falsy
+ end
+ end
end
context 'is not a tag' do
@@ -55,5 +65,15 @@ describe HasRef do
is_expected.to start_with(Gitlab::Git::BRANCH_REF_PREFIX)
end
end
+
+ context 'when it is triggered by a merge request' do
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:pipeline) { merge_request.merge_request_pipelines.first }
+ let(:build) { create(:ci_build, tag: false, pipeline: pipeline) }
+
+ it 'returns nil' do
+ is_expected.to be_nil
+ end
+ end
end
end
diff --git a/spec/models/concerns/has_status_spec.rb b/spec/models/concerns/has_status_spec.rb
index 6b1038cb8fd..e8b1eba67cc 100644
--- a/spec/models/concerns/has_status_spec.rb
+++ b/spec/models/concerns/has_status_spec.rb
@@ -34,6 +34,22 @@ describe HasStatus do
it { is_expected.to eq 'running' }
end
+ context 'all preparing' do
+ let!(:statuses) do
+ [create(type, status: :preparing), create(type, status: :preparing)]
+ end
+
+ it { is_expected.to eq 'preparing' }
+ end
+
+ context 'at least one preparing' do
+ let!(:statuses) do
+ [create(type, status: :success), create(type, status: :preparing)]
+ end
+
+ it { is_expected.to eq 'preparing' }
+ end
+
context 'success and failed but allowed to fail' do
let!(:statuses) do
[create(type, status: :success),
@@ -188,7 +204,7 @@ describe HasStatus do
end
end
- %i[created running pending success
+ %i[created preparing running pending success
failed canceled skipped].each do |status|
it_behaves_like 'having a job', status
end
@@ -234,7 +250,7 @@ describe HasStatus do
describe '.alive' do
subject { CommitStatus.alive }
- %i[running pending created].each do |status|
+ %i[running pending preparing created].each do |status|
it_behaves_like 'containing the job', status
end
@@ -270,7 +286,7 @@ describe HasStatus do
describe '.cancelable' do
subject { CommitStatus.cancelable }
- %i[running pending created scheduled].each do |status|
+ %i[running pending preparing created scheduled].each do |status|
it_behaves_like 'containing the job', status
end
diff --git a/spec/models/concerns/has_variable_spec.rb b/spec/models/concerns/has_variable_spec.rb
index 3fbe86c5b56..bff96e12ffa 100644
--- a/spec/models/concerns/has_variable_spec.rb
+++ b/spec/models/concerns/has_variable_spec.rb
@@ -57,7 +57,7 @@ describe HasVariable do
describe '#to_runner_variable' do
it 'returns a hash for the runner' do
expect(subject.to_runner_variable)
- .to eq(key: subject.key, value: subject.value, public: false)
+ .to include(key: subject.key, value: subject.value, public: false)
end
end
end
diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb
index 41159348e04..72c6161424b 100644
--- a/spec/models/concerns/issuable_spec.rb
+++ b/spec/models/concerns/issuable_spec.rb
@@ -32,17 +32,56 @@ describe Issuable do
end
describe "Validation" do
- subject { build(:issue) }
+ context 'general validations' do
+ subject { build(:issue) }
- before do
- allow(InternalId).to receive(:generate_next).and_return(nil)
+ before do
+ allow(InternalId).to receive(:generate_next).and_return(nil)
+ end
+
+ it { is_expected.to validate_presence_of(:project) }
+ it { is_expected.to validate_presence_of(:iid) }
+ it { is_expected.to validate_presence_of(:author) }
+ it { is_expected.to validate_presence_of(:title) }
+ it { is_expected.to validate_length_of(:title).is_at_most(255) }
end
- it { is_expected.to validate_presence_of(:project) }
- it { is_expected.to validate_presence_of(:iid) }
- it { is_expected.to validate_presence_of(:author) }
- it { is_expected.to validate_presence_of(:title) }
- it { is_expected.to validate_length_of(:title).is_at_most(255) }
+ describe 'milestone' do
+ let(:project) { create(:project) }
+ let(:milestone_id) { create(:milestone, project: project).id }
+ let(:params) do
+ {
+ title: 'something',
+ project: project,
+ author: build(:user),
+ milestone_id: milestone_id
+ }
+ end
+
+ subject { issuable_class.new(params) }
+
+ context 'with correct params' do
+ it { is_expected.to be_valid }
+ end
+
+ context 'with empty string milestone' do
+ let(:milestone_id) { '' }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'with nil milestone id' do
+ let(:milestone_id) { nil }
+
+ it { is_expected.to be_valid }
+ end
+
+ context 'with a milestone id from another project' do
+ let(:milestone_id) { create(:milestone).id }
+
+ it { is_expected.to be_invalid }
+ end
+ end
end
describe "Scope" do
@@ -66,6 +105,48 @@ describe Issuable do
end
end
+ describe '#milestone_available?' do
+ let(:group) { create(:group) }
+ let(:project) { create(:project, group: group) }
+ let(:issue) { create(:issue, project: project) }
+
+ def build_issuable(milestone_id)
+ issuable_class.new(project: project, milestone_id: milestone_id)
+ end
+
+ it 'returns true with a milestone from the issue project' do
+ milestone = create(:milestone, project: project)
+
+ expect(build_issuable(milestone.id).milestone_available?).to be_truthy
+ end
+
+ it 'returns true with a milestone from the issue project group' do
+ milestone = create(:milestone, group: group)
+
+ expect(build_issuable(milestone.id).milestone_available?).to be_truthy
+ end
+
+ it 'returns true with a milestone from the the parent of the issue project group', :nested_groups do
+ parent = create(:group)
+ group.update(parent: parent)
+ milestone = create(:milestone, group: parent)
+
+ expect(build_issuable(milestone.id).milestone_available?).to be_truthy
+ end
+
+ it 'returns false with a milestone from another project' do
+ milestone = create(:milestone)
+
+ expect(build_issuable(milestone.id).milestone_available?).to be_falsey
+ end
+
+ it 'returns false with a milestone from another group' do
+ milestone = create(:milestone, group: create(:group))
+
+ expect(build_issuable(milestone.id).milestone_available?).to be_falsey
+ end
+ end
+
describe ".search" do
let!(:searchable_issue) { create(:issue, title: "Searchable awesome issue") }
let!(:searchable_issue2) { create(:issue, title: 'Aw') }
diff --git a/spec/models/concerns/maskable_spec.rb b/spec/models/concerns/maskable_spec.rb
new file mode 100644
index 00000000000..aeba7ad862f
--- /dev/null
+++ b/spec/models/concerns/maskable_spec.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Maskable do
+ let(:variable) { build(:ci_variable) }
+
+ describe 'masked value validations' do
+ subject { variable }
+
+ context 'when variable is masked' do
+ before do
+ subject.masked = true
+ end
+
+ it { is_expected.not_to allow_value('hello').for(:value) }
+ it { is_expected.not_to allow_value('hello world').for(:value) }
+ it { is_expected.not_to allow_value('hello$VARIABLEworld').for(:value) }
+ it { is_expected.not_to allow_value('hello\rworld').for(:value) }
+ it { is_expected.to allow_value('helloworld').for(:value) }
+ end
+
+ context 'when variable is not masked' do
+ before do
+ subject.masked = false
+ end
+
+ it { is_expected.to allow_value('hello').for(:value) }
+ it { is_expected.to allow_value('hello world').for(:value) }
+ it { is_expected.to allow_value('hello$VARIABLEworld').for(:value) }
+ it { is_expected.to allow_value('hello\rworld').for(:value) }
+ it { is_expected.to allow_value('helloworld').for(:value) }
+ end
+ end
+
+ describe 'REGEX' do
+ subject { Maskable::REGEX }
+
+ it 'does not match strings shorter than 8 letters' do
+ expect(subject.match?('hello')).to eq(false)
+ end
+
+ it 'does not match strings with spaces' do
+ expect(subject.match?('hello world')).to eq(false)
+ end
+
+ it 'does not match strings with shell variables' do
+ expect(subject.match?('hello$VARIABLEworld')).to eq(false)
+ end
+
+ it 'does not match strings with escape characters' do
+ expect(subject.match?('hello\rworld')).to eq(false)
+ end
+
+ it 'does not match strings that span more than one line' do
+ string = <<~EOS
+ hello
+ world
+ EOS
+
+ expect(subject.match?(string)).to eq(false)
+ end
+
+ it 'matches valid strings' do
+ expect(subject.match?('helloworld')).to eq(true)
+ end
+ end
+
+ describe '#to_runner_variable' do
+ subject { variable.to_runner_variable }
+
+ it 'exposes the masked attribute' do
+ expect(subject).to include(:masked)
+ end
+ end
+end
diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb
index 87bf731340f..81ca5b638fe 100644
--- a/spec/models/concerns/milestoneish_spec.rb
+++ b/spec/models/concerns/milestoneish_spec.rb
@@ -9,8 +9,10 @@ describe Milestone, 'Milestoneish' do
let(:admin) { create(:admin) }
let(:project) { create(:project, :public) }
let(:milestone) { create(:milestone, project: project) }
- let!(:issue) { create(:issue, project: project, milestone: milestone) }
- let!(:security_issue_1) { create(:issue, :confidential, project: project, author: author, milestone: milestone) }
+ let(:label1) { create(:label, project: project) }
+ let(:label2) { create(:label, project: project) }
+ let!(:issue) { create(:issue, project: project, milestone: milestone, assignees: [member], labels: [label1]) }
+ let!(:security_issue_1) { create(:issue, :confidential, project: project, author: author, milestone: milestone, labels: [label2]) }
let!(:security_issue_2) { create(:issue, :confidential, project: project, assignees: [assignee], milestone: milestone) }
let!(:closed_issue_1) { create(:issue, :closed, project: project, milestone: milestone) }
let!(:closed_issue_2) { create(:issue, :closed, project: project, milestone: milestone) }
@@ -42,13 +44,102 @@ describe Milestone, 'Milestoneish' do
end
end
+ context 'attributes visibility' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:users) do
+ {
+ anonymous: nil,
+ non_member: non_member,
+ guest: guest,
+ member: member,
+ assignee: assignee
+ }
+ end
+
+ let(:project_visibility_levels) do
+ {
+ public: Gitlab::VisibilityLevel::PUBLIC,
+ internal: Gitlab::VisibilityLevel::INTERNAL,
+ private: Gitlab::VisibilityLevel::PRIVATE
+ }
+ end
+
+ describe '#issue_participants_visible_by_user' do
+ where(:visibility, :user_role, :result) do
+ :public | nil | [:member]
+ :public | :non_member | [:member]
+ :public | :guest | [:member]
+ :public | :member | [:member, :assignee]
+ :internal | nil | []
+ :internal | :non_member | [:member]
+ :internal | :guest | [:member]
+ :internal | :member | [:member, :assignee]
+ :private | nil | []
+ :private | :non_member | []
+ :private | :guest | [:member]
+ :private | :member | [:member, :assignee]
+ end
+
+ with_them do
+ before do
+ project.update(visibility_level: project_visibility_levels[visibility])
+ end
+
+ it 'returns the proper participants' do
+ user = users[user_role]
+ participants = result.map { |role| users[role] }
+
+ expect(milestone.issue_participants_visible_by_user(user)).to match_array(participants)
+ end
+ end
+ end
+
+ describe '#issue_labels_visible_by_user' do
+ let(:labels) do
+ {
+ label1: label1,
+ label2: label2
+ }
+ end
+
+ where(:visibility, :user_role, :result) do
+ :public | nil | [:label1]
+ :public | :non_member | [:label1]
+ :public | :guest | [:label1]
+ :public | :member | [:label1, :label2]
+ :internal | nil | []
+ :internal | :non_member | [:label1]
+ :internal | :guest | [:label1]
+ :internal | :member | [:label1, :label2]
+ :private | nil | []
+ :private | :non_member | []
+ :private | :guest | [:label1]
+ :private | :member | [:label1, :label2]
+ end
+
+ with_them do
+ before do
+ project.update(visibility_level: project_visibility_levels[visibility])
+ end
+
+ it 'returns the proper participants' do
+ user = users[user_role]
+ expected_labels = result.map { |label| labels[label] }
+
+ expect(milestone.issue_labels_visible_by_user(user)).to match_array(expected_labels)
+ end
+ end
+ end
+ end
+
describe '#sorted_merge_requests' do
it 'sorts merge requests by label priority' do
merge_request_1 = create(:labeled_merge_request, labels: [label_2], source_project: project, source_branch: 'branch_1', milestone: milestone)
merge_request_2 = create(:labeled_merge_request, labels: [label_1], source_project: project, source_branch: 'branch_2', milestone: milestone)
merge_request_3 = create(:labeled_merge_request, labels: [label_3], source_project: project, source_branch: 'branch_3', milestone: milestone)
- merge_requests = milestone.sorted_merge_requests
+ merge_requests = milestone.sorted_merge_requests(member)
expect(merge_requests.first).to eq(merge_request_2)
expect(merge_requests.second).to eq(merge_request_1)
@@ -56,6 +147,51 @@ describe Milestone, 'Milestoneish' do
end
end
+ describe '#merge_requests_visible_to_user' do
+ let(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) }
+
+ context 'when project is private' do
+ before do
+ project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'does not return any merge request for a non member' do
+ merge_requests = milestone.merge_requests_visible_to_user(non_member)
+ expect(merge_requests).to be_empty
+ end
+
+ it 'returns milestone merge requests for a member' do
+ merge_requests = milestone.merge_requests_visible_to_user(member)
+ expect(merge_requests).to contain_exactly(merge_request)
+ end
+ end
+
+ context 'when project is public' do
+ context 'when merge requests are available to anyone' do
+ it 'returns milestone merge requests for a non member' do
+ merge_requests = milestone.merge_requests_visible_to_user(non_member)
+ expect(merge_requests).to contain_exactly(merge_request)
+ end
+ end
+
+ context 'when merge requests are available to project members' do
+ before do
+ project.project_feature.update(merge_requests_access_level: ProjectFeature::PRIVATE)
+ end
+
+ it 'does not return any merge request for a non member' do
+ merge_requests = milestone.merge_requests_visible_to_user(non_member)
+ expect(merge_requests).to be_empty
+ end
+
+ it 'returns milestone merge requests for a member' do
+ merge_requests = milestone.merge_requests_visible_to_user(member)
+ expect(merge_requests).to contain_exactly(merge_request)
+ end
+ end
+ end
+ end
+
describe '#closed_items_count' do
it 'does not count confidential issues for non project members' do
expect(milestone.closed_items_count(non_member)).to eq 2
diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb
index 39c16ae60af..0a9d2021a19 100644
--- a/spec/models/concerns/sortable_spec.rb
+++ b/spec/models/concerns/sortable_spec.rb
@@ -99,7 +99,7 @@ describe Sortable do
expect(ordered_group_names('id_desc')).to eq(%w(bbb BB AAA aa))
end
- it 'sorts groups by name via case-insentitive comparision' do
+ it 'sorts groups by name via case-insensitive comparision' do
expect(ordered_group_names('name_asc')).to eq(%w(aa AAA BB bbb))
expect(ordered_group_names('name_desc')).to eq(%w(bbb BB AAA aa))
end
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 55d83bc3a6b..40cb4eef60a 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -97,14 +97,31 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
end
describe PersonalAccessToken, 'TokenAuthenticatable' do
- let(:personal_access_token_name) { 'test-pat-01' }
+ shared_examples 'changes personal access token' do
+ it 'sets new token' do
+ subject
+
+ expect(personal_access_token.token).to eq(token_value)
+ expect(personal_access_token.token_digest).to eq(Gitlab::CryptoHelper.sha256(token_value))
+ end
+ end
+
+ shared_examples 'does not change personal access token' do
+ it 'sets new token' do
+ subject
+
+ expect(personal_access_token.token).to be(nil)
+ expect(personal_access_token.token_digest).to eq(token_digest)
+ end
+ end
+
let(:token_value) { 'token' }
+ let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
let(:user) { create(:user) }
let(:personal_access_token) do
- described_class.new(name: personal_access_token_name,
+ described_class.new(name: 'test-pat-01',
user_id: user.id,
scopes: [:api],
- token: token,
token_digest: token_digest)
end
@@ -115,239 +132,71 @@ describe PersonalAccessToken, 'TokenAuthenticatable' do
describe '.find_by_token' do
subject { PersonalAccessToken.find_by_token(token_value) }
- before do
+ it 'finds the token' do
personal_access_token.save
- end
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
-
- it 'finds the token' do
- expect(subject).not_to be_nil
- expect(subject.name).to eql(personal_access_token_name)
- end
- end
-
- context 'token_digest does not exist' do
- let(:token) { token_value }
- let(:token_digest) { nil }
-
- it 'finds the token' do
- expect(subject).not_to be_nil
- expect(subject.name).to eql(personal_access_token_name)
- end
+ expect(subject).to eq(personal_access_token)
end
end
describe '#set_token' do
let(:new_token_value) { 'new-token' }
- subject { personal_access_token.set_token(new_token_value) }
-
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
- it 'overwrites token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(new_token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(new_token_value))
- end
- end
-
- context 'token_digest does not exist but token does' do
- let(:token) { token_value }
- let(:token_digest) { nil }
-
- it 'creates new token_digest and clears token' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(new_token_value)
- expect(personal_access_token.token_digest).to eql(Gitlab::CryptoHelper.sha256(new_token_value))
- end
- end
-
- context 'token_digest does not exist, nor token' do
- let(:token) { nil }
- let(:token_digest) { nil }
+ subject { personal_access_token.set_token(new_token_value) }
- it 'creates new token_digest' do
- subject
+ it 'sets new token' do
+ subject
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(new_token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(new_token_value))
- end
+ expect(personal_access_token.token).to eq(new_token_value)
+ expect(personal_access_token.token_digest).to eq(Gitlab::CryptoHelper.sha256(new_token_value))
end
end
describe '#ensure_token' do
subject { personal_access_token.ensure_token }
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
-
- it 'does not change token fields' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to be_nil
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest does not exist but token does' do
- let(:token) { token_value }
+ context 'token_digest does not exist' do
let(:token_digest) { nil }
- it 'does not change token fields' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to eql(token_value)
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to be_nil
- end
+ it_behaves_like 'changes personal access token'
end
- context 'token_digest does not exist, nor token' do
- let(:token) { nil }
- let(:token_digest) { nil }
-
- it 'creates token_digest' do
- subject
+ context 'token_digest already generated' do
+ let(:token_digest) { 's3cr3t' }
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
+ it_behaves_like 'does not change personal access token'
end
end
describe '#ensure_token!' do
subject { personal_access_token.ensure_token! }
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256(token_value) }
-
- it 'does not change token fields' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to be_nil
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest does not exist but token does' do
- let(:token) { token_value }
+ context 'token_digest does not exist' do
let(:token_digest) { nil }
- it 'does not change token fields' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to eql(token_value)
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to be_nil
- end
+ it_behaves_like 'changes personal access token'
end
- context 'token_digest does not exist, nor token' do
- let(:token) { nil }
- let(:token_digest) { nil }
+ context 'token_digest already generated' do
+ let(:token_digest) { 's3cr3t' }
- it 'creates token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
+ it_behaves_like 'does not change personal access token'
end
end
describe '#reset_token!' do
subject { personal_access_token.reset_token! }
- context 'token_digest already exists' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256('old-token') }
-
- it 'creates new token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest does not exist but token does' do
- let(:token) { 'old-token' }
- let(:token_digest) { nil }
-
- it 'creates new token_digest and clears token' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql(Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest does not exist, nor token' do
- let(:token) { nil }
+ context 'token_digest does not exist' do
let(:token_digest) { nil }
- it 'creates new token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
- end
-
- context 'token_digest exists and newly generated token would be the same' do
- let(:token) { nil }
- let(:token_digest) { Gitlab::CryptoHelper.sha256('old-token') }
-
- before do
- personal_access_token.save
- allow(Devise).to receive(:friendly_token).and_return(
- 'old-token', token_value, 'boom!')
- end
-
- it 'regenerates a new token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
+ it_behaves_like 'changes personal access token'
end
- context 'token exists and newly generated token would be the same' do
- let(:token) { 'old-token' }
- let(:token_digest) { nil }
-
- before do
- personal_access_token.save
- allow(Devise).to receive(:friendly_token).and_return(
- 'old-token', token_value, 'boom!')
- end
+ context 'token_digest already generated' do
+ let(:token_digest) { 's3cr3t' }
- it 'regenerates a new token_digest' do
- subject
-
- expect(personal_access_token.read_attribute('token')).to be_nil
- expect(personal_access_token.token).to eql(token_value)
- expect(personal_access_token.token_digest).to eql( Gitlab::CryptoHelper.sha256(token_value))
- end
+ it_behaves_like 'changes personal access token'
end
end
end
diff --git a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
index 6605f1f5a5f..2a0182b4294 100644
--- a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb
@@ -15,7 +15,7 @@ describe TokenAuthenticatableStrategies::Base do
context 'when encrypted strategy is specified' do
it 'fabricates encrypted strategy object' do
- strategy = described_class.fabricate(instance, field, encrypted: true)
+ strategy = described_class.fabricate(instance, field, encrypted: :required)
expect(strategy).to be_a TokenAuthenticatableStrategies::Encrypted
end
@@ -23,7 +23,7 @@ describe TokenAuthenticatableStrategies::Base do
context 'when no strategy is specified' do
it 'fabricates insecure strategy object' do
- strategy = described_class.fabricate(instance, field, something: true)
+ strategy = described_class.fabricate(instance, field, something: :required)
expect(strategy).to be_a TokenAuthenticatableStrategies::Insecure
end
@@ -31,35 +31,9 @@ describe TokenAuthenticatableStrategies::Base do
context 'when incompatible options are provided' do
it 'raises an error' do
- expect { described_class.fabricate(instance, field, digest: true, encrypted: true) }
+ expect { described_class.fabricate(instance, field, digest: true, encrypted: :required) }
.to raise_error ArgumentError
end
end
end
-
- describe '#fallback?' do
- context 'when fallback is set' do
- it 'recognizes fallback setting' do
- strategy = described_class.new(instance, field, fallback: true)
-
- expect(strategy.fallback?).to be true
- end
- end
-
- context 'when fallback is not a valid value' do
- it 'raises an error' do
- strategy = described_class.new(instance, field, fallback: 'something')
-
- expect { strategy.fallback? }.to raise_error ArgumentError
- end
- end
-
- context 'when fallback is not set' do
- it 'raises an error' do
- strategy = described_class.new(instance, field, {})
-
- expect(strategy.fallback?).to eq false
- end
- end
- end
end
diff --git a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
index 93cab80cb1f..ca38f86c5ab 100644
--- a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
+++ b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb
@@ -12,19 +12,9 @@ describe TokenAuthenticatableStrategies::Encrypted do
described_class.new(model, 'some_field', options)
end
- describe '.new' do
- context 'when fallback and migration strategies are set' do
- let(:options) { { fallback: true, migrating: true } }
-
- it 'raises an error' do
- expect { subject }.to raise_error ArgumentError, /not compatible/
- end
- end
- end
-
describe '#find_token_authenticatable' do
- context 'when using fallback strategy' do
- let(:options) { { fallback: true } }
+ context 'when using optional strategy' do
+ let(:options) { { encrypted: :optional } }
it 'finds the encrypted resource by cleartext' do
allow(model).to receive(:find_by)
@@ -50,7 +40,7 @@ describe TokenAuthenticatableStrategies::Encrypted do
end
context 'when using migration strategy' do
- let(:options) { { migrating: true } }
+ let(:options) { { encrypted: :migrating } }
it 'finds the cleartext resource by cleartext' do
allow(model).to receive(:find_by)
@@ -73,8 +63,8 @@ describe TokenAuthenticatableStrategies::Encrypted do
end
describe '#get_token' do
- context 'when using fallback strategy' do
- let(:options) { { fallback: true } }
+ context 'when using optional strategy' do
+ let(:options) { { encrypted: :optional } }
it 'returns decrypted token when an encrypted token is present' do
allow(instance).to receive(:read_attribute)
@@ -98,7 +88,7 @@ describe TokenAuthenticatableStrategies::Encrypted do
end
context 'when using migration strategy' do
- let(:options) { { migrating: true } }
+ let(:options) { { encrypted: :migrating } }
it 'returns cleartext token when an encrypted token is present' do
allow(instance).to receive(:read_attribute)
@@ -127,8 +117,8 @@ describe TokenAuthenticatableStrategies::Encrypted do
end
describe '#set_token' do
- context 'when using fallback strategy' do
- let(:options) { { fallback: true } }
+ context 'when using optional strategy' do
+ let(:options) { { encrypted: :optional } }
it 'writes encrypted token and removes plaintext token and returns it' do
expect(instance).to receive(:[]=)
@@ -141,7 +131,7 @@ describe TokenAuthenticatableStrategies::Encrypted do
end
context 'when using migration strategy' do
- let(:options) { { migrating: true } }
+ let(:options) { { encrypted: :migrating } }
it 'writes encrypted token and writes plaintext token' do
expect(instance).to receive(:[]=)
diff --git a/spec/models/deployment_spec.rb b/spec/models/deployment_spec.rb
index a8d53cfcd7d..5fce9504334 100644
--- a/spec/models/deployment_spec.rb
+++ b/spec/models/deployment_spec.rb
@@ -356,4 +356,32 @@ describe Deployment do
end
end
end
+
+ describe '#cluster' do
+ let(:deployment) { create(:deployment) }
+ let(:project) { deployment.project }
+ let(:environment) { deployment.environment }
+
+ subject { deployment.cluster }
+
+ before do
+ expect(project).to receive(:deployment_platform)
+ .with(environment: environment.name).and_call_original
+ end
+
+ context 'project has no deployment platform' do
+ before do
+ expect(project.clusters).to be_empty
+ end
+
+ it { is_expected.to be_nil }
+ end
+
+ context 'project has a deployment platform' do
+ let!(:cluster) { create(:cluster, projects: [project]) }
+ let!(:platform) { create(:cluster_platform_kubernetes, cluster: cluster) }
+
+ it { is_expected.to eq cluster }
+ end
+ end
end
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index fda00a693f0..67e5f4f7e41 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -336,6 +336,16 @@ describe DiffNote do
end
end
+ describe '#banzai_render_context' do
+ let(:note) { create(:diff_note_on_merge_request) }
+
+ it 'includes expected context' do
+ context = note.banzai_render_context(:note)
+
+ expect(context).to include(suggestions_filter_enabled: true, noteable: note.noteable, project: note.project)
+ end
+ end
+
describe "image diff notes" do
subject { build(:image_diff_note_on_merge_request, project: project, noteable: merge_request) }
diff --git a/spec/models/environment_spec.rb b/spec/models/environment_spec.rb
index 2d554326f05..ab1b306e597 100644
--- a/spec/models/environment_spec.rb
+++ b/spec/models/environment_spec.rb
@@ -164,6 +164,28 @@ describe Environment do
end
end
+ describe '#name_without_type' do
+ context 'when it is inside a folder' do
+ subject(:environment) do
+ create(:environment, name: 'staging/review-1')
+ end
+
+ it 'returns name without folder' do
+ expect(environment.name_without_type).to eq 'review-1'
+ end
+ end
+
+ context 'when the environment if a top-level item itself' do
+ subject(:environment) do
+ create(:environment, name: 'production')
+ end
+
+ it 'returns full name' do
+ expect(environment.name_without_type).to eq 'production'
+ end
+ end
+ end
+
describe '#nullify_external_url' do
it 'replaces a blank url with nil' do
env = build(:environment, external_url: "")
diff --git a/spec/models/environment_status_spec.rb b/spec/models/environment_status_spec.rb
index 9da16dea929..2576a9aba06 100644
--- a/spec/models/environment_status_spec.rb
+++ b/spec/models/environment_status_spec.rb
@@ -64,8 +64,8 @@ describe EnvironmentStatus do
end
describe '.for_merge_request' do
- let(:admin) { create(:admin) }
- let(:pipeline) { create(:ci_pipeline, sha: sha) }
+ let(:admin) { create(:admin) }
+ let!(:pipeline) { create(:ci_pipeline, sha: sha, merge_requests_as_head_pipeline: [merge_request]) }
it 'is based on merge_request.diff_head_sha' do
expect(merge_request).to receive(:diff_head_sha)
diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
index d30228b863c..cbde13a2c7a 100644
--- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb
+++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
@@ -62,11 +62,32 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
end
context 'URL path' do
- it 'fails validation with wrong path' do
+ it 'fails validation without api/0/projects' do
subject.api_url = 'http://gitlab.com/project1/something'
expect(subject).not_to be_valid
- expect(subject.errors.messages[:api_url]).to include('path needs to start with /api/0/projects')
+ expect(subject.errors.messages[:api_url]).to include('is invalid')
+ end
+
+ it 'fails validation without org and project slugs' do
+ subject.api_url = 'http://gitlab.com/api/0/projects/'
+
+ expect(subject).not_to be_valid
+ expect(subject.errors.messages[:project]).to include('is a required field')
+ end
+
+ it 'fails validation when api_url has extra parts' do
+ subject.api_url = 'http://gitlab.com/api/0/projects/org/proj/something'
+
+ expect(subject).not_to be_valid
+ expect(subject.errors.messages[:api_url]).to include("is invalid")
+ end
+
+ it 'fails validation when api_url has less parts' do
+ subject.api_url = 'http://gitlab.com/api/0/projects/org'
+
+ expect(subject).not_to be_valid
+ expect(subject.errors.messages[:api_url]).to include("is invalid")
end
it 'passes validation with correct path' do
@@ -145,6 +166,24 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
expect(result).to be_nil
end
end
+
+ context 'when sentry client raises exception' do
+ let(:sentry_client) { spy(:sentry_client) }
+
+ before do
+ synchronous_reactive_cache(subject)
+
+ allow(subject).to receive(:sentry_client).and_return(sentry_client)
+ allow(sentry_client).to receive(:list_issues).with(opts)
+ .and_raise(Sentry::Client::Error, 'error message')
+ end
+
+ it 'returns error' do
+ expect(result).to eq(error: 'error message')
+ expect(subject).to have_received(:sentry_client)
+ expect(sentry_client).to have_received(:list_issues)
+ end
+ end
end
describe '#list_sentry_projects' do
@@ -257,6 +296,16 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
expect(api_url).to eq(':::')
end
+
+ it 'returns nil when api_host is blank' do
+ api_url = described_class.build_api_url_from(
+ api_host: '',
+ organization_slug: 'org-slug',
+ project_slug: 'proj-slug'
+ )
+
+ expect(api_url).to be_nil
+ end
end
describe '#api_host' do
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 9dc32a815d8..16624ce47d0 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -810,4 +810,125 @@ describe Group do
it { is_expected.to be_truthy }
end
end
+
+ describe '#first_auto_devops_config' do
+ using RSpec::Parameterized::TableSyntax
+
+ let(:group) { create(:group) }
+
+ subject { group.first_auto_devops_config }
+
+ where(:instance_value, :group_value, :config) do
+ # Instance level enabled
+ true | nil | { status: true, scope: :instance }
+ true | true | { status: true, scope: :group }
+ true | false | { status: false, scope: :group }
+
+ # Instance level disabled
+ false | nil | { status: false, scope: :instance }
+ false | true | { status: true, scope: :group }
+ false | false | { status: false, scope: :group }
+ end
+
+ with_them do
+ before do
+ stub_application_setting(auto_devops_enabled: instance_value)
+
+ group.update_attribute(:auto_devops_enabled, group_value)
+ end
+
+ it { is_expected.to eq(config) }
+ end
+
+ context 'with parent groups', :nested_groups do
+ where(:instance_value, :parent_value, :group_value, :config) do
+ # Instance level enabled
+ true | nil | nil | { status: true, scope: :instance }
+ true | nil | true | { status: true, scope: :group }
+ true | nil | false | { status: false, scope: :group }
+
+ true | true | nil | { status: true, scope: :group }
+ true | true | true | { status: true, scope: :group }
+ true | true | false | { status: false, scope: :group }
+
+ true | false | nil | { status: false, scope: :group }
+ true | false | true | { status: true, scope: :group }
+ true | false | false | { status: false, scope: :group }
+
+ # Instance level disable
+ false | nil | nil | { status: false, scope: :instance }
+ false | nil | true | { status: true, scope: :group }
+ false | nil | false | { status: false, scope: :group }
+
+ false | true | nil | { status: true, scope: :group }
+ false | true | true | { status: true, scope: :group }
+ false | true | false | { status: false, scope: :group }
+
+ false | false | nil | { status: false, scope: :group }
+ false | false | true | { status: true, scope: :group }
+ false | false | false | { status: false, scope: :group }
+ end
+
+ with_them do
+ before do
+ stub_application_setting(auto_devops_enabled: instance_value)
+ parent = create(:group, auto_devops_enabled: parent_value)
+
+ group.update!(
+ auto_devops_enabled: group_value,
+ parent: parent
+ )
+ end
+
+ it { is_expected.to eq(config) }
+ end
+ end
+ end
+
+ describe '#auto_devops_enabled?' do
+ subject { group.auto_devops_enabled? }
+
+ context 'when auto devops is explicitly enabled on group' do
+ let(:group) { create(:group, :auto_devops_enabled) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when auto devops is explicitly disabled on group' do
+ let(:group) { create(:group, :auto_devops_disabled) }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when auto devops is implicitly enabled or disabled' do
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+
+ group.update!(parent: parent_group)
+ end
+
+ context 'when auto devops is enabled on root group' do
+ let(:root_group) { create(:group, :auto_devops_enabled) }
+ let(:subgroup) { create(:group, parent: root_group) }
+ let(:parent_group) { create(:group, parent: subgroup) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when auto devops is disabled on root group' do
+ let(:root_group) { create(:group, :auto_devops_disabled) }
+ let(:subgroup) { create(:group, parent: root_group) }
+ let(:parent_group) { create(:group, parent: subgroup) }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when auto devops is disabled on parent group and enabled on root group' do
+ let(:root_group) { create(:group, :auto_devops_enabled) }
+ let(:parent_group) { create(:group, :auto_devops_disabled, parent: root_group) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+ end
end
diff --git a/spec/models/issue/metrics_spec.rb b/spec/models/issue/metrics_spec.rb
index 1bf0ecb98ad..b7291eebe64 100644
--- a/spec/models/issue/metrics_spec.rb
+++ b/spec/models/issue/metrics_spec.rb
@@ -9,7 +9,7 @@ describe Issue::Metrics do
context "milestones" do
it "records the first time an issue is associated with a milestone" do
time = Time.now
- Timecop.freeze(time) { subject.update(milestone: create(:milestone)) }
+ Timecop.freeze(time) { subject.update(milestone: create(:milestone, project: project)) }
metrics = subject.metrics
expect(metrics).to be_present
@@ -18,9 +18,9 @@ describe Issue::Metrics do
it "does not record the second time an issue is associated with a milestone" do
time = Time.now
- Timecop.freeze(time) { subject.update(milestone: create(:milestone)) }
+ Timecop.freeze(time) { subject.update(milestone: create(:milestone, project: project)) }
Timecop.freeze(time + 2.hours) { subject.update(milestone: nil) }
- Timecop.freeze(time + 6.hours) { subject.update(milestone: create(:milestone)) }
+ Timecop.freeze(time + 6.hours) { subject.update(milestone: create(:milestone, project: project)) }
metrics = subject.metrics
expect(metrics).to be_present
diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb
index 5d18e085a6f..6101df2e099 100644
--- a/spec/models/issue_spec.rb
+++ b/spec/models/issue_spec.rb
@@ -765,6 +765,15 @@ describe Issue do
end
end
+ describe '.confidential_only' do
+ it 'only returns confidential_only issues' do
+ create(:issue)
+ confidential_issue = create(:issue, confidential: true)
+
+ expect(described_class.confidential_only).to eq([confidential_issue])
+ end
+ end
+
it_behaves_like 'throttled touch' do
subject { create(:issue, updated_at: 1.hour.ago) }
end
diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb
index 1849d3bac12..53f5307ea0b 100644
--- a/spec/models/merge_request_diff_spec.rb
+++ b/spec/models/merge_request_diff_spec.rb
@@ -1,8 +1,22 @@
require 'spec_helper'
describe MergeRequestDiff do
+ include RepoHelpers
+
let(:diff_with_commits) { create(:merge_request).merge_request_diff }
+ describe 'validations' do
+ subject { diff_with_commits }
+
+ it 'checks sha format of base_commit_sha, head_commit_sha and start_commit_sha' do
+ subject.base_commit_sha = subject.head_commit_sha = subject.start_commit_sha = 'foobar'
+
+ expect(subject.valid?).to be false
+ expect(subject.errors.count).to eq 3
+ expect(subject.errors).to all(include('is not a valid SHA'))
+ end
+ end
+
describe 'create new record' do
subject { diff_with_commits }
@@ -78,7 +92,7 @@ describe MergeRequestDiff do
it 'returns persisted diffs if cannot compare with diff refs' do
expect(diff).to receive(:load_diffs).and_call_original
- diff.update!(head_commit_sha: 'invalid-sha')
+ diff.update!(head_commit_sha: Digest::SHA1.hexdigest(SecureRandom.hex))
diff.diffs.diff_files
end
@@ -182,6 +196,25 @@ describe MergeRequestDiff do
expect(diff_file).to be_binary
expect(diff_file.diff).to eq(mr_diff.compare.diffs(paths: [path]).to_a.first.diff)
end
+
+ context 'with diffs that contain a null byte' do
+ let(:filename) { 'test-null.txt' }
+ let(:content) { "a" * 10000 + "\x00" }
+ let(:project) { create(:project, :repository) }
+ let(:branch) { 'null-data' }
+ let(:target_branch) { 'master' }
+
+ it 'saves diffs correctly' do
+ create_file_in_repo(project, target_branch, branch, filename, content)
+
+ mr_diff = create(:merge_request, target_project: project, source_project: project, source_branch: branch, target_branch: target_branch).merge_request_diff
+ diff_file = mr_diff.merge_request_diff_files.find_by(new_path: filename)
+
+ expect(diff_file).to be_binary
+ expect(diff_file.diff).to eq(mr_diff.compare.diffs(paths: [filename]).to_a.first.diff)
+ expect(diff_file.diff).to include(content)
+ end
+ end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 82a853a23b9..a1de0c63623 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -84,32 +84,27 @@ describe MergeRequest do
describe '#default_squash_commit_message' do
let(:project) { subject.project }
-
- def commit_collection(commit_hashes)
- raw_commits = commit_hashes.map { |raw| Commit.from_hash(raw, project) }
-
- CommitCollection.new(project, raw_commits)
- end
+ let(:is_multiline) { -> (c) { c.description.present? } }
+ let(:multiline_commits) { subject.commits.select(&is_multiline) }
+ let(:singleline_commits) { subject.commits.reject(&is_multiline) }
it 'returns the oldest multiline commit message' do
- commits = commit_collection([
- { message: 'Singleline', parent_ids: [] },
- { message: "Second multiline\nCommit message", parent_ids: [] },
- { message: "First multiline\nCommit message", parent_ids: [] }
- ])
-
- expect(subject).to receive(:commits).and_return(commits)
-
- expect(subject.default_squash_commit_message).to eq("First multiline\nCommit message")
+ expect(subject.default_squash_commit_message).to eq(multiline_commits.last.message)
end
it 'returns the merge request title if there are no multiline commits' do
- commits = commit_collection([
- { message: 'Singleline', parent_ids: [] }
- ])
+ expect(subject).to receive(:commits).and_return(
+ CommitCollection.new(project, singleline_commits)
+ )
+
+ expect(subject.default_squash_commit_message).to eq(subject.title)
+ end
- expect(subject).to receive(:commits).and_return(commits)
+ it 'does not return commit messages from multiline merge commits' do
+ collection = CommitCollection.new(project, multiline_commits).enrich!
+ expect(collection.commits).to all( receive(:merge_commit?).and_return(true) )
+ expect(subject).to receive(:commits).and_return(collection)
expect(subject.default_squash_commit_message).to eq(subject.title)
end
end
@@ -184,6 +179,31 @@ describe MergeRequest do
expect(MergeRequest::Metrics.count).to eq(1)
end
end
+
+ describe '#refresh_merge_request_assignees' do
+ set(:user) { create(:user) }
+
+ it 'creates merge request assignees relation upon MR creation' do
+ merge_request = create(:merge_request, assignee: nil)
+
+ expect(merge_request.merge_request_assignees).to be_empty
+
+ expect { merge_request.update!(assignee: user) }
+ .to change { merge_request.reload.merge_request_assignees.count }
+ .from(0).to(1)
+ end
+
+ it 'updates merge request assignees relation upon MR assignee change' do
+ another_user = create(:user)
+ merge_request = create(:merge_request, assignee: user)
+
+ expect { merge_request.update!(assignee: another_user) }
+ .to change { merge_request.reload.merge_request_assignees.first.assignee }
+ .from(user).to(another_user)
+
+ expect(merge_request.merge_request_assignees.count).to eq(1)
+ end
+ end
end
describe 'respond to' do
@@ -270,6 +290,25 @@ describe MergeRequest do
end
end
+ describe '.recent_target_branches' do
+ let(:project) { create(:project) }
+ let!(:merge_request1) { create(:merge_request, :opened, source_project: project, target_branch: 'feature') }
+ let!(:merge_request2) { create(:merge_request, :closed, source_project: project, target_branch: 'merge-test') }
+ let!(:merge_request3) { create(:merge_request, :opened, source_project: project, target_branch: 'fix') }
+ let!(:merge_request4) { create(:merge_request, :closed, source_project: project, target_branch: 'feature') }
+
+ before do
+ merge_request1.update_columns(updated_at: 1.day.since)
+ merge_request2.update_columns(updated_at: 2.days.since)
+ merge_request3.update_columns(updated_at: 3.days.since)
+ merge_request4.update_columns(updated_at: 4.days.since)
+ end
+
+ it 'returns target branches sort by updated at desc' do
+ expect(described_class.recent_target_branches).to match_array(['feature', 'merge-test', 'fix'])
+ end
+ end
+
describe '#target_branch_sha' do
let(:project) { create(:project, :repository) }
@@ -1026,7 +1065,7 @@ describe MergeRequest do
describe '#commit_authors' do
it 'returns all the authors of every commit in the merge request' do
- users = subject.commits.map(&:author_email).uniq.map do |email|
+ users = subject.commits.without_merge_commits.map(&:author_email).uniq.map do |email|
create(:user, email: email)
end
@@ -1040,7 +1079,7 @@ describe MergeRequest do
describe '#authors' do
it 'returns a list with all the commit authors in the merge request and author' do
- users = subject.commits.map(&:author_email).uniq.map do |email|
+ users = subject.commits.without_merge_commits.map(&:author_email).uniq.map do |email|
create(:user, email: email)
end
@@ -1168,8 +1207,10 @@ describe MergeRequest do
end
context 'head pipeline' do
+ let(:diff_head_sha) { Digest::SHA1.hexdigest(SecureRandom.hex) }
+
before do
- allow(subject).to receive(:diff_head_sha).and_return('lastsha')
+ allow(subject).to receive(:diff_head_sha).and_return(diff_head_sha)
end
describe '#head_pipeline' do
@@ -1197,7 +1238,15 @@ describe MergeRequest do
end
it 'returns the pipeline for MR with recent pipeline' do
- pipeline = create(:ci_empty_pipeline, sha: 'lastsha')
+ pipeline = create(:ci_empty_pipeline, sha: diff_head_sha)
+ subject.update_attribute(:head_pipeline_id, pipeline.id)
+
+ expect(subject.actual_head_pipeline).to eq(subject.head_pipeline)
+ expect(subject.actual_head_pipeline).to eq(pipeline)
+ end
+
+ it 'returns the pipeline for MR with recent merge request pipeline' do
+ pipeline = create(:ci_empty_pipeline, sha: 'merge-sha', source_sha: diff_head_sha)
subject.update_attribute(:head_pipeline_id, pipeline.id)
expect(subject.actual_head_pipeline).to eq(subject.head_pipeline)
@@ -1331,9 +1380,9 @@ describe MergeRequest do
sha: shas.second)
end
- let!(:merge_request_pipeline) do
+ let!(:detached_merge_request_pipeline) do
create(:ci_pipeline,
- source: :merge_request,
+ source: :merge_request_event,
project: project,
ref: source_ref,
sha: shas.second,
@@ -1357,7 +1406,7 @@ describe MergeRequest do
it 'returns merge request pipeline first' do
expect(merge_request.all_pipelines)
- .to eq([merge_request_pipeline,
+ .to eq([detached_merge_request_pipeline,
branch_pipeline])
end
@@ -1370,9 +1419,9 @@ describe MergeRequest do
sha: shas.first)
end
- let!(:merge_request_pipeline_2) do
+ let!(:detached_merge_request_pipeline_2) do
create(:ci_pipeline,
- source: :merge_request,
+ source: :merge_request_event,
project: project,
ref: source_ref,
sha: shas.first,
@@ -1381,8 +1430,8 @@ describe MergeRequest do
it 'returns merge request pipelines first' do
expect(merge_request.all_pipelines)
- .to eq([merge_request_pipeline_2,
- merge_request_pipeline,
+ .to eq([detached_merge_request_pipeline_2,
+ detached_merge_request_pipeline,
branch_pipeline_2,
branch_pipeline])
end
@@ -1397,9 +1446,9 @@ describe MergeRequest do
sha: shas.first)
end
- let!(:merge_request_pipeline_2) do
+ let!(:detached_merge_request_pipeline_2) do
create(:ci_pipeline,
- source: :merge_request,
+ source: :merge_request_event,
project: project,
ref: source_ref,
sha: shas.first,
@@ -1420,16 +1469,35 @@ describe MergeRequest do
it 'returns only related merge request pipelines' do
expect(merge_request.all_pipelines)
- .to eq([merge_request_pipeline,
+ .to eq([detached_merge_request_pipeline,
branch_pipeline_2,
branch_pipeline])
expect(merge_request_2.all_pipelines)
- .to eq([merge_request_pipeline_2,
+ .to eq([detached_merge_request_pipeline_2,
branch_pipeline_2,
branch_pipeline])
end
end
+
+ context 'when detached merge request pipeline is run on head ref of the merge request' do
+ let!(:detached_merge_request_pipeline) do
+ create(:ci_pipeline,
+ source: :merge_request_event,
+ project: project,
+ ref: merge_request.ref_path,
+ sha: shas.second,
+ merge_request: merge_request)
+ end
+
+ it 'sets the head ref of the merge request to the pipeline ref' do
+ expect(detached_merge_request_pipeline.ref).to match(%r{refs/merge-requests/\d+/head})
+ end
+
+ it 'includes the detached merge request pipeline even though the ref is custom path' do
+ expect(merge_request.all_pipelines).to include(detached_merge_request_pipeline)
+ end
+ end
end
end
@@ -1470,6 +1538,37 @@ describe MergeRequest do
end
end
+ context 'when detached merge request pipeline is run on head ref of the merge request' do
+ let!(:pipeline) do
+ create(:ci_pipeline,
+ source: :merge_request_event,
+ project: merge_request.source_project,
+ ref: merge_request.ref_path,
+ sha: sha,
+ merge_request: merge_request)
+ end
+
+ let(:sha) { merge_request.diff_head_sha }
+
+ it 'sets the head ref of the merge request to the pipeline ref' do
+ expect(pipeline.ref).to match(%r{refs/merge-requests/\d+/head})
+ end
+
+ it 'updates correctly even though the target branch name of the merge request is different from the pipeline ref' do
+ expect { subject }
+ .to change { merge_request.reload.head_pipeline }
+ .from(nil).to(pipeline)
+ end
+
+ context 'when sha is not HEAD of the source branch' do
+ let(:sha) { merge_request.diff_base_sha }
+
+ it 'does not update head pipeline' do
+ expect { subject }.not_to change { merge_request.reload.head_pipeline }
+ end
+ end
+ end
+
context 'when there are no pipelines with the diff head sha' do
it 'does not update the head pipeline' do
expect { subject }
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index af7e3d3a6c9..717d4ae4ec0 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -182,7 +182,7 @@ describe Milestone do
describe '#total_items_count' do
before do
create :closed_issue, milestone: milestone, project: project
- create :merge_request, milestone: milestone
+ create :merge_request, milestone: milestone, source_project: project
end
it 'returns total count of issues and merge requests assigned to milestone' do
@@ -192,10 +192,10 @@ describe Milestone do
describe '#can_be_closed?' do
before do
- milestone = create :milestone
- create :closed_issue, milestone: milestone
+ milestone = create :milestone, project: project
+ create :closed_issue, milestone: milestone, project: project
- create :issue
+ create :issue, project: project
end
it 'returns true if milestone active and all nested issues closed' do
@@ -379,6 +379,21 @@ describe Milestone do
expect(milestone_ids).to be_empty
end
end
+
+ context 'when there is a milestone with a date after 294276 AD', :postgresql do
+ before do
+ past_milestone_project_1.update!(due_date: Date.new(294277, 1, 1))
+ end
+
+ it 'returns the next upcoming open milestone ID for each project and group' do
+ expect(milestone_ids).to contain_exactly(
+ current_milestone_project_1.id,
+ current_milestone_project_2.id,
+ current_milestone_group_1.id,
+ current_milestone_group_2.id
+ )
+ end
+ end
end
describe '#to_reference' do
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 475fbe56e4d..aadc298ae0b 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -775,4 +775,28 @@ describe Namespace do
end
end
end
+
+ describe '#auto_devops_enabled' do
+ context 'with users' do
+ let(:user) { create(:user) }
+
+ subject { user.namespace.auto_devops_enabled? }
+
+ before do
+ user.namespace.update!(auto_devops_enabled: auto_devops_enabled)
+ end
+
+ context 'when auto devops is explicitly enabled' do
+ let(:auto_devops_enabled) { true }
+
+ it { is_expected.to eq(true) }
+ end
+
+ context 'when auto devops is explicitly disabled' do
+ let(:auto_devops_enabled) { false }
+
+ it { is_expected.to eq(false) }
+ end
+ end
+ end
end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 385b8a7959f..eb6f6ff5faf 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -208,6 +208,24 @@ describe Note do
end
end
+ describe "edited?" do
+ let(:note) { build(:note, updated_by_id: nil, created_at: Time.now, updated_at: Time.now + 5.hours) }
+
+ context "with updated_by" do
+ it "returns true" do
+ note.updated_by = build(:user)
+
+ expect(note.edited?).to be_truthy
+ end
+ end
+
+ context "without updated_by" do
+ it "returns false" do
+ expect(note.edited?).to be_falsy
+ end
+ end
+ end
+
describe "confidential?" do
it "delegates to noteable" do
issue_note = build(:note, :on_issue)
diff --git a/spec/models/project_daily_statistic_spec.rb b/spec/models/project_daily_statistic_spec.rb
new file mode 100644
index 00000000000..86210af15d8
--- /dev/null
+++ b/spec/models/project_daily_statistic_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ProjectDailyStatistic do
+ it { is_expected.to belong_to(:project) }
+end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index 788b3179b01..5428fcb1271 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -177,9 +177,10 @@ describe JiraService do
expect(WebMock).to have_requested(:post, @remote_link_url).with(
body: hash_including(
GlobalID: 'GitLab',
+ relationship: 'mentioned on',
object: {
url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{commit_id}",
- title: "GitLab: Solved by commit #{commit_id}.",
+ title: "Solved by commit #{commit_id}.",
icon: { title: 'GitLab', url16x16: favicon_path },
status: { resolved: true }
}
diff --git a/spec/models/project_services/kubernetes_service_spec.rb b/spec/models/project_services/kubernetes_service_spec.rb
index 9c27357ffaf..56e587262ef 100644
--- a/spec/models/project_services/kubernetes_service_spec.rb
+++ b/spec/models/project_services/kubernetes_service_spec.rb
@@ -276,7 +276,7 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
it 'sets the variables' do
expect(subject.predefined_variables(project: project)).to include(
{ key: 'KUBE_URL', value: 'https://kube.domain.com', public: true },
- { key: 'KUBE_TOKEN', value: 'token', public: false },
+ { key: 'KUBE_TOKEN', value: 'token', public: false, masked: true },
{ key: 'KUBE_NAMESPACE', value: namespace, public: true },
{ key: 'KUBECONFIG', value: kubeconfig, public: false, file: true },
{ key: 'KUBE_CA_PEM', value: 'CA PEM DATA', public: true },
@@ -323,13 +323,14 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
end
context 'with valid pods' do
- let(:pod) { kube_pod(app: environment.slug) }
+ let(:pod) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug) }
+ let(:pod_with_no_terminal) { kube_pod(environment_slug: environment.slug, project_slug: project.full_path_slug, status: "Pending") }
let(:terminals) { kube_terminals(service, pod) }
before do
stub_reactive_cache(
service,
- pods: [pod, pod, kube_pod(app: "should-be-filtered-out")]
+ pods: [pod, pod, pod_with_no_terminal, kube_pod(environment_slug: "should-be-filtered-out")]
)
end
@@ -360,14 +361,16 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
context 'when kubernetes responds with valid pods' do
before do
stub_kubeclient_pods
+ stub_kubeclient_deployments # Used by EE
end
- it { is_expected.to eq(pods: [kube_pod]) }
+ it { is_expected.to include(pods: [kube_pod]) }
end
context 'when kubernetes responds with 500s' do
before do
stub_kubeclient_pods(status: 500)
+ stub_kubeclient_deployments(status: 500) # Used by EE
end
it { expect { subject }.to raise_error(Kubeclient::HttpError) }
@@ -376,9 +379,10 @@ describe KubernetesService, :use_clean_rails_memory_store_caching do
context 'when kubernetes responds with 404s' do
before do
stub_kubeclient_pods(status: 404)
+ stub_kubeclient_deployments(status: 404) # Used by EE
end
- it { is_expected.to eq(pods: []) }
+ it { is_expected.to include(pods: []) }
end
end
diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb
index b6cf4c72450..e9c7c94ad70 100644
--- a/spec/models/project_services/prometheus_service_spec.rb
+++ b/spec/models/project_services/prometheus_service_spec.rb
@@ -33,18 +33,38 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
describe 'Validations' do
context 'when manual_configuration is enabled' do
before do
- subject.manual_configuration = true
+ service.manual_configuration = true
end
- it { is_expected.to validate_presence_of(:api_url) }
+ it 'validates presence of api_url' do
+ expect(service).to validate_presence_of(:api_url)
+ end
end
context 'when manual configuration is disabled' do
before do
- subject.manual_configuration = false
+ service.manual_configuration = false
end
- it { is_expected.not_to validate_presence_of(:api_url) }
+ it 'does not validate presence of api_url' do
+ expect(service).not_to validate_presence_of(:api_url)
+ end
+ end
+
+ context 'when the api_url domain points to localhost or local network' do
+ let(:domain) { Addressable::URI.parse(service.api_url).hostname }
+
+ it 'cannot query' do
+ expect(service.can_query?).to be true
+
+ aggregate_failures do
+ ['127.0.0.1', '192.168.2.3'].each do |url|
+ allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)])
+
+ expect(service.can_query?).to be false
+ end
+ end
+ end
end
end
@@ -74,30 +94,35 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do
end
describe '#prometheus_client' do
+ let(:api_url) { 'http://some_url' }
+
+ before do
+ service.active = true
+ service.api_url = api_url
+ service.manual_configuration = manual_configuration
+ end
+
context 'manual configuration is enabled' do
- let(:api_url) { 'http://some_url' }
+ let(:manual_configuration) { true }
- before do
- subject.active = true
- subject.manual_configuration = true
- subject.api_url = api_url
+ it 'returns rest client from api_url' do
+ expect(service.prometheus_client.url).to eq(api_url)
end
- it 'returns rest client from api_url' do
- expect(subject.prometheus_client.url).to eq(api_url)
+ it 'calls valid?' do
+ allow(service).to receive(:valid?).and_call_original
+
+ expect(service.prometheus_client).not_to be_nil
+
+ expect(service).to have_received(:valid?)
end
end
context 'manual configuration is disabled' do
- let(:api_url) { 'http://some_url' }
-
- before do
- subject.manual_configuration = false
- subject.api_url = api_url
- end
+ let(:manual_configuration) { false }
it 'no client provided' do
- expect(subject.prometheus_client).to be_nil
+ expect(service.prometheus_client).to be_nil
end
end
end
diff --git a/spec/models/project_services/slack_slash_commands_service_spec.rb b/spec/models/project_services/slack_slash_commands_service_spec.rb
index 0d95f454819..5c4bce90ace 100644
--- a/spec/models/project_services/slack_slash_commands_service_spec.rb
+++ b/spec/models/project_services/slack_slash_commands_service_spec.rb
@@ -38,4 +38,11 @@ describe SlackSlashCommandsService do
end
end
end
+
+ describe '#chat_responder' do
+ it 'returns the responder to use for Slack' do
+ expect(described_class.new.chat_responder)
+ .to eq(Gitlab::Chat::Responder::Slack)
+ end
+ end
end
diff --git a/spec/models/project_services/youtrack_service_spec.rb b/spec/models/project_services/youtrack_service_spec.rb
new file mode 100644
index 00000000000..9524b526a46
--- /dev/null
+++ b/spec/models/project_services/youtrack_service_spec.rb
@@ -0,0 +1,38 @@
+require 'spec_helper'
+
+describe YoutrackService 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(:project_url) }
+ it { is_expected.to validate_presence_of(:issues_url) }
+ it_behaves_like 'issue tracker service URL attribute', :project_url
+ it_behaves_like 'issue tracker service URL attribute', :issues_url
+ end
+
+ context 'when service is inactive' do
+ before do
+ subject.active = false
+ end
+
+ it { is_expected.not_to validate_presence_of(:project_url) }
+ it { is_expected.not_to validate_presence_of(:issues_url) }
+ end
+ end
+
+ describe '.reference_pattern' do
+ it_behaves_like 'allows project key on reference pattern'
+
+ it 'does allow project prefix on the reference' do
+ expect(described_class.reference_pattern.match('YT-123')[:issue]).to eq('YT-123')
+ end
+ end
+end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 1f9088c2e6b..90dcf861849 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -50,6 +50,7 @@ describe Project do
it { is_expected.to have_one(:teamcity_service) }
it { is_expected.to have_one(:jira_service) }
it { is_expected.to have_one(:redmine_service) }
+ it { is_expected.to have_one(:youtrack_service) }
it { is_expected.to have_one(:custom_issue_tracker_service) }
it { is_expected.to have_one(:bugzilla_service) }
it { is_expected.to have_one(:gitlab_issue_tracker_service) }
@@ -135,15 +136,6 @@ describe Project do
end
end
- describe '#boards' do
- it 'raises an error when attempting to add more than one board to the project' do
- subject.boards.build
-
- expect { subject.boards.build }.to raise_error(Project::BoardLimitExceeded, 'Number of permitted boards exceeded')
- expect(subject.boards.size).to eq 1
- end
- end
-
describe 'ci_pipelines association' do
it 'returns only pipelines from ci_sources' do
expect(Ci::Pipeline).to receive(:ci_sources).and_call_original
@@ -428,6 +420,30 @@ describe Project do
end
end
+ describe '#ci_pipelines' do
+ let(:project) { create(:project) }
+
+ before do
+ create(:ci_pipeline, project: project, ref: 'master', source: :web)
+ create(:ci_pipeline, project: project, ref: 'master', source: :external)
+ end
+
+ it 'has ci pipelines' do
+ expect(project.ci_pipelines.size).to eq(2)
+ end
+
+ context 'when builds are disabled' do
+ before do
+ project.project_feature.update_attribute(:builds_access_level, ProjectFeature::DISABLED)
+ end
+
+ it 'should return .external pipelines' do
+ expect(project.ci_pipelines).to all(have_attributes(source: 'external'))
+ expect(project.ci_pipelines.size).to eq(1)
+ end
+ end
+ end
+
describe 'project token' do
it 'sets an random token if none provided' do
project = FactoryBot.create(:project, runners_token: '')
@@ -458,6 +474,7 @@ describe Project do
it { is_expected.to delegate_method(:name).to(:owner).with_prefix(true).with_arguments(allow_nil: true) }
it { is_expected.to delegate_method(:group_clusters_enabled?).to(:group).with_arguments(allow_nil: true) }
it { is_expected.to delegate_method(:root_ancestor).to(:namespace).with_arguments(allow_nil: true) }
+ it { is_expected.to delegate_method(:last_pipeline).to(:commit).with_arguments(allow_nil: true) }
end
describe '#to_reference_with_postfix' do
@@ -2335,6 +2352,18 @@ describe Project do
end
end
+ describe '#daily_statistics_enabled?' do
+ it { is_expected.to be_daily_statistics_enabled }
+
+ context 'when :project_daily_statistics is disabled for the project' do
+ before do
+ stub_feature_flags(project_daily_statistics: { thing: subject, enabled: false })
+ end
+
+ it { is_expected.not_to be_daily_statistics_enabled }
+ end
+ end
+
describe '#change_head' do
let(:project) { create(:project, :repository) }
@@ -2350,6 +2379,12 @@ describe Project do
project.change_head(project.default_branch)
end
+ it 'updates commit count' do
+ expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:commit_count])
+
+ project.change_head(project.default_branch)
+ end
+
it 'copies the gitattributes' do
expect(project.repository).to receive(:copy_gitattributes).with(project.default_branch)
project.change_head(project.default_branch)
@@ -2486,6 +2521,16 @@ describe Project do
end
end
+ describe '#set_repository_writable!' do
+ it 'sets repository_read_only to false' do
+ project = create(:project, :read_only)
+
+ expect { project.set_repository_writable! }
+ .to change(project, :repository_read_only)
+ .from(true).to(false)
+ end
+ end
+
describe '#pushes_since_gc' do
let(:project) { create(:project) }
@@ -2559,7 +2604,7 @@ describe Project do
shared_examples 'same behavior between KubernetesService and Platform::Kubernetes' do
it 'returns variables from this service' do
expect(project.deployment_variables).to include(
- { key: 'KUBE_TOKEN', value: project.deployment_platform.token, public: false }
+ { key: 'KUBE_TOKEN', value: project.deployment_platform.token, public: false, masked: true }
)
end
end
@@ -2584,7 +2629,7 @@ describe Project do
it 'should return token from kubernetes namespace' do
expect(project.deployment_variables).to include(
- { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false }
+ { key: 'KUBE_TOKEN', value: kubernetes_namespace.service_account_token, public: false, masked: true }
)
end
end
@@ -2665,7 +2710,7 @@ describe Project do
end
describe '#any_lfs_file_locks?', :request_store do
- set(:project) { create(:project) }
+ let!(:project) { create(:project) }
it 'returns false when there are no LFS file locks' do
expect(project.any_lfs_file_locks?).to be_falsey
@@ -3103,6 +3148,53 @@ describe Project do
expect(projects).to eq([public_project])
end
end
+
+ context 'with requested visibility levels' do
+ set(:internal_project) { create(:project, :internal, :repository) }
+ set(:private_project_2) { create(:project, :private) }
+
+ context 'with admin user' do
+ set(:admin) { create(:admin) }
+
+ it 'returns all projects' do
+ projects = described_class.all.public_or_visible_to_user(admin, [])
+
+ expect(projects).to match_array([public_project, private_project, private_project_2, internal_project])
+ end
+
+ it 'returns all public and private projects' do
+ projects = described_class.all.public_or_visible_to_user(admin, [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::PRIVATE])
+
+ expect(projects).to match_array([public_project, private_project, private_project_2])
+ end
+
+ it 'returns all private projects' do
+ projects = described_class.all.public_or_visible_to_user(admin, [Gitlab::VisibilityLevel::PRIVATE])
+
+ expect(projects).to match_array([private_project, private_project_2])
+ end
+ end
+
+ context 'with regular user' do
+ it 'returns authorized projects' do
+ projects = described_class.all.public_or_visible_to_user(user, [])
+
+ expect(projects).to match_array([public_project, private_project, internal_project])
+ end
+
+ it "returns user's public and private projects" do
+ projects = described_class.all.public_or_visible_to_user(user, [Gitlab::VisibilityLevel::PUBLIC, Gitlab::VisibilityLevel::PRIVATE])
+
+ expect(projects).to match_array([public_project, private_project])
+ end
+
+ it 'returns one private project' do
+ projects = described_class.all.public_or_visible_to_user(user, [Gitlab::VisibilityLevel::PRIVATE])
+
+ expect(projects).to eq([private_project])
+ end
+ end
+ end
end
describe '.with_feature_available_for_user' do
@@ -3382,28 +3474,42 @@ describe Project do
project.migrate_to_hashed_storage!
end
- it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the project repo is in use' do
- Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: false)).increase
+ it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the project repo is in use' do
+ Gitlab::ReferenceCounter.new(Gitlab::GlRepository::PROJECT.identifier_for_subject(project)).increase
- expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in)
+ expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
project.migrate_to_hashed_storage!
end
- it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the wiki repo is in use' do
- Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: true)).increase
+ it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the wiki repo is in use' do
+ Gitlab::ReferenceCounter.new(Gitlab::GlRepository::WIKI.identifier_for_subject(project)).increase
- expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in)
+ expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
project.migrate_to_hashed_storage!
end
- it 'schedules ProjectMigrateHashedStorageWorker' do
- expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).with(project.id)
+ it 'schedules HashedStorage::ProjectMigrateWorker' do
+ expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_async).with(project.id)
project.migrate_to_hashed_storage!
end
end
+
+ describe '#rollback_to_legacy_storage!' do
+ let(:project) { create(:project, :empty_repo, :legacy_storage) }
+
+ it 'returns nil' do
+ expect(project.rollback_to_legacy_storage!).to be_nil
+ end
+
+ it 'does not run validations' do
+ expect(project).not_to receive(:valid?)
+
+ project.rollback_to_legacy_storage!
+ end
+ end
end
context 'hashed storage' do
@@ -3479,20 +3585,34 @@ describe Project do
project = create(:project, storage_version: 1, skip_disk_validation: true)
Sidekiq::Testing.fake! do
- expect { project.migrate_to_hashed_storage! }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(1)
+ expect { project.migrate_to_hashed_storage! }.to change(HashedStorage::ProjectMigrateWorker.jobs, :size).by(1)
end
end
end
end
- end
- describe '#gl_repository' do
- let(:project) { create(:project) }
+ describe '#rollback_to_legacy_storage!' do
+ let(:project) { create(:project, :repository, skip_disk_validation: true) }
+
+ it 'returns true' do
+ expect(project.rollback_to_legacy_storage!).to be_truthy
+ end
+
+ it 'does not run validations' do
+ expect(project).not_to receive(:valid?)
- it 'delegates to Gitlab::GlRepository.gl_repository' do
- expect(Gitlab::GlRepository).to receive(:gl_repository).with(project, true)
+ project.rollback_to_legacy_storage!
+ end
+
+ it 'does not flag as read-only' do
+ expect { project.rollback_to_legacy_storage! }.not_to change { project.repository_read_only }
+ end
- project.gl_repository(is_wiki: true)
+ it 'enqueues a job' do
+ Sidekiq::Testing.fake! do
+ expect { project.rollback_to_legacy_storage! }.to change(HashedStorage::ProjectRollbackWorker.jobs, :size).by(1)
+ end
+ end
end
end
@@ -3545,12 +3665,36 @@ describe Project do
subject { project.auto_devops_enabled? }
+ context 'when explicitly enabled' do
+ before do
+ create(:project_auto_devops, project: project)
+ end
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when explicitly disabled' do
+ before do
+ create(:project_auto_devops, project: project, enabled: false)
+ end
+
+ it { is_expected.to be_falsey }
+ end
+
context 'when enabled in settings' do
before do
stub_application_setting(auto_devops_enabled: true)
end
it { is_expected.to be_truthy }
+ end
+
+ context 'when disabled in settings' do
+ before do
+ stub_application_setting(auto_devops_enabled: false)
+ end
+
+ it { is_expected.to be_falsey }
context 'when explicitly enabled' do
before do
@@ -3562,34 +3706,91 @@ describe Project do
context 'when explicitly disabled' do
before do
- create(:project_auto_devops, project: project, enabled: false)
+ create(:project_auto_devops, :disabled, project: project)
end
it { is_expected.to be_falsey }
end
end
- context 'when disabled in settings' do
+ context 'when force_autodevops_on_by_default is enabled for the project' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'with group parents' do
+ let(:instance_enabled) { true }
+
before do
- stub_application_setting(auto_devops_enabled: false)
+ stub_application_setting(auto_devops_enabled: instance_enabled)
+ project.update!(namespace: parent_group)
end
- it { is_expected.to be_falsey }
+ context 'when enabled on parent' do
+ let(:parent_group) { create(:group, :auto_devops_enabled) }
- context 'when explicitly enabled' do
- before do
- create(:project_auto_devops, project: project)
+ context 'when auto devops instance enabled' do
+ it { is_expected.to be_truthy }
end
- it { is_expected.to be_truthy }
+ context 'when auto devops instance disabled' do
+ let(:instance_disabled) { false }
+
+ it { is_expected.to be_truthy }
+ end
end
- context 'when force_autodevops_on_by_default is enabled for the project' do
- before do
- Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(100)
+ context 'when disabled on parent' do
+ let(:parent_group) { create(:group, :auto_devops_disabled) }
+
+ context 'when auto devops instance enabled' do
+ it { is_expected.to be_falsy }
end
- it { is_expected.to be_truthy }
+ context 'when auto devops instance disabled' do
+ let(:instance_disabled) { false }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ context 'when enabled on root parent', :nested_groups do
+ let(:parent_group) { create(:group, parent: create(:group, :auto_devops_enabled)) }
+
+ context 'when auto devops instance enabled' do
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when auto devops instance disabled' do
+ let(:instance_disabled) { false }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when explicitly disabled on parent' do
+ let(:parent_group) { create(:group, :auto_devops_disabled, parent: create(:group, :auto_devops_enabled)) }
+
+ it { is_expected.to be_falsy }
+ end
+ end
+
+ context 'when disabled on root parent', :nested_groups do
+ let(:parent_group) { create(:group, parent: create(:group, :auto_devops_disabled)) }
+
+ context 'when auto devops instance enabled' do
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when auto devops instance disabled' do
+ let(:instance_disabled) { false }
+
+ it { is_expected.to be_falsy }
+ end
+
+ context 'when explicitly disabled on parent' do
+ let(:parent_group) { create(:group, :auto_devops_disabled, parent: create(:group, :auto_devops_enabled)) }
+
+ it { is_expected.to be_falsy }
+ end
end
end
end
@@ -3636,15 +3837,52 @@ describe Project do
end
end
end
+
+ context 'when enabled on group' do
+ it 'has auto devops implicitly enabled' do
+ project.update(namespace: create(:group, :auto_devops_enabled))
+
+ expect(project).to have_auto_devops_implicitly_enabled
+ end
+ end
+
+ context 'when enabled on parent group' do
+ it 'has auto devops implicitly enabled' do
+ subgroup = create(:group, parent: create(:group, :auto_devops_enabled))
+ project.update(namespace: subgroup)
+
+ expect(project).to have_auto_devops_implicitly_enabled
+ end
+ end
end
describe '#has_auto_devops_implicitly_disabled?' do
+ set(:project) { create(:project) }
+
before do
allow(Feature).to receive(:enabled?).and_call_original
Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(0)
end
- set(:project) { create(:project) }
+ 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_disabled
+ 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_disabled
+ end
+ end
context 'when enabled in settings' do
before do
@@ -3667,6 +3905,8 @@ describe Project do
context 'when force_autodevops_on_by_default is enabled for the project' do
before do
+ create(:project_auto_devops, project: project, enabled: false)
+
Feature.get(:force_autodevops_on_by_default).enable_percentage_of_actors(100)
end
@@ -3675,23 +3915,20 @@ describe Project do
end
end
- context 'when explicitly disabled' do
- before do
- create(:project_auto_devops, project: project, enabled: false)
- end
+ context 'when disabled on group' do
+ it 'has auto devops implicitly disabled' do
+ project.update!(namespace: create(:group, :auto_devops_disabled))
- it 'does not have auto devops implicitly disabled' do
- expect(project).not_to have_auto_devops_implicitly_disabled
+ expect(project).to have_auto_devops_implicitly_disabled
end
end
- context 'when explicitly enabled' do
- before do
- create(:project_auto_devops, project: project, enabled: true)
- end
+ context 'when disabled on parent group' do
+ it 'has auto devops implicitly disabled' do
+ subgroup = create(:group, parent: create(:group, :auto_devops_disabled))
+ project.update!(namespace: subgroup)
- it 'does not have auto devops implicitly disabled' do
- expect(project).not_to have_auto_devops_implicitly_disabled
+ expect(project).to have_auto_devops_implicitly_disabled
end
end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 3ccc706edf2..7be8d67ba9e 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -71,6 +71,14 @@ describe ProjectWiki do
expect(project_wiki.create_page("index", "test content")).to be_truthy
end
+ it "creates a new wiki repo with a default commit message" do
+ expect(project_wiki.create_page("index", "test content", :markdown, "")).to be_truthy
+
+ page = project_wiki.find_page('index')
+
+ expect(page.last_version.message).to eq("#{user.username} created page: index")
+ end
+
it "raises CouldNotCreateWikiError if it can't create the wiki repository" do
# Create a fresh project which will not have a wiki
project_wiki = described_class.new(create(:project), user)
diff --git a/spec/models/prometheus_metric_spec.rb b/spec/models/prometheus_metric_spec.rb
index 2b978c1c8ff..3610408c138 100644
--- a/spec/models/prometheus_metric_spec.rb
+++ b/spec/models/prometheus_metric_spec.rb
@@ -4,7 +4,6 @@ require 'spec_helper'
describe PrometheusMetric do
subject { build(:prometheus_metric) }
- let(:other_project) { build(:project) }
it_behaves_like 'having unique enum values'
@@ -16,17 +15,17 @@ describe PrometheusMetric do
describe 'common metrics' do
using RSpec::Parameterized::TableSyntax
- where(:common, :project, :result) do
- false | other_project | true
- false | nil | false
- true | other_project | false
- true | nil | true
+ where(:common, :with_project, :result) do
+ false | true | true
+ false | false | false
+ true | true | false
+ true | false | true
end
with_them do
before do
subject.common = common
- subject.project = project
+ subject.project = with_project ? build(:project) : nil
end
it { expect(subject.valid?).to eq(result) }
diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb
index 4c677200ae2..dafe7646366 100644
--- a/spec/models/protected_branch_spec.rb
+++ b/spec/models/protected_branch_spec.rb
@@ -190,4 +190,32 @@ describe ProtectedBranch do
end
end
end
+
+ describe '#any_protected?' do
+ context 'existing project' do
+ let(:project) { create(:project, :repository) }
+
+ it 'returns true when any of the branch names match a protected branch via direct match' do
+ create(:protected_branch, project: project, name: 'foo')
+
+ expect(described_class.any_protected?(project, ['foo', 'production/some-branch'])).to eq(true)
+ end
+
+ it 'returns true when any of the branch matches a protected branch via wildcard match' do
+ create(:protected_branch, project: project, name: 'production/*')
+
+ expect(described_class.any_protected?(project, ['foo', 'production/some-branch'])).to eq(true)
+ end
+
+ it 'returns false when none of branches does not match a protected branch via direct match' do
+ expect(described_class.any_protected?(project, ['foo'])).to eq(false)
+ end
+
+ it 'returns false when none of the branches does not match a protected branch via wildcard match' do
+ create(:protected_branch, project: project, name: 'production/*')
+
+ expect(described_class.any_protected?(project, ['staging/some-branch'])).to eq(false)
+ end
+ end
+ end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index f78760bf567..6599b4e765a 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -1095,65 +1095,69 @@ describe Repository do
end
end
- describe '#exists?' do
- it 'returns true when a repository exists' do
- expect(repository.exists?).to be(true)
- end
-
- it 'returns false if no full path can be constructed' do
- allow(repository).to receive(:full_path).and_return(nil)
-
- expect(repository.exists?).to be(false)
- end
-
- context 'with broken storage', :broken_storage do
- it 'should raise a storage error' do
- expect_to_raise_storage_error { broken_repository.exists? }
- end
- end
-
+ shared_examples 'asymmetric cached method' do |method|
context 'asymmetric caching', :use_clean_rails_memory_store_caching, :request_store do
let(:cache) { repository.send(:cache) }
let(:request_store_cache) { repository.send(:request_store_cache) }
context 'when it returns true' do
before do
- expect(repository.raw_repository).to receive(:exists?).once.and_return(true)
+ expect(repository.raw_repository).to receive(method).once.and_return(true)
end
it 'caches the output in RequestStore' do
expect do
- repository.exists?
- end.to change { request_store_cache.read(:exists?) }.from(nil).to(true)
+ repository.send(method)
+ end.to change { request_store_cache.read(method) }.from(nil).to(true)
end
it 'caches the output in RepositoryCache' do
expect do
- repository.exists?
- end.to change { cache.read(:exists?) }.from(nil).to(true)
+ repository.send(method)
+ end.to change { cache.read(method) }.from(nil).to(true)
end
end
context 'when it returns false' do
before do
- expect(repository.raw_repository).to receive(:exists?).once.and_return(false)
+ expect(repository.raw_repository).to receive(method).once.and_return(false)
end
it 'caches the output in RequestStore' do
expect do
- repository.exists?
- end.to change { request_store_cache.read(:exists?) }.from(nil).to(false)
+ repository.send(method)
+ end.to change { request_store_cache.read(method) }.from(nil).to(false)
end
it 'does NOT cache the output in RepositoryCache' do
expect do
- repository.exists?
- end.not_to change { cache.read(:exists?) }.from(nil)
+ repository.send(method)
+ end.not_to change { cache.read(method) }.from(nil)
end
end
end
end
+ describe '#exists?' do
+ it 'returns true when a repository exists' do
+ expect(repository.exists?).to be(true)
+ end
+
+ it 'returns false if no full path can be constructed' do
+ allow(repository).to receive(:full_path).and_return(nil)
+
+ expect(repository.exists?).to be(false)
+ end
+
+ context 'with broken storage', :broken_storage do
+ it 'should raise a storage error' do
+ expect_to_raise_storage_error { broken_repository.exists? }
+ end
+ end
+
+ it_behaves_like 'asymmetric cached method', :exists?
+ end
+
describe '#has_visible_content?' do
before do
# If raw_repository.has_visible_content? gets called more than once then
@@ -1271,6 +1275,8 @@ describe Repository do
repository.root_ref
repository.root_ref
end
+
+ it_behaves_like 'asymmetric cached method', :root_ref
end
describe '#expire_root_ref_cache' do
@@ -1373,6 +1379,29 @@ describe Repository do
end
end
+ describe '#merge_to_ref' do
+ let(:merge_request) do
+ create(:merge_request, source_branch: 'feature',
+ target_branch: 'master',
+ source_project: project)
+ end
+
+ it 'writes merge of source and target to MR merge_ref_path' do
+ merge_commit_id = repository.merge_to_ref(user,
+ merge_request.diff_head_sha,
+ merge_request,
+ merge_request.merge_ref_path,
+ 'Custom message')
+
+ merge_commit = repository.commit(merge_commit_id)
+
+ expect(merge_commit.message).to eq('Custom message')
+ expect(merge_commit.author_name).to eq(user.name)
+ expect(merge_commit.author_email).to eq(user.commit_email)
+ expect(repository.blob_at(merge_commit.id, 'files/ruby/feature.rb')).to be_present
+ end
+ end
+
describe '#ff_merge' do
before do
repository.add_branch(user, 'ff-target', 'feature~5')
@@ -2214,7 +2243,7 @@ describe Repository do
rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id)
end
- describe '#ancestor?' do
+ shared_examples '#ancestor?' do
let(:commit) { repository.commit }
let(:ancestor) { commit.parents.first }
@@ -2238,6 +2267,20 @@ describe Repository do
end
end
+ describe '#ancestor? with Gitaly enabled' do
+ it_behaves_like "#ancestor?"
+ end
+
+ describe '#ancestor? with Rugged enabled', :enable_rugged do
+ it 'calls out to the Rugged implementation' do
+ allow_any_instance_of(Rugged).to receive(:merge_base).with(repository.commit.id, Gitlab::Git::BLANK_SHA).and_call_original
+
+ repository.ancestor?(repository.commit.id, Gitlab::Git::BLANK_SHA)
+ end
+
+ it_behaves_like '#ancestor?'
+ end
+
describe '#archive_metadata' do
let(:ref) { 'master' }
let(:storage_path) { '/tmp' }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 1edd8e69b8f..1be29d039a7 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -660,6 +660,68 @@ describe User do
end
end
+ describe '#highest_role' do
+ let(:user) { create(:user) }
+
+ let(:group) { create(:group) }
+
+ it 'returns NO_ACCESS if none has been set' do
+ expect(user.highest_role).to eq(Gitlab::Access::NO_ACCESS)
+ end
+
+ it 'returns MAINTAINER if user is maintainer of a project' do
+ create(:project, group: group) do |project|
+ project.add_maintainer(user)
+ end
+
+ expect(user.highest_role).to eq(Gitlab::Access::MAINTAINER)
+ end
+
+ it 'returns the highest role if user is member of multiple projects' do
+ create(:project, group: group) do |project|
+ project.add_maintainer(user)
+ end
+
+ create(:project, group: group) do |project|
+ project.add_developer(user)
+ end
+
+ expect(user.highest_role).to eq(Gitlab::Access::MAINTAINER)
+ end
+
+ it 'returns MAINTAINER if user is maintainer of a group' do
+ create(:group) do |group|
+ group.add_user(user, GroupMember::MAINTAINER)
+ end
+
+ expect(user.highest_role).to eq(Gitlab::Access::MAINTAINER)
+ end
+
+ it 'returns the highest role if user is member of multiple groups' do
+ create(:group) do |group|
+ group.add_user(user, GroupMember::MAINTAINER)
+ end
+
+ create(:group) do |group|
+ group.add_user(user, GroupMember::DEVELOPER)
+ end
+
+ expect(user.highest_role).to eq(Gitlab::Access::MAINTAINER)
+ end
+
+ it 'returns the highest role if user is member of multiple groups and projects' do
+ create(:group) do |group|
+ group.add_user(user, GroupMember::DEVELOPER)
+ end
+
+ create(:project, group: group) do |project|
+ project.add_maintainer(user)
+ end
+
+ expect(user.highest_role).to eq(Gitlab::Access::MAINTAINER)
+ end
+ end
+
describe '#update_tracked_fields!', :clean_gitlab_redis_shared_state do
let(:request) { OpenStruct.new(remote_ip: "127.0.0.1") }
let(:user) { create(:user) }
@@ -976,43 +1038,43 @@ describe User do
end
end
- describe '.filter' do
+ describe '.filter_items' do
let(:user) { double }
it 'filters by active users by default' do
expect(described_class).to receive(:active).and_return([user])
- expect(described_class.filter(nil)).to include user
+ expect(described_class.filter_items(nil)).to include user
end
it 'filters by admins' do
expect(described_class).to receive(:admins).and_return([user])
- expect(described_class.filter('admins')).to include user
+ expect(described_class.filter_items('admins')).to include user
end
it 'filters by blocked' do
expect(described_class).to receive(:blocked).and_return([user])
- expect(described_class.filter('blocked')).to include user
+ expect(described_class.filter_items('blocked')).to include user
end
it 'filters by two_factor_disabled' do
expect(described_class).to receive(:without_two_factor).and_return([user])
- expect(described_class.filter('two_factor_disabled')).to include user
+ expect(described_class.filter_items('two_factor_disabled')).to include user
end
it 'filters by two_factor_enabled' do
expect(described_class).to receive(:with_two_factor).and_return([user])
- expect(described_class.filter('two_factor_enabled')).to include user
+ expect(described_class.filter_items('two_factor_enabled')).to include user
end
it 'filters by wop' do
expect(described_class).to receive(:without_projects).and_return([user])
- expect(described_class.filter('wop')).to include user
+ expect(described_class.filter_items('wop')).to include user
end
end
diff --git a/spec/policies/commit_policy_spec.rb b/spec/policies/commit_policy_spec.rb
new file mode 100644
index 00000000000..41f6fb08426
--- /dev/null
+++ b/spec/policies/commit_policy_spec.rb
@@ -0,0 +1,55 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe CommitPolicy do
+ describe '#rules' do
+ let(:user) { create(:user) }
+ let(:commit) { project.repository.head_commit }
+ let(:policy) { described_class.new(user, commit) }
+
+ context 'when project is public' do
+ let(:project) { create(:project, :public, :repository) }
+
+ it 'can read commit and create a note' do
+ expect(policy).to be_allowed(:read_commit)
+ end
+
+ context 'when repository access level is private' do
+ let(:project) { create(:project, :public, :repository, :repository_private) }
+
+ it 'can not read commit and create a note' do
+ expect(policy).to be_disallowed(:read_commit)
+ end
+
+ context 'when the user is a project member' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'can read commit and create a note' do
+ expect(policy).to be_allowed(:read_commit)
+ end
+ end
+ end
+ end
+
+ context 'when project is private' do
+ let(:project) { create(:project, :private, :repository) }
+
+ it 'can not read commit and create a note' do
+ expect(policy).to be_disallowed(:read_commit)
+ end
+
+ context 'when the user is a project member' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'can read commit and create a note' do
+ expect(policy).to be_allowed(:read_commit)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/policies/global_policy_spec.rb b/spec/policies/global_policy_spec.rb
index 30d68e7dc9d..12be3927e18 100644
--- a/spec/policies/global_policy_spec.rb
+++ b/spec/policies/global_policy_spec.rb
@@ -181,6 +181,18 @@ describe GlobalPolicy do
end
end
+ describe 'read instance metadata' do
+ context 'regular user' do
+ it { is_expected.to be_allowed(:read_instance_metadata) }
+ end
+
+ context 'anonymous' do
+ let(:current_user) { nil }
+
+ it { is_expected.not_to be_allowed(:read_instance_metadata) }
+ end
+ end
+
describe 'read instance statistics' do
context 'regular user' do
it { is_expected.to be_allowed(:read_instance_statistics) }
diff --git a/spec/policies/group_policy_spec.rb b/spec/policies/group_policy_spec.rb
index be1804c5ce0..92bdaa8b8b8 100644
--- a/spec/policies/group_policy_spec.rb
+++ b/spec/policies/group_policy_spec.rb
@@ -74,6 +74,29 @@ describe GroupPolicy do
end
end
+ context 'with no user and public project' do
+ let(:project) { create(:project, :public) }
+ let(:current_user) { nil }
+
+ before do
+ create(:project_group_link, project: project, group: group)
+ end
+
+ it { expect_disallowed(:read_group) }
+ end
+
+ context 'with foreign user and public project' do
+ let(:project) { create(:project, :public) }
+ let(:user) { create(:user) }
+ let(:current_user) { create(:user) }
+
+ before do
+ create(:project_group_link, project: project, group: group)
+ end
+
+ it { expect_disallowed(:read_group) }
+ end
+
context 'has projects' do
let(:current_user) { create(:user) }
let(:project) { create(:project, namespace: group) }
@@ -82,17 +105,13 @@ describe GroupPolicy do
project.add_developer(current_user)
end
- it do
- expect_allowed(:read_group, :read_label)
- end
+ it { expect_allowed(:read_label, :read_list) }
context 'in subgroups', :nested_groups do
let(:subgroup) { create(:group, :private, parent: group) }
let(:project) { create(:project, namespace: subgroup) }
- it do
- expect_allowed(:read_group, :read_label)
- end
+ it { expect_allowed(:read_label, :read_list) }
end
end
diff --git a/spec/policies/identity_provider_policy_spec.rb b/spec/policies/identity_provider_policy_spec.rb
new file mode 100644
index 00000000000..2520469d4e7
--- /dev/null
+++ b/spec/policies/identity_provider_policy_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe IdentityProviderPolicy do
+ subject(:policy) { described_class.new(user, provider) }
+ let(:user) { User.new }
+ let(:provider) { :a_provider }
+
+ describe '#rules' do
+ it { is_expected.to be_allowed(:link) }
+ it { is_expected.to be_allowed(:unlink) }
+
+ context 'when user is anonymous' do
+ let(:user) { nil }
+
+ it { is_expected.not_to be_allowed(:link) }
+ it { is_expected.not_to be_allowed(:unlink) }
+ end
+
+ %w[saml cas3].each do |provider_name|
+ context "when provider is #{provider_name}" do
+ let(:provider) { provider_name }
+
+ it { is_expected.to be_allowed(:link) }
+ it { is_expected.not_to be_allowed(:unlink) }
+ end
+ end
+ end
+end
diff --git a/spec/policies/issuable_policy_spec.rb b/spec/policies/issuable_policy_spec.rb
index db3df760472..6d34b0a8b4b 100644
--- a/spec/policies/issuable_policy_spec.rb
+++ b/spec/policies/issuable_policy_spec.rb
@@ -13,7 +13,7 @@ describe IssuablePolicy, models: true do
context 'when user is able to read project' do
it 'enables user to read and update issuables' do
- expect(policies).to be_allowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request)
+ expect(policies).to be_allowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request, :reopen_merge_request)
end
end
@@ -24,12 +24,12 @@ describe IssuablePolicy, models: true do
it 'enables user to read and update issuables' do
project.add_maintainer(user)
- expect(policies).to be_allowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request)
+ expect(policies).to be_allowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request, :reopen_merge_request)
end
end
it 'disallows user from reading and updating issuables from that project' do
- expect(policies).to be_disallowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request)
+ expect(policies).to be_disallowed(:read_issue, :update_issue, :reopen_issue, :read_merge_request, :update_merge_request, :reopen_merge_request)
end
end
end
diff --git a/spec/policies/merge_request_policy_spec.rb b/spec/policies/merge_request_policy_spec.rb
new file mode 100644
index 00000000000..1efa70addc2
--- /dev/null
+++ b/spec/policies/merge_request_policy_spec.rb
@@ -0,0 +1,50 @@
+require 'spec_helper'
+
+describe MergeRequestPolicy do
+ let(:guest) { create(:user) }
+ let(:author) { create(:user) }
+ let(:developer) { create(:user) }
+ let(:project) { create(:project, :public) }
+
+ def permissions(user, merge_request)
+ described_class.new(user, merge_request)
+ end
+
+ before do
+ project.add_guest(guest)
+ project.add_guest(author)
+ project.add_developer(developer)
+ end
+
+ context 'when merge request is unlocked' do
+ let(:merge_request) { create(:merge_request, :closed, source_project: project, target_project: project, author: author) }
+
+ it 'allows author to reopen merge request' do
+ expect(permissions(author, merge_request)).to be_allowed(:reopen_merge_request)
+ end
+
+ it 'allows developer to reopen merge request' do
+ expect(permissions(developer, merge_request)).to be_allowed(:reopen_merge_request)
+ end
+
+ it 'prevents guest from reopening merge request' do
+ expect(permissions(guest, merge_request)).to be_disallowed(:reopen_merge_request)
+ end
+ end
+
+ context 'when merge request is locked' do
+ let(:merge_request_locked) { create(:merge_request, :closed, discussion_locked: true, source_project: project, target_project: project, author: author) }
+
+ it 'prevents author from reopening merge request' do
+ expect(permissions(author, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+
+ it 'prevents developer from reopening merge request' do
+ expect(permissions(developer, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+
+ it 'prevents guests from reopening merge request' do
+ expect(permissions(guest, merge_request_locked)).to be_disallowed(:reopen_merge_request)
+ end
+ end
+end
diff --git a/spec/policies/note_policy_spec.rb b/spec/policies/note_policy_spec.rb
index 0e848c74659..4be7a0266d1 100644
--- a/spec/policies/note_policy_spec.rb
+++ b/spec/policies/note_policy_spec.rb
@@ -1,28 +1,15 @@
require 'spec_helper'
-describe NotePolicy, mdoels: true do
+describe NotePolicy do
describe '#rules' do
let(:user) { create(:user) }
let(:project) { create(:project, :public) }
let(:issue) { create(:issue, project: project) }
-
- def policies(noteable = nil)
- return @policies if @policies
-
- noteable ||= issue
- note = if noteable.is_a?(Commit)
- create(:note_on_commit, commit_id: noteable.id, author: user, project: project)
- else
- create(:note, noteable: noteable, author: user, project: project)
- end
-
- @policies = described_class.new(user, note)
- end
+ let(:noteable) { issue }
+ let(:policy) { described_class.new(user, note) }
+ let(:note) { create(:note, noteable: noteable, author: user, project: project) }
shared_examples_for 'a discussion with a private noteable' do
- let(:noteable) { issue }
- let(:policy) { policies(noteable) }
-
context 'when the note author can no longer see the noteable' do
it 'can not edit nor read the note' do
expect(policy).to be_disallowed(:admin_note)
@@ -46,12 +33,21 @@ describe NotePolicy, mdoels: true do
end
end
- context 'when the project is private' do
- let(:project) { create(:project, :private, :repository) }
+ context 'when the noteable is a commit' do
+ let(:commit) { project.repository.head_commit }
+ let(:note) { create(:note_on_commit, commit_id: commit.id, author: user, project: project) }
+
+ context 'when the project is private' do
+ let(:project) { create(:project, :private, :repository) }
+
+ it_behaves_like 'a discussion with a private noteable'
+ end
- context 'when the noteable is a commit' do
- it_behaves_like 'a discussion with a private noteable' do
- let(:noteable) { project.repository.head_commit }
+ context 'when the project is public' do
+ context 'when repository access level is private' do
+ let(:project) { create(:project, :public, :repository, :repository_private) }
+
+ it_behaves_like 'a discussion with a private noteable'
end
end
end
@@ -59,44 +55,44 @@ describe NotePolicy, mdoels: true do
context 'when the project is public' do
context 'when the note author is not a project member' do
it 'can edit a note' do
- expect(policies).to be_allowed(:admin_note)
- expect(policies).to be_allowed(:resolve_note)
- expect(policies).to be_allowed(:read_note)
+ expect(policy).to be_allowed(:admin_note)
+ expect(policy).to be_allowed(:resolve_note)
+ expect(policy).to be_allowed(:read_note)
end
end
context 'when the noteable is a project snippet' do
- it 'can edit note' do
- policies = policies(create(:project_snippet, :public, project: project))
+ let(:noteable) { create(:project_snippet, :public, project: project) }
- expect(policies).to be_allowed(:admin_note)
- expect(policies).to be_allowed(:resolve_note)
- expect(policies).to be_allowed(:read_note)
+ it 'can edit note' do
+ expect(policy).to be_allowed(:admin_note)
+ expect(policy).to be_allowed(:resolve_note)
+ expect(policy).to be_allowed(:read_note)
end
context 'when it is private' do
- it_behaves_like 'a discussion with a private noteable' do
- let(:noteable) { create(:project_snippet, :private, project: project) }
- end
+ let(:noteable) { create(:project_snippet, :private, project: project) }
+
+ it_behaves_like 'a discussion with a private noteable'
end
end
context 'when the noteable is a personal snippet' do
- it 'can edit note' do
- policies = policies(create(:personal_snippet, :public))
+ let(:noteable) { create(:personal_snippet, :public) }
- expect(policies).to be_allowed(:admin_note)
- expect(policies).to be_allowed(:resolve_note)
- expect(policies).to be_allowed(:read_note)
+ it 'can edit note' do
+ expect(policy).to be_allowed(:admin_note)
+ expect(policy).to be_allowed(:resolve_note)
+ expect(policy).to be_allowed(:read_note)
end
context 'when it is private' do
- it 'can not edit nor read the note' do
- policies = policies(create(:personal_snippet, :private))
+ let(:noteable) { create(:personal_snippet, :private) }
- expect(policies).to be_disallowed(:admin_note)
- expect(policies).to be_disallowed(:resolve_note)
- expect(policies).to be_disallowed(:read_note)
+ it 'can not edit nor read the note' do
+ expect(policy).to be_disallowed(:admin_note)
+ expect(policy).to be_disallowed(:resolve_note)
+ expect(policy).to be_disallowed(:read_note)
end
end
end
@@ -120,20 +116,20 @@ describe NotePolicy, mdoels: true do
end
it 'can edit a note' do
- expect(policies).to be_allowed(:admin_note)
- expect(policies).to be_allowed(:resolve_note)
- expect(policies).to be_allowed(:read_note)
+ expect(policy).to be_allowed(:admin_note)
+ expect(policy).to be_allowed(:resolve_note)
+ expect(policy).to be_allowed(:read_note)
end
end
context 'when the note author is not a project member' do
it 'can not edit a note' do
- expect(policies).to be_disallowed(:admin_note)
- expect(policies).to be_disallowed(:resolve_note)
+ expect(policy).to be_disallowed(:admin_note)
+ expect(policy).to be_disallowed(:resolve_note)
end
it 'can read a note' do
- expect(policies).to be_allowed(:read_note)
+ expect(policy).to be_allowed(:read_note)
end
end
end
diff --git a/spec/policies/project_policy_spec.rb b/spec/policies/project_policy_spec.rb
index 93a468f585b..772d1fbee2b 100644
--- a/spec/policies/project_policy_spec.rb
+++ b/spec/policies/project_policy_spec.rb
@@ -45,10 +45,10 @@ describe ProjectPolicy do
let(:base_maintainer_permissions) do
%i[
push_to_delete_protected_branch update_project_snippet update_environment
- update_deployment admin_project_snippet
- admin_project_member admin_note admin_wiki admin_project
+ update_deployment admin_project_snippet admin_project_member admin_note admin_wiki admin_project
admin_commit_status admin_build admin_container_image
admin_pipeline admin_environment admin_deployment destroy_release add_cluster
+ daily_statistics
]
end
@@ -130,22 +130,26 @@ describe ProjectPolicy do
subject { described_class.new(owner, project) }
context 'when the feature is disabled' do
- it 'does not include the issues permissions' do
+ before do
project.issues_enabled = false
project.save!
+ end
+ it 'does not include the issues permissions' do
expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue
end
- end
- context 'when the feature is disabled and external tracker configured' do
- it 'does not include the issues permissions' do
- create(:jira_service, project: project)
+ it 'disables boards and lists permissions' do
+ expect_disallowed :read_board, :create_board, :update_board, :admin_board
+ expect_disallowed :read_list, :create_list, :update_list, :admin_list
+ end
- project.issues_enabled = false
- project.save!
+ context 'when external tracker configured' do
+ it 'does not include the issues permissions' do
+ create(:jira_service, project: project)
- expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue
+ expect_disallowed :read_issue, :read_issue_iid, :create_issue, :update_issue, :admin_issue
+ end
end
end
end
diff --git a/spec/presenters/blobs/unfold_presenter_spec.rb b/spec/presenters/blobs/unfold_presenter_spec.rb
new file mode 100644
index 00000000000..7ece5f623ce
--- /dev/null
+++ b/spec/presenters/blobs/unfold_presenter_spec.rb
@@ -0,0 +1,159 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Blobs::UnfoldPresenter do
+ include FakeBlobHelpers
+
+ let(:project) { create(:project, :repository) }
+ let(:blob) { fake_blob(path: 'foo', data: "1\n2\n3") }
+ let(:subject) { described_class.new(blob, params) }
+
+ describe '#initialize' do
+ context 'when full is false' do
+ let(:params) { { full: false, since: 2, to: 3, bottom: false, offset: 1, indent: 1 } }
+
+ it 'sets attributes' do
+ result = subject
+
+ expect(result.full?).to eq(false)
+ expect(result.since).to eq(2)
+ expect(result.to).to eq(3)
+ expect(result.bottom).to eq(false)
+ expect(result.offset).to eq(1)
+ expect(result.indent).to eq(1)
+ end
+ end
+
+ context 'when full is true' do
+ let(:params) { { full: true, since: 2, to: 3, bottom: false, offset: 1, indent: 1 } }
+
+ it 'sets other attributes' do
+ result = subject
+
+ expect(result.full?).to eq(true)
+ expect(result.since).to eq(1)
+ expect(result.to).to eq(blob.lines.size)
+ expect(result.bottom).to eq(false)
+ expect(result.offset).to eq(0)
+ expect(result.indent).to eq(0)
+ end
+ end
+ end
+
+ describe '#diff_lines' do
+ let(:total_lines) { 50 }
+ let(:blob) { fake_blob(path: 'foo', data: (1..total_lines).to_a.join("\n")) }
+
+ context 'when "full" is true' do
+ let(:params) { { full: true } }
+
+ it 'returns all lines' do
+ lines = subject.diff_lines
+
+ expect(lines.size).to eq(total_lines)
+
+ lines.each.with_index do |line, index|
+ expect(line.text).to include("LC#{index + 1}")
+ expect(line.text).to eq(line.rich_text)
+ expect(line.type).to be_nil
+ end
+ end
+
+ context 'when last line is empty' do
+ let(:blob) { fake_blob(path: 'foo', data: "1\n2\n") }
+
+ it 'disregards last line' do
+ lines = subject.diff_lines
+
+ expect(lines.size).to eq(2)
+ end
+ end
+ end
+
+ context 'when "since" is equal to 1' do
+ let(:params) { { since: 1, to: 10, offset: 10 } }
+
+ it 'does not add top match line' do
+ line = subject.diff_lines.first
+
+ expect(line.type).to be_nil
+ end
+ end
+
+ context 'when since is greater than 1' do
+ let(:params) { { since: 5, to: 10, offset: 10 } }
+
+ it 'adds top match line' do
+ line = subject.diff_lines.first
+
+ expect(line.type).to eq('match')
+ expect(line.old_pos).to eq(5)
+ expect(line.new_pos).to eq(5)
+ end
+ end
+
+ context 'when "to" is less than blob size' do
+ let(:params) { { since: 1, to: 5, offset: 10, bottom: true } }
+
+ it 'adds bottom match line' do
+ line = subject.diff_lines.last
+
+ expect(line.type).to eq('match')
+ expect(line.old_pos).to eq(-5)
+ expect(line.new_pos).to eq(5)
+ end
+ end
+
+ context 'when "to" is equal to blob size' do
+ let(:params) { { since: 1, to: total_lines, offset: 10, bottom: true } }
+
+ it 'does not add bottom match line' do
+ line = subject.diff_lines.last
+
+ expect(line.type).to be_nil
+ end
+ end
+ end
+
+ describe '#lines' do
+ context 'when scope is specified' do
+ let(:params) { { since: 2, to: 3 } }
+
+ it 'returns lines cropped by params' do
+ expect(subject.lines.size).to eq(2)
+ expect(subject.lines[0]).to include('LC2')
+ expect(subject.lines[1]).to include('LC3')
+ end
+ end
+
+ context 'when full is true' do
+ let(:params) { { full: true } }
+
+ it 'returns all lines' do
+ expect(subject.lines.size).to eq(3)
+ expect(subject.lines[0]).to include('LC1')
+ expect(subject.lines[1]).to include('LC2')
+ expect(subject.lines[2]).to include('LC3')
+ end
+ end
+ end
+
+ describe '#match_line_text' do
+ context 'when bottom is true' do
+ let(:params) { { since: 2, to: 3, bottom: true } }
+
+ it 'returns empty string' do
+ expect(subject.match_line_text).to eq('')
+ end
+ end
+
+ context 'when bottom is false' do
+ let(:params) { { since: 2, to: 3, bottom: false } }
+
+ it 'returns match line string' do
+ expect(subject.match_line_text).to eq("@@ -2,1+2,1 @@")
+ end
+ end
+ end
+end
diff --git a/spec/presenters/ci/build_runner_presenter_spec.rb b/spec/presenters/ci/build_runner_presenter_spec.rb
index 170e0ac5717..f50bcf54b46 100644
--- a/spec/presenters/ci/build_runner_presenter_spec.rb
+++ b/spec/presenters/ci/build_runner_presenter_spec.rb
@@ -98,4 +98,72 @@ describe Ci::BuildRunnerPresenter do
end
end
end
+
+ describe '#ref_type' do
+ subject { presenter.ref_type }
+
+ let(:build) { create(:ci_build, tag: tag) }
+ let(:tag) { true }
+
+ it 'returns the correct ref type' do
+ is_expected.to eq('tag')
+ end
+
+ context 'when tag is false' do
+ let(:tag) { false }
+
+ it 'returns the correct ref type' do
+ is_expected.to eq('branch')
+ end
+ end
+ end
+
+ describe '#git_depth' do
+ subject { presenter.git_depth }
+
+ let(:build) { create(:ci_build) }
+
+ it 'returns the correct git depth' do
+ is_expected.to eq(0)
+ end
+
+ context 'when GIT_DEPTH variable is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: build.pipeline)
+ end
+
+ it 'returns the correct git depth' do
+ is_expected.to eq(1)
+ end
+ end
+ end
+
+ describe '#refspecs' do
+ subject { presenter.refspecs }
+
+ let(:build) { create(:ci_build) }
+
+ it 'returns the correct refspecs' do
+ is_expected.to contain_exactly('+refs/tags/*:refs/tags/*',
+ '+refs/heads/*:refs/remotes/origin/*')
+ end
+
+ context 'when GIT_DEPTH variable is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: build.pipeline)
+ end
+
+ it 'returns the correct refspecs' do
+ is_expected.to contain_exactly("+refs/heads/#{build.ref}:refs/remotes/origin/#{build.ref}")
+ end
+
+ context 'when ref is tag' do
+ let(:build) { create(:ci_build, :tag) }
+
+ it 'returns the correct refspecs' do
+ is_expected.to contain_exactly("+refs/tags/#{build.ref}:refs/tags/#{build.ref}")
+ end
+ end
+ end
+ end
end
diff --git a/spec/presenters/ci/pipeline_presenter_spec.rb b/spec/presenters/ci/pipeline_presenter_spec.rb
index f7ceaf844be..cda07a0ae09 100644
--- a/spec/presenters/ci/pipeline_presenter_spec.rb
+++ b/spec/presenters/ci/pipeline_presenter_spec.rb
@@ -1,6 +1,9 @@
require 'spec_helper'
describe Ci::PipelinePresenter do
+ include Gitlab::Routing
+
+ let(:user) { create(:user) }
let(:project) { create(:project) }
let(:pipeline) { create(:ci_pipeline, project: project) }
@@ -8,6 +11,11 @@ describe Ci::PipelinePresenter do
described_class.new(pipeline)
end
+ before do
+ project.add_developer(user)
+ allow(presenter).to receive(:current_user) { user }
+ end
+
it 'inherits from Gitlab::View::Presenter::Delegated' do
expect(described_class.superclass).to eq(Gitlab::View::Presenter::Delegated)
end
@@ -68,4 +76,130 @@ describe Ci::PipelinePresenter do
end
end
end
+
+ describe '#ref_text' do
+ subject { presenter.ref_text }
+
+ context 'when pipeline is detached merge request pipeline' do
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:pipeline) { merge_request.all_pipelines.last }
+
+ it 'returns a correct ref text' do
+ is_expected.to eq("for <a class=\"mr-iid\" href=\"#{project_merge_request_path(merge_request.project, merge_request)}\">#{merge_request.to_reference}</a> " \
+ "with <a class=\"ref-name\" href=\"#{project_commits_path(merge_request.source_project, merge_request.source_branch)}\">#{merge_request.source_branch}</a>")
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) }
+ let(:pipeline) { merge_request.all_pipelines.last }
+
+ it 'returns a correct ref text' do
+ is_expected.to eq("for <a class=\"mr-iid\" href=\"#{project_merge_request_path(merge_request.project, merge_request)}\">#{merge_request.to_reference}</a> " \
+ "with <a class=\"ref-name\" href=\"#{project_commits_path(merge_request.source_project, merge_request.source_branch)}\">#{merge_request.source_branch}</a> " \
+ "into <a class=\"ref-name\" href=\"#{project_commits_path(merge_request.target_project, merge_request.target_branch)}\">#{merge_request.target_branch}</a>")
+ end
+ end
+
+ context 'when pipeline is branch pipeline' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when ref exists in the repository' do
+ before do
+ allow(pipeline).to receive(:ref_exists?) { true }
+ end
+
+ it 'returns a correct ref text' do
+ is_expected.to eq("for <a class=\"ref-name\" href=\"#{project_commits_path(pipeline.project, pipeline.ref)}\">#{pipeline.ref}</a>")
+ end
+
+ context 'when ref contains malicious script' do
+ let(:pipeline) { create(:ci_pipeline, ref: "<script>alter('1')</script>", project: project) }
+
+ it 'does not include the malicious script' do
+ is_expected.not_to include("<script>alter('1')</script>")
+ end
+ end
+ end
+
+ context 'when ref exists in the repository' do
+ before do
+ allow(pipeline).to receive(:ref_exists?) { false }
+ end
+
+ it 'returns a correct ref text' do
+ is_expected.to eq("for <span class=\"ref-name\">#{pipeline.ref}</span>")
+ end
+
+ context 'when ref contains malicious script' do
+ let(:pipeline) { create(:ci_pipeline, ref: "<script>alter('1')</script>", project: project) }
+
+ it 'does not include the malicious script' do
+ is_expected.not_to include("<script>alter('1')</script>")
+ end
+ end
+ end
+ end
+ end
+
+ describe '#link_to_merge_request' do
+ subject { presenter.link_to_merge_request }
+
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:pipeline) { merge_request.all_pipelines.last }
+
+ it 'returns a correct link' do
+ is_expected
+ .to include(project_merge_request_path(merge_request.project, merge_request))
+ end
+
+ context 'when pipeline is branch pipeline' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#link_to_merge_request_source_branch' do
+ subject { presenter.link_to_merge_request_source_branch }
+
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:pipeline) { merge_request.all_pipelines.last }
+
+ it 'returns a correct link' do
+ is_expected
+ .to include(project_commits_path(merge_request.source_project,
+ merge_request.source_branch))
+ end
+
+ context 'when pipeline is branch pipeline' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
+ end
+
+ describe '#link_to_merge_request_target_branch' do
+ subject { presenter.link_to_merge_request_target_branch }
+
+ let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) }
+ let(:pipeline) { merge_request.all_pipelines.last }
+
+ it 'returns a correct link' do
+ is_expected
+ .to include(project_commits_path(merge_request.target_project, merge_request.target_branch))
+ end
+
+ context 'when pipeline is branch pipeline' do
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ it 'returns nothing' do
+ is_expected.to be_nil
+ end
+ end
+ end
end
diff --git a/spec/presenters/group_clusterable_presenter_spec.rb b/spec/presenters/group_clusterable_presenter_spec.rb
index 205160742bf..fa77273f6aa 100644
--- a/spec/presenters/group_clusterable_presenter_spec.rb
+++ b/spec/presenters/group_clusterable_presenter_spec.rb
@@ -69,6 +69,14 @@ describe GroupClusterablePresenter do
it { is_expected.to eq(install_applications_group_cluster_path(group, cluster, application)) }
end
+ describe '#update_applications_cluster_path' do
+ let(:application) { :helm }
+
+ subject { presenter.update_applications_cluster_path(cluster, application) }
+
+ it { is_expected.to eq(update_applications_group_cluster_path(group, cluster, application)) }
+ end
+
describe '#cluster_path' do
subject { presenter.cluster_path(cluster) }
diff --git a/spec/presenters/merge_request_presenter_spec.rb b/spec/presenters/merge_request_presenter_spec.rb
index bafcddebbb7..4a0f91c4c7a 100644
--- a/spec/presenters/merge_request_presenter_spec.rb
+++ b/spec/presenters/merge_request_presenter_spec.rb
@@ -1,8 +1,8 @@
require 'spec_helper'
describe MergeRequestPresenter do
- let(:resource) { create :merge_request, source_project: project }
- let(:project) { create :project }
+ let(:resource) { create(:merge_request, source_project: project) }
+ let(:project) { create(:project) }
let(:user) { create(:user) }
describe '#ci_status' do
@@ -345,6 +345,30 @@ describe MergeRequestPresenter do
end
end
+ describe '#source_branch_commits_path' do
+ subject do
+ described_class.new(resource, current_user: user)
+ .source_branch_commits_path
+ end
+
+ context 'when source branch exists' do
+ it 'returns path' do
+ allow(resource).to receive(:source_branch_exists?) { true }
+
+ is_expected
+ .to eq("/#{resource.source_project.full_path}/commits/#{resource.source_branch}")
+ end
+ end
+
+ context 'when source branch does not exist' do
+ it 'returns nil' do
+ allow(resource).to receive(:source_branch_exists?) { false }
+
+ is_expected.to be_nil
+ end
+ end
+ end
+
describe '#target_branch_tree_path' do
subject do
described_class.new(resource, current_user: user)
@@ -392,6 +416,29 @@ describe MergeRequestPresenter do
end
end
+ describe '#target_branch_path' do
+ subject do
+ described_class.new(resource, current_user: user).target_branch_path
+ end
+
+ context 'when target branch exists' do
+ it 'returns path' do
+ allow(resource).to receive(:target_branch_exists?) { true }
+
+ is_expected
+ .to eq("/#{resource.source_project.full_path}/branches/#{resource.target_branch}")
+ end
+ end
+
+ context 'when target branch does not exist' do
+ it 'returns nil' do
+ allow(resource).to receive(:target_branch_exists?) { false }
+
+ is_expected.to be_nil
+ end
+ end
+ end
+
describe '#source_branch_with_namespace_link' do
subject do
described_class.new(resource, current_user: user).source_branch_with_namespace_link
@@ -476,4 +523,46 @@ describe MergeRequestPresenter do
end
end
end
+
+ describe '#can_push_to_source_branch' do
+ before do
+ allow(resource).to receive(:source_branch_exists?) { source_branch_exists }
+
+ allow_any_instance_of(Gitlab::UserAccess::RequestCacheExtension)
+ .to receive(:can_push_to_branch?)
+ .with(resource.source_branch)
+ .and_return(can_push_to_branch)
+ end
+
+ subject do
+ described_class.new(resource, current_user: user).can_push_to_source_branch?
+ end
+
+ context 'when source branch exists AND user can push to source branch' do
+ let(:source_branch_exists) { true }
+ let(:can_push_to_branch) { true }
+
+ it 'returns true' do
+ is_expected.to eq(true)
+ end
+ end
+
+ context 'when source branch does not exists' do
+ let(:source_branch_exists) { false }
+ let(:can_push_to_branch) { true }
+
+ it 'returns false' do
+ is_expected.to eq(false)
+ end
+ end
+
+ context 'when user cannot push to source branch' do
+ let(:source_branch_exists) { true }
+ let(:can_push_to_branch) { false }
+
+ it 'returns false' do
+ is_expected.to eq(false)
+ end
+ end
+ end
end
diff --git a/spec/presenters/project_clusterable_presenter_spec.rb b/spec/presenters/project_clusterable_presenter_spec.rb
index c50d90ae1e8..6786a84243f 100644
--- a/spec/presenters/project_clusterable_presenter_spec.rb
+++ b/spec/presenters/project_clusterable_presenter_spec.rb
@@ -69,6 +69,14 @@ describe ProjectClusterablePresenter do
it { is_expected.to eq(install_applications_project_cluster_path(project, cluster, application)) }
end
+ describe '#update_applications_cluster_path' do
+ let(:application) { :helm }
+
+ subject { presenter.update_applications_cluster_path(cluster, application) }
+
+ it { is_expected.to eq(update_applications_project_cluster_path(project, cluster, application)) }
+ end
+
describe '#cluster_path' do
subject { presenter.cluster_path(cluster) }
diff --git a/spec/rack_servers/puma_spec.rb b/spec/rack_servers/puma_spec.rb
index 431fab87857..891df4f1a66 100644
--- a/spec/rack_servers/puma_spec.rb
+++ b/spec/rack_servers/puma_spec.rb
@@ -44,11 +44,9 @@ describe 'Puma' do
end
after(:all) do
- begin
- WebMock.disable_net_connect!(allow_localhost: true)
- Process.kill('TERM', @puma_master_pid)
- rescue Errno::ESRCH
- end
+ WebMock.disable_net_connect!(allow_localhost: true)
+ Process.kill('TERM', @puma_master_pid)
+ rescue Errno::ESRCH
end
def wait_puma_boot!(master_pid, ready_file)
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 066f1d6862a..a132b85b878 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -1430,8 +1430,8 @@ describe API::Commits do
end
describe 'GET /projects/:id/repository/commits/:sha/merge_requests' do
- let!(:project) { create(:project, :repository, :private) }
- let!(:merged_mr) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'feature') }
+ let(:project) { create(:project, :repository, :private) }
+ let(:merged_mr) { create(:merge_request, source_project: project, source_branch: 'master', target_branch: 'feature') }
let(:commit) { merged_mr.merge_request_diff.commits.last }
it 'returns the correct merge request' do
@@ -1456,6 +1456,17 @@ describe API::Commits do
expect(response).to have_gitlab_http_status(404)
end
+
+ context 'public project' do
+ let(:project) { create(:project, :repository, :public, :merge_requests_private) }
+ let(:non_member) { create(:user) }
+
+ it 'responds 403 when only members are allowed to read merge requests' do
+ get api("/projects/#{project.id}/repository/commits/#{commit.id}/merge_requests", non_member)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
end
describe 'GET /projects/:id/repository/commits/:sha/signature' do
diff --git a/spec/requests/api/graphql/metadata_query_spec.rb b/spec/requests/api/graphql/metadata_query_spec.rb
new file mode 100644
index 00000000000..4c56c559cf9
--- /dev/null
+++ b/spec/requests/api/graphql/metadata_query_spec.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'getting project information' do
+ include GraphqlHelpers
+
+ let(:query) { graphql_query_for('metadata', {}, all_graphql_fields_for('Metadata')) }
+
+ context 'logged in' do
+ it 'returns version and revision' do
+ post_graphql(query, current_user: create(:user))
+
+ expect(graphql_errors).to be_nil
+ expect(graphql_data).to eq(
+ 'metadata' => {
+ 'version' => Gitlab::VERSION,
+ 'revision' => Gitlab.revision
+ }
+ )
+ end
+ end
+
+ context 'anonymous user' do
+ it 'returns nothing' do
+ post_graphql(query, current_user: nil)
+
+ expect(graphql_errors).to be_nil
+ expect(graphql_data).to eq('metadata' => nil)
+ end
+ end
+end
diff --git a/spec/requests/api/graphql/project/merge_request_spec.rb b/spec/requests/api/graphql/project/merge_request_spec.rb
index deb6abbc026..74820d39102 100644
--- a/spec/requests/api/graphql/project/merge_request_spec.rb
+++ b/spec/requests/api/graphql/project/merge_request_spec.rb
@@ -70,13 +70,13 @@ describe 'getting merge request information nested in a project' do
context 'when there are pipelines' do
before do
- pipeline = create(
+ 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)
+ merge_request.update_head_pipeline
end
it 'has a head pipeline' do
diff --git a/spec/requests/api/graphql_spec.rb b/spec/requests/api/graphql_spec.rb
new file mode 100644
index 00000000000..cca87c16f27
--- /dev/null
+++ b/spec/requests/api/graphql_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe 'GraphQL' do
+ include GraphqlHelpers
+
+ let(:query) { graphql_query_for('echo', 'text' => 'Hello world' ) }
+
+ context 'graphql is disabled by feature flag' do
+ before do
+ stub_feature_flags(graphql: false)
+ end
+
+ it 'does not generate a route for GraphQL' do
+ expect { post_graphql(query) }.to raise_error(ActionController::RoutingError)
+ end
+ end
+
+ context 'invalid variables' do
+ it 'returns an error' do
+ post_graphql(query, variables: "This is not JSON")
+
+ expect(response).to have_gitlab_http_status(422)
+ expect(json_response['errors'].first['message']).not_to be_nil
+ end
+ end
+
+ context 'authentication', :allow_forgery_protection do
+ let(:user) { create(:user) }
+
+ it 'allows access to public data without authentication' do
+ post_graphql(query)
+
+ expect(graphql_data['echo']).to eq('nil says: Hello world')
+ end
+
+ it 'does not authenticate a user with an invalid CSRF' do
+ login_as(user)
+
+ post_graphql(query, headers: { 'X-CSRF-Token' => 'invalid' })
+
+ expect(graphql_data['echo']).to eq('nil says: Hello world')
+ end
+
+ it 'authenticates a user with a valid session token' do
+ # Create a session to get a CSRF token from
+ login_as(user)
+ get('/')
+
+ post '/api/graphql', params: { query: query }, headers: { 'X-CSRF-Token' => response.session['_csrf_token'] }
+
+ expect(graphql_data['echo']).to eq("\"#{user.username}\" says: Hello world")
+ end
+
+ context 'token authentication' do
+ let(:token) { create(:personal_access_token) }
+
+ before do
+ stub_authentication_activity_metrics(debug: false)
+ end
+
+ it 'Authenticates users with a PAT' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+ .and increment(:user_session_override_counter)
+ .and increment(:user_sessionless_authentication_counter)
+
+ post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token })
+
+ expect(graphql_data['echo']).to eq("\"#{token.user.username}\" says: Hello world")
+ end
+
+ context 'when the personal access token has no api scope' do
+ it 'does not log the user in' do
+ token.update(scopes: [:read_user])
+
+ post_graphql(query, headers: { 'PRIVATE-TOKEN' => token.token })
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(graphql_data['echo']).to eq('nil says: Hello world')
+ end
+ end
+ end
+ end
+end
diff --git a/spec/requests/api/group_variables_spec.rb b/spec/requests/api/group_variables_spec.rb
index e52f4c70407..66b9aae4b58 100644
--- a/spec/requests/api/group_variables_spec.rb
+++ b/spec/requests/api/group_variables_spec.rb
@@ -87,12 +87,12 @@ describe API::GroupVariables do
it 'creates variable' do
expect do
- post api("/groups/#{group.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'VALUE_2', protected: true }
+ post api("/groups/#{group.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'PROTECTED_VALUE_2', protected: true }
end.to change {group.variables.count}.by(1)
expect(response).to have_gitlab_http_status(201)
expect(json_response['key']).to eq('TEST_VARIABLE_2')
- expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['value']).to eq('PROTECTED_VALUE_2')
expect(json_response['protected']).to be_truthy
end
diff --git a/spec/requests/api/internal_spec.rb b/spec/requests/api/internal_spec.rb
index cd85151ec1b..537194b8e11 100644
--- a/spec/requests/api/internal_spec.rb
+++ b/spec/requests/api/internal_spec.rb
@@ -26,6 +26,21 @@ describe API::Internal do
expect(json_response['redis']).to be(false)
end
+
+ context 'authenticating' do
+ it 'authenticates using a header' do
+ get api("/internal/check"),
+ headers: { API::Helpers::GITLAB_SHARED_SECRET_HEADER => Base64.encode64(secret_token) }
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'returns 401 when no credentials provided' do
+ get(api("/internal/check"))
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+ end
end
describe 'GET /internal/broadcast_message' do
@@ -237,6 +252,14 @@ describe API::Internal do
expect(json_response['name']).to eq(user.name)
end
+
+ it 'responds successfully when a user is not found' do
+ get(api("/internal/discover"), params: { username: 'noone', secret_token: secret_token })
+
+ expect(response).to have_gitlab_http_status(200)
+
+ expect(response.body).to eq('null')
+ end
end
describe "GET /internal/authorized_keys" do
@@ -298,7 +321,7 @@ describe API::Internal do
end
context 'with env passed as a JSON' do
- let(:gl_repository) { project.gl_repository(is_wiki: true) }
+ let(:gl_repository) { Gitlab::GlRepository::WIKI.identifier_for_subject(project) }
it 'sets env in RequestStore' do
obj_dir_relative = './objects'
@@ -324,7 +347,6 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_project_path"]).to eq(project.wiki.full_path)
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(user.reload.last_activity_on).to be_nil
@@ -337,7 +359,6 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_project_path"]).to eq(project.wiki.full_path)
expect(json_response["gl_repository"]).to eq("wiki-#{project.id}")
expect(user.reload.last_activity_on).to eql(Date.today)
@@ -350,7 +371,6 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("project-#{project.id}")
expect(json_response["gl_project_path"]).to eq(project.full_path)
expect(json_response["gitaly"]).not_to be_nil
@@ -370,7 +390,6 @@ describe API::Internal do
expect(response).to have_gitlab_http_status(200)
expect(json_response["status"]).to be_truthy
- expect(json_response["repository_path"]).to eq('/')
expect(json_response["gl_repository"]).to eq("project-#{project.id}")
expect(json_response["gl_project_path"]).to eq(project.full_path)
expect(json_response["gitaly"]).not_to be_nil
@@ -956,9 +975,9 @@ describe API::Internal do
def gl_repository_for(project_or_wiki)
case project_or_wiki
when ProjectWiki
- project_or_wiki.project.gl_repository(is_wiki: true)
+ Gitlab::GlRepository::WIKI.identifier_for_subject(project_or_wiki.project)
when Project
- project_or_wiki.gl_repository(is_wiki: false)
+ Gitlab::GlRepository::PROJECT.identifier_for_subject(project_or_wiki)
else
nil
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index d10ee6cc320..a5434d3ea80 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -49,7 +49,7 @@ describe API::Issues do
create(:label, title: 'label', color: '#FFAABB', project: project)
end
let!(:label_link) { create(:label_link, label: label, target: issue) }
- set(:milestone) { create(:milestone, title: '1.0.0', project: project) }
+ let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
set(:empty_milestone) do
create(:milestone, title: '2.0.0', project: project)
end
@@ -183,6 +183,18 @@ describe API::Issues do
expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
end
+ it 'returns only confidential issues' do
+ get api('/issues', user), params: { confidential: true, scope: 'all' }
+
+ expect_paginated_array_response(confidential_issue.id)
+ end
+
+ it 'returns only public issues' do
+ get api('/issues', user), params: { confidential: false }
+
+ expect_paginated_array_response([issue.id, closed_issue.id])
+ end
+
it 'returns issues reacted by the authenticated user' do
issue2 = create(:issue, project: project, author: user, assignees: [user])
create(:award_emoji, awardable: issue2, user: user2, name: 'star')
@@ -259,7 +271,14 @@ describe API::Issues do
end
it 'returns an array of labeled issues' do
- get api("/issues", user), params: { labels: label.title }
+ get api('/issues', user), params: { labels: label.title }
+
+ expect_paginated_array_response(issue.id)
+ expect(json_response.first['labels']).to eq([label.title])
+ end
+
+ it 'returns an array of labeled issues with labels param as array' do
+ get api('/issues', user), params: { labels: [label.title] }
expect_paginated_array_response(issue.id)
expect(json_response.first['labels']).to eq([label.title])
@@ -272,7 +291,20 @@ describe API::Issues do
create(:label_link, label: label_b, target: issue)
create(:label_link, label: label_c, target: issue)
- get api("/issues", user), params: { labels: "#{label.title},#{label_b.title},#{label_c.title}" }
+ get api('/issues', user), params: { labels: "#{label.title},#{label_b.title},#{label_c.title}" }
+
+ expect_paginated_array_response(issue.id)
+ expect(json_response.first['labels']).to eq([label_c.title, label_b.title, label.title])
+ end
+
+ it 'returns an array of labeled issues when all labels matches with labels param as array' do
+ label_b = create(:label, title: 'foo', project: project)
+ label_c = create(:label, title: 'bar', project: project)
+
+ create(:label_link, label: label_b, target: issue)
+ create(:label_link, label: label_c, target: issue)
+
+ get api('/issues', user), params: { labels: [label.title, label_b.title, label_c.title] }
expect_paginated_array_response(issue.id)
expect(json_response.first['labels']).to eq([label_c.title, label_b.title, label.title])
@@ -284,8 +316,22 @@ describe API::Issues do
expect_paginated_array_response([])
end
+ it 'returns an empty array if no issue matches labels with labels param as array' do
+ get api('/issues', user), params: { labels: %w(foo bar) }
+
+ expect_paginated_array_response([])
+ end
+
it 'returns an array of labeled issues matching given state' do
- get api("/issues", user), params: { labels: label.title, state: :opened }
+ get api('/issues', user), params: { labels: label.title, state: :opened }
+
+ expect_paginated_array_response(issue.id)
+ expect(json_response.first['labels']).to eq([label.title])
+ expect(json_response.first['state']).to eq('opened')
+ end
+
+ it 'returns an array of labeled issues matching given state with labels param as array' do
+ get api('/issues', user), params: { labels: [label.title], state: :opened }
expect_paginated_array_response(issue.id)
expect(json_response.first['labels']).to eq([label.title])
@@ -293,25 +339,43 @@ describe API::Issues do
end
it 'returns an empty array if no issue matches labels and state filters' do
- get api("/issues", user), params: { labels: label.title, state: :closed }
+ get api('/issues', user), params: { labels: label.title, state: :closed }
expect_paginated_array_response([])
end
it 'returns an array of issues with any label' do
- get api("/issues", user), params: { labels: IssuesFinder::FILTER_ANY }
+ get api('/issues', user), params: { labels: IssuesFinder::FILTER_ANY }
+
+ expect_paginated_array_response(issue.id)
+ end
+
+ it 'returns an array of issues with any label with labels param as array' do
+ get api('/issues', user), params: { labels: [IssuesFinder::FILTER_ANY] }
expect_paginated_array_response(issue.id)
end
it 'returns an array of issues with no label' do
- get api("/issues", user), params: { labels: IssuesFinder::FILTER_NONE }
+ get api('/issues', user), params: { labels: IssuesFinder::FILTER_NONE }
+
+ expect_paginated_array_response(closed_issue.id)
+ end
+
+ it 'returns an array of issues with no label with labels param as array' do
+ get api('/issues', user), params: { labels: [IssuesFinder::FILTER_NONE] }
expect_paginated_array_response(closed_issue.id)
end
it 'returns an array of issues with no label when using the legacy No+Label filter' do
- get api("/issues", user), params: { labels: "No Label" }
+ get api('/issues', user), params: { labels: 'No Label' }
+
+ expect_paginated_array_response(closed_issue.id)
+ end
+
+ it 'returns an array of issues with no label when using the legacy No+Label filter with labels param as array' do
+ get api('/issues', user), params: { labels: ['No Label'] }
expect_paginated_array_response(closed_issue.id)
end
@@ -354,7 +418,7 @@ describe API::Issues do
end
it 'returns an empty array if iid does not exist' do
- get api("/issues", user), params: { iids: [99999] }
+ get api("/issues", user), params: { iids: [0] }
expect_paginated_array_response([])
end
@@ -557,6 +621,18 @@ describe API::Issues do
expect_paginated_array_response([group_confidential_issue.id, group_issue.id])
end
+ it 'returns only confidential issues' do
+ get api(base_url, user), params: { confidential: true }
+
+ expect_paginated_array_response(group_confidential_issue.id)
+ end
+
+ it 'returns only public issues' do
+ get api(base_url, user), params: { confidential: false }
+
+ expect_paginated_array_response([group_closed_issue.id, group_issue.id])
+ end
+
it 'returns an array of labeled group issues' do
get api(base_url, user), params: { labels: group_label.title }
@@ -564,12 +640,25 @@ describe API::Issues do
expect(json_response.first['labels']).to eq([group_label.title])
end
+ it 'returns an array of labeled group issues with labels param as array' do
+ get api(base_url, user), params: { labels: [group_label.title] }
+
+ expect_paginated_array_response(group_issue.id)
+ expect(json_response.first['labels']).to eq([group_label.title])
+ end
+
it 'returns an array of labeled group issues where all labels match' do
get api(base_url, user), params: { labels: "#{group_label.title},foo,bar" }
expect_paginated_array_response([])
end
+ it 'returns an array of labeled group issues where all labels match with labels param as array' do
+ get api(base_url, user), params: { labels: [group_label.title, 'foo', 'bar'] }
+
+ expect_paginated_array_response([])
+ end
+
it 'returns issues matching given search string for title' do
get api(base_url, user), params: { search: group_issue.title }
@@ -595,6 +684,19 @@ describe API::Issues do
expect(json_response.first['labels']).to eq([label_c.title, label_b.title, group_label.title])
end
+ it 'returns an array of labeled issues when all labels matches with labels param as array' do
+ label_b = create(:label, title: 'foo', project: group_project)
+ label_c = create(:label, title: 'bar', project: group_project)
+
+ create(:label_link, label: label_b, target: group_issue)
+ create(:label_link, label: label_c, target: group_issue)
+
+ get api(base_url, user), params: { labels: [group_label.title, label_b.title, label_c.title] }
+
+ expect_paginated_array_response(group_issue.id)
+ expect(json_response.first['labels']).to eq([label_c.title, label_b.title, group_label.title])
+ end
+
it 'returns an array of issues found by iids' do
get api(base_url, user), params: { iids: [group_issue.iid] }
@@ -603,7 +705,7 @@ describe API::Issues do
end
it 'returns an empty array if iid does not exist' do
- get api(base_url, user), params: { iids: [99999] }
+ get api(base_url, user), params: { iids: [0] }
expect_paginated_array_response([])
end
@@ -621,12 +723,25 @@ describe API::Issues do
expect(json_response.first['id']).to eq(group_issue.id)
end
+ it 'returns an array of group issues with any label with labels param as array' do
+ get api(base_url, user), params: { labels: [IssuesFinder::FILTER_ANY] }
+
+ expect_paginated_array_response(group_issue.id)
+ expect(json_response.first['id']).to eq(group_issue.id)
+ end
+
it 'returns an array of group issues with no label' do
get api(base_url, user), params: { labels: IssuesFinder::FILTER_NONE }
expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id])
end
+ it 'returns an array of group issues with no label with labels param as array' do
+ get api(base_url, user), params: { labels: [IssuesFinder::FILTER_NONE] }
+
+ expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id])
+ end
+
it 'returns an empty array if no issue matches milestone' do
get api(base_url, user), params: { milestone: group_empty_milestone.title }
@@ -782,6 +897,18 @@ describe API::Issues do
expect_paginated_array_response([issue.id, confidential_issue.id, closed_issue.id])
end
+ it 'returns only confidential issues' do
+ get api("#{base_url}/issues", author), params: { confidential: true }
+
+ expect_paginated_array_response(confidential_issue.id)
+ end
+
+ it 'returns only public issues' do
+ get api("#{base_url}/issues", author), params: { confidential: false }
+
+ expect_paginated_array_response([issue.id, closed_issue.id])
+ end
+
it 'returns project confidential issues for assignee' do
get api("#{base_url}/issues", assignee)
@@ -806,6 +933,12 @@ describe API::Issues do
expect_paginated_array_response(issue.id)
end
+ it 'returns an array of labeled project issues with labels param as array' do
+ get api("#{base_url}/issues", user), params: { labels: [label.title] }
+
+ expect_paginated_array_response(issue.id)
+ end
+
it 'returns an array of labeled issues when all labels matches' do
label_b = create(:label, title: 'foo', project: project)
label_c = create(:label, title: 'bar', project: project)
@@ -818,6 +951,18 @@ describe API::Issues do
expect_paginated_array_response(issue.id)
end
+ it 'returns an array of labeled issues when all labels matches with labels param as array' do
+ label_b = create(:label, title: 'foo', project: project)
+ label_c = create(:label, title: 'bar', project: project)
+
+ create(:label_link, label: label_b, target: issue)
+ create(:label_link, label: label_c, target: issue)
+
+ get api("#{base_url}/issues", user), params: { labels: [label.title, label_b.title, label_c.title] }
+
+ expect_paginated_array_response(issue.id)
+ end
+
it 'returns issues matching given search string for title' do
get api("#{base_url}/issues?search=#{issue.title}", user)
@@ -837,7 +982,7 @@ describe API::Issues do
end
it 'returns an empty array if iid does not exist' do
- get api("#{base_url}/issues", user), params: { iids: [99999] }
+ get api("#{base_url}/issues", user), params: { iids: [0] }
expect_paginated_array_response([])
end
@@ -854,12 +999,24 @@ describe API::Issues do
expect_paginated_array_response(issue.id)
end
+ it 'returns an array of project issues with any label with labels param as array' do
+ get api("#{base_url}/issues", user), params: { labels: [IssuesFinder::FILTER_ANY] }
+
+ expect_paginated_array_response(issue.id)
+ end
+
it 'returns an array of project issues with no label' do
get api("#{base_url}/issues", user), params: { labels: IssuesFinder::FILTER_NONE }
expect_paginated_array_response([confidential_issue.id, closed_issue.id])
end
+ it 'returns an array of project issues with no label with labels param as array' do
+ get api("#{base_url}/issues", user), params: { labels: [IssuesFinder::FILTER_NONE] }
+
+ expect_paginated_array_response([confidential_issue.id, closed_issue.id])
+ end
+
it 'returns an empty array if no project issue matches labels' do
get api("#{base_url}/issues", user), params: { labels: 'foo,bar' }
@@ -1179,6 +1336,19 @@ describe API::Issues do
expect(json_response['assignees'].first['name']).to eq(user2.name)
end
+ it 'creates a new project issue with labels param as array' do
+ post api("/projects/#{project.id}/issues", user),
+ params: { title: 'new issue', labels: %w(label label2), weight: 3, assignee_ids: [user2.id] }
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['title']).to eq('new issue')
+ expect(json_response['description']).to be_nil
+ expect(json_response['labels']).to eq(%w(label label2))
+ expect(json_response['confidential']).to be_falsy
+ expect(json_response['assignee']['name']).to eq(user2.name)
+ expect(json_response['assignees'].first['name']).to eq(user2.name)
+ end
+
it 'creates a new confidential project issue' do
post api("/projects/#{project.id}/issues", user),
params: { title: 'new issue', confidential: true }
@@ -1233,6 +1403,20 @@ describe API::Issues do
expect(json_response['labels']).to include '&'
end
+ it 'allows special label names with labels param as array' do
+ post api("/projects/#{project.id}/issues", user),
+ params: {
+ title: 'new issue',
+ labels: ['label', 'label?', 'label&foo, ?, &']
+ }
+ expect(response.status).to eq(201)
+ expect(json_response['labels']).to include 'label'
+ expect(json_response['labels']).to include 'label?'
+ expect(json_response['labels']).to include 'label&foo'
+ expect(json_response['labels']).to include '?'
+ expect(json_response['labels']).to include '&'
+ end
+
it 'returns 400 if title is too long' do
post api("/projects/#{project.id}/issues", user),
params: { title: 'g' * 256 }
@@ -1341,6 +1525,12 @@ describe API::Issues do
post api("/projects/#{project.id}/issues", non_member), params: { title: 'new issue', labels: 'label, label2' }
end.not_to change { project.labels.count }
end
+
+ it 'cannot create new labels with labels param as array' do
+ expect do
+ post api("/projects/#{project.id}/issues", non_member), params: { title: 'new issue', labels: %w(label label2) }
+ end.not_to change { project.labels.count }
+ end
end
end
@@ -1408,6 +1598,21 @@ describe API::Issues do
expect(json_response['labels']).to include '&'
end
+ it 'allows special label names with labels param as array' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ params: {
+ title: 'updated title',
+ labels: ['label', 'label?', 'label&foo, ?, &']
+ }
+
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to include 'label'
+ expect(json_response['labels']).to include 'label?'
+ expect(json_response['labels']).to include 'label&foo'
+ expect(json_response['labels']).to include '?'
+ expect(json_response['labels']).to include '&'
+ end
+
context 'confidential issues' do
it "returns 403 for non project members" do
put api("/projects/#{project.id}/issues/#{confidential_issue.iid}", non_member),
@@ -1567,6 +1772,16 @@ describe API::Issues do
expect(json_response['updated_at']).to be > Time.now
end
+ it 'removes all labels and touches the record with labels param as array' do
+ Timecop.travel(1.minute.from_now) do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user), params: { labels: [''] }
+ end
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['labels']).to eq([])
+ expect(json_response['updated_at']).to be > Time.now
+ end
+
it 'updates labels and touches the record' do
Timecop.travel(1.minute.from_now) do
put api("/projects/#{project.id}/issues/#{issue.iid}", user),
@@ -1578,6 +1793,17 @@ describe API::Issues do
expect(json_response['updated_at']).to be > Time.now
end
+ it 'updates labels and touches the record with labels param as array' do
+ Timecop.travel(1.minute.from_now) do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ params: { labels: %w(foo bar) }
+ end
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['labels']).to include 'foo'
+ expect(json_response['labels']).to include 'bar'
+ expect(json_response['updated_at']).to be > Time.now
+ end
+
it 'allows special label names' do
put api("/projects/#{project.id}/issues/#{issue.iid}", user),
params: { labels: 'label:foo, label-bar,label_bar,label/bar,label?bar,label&bar,?,&' }
@@ -1592,6 +1818,20 @@ describe API::Issues do
expect(json_response['labels']).to include '&'
end
+ it 'allows special label names with labels param as array' do
+ put api("/projects/#{project.id}/issues/#{issue.iid}", user),
+ params: { labels: ['label:foo', 'label-bar', 'label_bar', 'label/bar,label?bar,label&bar,?,&'] }
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to include 'label:foo'
+ expect(json_response['labels']).to include 'label-bar'
+ expect(json_response['labels']).to include 'label_bar'
+ expect(json_response['labels']).to include 'label/bar'
+ expect(json_response['labels']).to include 'label?bar'
+ expect(json_response['labels']).to include 'label&bar'
+ expect(json_response['labels']).to include '?'
+ expect(json_response['labels']).to include '&'
+ end
+
it 'returns 400 if title is too long' do
put api("/projects/#{project.id}/issues/#{issue.iid}", user),
params: { title: 'g' * 256 }
@@ -1873,7 +2113,7 @@ describe API::Issues do
end
it "returns 404 when issue doesn't exists" do
- get api("/projects/#{project.id}/issues/9999/closed_by", user)
+ get api("/projects/#{project.id}/issues/0/closed_by", user)
expect(response).to have_gitlab_http_status(404)
end
@@ -1958,7 +2198,7 @@ describe API::Issues do
end
it "returns 404 when issue doesn't exists" do
- get_related_merge_requests(project.id, 999999, user)
+ get_related_merge_requests(project.id, 0, user)
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 3defe8bbf51..ed2ef4c730b 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -321,6 +321,49 @@ describe API::Jobs do
end
end
+ describe 'DELETE /projects/:id/jobs/:job_id/artifacts' do
+ let!(:job) { create(:ci_build, :artifacts, pipeline: pipeline, user: api_user) }
+
+ before do
+ delete api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
+ end
+
+ context 'when user is anonymous' do
+ let(:api_user) { nil }
+
+ it 'does not delete artifacts' do
+ expect(job.job_artifacts.size).to eq 2
+ end
+
+ it 'returns status 401 (unauthorized)' do
+ expect(response).to have_http_status :unauthorized
+ end
+ end
+
+ context 'with developer' do
+ it 'does not delete artifacts' do
+ expect(job.job_artifacts.size).to eq 2
+ end
+
+ it 'returns status 403 (forbidden)' do
+ expect(response).to have_http_status :forbidden
+ end
+ end
+
+ context 'with authorized user' do
+ let(:maintainer) { create(:project_member, :maintainer, project: project).user }
+ let!(:api_user) { maintainer }
+
+ it 'deletes artifacts' do
+ expect(job.job_artifacts.size).to eq 0
+ end
+
+ it 'returns status 204 (no content)' do
+ expect(response).to have_http_status :no_content
+ end
+ end
+ end
+
describe 'GET /projects/:id/jobs/:job_id/artifacts/:artifact_path' do
context 'when job has artifacts' do
let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }
diff --git a/spec/requests/api/keys_spec.rb b/spec/requests/api/keys_spec.rb
index 3c4719964b6..f37d84fddef 100644
--- a/spec/requests/api/keys_spec.rb
+++ b/spec/requests/api/keys_spec.rb
@@ -16,7 +16,7 @@ describe API::Keys do
context 'when authenticated' do
it 'returns 404 for non-existing key' do
- get api('/keys/999999', admin)
+ get api('/keys/0', admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 Not found')
end
diff --git a/spec/requests/api/merge_request_diffs_spec.rb b/spec/requests/api/merge_request_diffs_spec.rb
index 6530dc956cb..8a67d98fc4c 100644
--- a/spec/requests/api/merge_request_diffs_spec.rb
+++ b/spec/requests/api/merge_request_diffs_spec.rb
@@ -30,7 +30,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
end
it 'returns a 404 when merge_request_iid not found' do
- get api("/projects/#{project.id}/merge_requests/999/versions", user)
+ get api("/projects/#{project.id}/merge_requests/0/versions", user)
expect(response).to have_gitlab_http_status(404)
end
end
@@ -53,7 +53,7 @@ describe API::MergeRequestDiffs, 'MergeRequestDiffs' do
end
it 'returns a 404 when merge_request version_id is not found' do
- get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions/999", user)
+ get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/versions/0", user)
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index b8426126bc6..4259fda7f04 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -4,32 +4,406 @@ describe API::MergeRequests do
include ProjectForksHelper
let(:base_time) { Time.now }
- let(:user) { create(:user) }
- let(:admin) { create(:user, :admin) }
- let(:non_member) { create(:user) }
- let!(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace, only_allow_merge_if_pipeline_succeeds: false) }
+ set(:user) { create(:user) }
+ set(:admin) { create(:user, :admin) }
+ let(:project) { create(:project, :public, :repository, creator: user, namespace: user.namespace, only_allow_merge_if_pipeline_succeeds: false) }
let(:milestone) { create(:milestone, title: '1.0.0', project: project) }
- let(:pipeline) { create(:ci_empty_pipeline) }
- let(:milestone1) { create(:milestone, title: '0.9', project: project) }
+ let(:milestone1) { create(:milestone, title: '0.9', project: project) }
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
- create(:label, title: 'label', color: '#FFAABB', project: project)
- end
- let!(:label2) { create(:label, title: 'a-test', color: '#FFFFFF', project: project) }
- let!(:label_link) { create(:label_link, label: label, target: merge_request) }
- let!(:label_link2) { create(:label_link, label: label2, target: merge_request) }
- let!(:downvote) { create(:award_emoji, :downvote, awardable: merge_request) }
- let!(:upvote) { create(:award_emoji, :upvote, awardable: merge_request) }
+ let(:label) { create(:label, title: 'label', color: '#FFAABB', project: project) }
+ let(:label2) { create(:label, title: 'a-test', color: '#FFFFFF', project: project) }
before do
project.add_reporter(user)
end
+ shared_context 'with labels' do
+ before do
+ create(:label_link, label: label, target: merge_request)
+ create(:label_link, label: label2, target: merge_request)
+ end
+ end
+
+ shared_examples 'merge requests list' do
+ context 'when unauthenticated' do
+ it 'returns merge requests for public projects' do
+ get api(endpoint_path)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ end
+ end
+
+ context 'when authenticated' do
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new do
+ get api(endpoint_path, user)
+ end
+
+ create(:merge_request, state: 'closed', milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: 'Test', created_at: base_time)
+
+ merge_request = create(:merge_request, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: 'Test', created_at: base_time)
+
+ merge_request.metrics.update!(merged_by: user,
+ latest_closed_by: user,
+ latest_closed_at: 1.hour.ago,
+ merged_at: 2.hours.ago)
+
+ expect do
+ get api(endpoint_path, user)
+ end.not_to exceed_query_limit(control)
+ end
+
+ context 'with labels' do
+ include_context 'with labels'
+
+ it 'returns an array of all merge_requests' do
+ get api(endpoint_path, 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(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)
+ expect(json_response.last['merge_commit_sha']).to be_nil
+ expect(json_response.last['merge_commit_sha']).to eq(merge_request.merge_commit_sha)
+ expect(json_response.last['downvotes']).to eq(0)
+ expect(json_response.last['upvotes']).to eq(0)
+ expect(json_response.last['labels']).to eq([label2.title, label.title])
+ expect(json_response.first['title']).to eq(merge_request_merged.title)
+ expect(json_response.first['sha']).to eq(merge_request_merged.diff_head_sha)
+ expect(json_response.first['merge_commit_sha']).not_to be_nil
+ expect(json_response.first['merge_commit_sha']).to eq(merge_request_merged.merge_commit_sha)
+ end
+ end
+
+ it 'returns an array of all merge_requests using simple mode' do
+ path = endpoint_path + '?view=simple'
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ 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(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')
+ expect(json_response.first['iid']).to eq(merge_request_merged.iid)
+ expect(json_response.first['title']).to eq(merge_request_merged.title)
+ expect(json_response.first).to have_key('web_url')
+ end
+
+ it 'returns an array of all merge_requests' do
+ path = endpoint_path + '?state'
+
+ get api(path, 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(4)
+ expect(json_response.last['title']).to eq(merge_request.title)
+ end
+
+ it 'returns an array of open merge_requests' do
+ path = endpoint_path + '?state=opened'
+
+ get api(path, 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(1)
+ expect(json_response.last['title']).to eq(merge_request.title)
+ end
+
+ it 'returns an array of closed merge_requests' do
+ path = endpoint_path + '?state=closed'
+
+ get api(path, 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(1)
+ expect(json_response.first['title']).to eq(merge_request_closed.title)
+ end
+
+ it 'returns an array of merged merge_requests' do
+ path = endpoint_path + '?state=merged'
+
+ get api(path, 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(1)
+ expect(json_response.first['title']).to eq(merge_request_merged.title)
+ end
+
+ it 'matches V4 response schema' do
+ get api(endpoint_path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/merge_requests')
+ end
+
+ it 'returns an empty array if no issue matches milestone' do
+ get api(endpoint_path, user), params: { milestone: '1.0.0' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
+ it 'returns an empty array if milestone does not exist' do
+ get api(endpoint_path, user), params: { milestone: 'foo' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
+ it 'returns an array of merge requests in given milestone' do
+ get api(endpoint_path, user), params: { milestone: '0.9' }
+
+ 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
+ get api(endpoint_path, user), params: { milestone: '0.9', state: 'closed' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(merge_request_closed.id)
+ end
+
+ context 'with labels' do
+ include_context 'with labels'
+
+ it 'returns an array of labeled merge requests' do
+ path = endpoint_path + "?labels=#{label.title}"
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label2.title, label.title])
+ end
+
+ it 'returns an array of labeled merge requests where all labels match' do
+ path = endpoint_path + "?labels=#{label.title},foo,bar"
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
+ it 'returns an empty array if no merge request matches labels' do
+ path = endpoint_path + '?labels=foo,bar'
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(0)
+ end
+
+ it 'returns an array of labeled merge requests where all labels match' do
+ path = endpoint_path + "?labels[]=#{label.title}&labels[]=#{label2.title}"
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label2.title, label.title])
+ end
+
+ it 'returns an array of merge requests with any label when filtering by any label' do
+ get api(endpoint_path, user), params: { labels: [" #{label.title} ", " #{label2.title} "] }
+
+ expect_paginated_array_response
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label2.title, label.title])
+ expect(json_response.first['id']).to eq(merge_request.id)
+ end
+
+ it 'returns an array of merge requests with any label when filtering by any label' do
+ get api(endpoint_path, user), params: { labels: ["#{label.title} , #{label2.title}"] }
+
+ expect_paginated_array_response
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['labels']).to eq([label2.title, label.title])
+ expect(json_response.first['id']).to eq(merge_request.id)
+ end
+
+ it 'returns an array of merge requests with any label when filtering by any label' do
+ get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_ANY }
+
+ expect_paginated_array_response
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(merge_request.id)
+ end
+
+ it 'returns an array of merge requests without a label when filtering by no label' do
+ get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_NONE }
+
+ response_ids = json_response.map { |merge_request| merge_request['id'] }
+
+ expect_paginated_array_response
+ expect(response_ids).to contain_exactly(merge_request_closed.id, merge_request_merged.id, merge_request_locked.id)
+ end
+ end
+
+ it 'returns an array of labeled merge requests that are merged for a milestone' do
+ bug_label = create(:label, title: 'bug', color: '#FFAABB', project: project)
+
+ mr1 = create(:merge_request, state: 'merged', source_project: project, target_project: project, milestone: milestone)
+ mr2 = create(:merge_request, state: 'merged', source_project: project, target_project: project, milestone: milestone1)
+ mr3 = create(:merge_request, state: 'closed', source_project: project, target_project: project, milestone: milestone1)
+ _mr = create(:merge_request, state: 'merged', source_project: project, target_project: project, milestone: milestone1)
+
+ create(:label_link, label: bug_label, target: mr1)
+ create(:label_link, label: bug_label, target: mr2)
+ create(:label_link, label: bug_label, target: mr3)
+
+ path = endpoint_path + "?labels=#{bug_label.title}&milestone=#{milestone1.title}&state=merged"
+
+ get api(path, user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to be_an Array
+ expect(json_response.length).to eq(1)
+ expect(json_response.first['id']).to eq(mr2.id)
+ end
+
+ context 'with ordering' do
+ before do
+ @mr_later = mr_with_later_created_and_updated_at_time
+ @mr_earlier = mr_with_earlier_created_and_updated_at_time
+ end
+
+ it 'returns an array of merge_requests in ascending order' do
+ path = endpoint_path + '?sort=asc'
+
+ get api(path, 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(4)
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
+ expect(response_dates).to eq(response_dates.sort)
+ end
+
+ it 'returns an array of merge_requests in descending order' do
+ path = endpoint_path + '?sort=desc'
+
+ get api(path, 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(4)
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ context '2 merge requests with equal created_at' do
+ let!(:closed_mr2) do
+ create :merge_request,
+ state: 'closed',
+ milestone: milestone1,
+ author: user,
+ assignee: user,
+ source_project: project,
+ target_project: project,
+ title: "Test",
+ created_at: @mr_earlier.created_at
+ end
+
+ it 'page breaks first page correctly' do
+ get api("#{endpoint_path}?sort=desc&per_page=4", user)
+
+ response_ids = json_response.map { |merge_request| merge_request['id'] }
+
+ expect(response_ids).to include(closed_mr2.id)
+ expect(response_ids).not_to include(@mr_earlier.id)
+ end
+
+ it 'page breaks second page correctly' do
+ get api("#{endpoint_path}?sort=desc&per_page=4&page=2", user)
+
+ response_ids = json_response.map { |merge_request| merge_request['id'] }
+
+ expect(response_ids).not_to include(closed_mr2.id)
+ expect(response_ids).to include(@mr_earlier.id)
+ end
+ end
+
+ it 'returns an array of merge_requests ordered by updated_at' do
+ path = endpoint_path + '?order_by=updated_at'
+
+ get api(path, 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(4)
+ response_dates = json_response.map { |merge_request| merge_request['updated_at'] }
+ expect(response_dates).to eq(response_dates.sort.reverse)
+ end
+
+ it 'returns an array of merge_requests ordered by created_at' do
+ path = endpoint_path + '?order_by=created_at&sort=asc'
+
+ get api(path, 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(4)
+ response_dates = json_response.map { |merge_request| merge_request['created_at'] }
+ expect(response_dates).to eq(response_dates.sort)
+ end
+ end
+
+ context 'source_branch param' do
+ it 'returns merge requests with the given source branch' do
+ get api(endpoint_path, user), params: { source_branch: merge_request_closed.source_branch, state: 'all' }
+
+ expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked)
+ end
+ end
+
+ context 'target_branch param' do
+ it 'returns merge requests with the given target branch' do
+ get api(endpoint_path, user), params: { target_branch: merge_request_closed.target_branch, state: 'all' }
+
+ expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked)
+ end
+ end
+ end
+ end
+
describe 'route shadowing' do
include GrapePathHelpers::NamedRouteMatcher
@@ -320,6 +694,18 @@ describe API::MergeRequests do
expect(json_response.first['title']).to eq merge_request_closed.title
expect(json_response.first['id']).to eq merge_request_closed.id
end
+
+ it 'avoids N+1 queries' do
+ control = ActiveRecord::QueryRecorder.new do
+ get api("/projects/#{project.id}/merge_requests", user)
+ end.count
+
+ create(:merge_request, author: user, assignee: user, source_project: project, target_project: project, created_at: base_time)
+
+ expect do
+ get api("/projects/#{project.id}/merge_requests", user)
+ end.not_to exceed_query_limit(control)
+ end
end
describe "GET /groups/:id/merge_requests" do
@@ -344,6 +730,9 @@ describe API::MergeRequests do
describe "GET /projects/:id/merge_requests/:merge_request_iid" do
it 'exposes known attributes' do
+ create(:award_emoji, :downvote, awardable: merge_request)
+ create(:award_emoji, :upvote, awardable: merge_request)
+
get api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user)
expect(response).to have_gitlab_http_status(200)
@@ -393,6 +782,8 @@ describe API::MergeRequests do
end
context 'merge_request_metrics' do
+ let(:pipeline) { create(:ci_empty_pipeline) }
+
before do
merge_request.metrics.update!(merged_by: user,
latest_closed_by: user,
@@ -441,7 +832,7 @@ describe API::MergeRequests do
end
it "returns a 404 error if merge_request_iid not found" do
- get api("/projects/#{project.id}/merge_requests/999", user)
+ get api("/projects/#{project.id}/merge_requests/0", user)
expect(response).to have_gitlab_http_status(404)
end
@@ -531,7 +922,7 @@ describe API::MergeRequests do
end
it 'returns a 404 when merge_request_iid not found' do
- get api("/projects/#{project.id}/merge_requests/999/commits", user)
+ get api("/projects/#{project.id}/merge_requests/0/commits", user)
expect(response).to have_gitlab_http_status(404)
end
@@ -551,7 +942,7 @@ describe API::MergeRequests do
end
it 'returns a 404 when merge_request_iid not found' do
- get api("/projects/#{project.id}/merge_requests/999/changes", user)
+ get api("/projects/#{project.id}/merge_requests/0/changes", user)
expect(response).to have_gitlab_http_status(404)
end
@@ -605,26 +996,115 @@ describe API::MergeRequests do
end
end
- describe "POST /projects/:id/merge_requests" do
+ describe 'POST /projects/:id/merge_requests' do
context 'between branches projects' do
- it "returns merge_request" do
- post api("/projects/#{project.id}/merge_requests", user),
- params: {
- title: 'Test merge_request',
- source_branch: 'feature_conflict',
- target_branch: 'master',
- author: user,
- labels: 'label, label2',
- milestone_id: milestone.id,
- squash: true
- }
+ context 'different labels' do
+ let(:params) do
+ {
+ title: 'Test merge_request',
+ source_branch: 'feature_conflict',
+ target_branch: 'master',
+ author_id: user.id,
+ milestone_id: milestone.id,
+ squash: true
+ }
+ end
- expect(response).to have_gitlab_http_status(201)
- expect(json_response['title']).to eq('Test merge_request')
- expect(json_response['labels']).to eq(%w(label label2))
- expect(json_response['milestone']['id']).to eq(milestone.id)
- expect(json_response['squash']).to be_truthy
- expect(json_response['force_remove_source_branch']).to be_falsy
+ shared_examples_for 'creates merge request with labels' do
+ it 'returns merge_request' do
+ params[:labels] = labels
+ post api("/projects/#{project.id}/merge_requests", user), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['title']).to eq('Test merge_request')
+ expect(json_response['labels']).to eq(%w(label label2))
+ expect(json_response['milestone']['id']).to eq(milestone.id)
+ expect(json_response['squash']).to be_truthy
+ expect(json_response['force_remove_source_branch']).to be_falsy
+ end
+ end
+
+ it_behaves_like 'creates merge request with labels' do
+ let(:labels) { 'label, label2' }
+ end
+
+ it_behaves_like 'creates merge request with labels' do
+ let(:labels) { %w(label label2) }
+ end
+
+ it_behaves_like 'creates merge request with labels' do
+ let(:labels) { %w(label label2) }
+ end
+
+ it 'creates merge request with special label names' do
+ params[:labels] = 'label, label?, label&foo, ?, &'
+ post api("/projects/#{project.id}/merge_requests", user), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['labels']).to include 'label'
+ expect(json_response['labels']).to include 'label?'
+ expect(json_response['labels']).to include 'label&foo'
+ expect(json_response['labels']).to include '?'
+ expect(json_response['labels']).to include '&'
+ end
+
+ it 'creates merge request with special label names as array' do
+ params[:labels] = ['label', 'label?', 'label&foo, ?, &', '1, 2', 3, 4]
+ post api("/projects/#{project.id}/merge_requests", user), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['labels']).to include 'label'
+ expect(json_response['labels']).to include 'label?'
+ expect(json_response['labels']).to include 'label&foo'
+ expect(json_response['labels']).to include '?'
+ expect(json_response['labels']).to include '&'
+ expect(json_response['labels']).to include '1'
+ expect(json_response['labels']).to include '2'
+ expect(json_response['labels']).to include '3'
+ expect(json_response['labels']).to include '4'
+ end
+
+ it 'empty label param does not add any labels' do
+ params[:labels] = ''
+ post api("/projects/#{project.id}/merge_requests", user), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['labels']).to eq([])
+ end
+
+ it 'empty label param as array does not add any labels, but only explicitly as json' do
+ params[:labels] = []
+ post api("/projects/#{project.id}/merge_requests", user),
+ params: params.to_json,
+ headers: { 'Content-Type': 'application/json' }
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['labels']).to eq([])
+ end
+
+ xit 'empty label param as array, does not add any labels' do
+ params[:labels] = []
+ post api("/projects/#{project.id}/merge_requests", user), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['labels']).to eq([])
+ end
+
+ it 'array with one empty string element does not add labels' do
+ params[:labels] = ['']
+ post api("/projects/#{project.id}/merge_requests", user), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['labels']).to eq([])
+ end
+
+ it 'array with multiple empty string elements, does not add labels' do
+ params[:labels] = ['', '', '']
+ post api("/projects/#{project.id}/merge_requests", user), params: params
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['labels']).to eq([])
+ end
end
it "returns 422 when source_branch equals target_branch" do
@@ -651,23 +1131,6 @@ describe API::MergeRequests do
expect(response).to have_gitlab_http_status(400)
end
- it 'allows special label names' do
- post api("/projects/#{project.id}/merge_requests", user),
- params: {
- title: 'Test merge_request',
- source_branch: 'markdown',
- target_branch: 'master',
- author: user,
- labels: 'label, label?, label&foo, ?, &'
- }
- expect(response).to have_gitlab_http_status(201)
- expect(json_response['labels']).to include 'label'
- expect(json_response['labels']).to include 'label?'
- expect(json_response['labels']).to include 'label&foo'
- expect(json_response['labels']).to include '?'
- expect(json_response['labels']).to include '&'
- end
-
context 'with existing MR' do
before do
post api("/projects/#{project.id}/merge_requests", user),
@@ -984,6 +1447,70 @@ describe API::MergeRequests do
expect(squash_commit.message).to eq(merge_request.default_squash_commit_message)
end
end
+
+ describe "the should_remove_source_branch param" do
+ let(:source_repository) { merge_request.source_project.repository }
+ let(:source_branch) { merge_request.source_branch }
+
+ it 'removes the source branch when set' do
+ put(
+ api("/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge", user),
+ params: { should_remove_source_branch: true }
+ )
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(source_repository.branch_exists?(source_branch)).to be_falsy
+ end
+ end
+ end
+
+ describe "PUT /projects/:id/merge_requests/:merge_request_iid/merge_to_ref" do
+ let(:pipeline) { create(:ci_pipeline_without_jobs) }
+ let(:url) do
+ "/projects/#{project.id}/merge_requests/#{merge_request.iid}/merge_to_ref"
+ end
+
+ it 'returns the generated ID from the merge service in case of success' do
+ put api(url, user), params: { merge_commit_message: 'Custom message' }
+
+ commit = project.commit(json_response['commit_id'])
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['commit_id']).to be_present
+ expect(commit.message).to eq('Custom message')
+ end
+
+ it "returns 400 if branch can't be merged" do
+ merge_request.update!(state: 'merged')
+
+ put api(url, user)
+
+ expect(response).to have_gitlab_http_status(400)
+ expect(json_response['message'])
+ .to eq("Merge request is not mergeable to #{merge_request.merge_ref_path}")
+ end
+
+ it 'returns 403 if user has no permissions to merge to the ref' do
+ user2 = create(:user)
+ project.add_reporter(user2)
+
+ put api(url, user2)
+
+ expect(response).to have_gitlab_http_status(403)
+ expect(json_response['message']).to eq('403 Forbidden')
+ end
+
+ it 'returns 404 for an invalid merge request IID' do
+ put api("/projects/#{project.id}/merge_requests/12345/merge_to_ref", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it "returns 404 if the merge request id is used instead of iid" do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/merge", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
end
describe "PUT /projects/:id/merge_requests/:merge_request_iid" do
@@ -1034,19 +1561,97 @@ describe API::MergeRequests do
expect(json_response['force_remove_source_branch']).to be_truthy
end
- it 'allows special label names' do
- put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user),
- params: {
- title: 'new issue',
- labels: 'label, label?, label&foo, ?, &'
- }
+ context 'when updating labels' do
+ it 'allows special label names' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user),
+ params: {
+ title: 'new issue',
+ labels: 'label, label?, label&foo, ?, &'
+ }
+
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to include 'label'
+ expect(json_response['labels']).to include 'label?'
+ expect(json_response['labels']).to include 'label&foo'
+ expect(json_response['labels']).to include '?'
+ expect(json_response['labels']).to include '&'
+ end
+
+ it 'also accepts labels as an array' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user),
+ params: {
+ title: 'new issue',
+ labels: ['label', 'label?', 'label&foo, ?, &', '1, 2', 3, 4]
+ }
+
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to include 'label'
+ expect(json_response['labels']).to include 'label?'
+ expect(json_response['labels']).to include 'label&foo'
+ expect(json_response['labels']).to include '?'
+ expect(json_response['labels']).to include '&'
+ expect(json_response['labels']).to include '1'
+ expect(json_response['labels']).to include '2'
+ expect(json_response['labels']).to include '3'
+ expect(json_response['labels']).to include '4'
+ end
+
+ it 'empty label param removes labels' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user),
+ params: {
+ title: 'new issue',
+ labels: ''
+ }
+
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to eq []
+ end
+
+ it 'label param as empty array, but only explicitly as json, removes labels' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user),
+ params: {
+ title: 'new issue',
+ labels: []
+ }.to_json,
+ headers: { 'Content-Type' => 'application/json' }
+
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to eq []
+ end
+
+ xit 'empty label as array, removes labels' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user),
+ params: {
+ title: 'new issue',
+ labels: []
+ }
+
+ expect(response.status).to eq(200)
+ # fails, as grape ommits for some reason empty array as optional param value, so nothing it passed along
+ expect(json_response['labels']).to eq []
+ end
+
+ it 'array with one empty string element removes labels' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user),
+ params: {
+ title: 'new issue',
+ labels: ['']
+ }
- expect(response.status).to eq(200)
- expect(json_response['labels']).to include 'label'
- expect(json_response['labels']).to include 'label?'
- expect(json_response['labels']).to include 'label&foo'
- expect(json_response['labels']).to include '?'
- expect(json_response['labels']).to include '&'
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to eq []
+ end
+
+ it 'array with multiple empty string elements, removes labels' do
+ put api("/projects/#{project.id}/merge_requests/#{merge_request.iid}", user),
+ params: {
+ title: 'new issue',
+ labels: ['', '', '']
+ }
+
+ expect(response.status).to eq(200)
+ expect(json_response['labels']).to eq []
+ end
end
it 'does not update state when title is empty' do
diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb
index 145356c4df5..2e376109b42 100644
--- a/spec/requests/api/namespaces_spec.rb
+++ b/spec/requests/api/namespaces_spec.rb
@@ -149,7 +149,7 @@ describe API::Namespaces do
context "when namespace doesn't exist" do
it 'returns not-found' do
- get api('/namespaces/9999', request_actor)
+ get api('/namespaces/0', request_actor)
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/requests/api/project_clusters_spec.rb b/spec/requests/api/project_clusters_spec.rb
index 9bab1f95150..4e42e233b4c 100644
--- a/spec/requests/api/project_clusters_spec.rb
+++ b/spec/requests/api/project_clusters_spec.rb
@@ -331,7 +331,6 @@ describe API::ProjectClusters do
it 'should update cluster attributes' do
expect(cluster.platform_kubernetes.namespace).to eq('new-namespace')
- expect(cluster.kubernetes_namespace.namespace).to eq('new-namespace')
end
end
diff --git a/spec/requests/api/project_milestones_spec.rb b/spec/requests/api/project_milestones_spec.rb
index 49b5dfb0b33..895f05a98e8 100644
--- a/spec/requests/api/project_milestones_spec.rb
+++ b/spec/requests/api/project_milestones_spec.rb
@@ -23,13 +23,13 @@ describe API::ProjectMilestones do
end
it 'returns 404 response when the project does not exists' do
- delete api("/projects/999/milestones/#{milestone.id}", user)
+ delete api("/projects/0/milestones/#{milestone.id}", user)
expect(response).to have_gitlab_http_status(404)
end
it 'returns 404 response when the milestone does not exists' do
- delete api("/projects/#{project.id}/milestones/999", user)
+ delete api("/projects/#{project.id}/milestones/0", user)
expect(response).to have_gitlab_http_status(404)
end
@@ -49,4 +49,74 @@ describe API::ProjectMilestones do
params: { state_event: 'close' }
end
end
+
+ describe 'POST /projects/:id/milestones/:milestone_id/promote' do
+ let(:group) { create(:group) }
+
+ before do
+ project.update(namespace: group)
+ end
+
+ context 'when user does not have permission to promote milestone' do
+ before do
+ group.add_guest(user)
+ end
+
+ it 'returns 403' do
+ post api("/projects/#{project.id}/milestones/#{milestone.id}/promote", user)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+
+ context 'when user has permission' do
+ before do
+ group.add_developer(user)
+ end
+
+ it 'returns 200' do
+ post api("/projects/#{project.id}/milestones/#{milestone.id}/promote", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(group.milestones.first.title).to eq(milestone.title)
+ end
+
+ it 'returns 200 for closed milestone' do
+ post api("/projects/#{project.id}/milestones/#{closed_milestone.id}/promote", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(group.milestones.first.title).to eq(closed_milestone.title)
+ end
+ end
+
+ context 'when no such resources' do
+ before do
+ group.add_developer(user)
+ end
+
+ it 'returns 404 response when the project does not exist' do
+ post api("/projects/0/milestones/#{milestone.id}/promote", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns 404 response when the milestone does not exist' do
+ post api("/projects/#{project.id}/milestones/0/promote", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context 'when project does not belong to group' do
+ before do
+ project.update(namespace: user.namespace)
+ end
+
+ it 'returns 403' do
+ post api("/projects/#{project.id}/milestones/#{milestone.id}/promote", user)
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/project_statistics_spec.rb b/spec/requests/api/project_statistics_spec.rb
new file mode 100644
index 00000000000..184d0a72c37
--- /dev/null
+++ b/spec/requests/api/project_statistics_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe API::ProjectStatistics do
+ let(:maintainer) { create(:user) }
+ let(:public_project) { create(:project, :public) }
+
+ before do
+ public_project.add_maintainer(maintainer)
+ end
+
+ describe 'GET /projects/:id/statistics' do
+ let!(:fetch_statistics1) { create(:project_daily_statistic, project: public_project, fetch_count: 30, date: 29.days.ago) }
+ let!(:fetch_statistics2) { create(:project_daily_statistic, project: public_project, fetch_count: 4, date: 3.days.ago) }
+ let!(:fetch_statistics3) { create(:project_daily_statistic, project: public_project, fetch_count: 3, date: 2.days.ago) }
+ let!(:fetch_statistics4) { create(:project_daily_statistic, project: public_project, fetch_count: 2, date: 1.day.ago) }
+ let!(:fetch_statistics5) { create(:project_daily_statistic, project: public_project, fetch_count: 1, date: Date.today) }
+ let!(:fetch_statistics_other_project) { create(:project_daily_statistic, project: create(:project), fetch_count: 29, date: 29.days.ago) }
+
+ it 'returns the fetch statistics of the last 30 days' do
+ get api("/projects/#{public_project.id}/statistics", maintainer)
+
+ expect(response).to have_gitlab_http_status(200)
+ fetches = json_response['fetches']
+ expect(fetches['total']).to eq(40)
+ expect(fetches['days'].length).to eq(5)
+ expect(fetches['days'].first).to eq({ 'count' => fetch_statistics5.fetch_count, 'date' => fetch_statistics5.date.to_s })
+ expect(fetches['days'].last).to eq({ 'count' => fetch_statistics1.fetch_count, 'date' => fetch_statistics1.date.to_s })
+ end
+
+ it 'excludes the fetch statistics older than 30 days' do
+ create(:project_daily_statistic, fetch_count: 31, project: public_project, date: 30.days.ago)
+
+ get api("/projects/#{public_project.id}/statistics", maintainer)
+
+ expect(response).to have_gitlab_http_status(200)
+ fetches = json_response['fetches']
+ expect(fetches['total']).to eq(40)
+ expect(fetches['days'].length).to eq(5)
+ expect(fetches['days'].last).to eq({ 'count' => fetch_statistics1.fetch_count, 'date' => fetch_statistics1.date.to_s })
+ end
+
+ it 'responds with 403 when the user is not a maintainer of the repository' do
+ developer = create(:user)
+ public_project.add_developer(developer)
+
+ get api("/projects/#{public_project.id}/statistics", developer)
+
+ expect(response).to have_gitlab_http_status(403)
+ expect(json_response['message']).to eq('403 Forbidden')
+ end
+
+ it 'responds with 404 when daily_statistics_enabled? is false' do
+ stub_feature_flags(project_daily_statistics: { thing: public_project, enabled: false })
+
+ get api("/projects/#{public_project.id}/statistics", maintainer)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+end
diff --git a/spec/requests/api/project_templates_spec.rb b/spec/requests/api/project_templates_spec.rb
index ab5d4de7ff7..80e5033dab4 100644
--- a/spec/requests/api/project_templates_spec.rb
+++ b/spec/requests/api/project_templates_spec.rb
@@ -92,6 +92,22 @@ describe API::ProjectTemplates do
expect(json_response['name']).to eq('Actionscript')
end
+ it 'returns C++ gitignore' do
+ get api("/projects/#{public_project.id}/templates/gitignores/C++")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('C++')
+ end
+
+ it 'returns C++ gitignore for URL-encoded names' do
+ get api("/projects/#{public_project.id}/templates/gitignores/C%2B%2B")
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(response).to match_response_schema('public_api/v4/template')
+ expect(json_response['name']).to eq('C++')
+ end
+
it 'returns a specific gitlab_ci_yml' do
get api("/projects/#{public_project.id}/templates/gitlab_ci_ymls/Android")
@@ -125,6 +141,18 @@ describe API::ProjectTemplates do
expect(response).to have_gitlab_http_status(200)
expect(response).to match_response_schema('public_api/v4/license')
end
+
+ shared_examples 'path traversal attempt' do |template_type|
+ it 'rejects invalid filenames' do
+ get api("/projects/#{public_project.id}/templates/#{template_type}/%2e%2e%2fPython%2ea")
+
+ expect(response).to have_gitlab_http_status(500)
+ end
+ end
+
+ TemplateFinder::VENDORED_TEMPLATES.each do |template_type, _|
+ it_behaves_like 'path traversal attempt', template_type
+ end
end
describe 'GET /projects/:id/templates/licenses/:key' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index cfa7a1a31a3..4c3c088b307 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -4,6 +4,15 @@ require 'spec_helper'
shared_examples 'languages and percentages JSON response' do
let(:expected_languages) { project.repository.languages.map { |language| language.values_at(:label, :value)}.to_h }
+ before do
+ allow(project.repository).to receive(:languages).and_return(
+ [{ value: 66.69, label: "Ruby", color: "#701516", highlight: "#701516" },
+ { value: 22.98, label: "JavaScript", color: "#f1e05a", highlight: "#f1e05a" },
+ { value: 7.91, label: "HTML", color: "#e34c26", highlight: "#e34c26" },
+ { value: 2.42, label: "CoffeeScript", color: "#244776", highlight: "#244776" }]
+ )
+ end
+
it 'returns expected language values' do
get api("/projects/#{project.id}/languages", user)
@@ -11,6 +20,23 @@ shared_examples 'languages and percentages JSON response' do
expect(json_response).to eq(expected_languages)
expect(json_response.count).to be > 1
end
+
+ context 'when the languages were detected before' do
+ before do
+ Projects::DetectRepositoryLanguagesService.new(project, project.owner).execute
+ end
+
+ it 'returns the detection from the database' do
+ # Allow this to happen once, so the expected languages can be determined
+ expect(project.repository).to receive(:languages).once
+
+ get api("/projects/#{project.id}/languages", user)
+
+ expect(response).to have_gitlab_http_status(:ok)
+ expect(json_response).to eq(expected_languages)
+ expect(json_response.count).to be > 1
+ end
+ end
end
describe API::Projects do
@@ -110,6 +136,7 @@ describe API::Projects do
end
let!(:public_project) { create(:project, :public, name: 'public_project') }
+
before do
project
project2
@@ -752,7 +779,7 @@ describe API::Projects do
let!(:public_project) { create(:project, :public, name: 'public_project', creator_id: user4.id, namespace: user4.namespace) }
it 'returns error when user not found' do
- get api('/users/9999/projects/')
+ get api('/users/0/projects/')
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
@@ -942,8 +969,16 @@ describe API::Projects do
describe 'GET /projects/:id' do
context 'when unauthenticated' do
- it 'returns the public projects' do
- public_project = create(:project, :public)
+ it 'does not return private projects' do
+ private_project = create(:project, :private)
+
+ get api("/projects/#{private_project.id}")
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns public projects' do
+ public_project = create(:project, :repository, :public)
get api("/projects/#{public_project.id}")
@@ -951,8 +986,34 @@ describe API::Projects do
expect(json_response['id']).to eq(public_project.id)
expect(json_response['description']).to eq(public_project.description)
expect(json_response['default_branch']).to eq(public_project.default_branch)
+ expect(json_response['ci_config_path']).to eq(public_project.ci_config_path)
expect(json_response.keys).not_to include('permissions')
end
+
+ context 'and the project has a private repository' do
+ let(:project) { create(:project, :repository, :public, :repository_private) }
+ let(:protected_attributes) { %w(default_branch ci_config_path) }
+
+ it 'hides protected attributes of private repositories if user is not a member' do
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ protected_attributes.each do |attribute|
+ expect(json_response.keys).not_to include(attribute)
+ end
+ end
+
+ it 'exposes protected attributes of private repositories if user is a member' do
+ project.add_developer(user)
+
+ get api("/projects/#{project.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ protected_attributes.each do |attribute|
+ expect(json_response.keys).to include(attribute)
+ end
+ end
+ end
end
context 'when authenticated' do
@@ -1104,6 +1165,36 @@ describe API::Projects do
expect(json_response).to include 'statistics'
end
+ context "and the project has a private repository" do
+ let(:project) { create(:project, :public, :repository, :repository_private) }
+
+ it "does not include statistics if user is not a member" do
+ get api("/projects/#{project.id}", user), params: { statistics: true }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).not_to include 'statistics'
+ end
+
+ it "includes statistics if user is a member" do
+ project.add_developer(user)
+
+ get api("/projects/#{project.id}", user), params: { statistics: true }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to include 'statistics'
+ end
+
+ it "includes statistics also when repository is disabled" do
+ project.add_developer(user)
+ project.project_feature.update_attribute(:repository_access_level, ProjectFeature::DISABLED)
+
+ get api("/projects/#{project.id}", user), params: { statistics: true }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response).to include 'statistics'
+ end
+ end
+
it "includes import_error if user can admin project" do
get api("/projects/#{project.id}", user)
@@ -1359,7 +1450,7 @@ describe API::Projects do
end
it 'fails if forked_from project which does not exist' do
- post api("/projects/#{project_fork_target.id}/fork/9999", admin)
+ post api("/projects/#{project_fork_target.id}/fork/0", admin)
expect(response).to have_gitlab_http_status(404)
end
@@ -1484,6 +1575,9 @@ describe API::Projects do
describe "POST /projects/:id/share" do
let(:group) { create(:group) }
+ before do
+ group.add_developer(user)
+ end
it "shares project with group" do
expires_at = 10.days.from_now.to_date
@@ -1534,6 +1628,15 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(400)
expect(json_response['error']).to eq 'group_access does not have a valid value'
end
+
+ it "returns a 409 error when link is not saved" do
+ allow(::Projects::GroupLinks::CreateService).to receive_message_chain(:new, :execute)
+ .and_return({ status: :error, http_status: 409, message: 'error' })
+
+ post api("/projects/#{project.id}/share", user), params: { group_id: group.id, group_access: Gitlab::Access::DEVELOPER }
+
+ expect(response).to have_gitlab_http_status(409)
+ end
end
describe 'DELETE /projects/:id/share/:group_id' do
@@ -1910,7 +2013,7 @@ describe API::Projects do
end
it 'returns not_found(404) for not existing project' do
- get api("/projects/9999999999/languages", user)
+ get api("/projects/0/languages", user)
expect(response).to have_gitlab_http_status(:not_found)
end
@@ -1995,6 +2098,11 @@ describe API::Projects do
let(:project) do
create(:project, :repository, creator: user, namespace: user.namespace)
end
+
+ let(:project2) do
+ create(:project, :repository, creator: user, namespace: user.namespace)
+ end
+
let(:group) { create(:group) }
let(:group2) do
group = create(:group, name: 'group2_name')
@@ -2010,6 +2118,7 @@ describe API::Projects do
before do
project.add_reporter(user2)
+ project2.add_reporter(user2)
end
context 'when authenticated' do
@@ -2124,6 +2233,48 @@ describe API::Projects do
expect(response).to have_gitlab_http_status(201)
expect(json_response['namespace']['name']).to eq(group.name)
end
+
+ it 'accepts a path for the target project' do
+ post api("/projects/#{project.id}/fork", user2), params: { path: 'foobar' }
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq(project.name)
+ expect(json_response['path']).to eq('foobar')
+ expect(json_response['owner']['id']).to eq(user2.id)
+ expect(json_response['namespace']['id']).to eq(user2.namespace.id)
+ expect(json_response['forked_from_project']['id']).to eq(project.id)
+ expect(json_response['import_status']).to eq('scheduled')
+ expect(json_response).to include("import_error")
+ end
+
+ it 'fails to fork if path is already taken' do
+ post api("/projects/#{project.id}/fork", user2), params: { path: 'foobar' }
+ post api("/projects/#{project2.id}/fork", user2), params: { path: 'foobar' }
+
+ expect(response).to have_gitlab_http_status(409)
+ expect(json_response['message']['path']).to eq(['has already been taken'])
+ end
+
+ it 'accepts a name for the target project' do
+ post api("/projects/#{project.id}/fork", user2), params: { name: 'My Random Project' }
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['name']).to eq('My Random Project')
+ expect(json_response['path']).to eq(project.path)
+ expect(json_response['owner']['id']).to eq(user2.id)
+ expect(json_response['namespace']['id']).to eq(user2.namespace.id)
+ expect(json_response['forked_from_project']['id']).to eq(project.id)
+ expect(json_response['import_status']).to eq('scheduled')
+ expect(json_response).to include("import_error")
+ end
+
+ it 'fails to fork if name is already taken' do
+ post api("/projects/#{project.id}/fork", user2), params: { name: 'My Random Project' }
+ post api("/projects/#{project2.id}/fork", user2), params: { name: 'My Random Project' }
+
+ expect(response).to have_gitlab_http_status(409)
+ expect(json_response['message']['name']).to eq(['has already been taken'])
+ end
end
context 'when unauthenticated' do
diff --git a/spec/requests/api/release/links_spec.rb b/spec/requests/api/release/links_spec.rb
index ba948e37e2f..3a59052bb29 100644
--- a/spec/requests/api/release/links_spec.rb
+++ b/spec/requests/api/release/links_spec.rb
@@ -73,6 +73,22 @@ describe API::Release::Links do
expect(response).to have_gitlab_http_status(:ok)
end
end
+
+ context 'when project is public and the repository is private' do
+ let(:project) { create(:project, :repository, :public, :repository_private) }
+
+ it_behaves_like '403 response' do
+ let(:request) { get api("/projects/#{project.id}/releases/v0.1/assets/links", non_project_member) }
+ end
+
+ context 'when the release does not exists' do
+ let!(:release) { }
+
+ it_behaves_like '403 response' do
+ let(:request) { get api("/projects/#{project.id}/releases/v0.1/assets/links", non_project_member) }
+ end
+ end
+ end
end
end
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index d7ddd97e8c8..3ccedd8dd06 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -417,7 +417,9 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
'ref' => job.ref,
'sha' => job.sha,
'before_sha' => job.before_sha,
- 'ref_type' => 'branch' }
+ 'ref_type' => 'branch',
+ 'refspecs' => %w[+refs/heads/*:refs/remotes/origin/* +refs/tags/*:refs/tags/*],
+ 'depth' => 0 }
end
let(:expected_steps) do
@@ -434,9 +436,9 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
let(:expected_variables) do
- [{ 'key' => 'CI_JOB_NAME', 'value' => 'spinach', 'public' => true },
- { 'key' => 'CI_JOB_STAGE', 'value' => 'test', 'public' => true },
- { 'key' => 'DB_NAME', 'value' => 'postgres', 'public' => true }]
+ [{ 'key' => 'CI_JOB_NAME', 'value' => 'spinach', 'public' => true, 'masked' => false },
+ { 'key' => 'CI_JOB_STAGE', 'value' => 'test', 'public' => true, 'masked' => false },
+ { 'key' => 'DB_NAME', 'value' => 'postgres', 'public' => true, 'masked' => false }]
end
let(:expected_artifacts) do
@@ -489,6 +491,29 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(201)
expect(json_response['git_info']['ref_type']).to eq('tag')
end
+
+ context 'when GIT_DEPTH is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: pipeline)
+ end
+
+ it 'specifies refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['refspecs']).to include("+refs/tags/#{job.ref}:refs/tags/#{job.ref}")
+ end
+ end
+
+ context 'when GIT_DEPTH is not specified' do
+ it 'specifies refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['refspecs'])
+ .to contain_exactly('+refs/tags/*:refs/tags/*', '+refs/heads/*:refs/remotes/origin/*')
+ end
+ end
end
context 'when job is made for branch' do
@@ -498,6 +523,55 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
expect(response).to have_gitlab_http_status(201)
expect(json_response['git_info']['ref_type']).to eq('branch')
end
+
+ context 'when GIT_DEPTH is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: pipeline)
+ end
+
+ it 'specifies refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['refspecs']).to include("+refs/heads/#{job.ref}:refs/remotes/origin/#{job.ref}")
+ end
+ end
+
+ context 'when GIT_DEPTH is not specified' do
+ it 'specifies refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['refspecs'])
+ .to contain_exactly('+refs/tags/*:refs/tags/*', '+refs/heads/*:refs/remotes/origin/*')
+ end
+ end
+ end
+
+ context 'when job is made for merge request' do
+ let(:pipeline) { create(:ci_pipeline_without_jobs, source: :merge_request_event, project: project, ref: 'feature', merge_request: merge_request) }
+ let!(:job) { create(:ci_build, pipeline: pipeline, name: 'spinach', ref: 'feature', stage: 'test', stage_idx: 0) }
+ let(:merge_request) { create(:merge_request) }
+
+ it 'sets branch as ref_type' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['ref_type']).to eq('branch')
+ end
+
+ context 'when GIT_DEPTH is specified' do
+ before do
+ create(:ci_pipeline_variable, key: 'GIT_DEPTH', value: 1, pipeline: pipeline)
+ end
+
+ it 'returns the overwritten git depth for merge request refspecs' do
+ request_job
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['git_info']['depth']).to eq(1)
+ end
+ end
end
it 'updates runner info' do
@@ -526,6 +600,15 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
expect(runner.reload.ip_address).to eq('123.222.123.222')
end
+ it "handles multiple X-Forwarded-For addresses" do
+ post api('/jobs/request'),
+ params: { token: runner.token },
+ headers: { 'User-Agent' => user_agent, 'X-Forwarded-For' => '123.222.123.222, 127.0.0.1' }
+
+ expect(response).to have_gitlab_http_status 201
+ expect(runner.reload.ip_address).to eq('123.222.123.222')
+ end
+
context 'when concurrently updating a job' do
before do
expect_any_instance_of(Ci::Build).to receive(:run!)
@@ -657,12 +740,12 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
context 'when triggered job is available' do
let(:expected_variables) do
- [{ 'key' => 'CI_JOB_NAME', 'value' => 'spinach', 'public' => true },
- { 'key' => 'CI_JOB_STAGE', 'value' => 'test', 'public' => true },
- { 'key' => 'CI_PIPELINE_TRIGGERED', 'value' => 'true', 'public' => true },
- { 'key' => 'DB_NAME', 'value' => 'postgres', 'public' => true },
- { 'key' => 'SECRET_KEY', 'value' => 'secret_value', 'public' => false },
- { 'key' => 'TRIGGER_KEY_1', 'value' => 'TRIGGER_VALUE_1', 'public' => false }]
+ [{ 'key' => 'CI_JOB_NAME', 'value' => 'spinach', 'public' => true, 'masked' => false },
+ { 'key' => 'CI_JOB_STAGE', 'value' => 'test', 'public' => true, 'masked' => false },
+ { 'key' => 'CI_PIPELINE_TRIGGERED', 'value' => 'true', 'public' => true, 'masked' => false },
+ { 'key' => 'DB_NAME', 'value' => 'postgres', 'public' => true, 'masked' => false },
+ { 'key' => 'SECRET_KEY', 'value' => 'secret_value', 'public' => false, 'masked' => false },
+ { 'key' => 'TRIGGER_KEY_1', 'value' => 'TRIGGER_VALUE_1', 'public' => false, 'masked' => false }]
end
let(:trigger) { create(:ci_trigger, project: project) }
@@ -835,6 +918,15 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
it { expect(job).to be_job_execution_timeout }
end
+
+ context 'when failure_reason is unmet_prerequisites' do
+ before do
+ update_job(state: 'failed', failure_reason: 'unmet_prerequisites')
+ job.reload
+ end
+
+ it { expect(job).to be_unmet_prerequisites }
+ end
end
context 'when trace is given' do
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index 7f11c8c9fe8..5548e3fd01a 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -90,6 +90,17 @@ describe API::Runners do
expect(response).to have_gitlab_http_status(400)
end
+
+ it 'filters runners by tag_list' do
+ create(:ci_runner, :project, description: 'Runner tagged with tag1 and tag2', projects: [project], tag_list: %w[tag1 tag2])
+ create(:ci_runner, :project, description: 'Runner tagged with tag2', projects: [project], tag_list: ['tag2'])
+
+ get api('/runners?tag_list=tag1,tag2', user)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Runner tagged with tag1 and tag2')
+ ]
+ end
end
context 'unauthorized user' do
@@ -181,6 +192,17 @@ describe API::Runners do
expect(response).to have_gitlab_http_status(400)
end
+
+ it 'filters runners by tag_list' do
+ create(:ci_runner, :project, description: 'Runner tagged with tag1 and tag2', projects: [project], tag_list: %w[tag1 tag2])
+ create(:ci_runner, :project, description: 'Runner tagged with tag2', projects: [project], tag_list: ['tag2'])
+
+ get api('/runners/all?tag_list=tag1,tag2', admin)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Runner tagged with tag1 and tag2')
+ ]
+ end
end
context 'without admin privileges' do
@@ -241,7 +263,7 @@ describe API::Runners do
end
it 'returns 404 if runner does not exists' do
- get api('/runners/9999', admin)
+ get api('/runners/0', admin)
expect(response).to have_gitlab_http_status(404)
end
@@ -394,7 +416,7 @@ describe API::Runners do
end
it 'returns 404 if runner does not exists' do
- update_runner(9999, admin, description: 'test')
+ update_runner(0, admin, description: 'test')
expect(response).to have_gitlab_http_status(404)
end
@@ -468,7 +490,7 @@ describe API::Runners do
end
it 'returns 404 if runner does not exists' do
- delete api('/runners/9999', admin)
+ delete api('/runners/0', admin)
expect(response).to have_gitlab_http_status(404)
end
@@ -573,7 +595,7 @@ describe API::Runners do
context "when runner doesn't exist" do
it 'returns 404' do
- get api('/runners/9999/jobs', admin)
+ get api('/runners/0/jobs', admin)
expect(response).to have_gitlab_http_status(404)
end
@@ -626,7 +648,7 @@ describe API::Runners do
context "when runner doesn't exist" do
it 'returns 404' do
- get api('/runners/9999/jobs', user)
+ get api('/runners/0/jobs', user)
expect(response).to have_gitlab_http_status(404)
end
@@ -716,6 +738,17 @@ describe API::Runners do
expect(response).to have_gitlab_http_status(400)
end
+
+ it 'filters runners by tag_list' do
+ create(:ci_runner, :project, description: 'Runner tagged with tag1 and tag2', projects: [project], tag_list: %w[tag1 tag2])
+ create(:ci_runner, :project, description: 'Runner tagged with tag2', projects: [project], tag_list: ['tag2'])
+
+ get api("/projects/#{project.id}/runners?tag_list=tag1,tag2", user)
+
+ expect(json_response).to match_array [
+ a_hash_including('description' => 'Runner tagged with tag1 and tag2')
+ ]
+ end
end
context 'authorized user without maintainer privileges' do
@@ -857,7 +890,7 @@ describe API::Runners do
end
it 'returns 404 is runner is not found' do
- delete api("/projects/#{project.id}/runners/9999", user)
+ delete api("/projects/#{project.id}/runners/0", user)
expect(response).to have_gitlab_http_status(404)
end
diff --git a/spec/requests/api/search_spec.rb b/spec/requests/api/search_spec.rb
index 831f47debeb..49672591b3b 100644
--- a/spec/requests/api/search_spec.rb
+++ b/spec/requests/api/search_spec.rb
@@ -77,6 +77,28 @@ describe API::Search do
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
end
+ context 'for users scope' do
+ before do
+ create(:user, name: 'billy')
+
+ get api('/search', user), params: { scope: 'users', search: 'billy' }
+ end
+
+ it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
+
+ context 'when users search feature is disabled' do
+ before do
+ allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
+
+ get api('/search', user), params: { scope: 'users', search: 'billy' }
+ end
+
+ it 'returns 400 error' do
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+ end
+
context 'for snippet_titles scope' do
before do
create(:snippet, :public, title: 'awesome snippet', content: 'snippet content')
@@ -126,7 +148,7 @@ describe API::Search do
context 'when group does not exist' do
it 'returns 404 error' do
- get api('/groups/9999/search', user), params: { scope: 'issues', search: 'awesome' }
+ get api('/groups/0/search', user), params: { scope: 'issues', search: 'awesome' }
expect(response).to have_gitlab_http_status(404)
end
@@ -192,6 +214,40 @@ describe API::Search do
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
end
+
+ context 'for users scope' do
+ before do
+ user = create(:user, name: 'billy')
+ create(:group_member, :developer, user: user, group: group)
+
+ get api("/groups/#{group.id}/search", user), params: { scope: 'users', search: 'billy' }
+ end
+
+ it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
+
+ context 'when users search feature is disabled' do
+ before do
+ allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
+
+ get api("/groups/#{group.id}/search", user), params: { scope: 'users', search: 'billy' }
+ end
+
+ it 'returns 400 error' do
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+ end
+
+ context 'for users scope with group path as id' do
+ before do
+ user1 = create(:user, name: 'billy')
+ create(:group_member, :developer, user: user1, group: group)
+
+ get api("/groups/#{CGI.escape(group.full_path)}/search", user), params: { scope: 'users', search: 'billy' }
+ end
+
+ it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
+ end
end
end
@@ -222,7 +278,7 @@ describe API::Search do
context 'when project does not exist' do
it 'returns 404 error' do
- get api('/projects/9999/search', user), params: { scope: 'issues', search: 'awesome' }
+ get api('/projects/0/search', user), params: { scope: 'issues', search: 'awesome' }
expect(response).to have_gitlab_http_status(404)
end
@@ -269,6 +325,29 @@ describe API::Search do
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
end
+ context 'for users scope' do
+ before do
+ user1 = create(:user, name: 'billy')
+ create(:project_member, :developer, user: user1, project: project)
+
+ get api("/projects/#{project.id}/search", user), params: { scope: 'users', search: 'billy' }
+ end
+
+ it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
+
+ context 'when users search feature is disabled' do
+ before do
+ allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
+
+ get api("/projects/#{project.id}/search", user), params: { scope: 'users', search: 'billy' }
+ end
+
+ it 'returns 400 error' do
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+ end
+
context 'for notes scope' do
before do
create(:note_on_merge_request, project: project, note: 'awesome note')
diff --git a/spec/requests/api/snippets_spec.rb b/spec/requests/api/snippets_spec.rb
index 7c8512f7589..d600076e9fb 100644
--- a/spec/requests/api/snippets_spec.rb
+++ b/spec/requests/api/snippets_spec.rb
@@ -84,10 +84,17 @@ describe API::Snippets do
end
describe 'GET /snippets/:id/raw' do
- let(:snippet) { create(:personal_snippet, author: user) }
+ set(:author) { create(:user) }
+ set(:snippet) { create(:personal_snippet, :private, author: author) }
+
+ it 'requires authentication' do
+ get api("/snippets/#{snippet.id}", nil)
+
+ expect(response).to have_gitlab_http_status(401)
+ end
it 'returns raw text' do
- get api("/snippets/#{snippet.id}/raw", user)
+ get api("/snippets/#{snippet.id}/raw", author)
expect(response).to have_gitlab_http_status(200)
expect(response.content_type).to eq 'text/plain'
@@ -95,38 +102,83 @@ describe API::Snippets do
end
it 'forces attachment content disposition' do
- get api("/snippets/#{snippet.id}/raw", user)
+ get api("/snippets/#{snippet.id}/raw", author)
expect(headers['Content-Disposition']).to match(/^attachment/)
end
it 'returns 404 for invalid snippet id' do
- get api("/snippets/1234/raw", user)
+ snippet.destroy
+
+ get api("/snippets/#{snippet.id}/raw", author)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 Snippet Not Found')
end
+
+ it 'hides private snippets from ordinary users' do
+ get api("/snippets/#{snippet.id}/raw", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'shows internal snippets to ordinary users' do
+ internal_snippet = create(:personal_snippet, :internal, author: author)
+
+ get api("/snippets/#{internal_snippet.id}/raw", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ end
end
describe 'GET /snippets/:id' do
- let(:snippet) { create(:personal_snippet, author: user) }
+ set(:admin) { create(:user, :admin) }
+ set(:author) { create(:user) }
+ set(:private_snippet) { create(:personal_snippet, :private, author: author) }
+ set(:internal_snippet) { create(:personal_snippet, :internal, author: author) }
+
+ it 'requires authentication' do
+ get api("/snippets/#{private_snippet.id}", nil)
+
+ expect(response).to have_gitlab_http_status(401)
+ end
it 'returns snippet json' do
- get api("/snippets/#{snippet.id}", user)
+ get api("/snippets/#{private_snippet.id}", author)
expect(response).to have_gitlab_http_status(200)
- expect(json_response['title']).to eq(snippet.title)
- expect(json_response['description']).to eq(snippet.description)
- expect(json_response['file_name']).to eq(snippet.file_name)
- expect(json_response['visibility']).to eq(snippet.visibility)
+ expect(json_response['title']).to eq(private_snippet.title)
+ expect(json_response['description']).to eq(private_snippet.description)
+ expect(json_response['file_name']).to eq(private_snippet.file_name)
+ expect(json_response['visibility']).to eq(private_snippet.visibility)
+ end
+
+ it 'shows private snippets to an admin' do
+ get api("/snippets/#{private_snippet.id}", admin)
+
+ expect(response).to have_gitlab_http_status(200)
+ end
+
+ it 'hides private snippets from an ordinary user' do
+ get api("/snippets/#{private_snippet.id}", user)
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'shows internal snippets to an ordinary user' do
+ get api("/snippets/#{internal_snippet.id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
end
it 'returns 404 for invalid snippet id' do
- get api("/snippets/1234", user)
+ private_snippet.destroy
+
+ get api("/snippets/#{private_snippet.id}", admin)
expect(response).to have_gitlab_http_status(404)
- expect(json_response['message']).to eq('404 Not found')
+ expect(json_response['message']).to eq('404 Snippet Not Found')
end
end
diff --git a/spec/requests/api/suggestions_spec.rb b/spec/requests/api/suggestions_spec.rb
index 3c2842e5725..5b07e598b8d 100644
--- a/spec/requests/api/suggestions_spec.rb
+++ b/spec/requests/api/suggestions_spec.rb
@@ -42,8 +42,7 @@ describe API::Suggestions do
expect(response).to have_gitlab_http_status(200)
expect(json_response)
- .to include('id', 'from_original_line', 'to_original_line',
- 'from_line', 'to_line', 'appliable', 'applied',
+ .to include('id', 'from_line', 'to_line', 'appliable', 'applied',
'from_content', 'to_content')
end
end
diff --git a/spec/requests/api/todos_spec.rb b/spec/requests/api/todos_spec.rb
index f121a1d3b78..9f0d5ad5d12 100644
--- a/spec/requests/api/todos_spec.rb
+++ b/spec/requests/api/todos_spec.rb
@@ -8,10 +8,14 @@ describe API::Todos do
let(:author_2) { create(:user) }
let(:john_doe) { create(:user, username: 'john_doe') }
let(:merge_request) { create(:merge_request, source_project: project_1) }
+ let!(:merge_request_todo) { create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request) }
let!(:pending_1) { create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe) }
let!(:pending_2) { create(:todo, project: project_2, author: author_2, user: john_doe) }
let!(:pending_3) { create(:on_commit_todo, project: project_1, author: author_2, user: john_doe) }
let!(:done) { create(:todo, :done, project: project_1, author: author_1, user: john_doe) }
+ let!(:award_emoji_1) { create(:award_emoji, awardable: merge_request, user: author_1, name: 'thumbsup') }
+ let!(:award_emoji_2) { create(:award_emoji, awardable: pending_1.target, user: author_1, name: 'thumbsup') }
+ let!(:award_emoji_3) { create(:award_emoji, awardable: pending_2.target, user: author_2, name: 'thumbsdown') }
before do
project_1.add_developer(john_doe)
@@ -34,7 +38,7 @@ describe API::Todos do
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(3)
+ expect(json_response.length).to eq(4)
expect(json_response[0]['id']).to eq(pending_3.id)
expect(json_response[0]['project']).to be_a Hash
expect(json_response[0]['author']).to be_a Hash
@@ -45,6 +49,23 @@ describe API::Todos do
expect(json_response[0]['state']).to eq('pending')
expect(json_response[0]['action_name']).to eq('assigned')
expect(json_response[0]['created_at']).to be_present
+ expect(json_response[0]['target_type']).to eq('Commit')
+
+ expect(json_response[1]['target_type']).to eq('Issue')
+ expect(json_response[1]['target']['upvotes']).to eq(0)
+ expect(json_response[1]['target']['downvotes']).to eq(1)
+ expect(json_response[1]['target']['merge_requests_count']).to eq(0)
+
+ expect(json_response[2]['target_type']).to eq('Issue')
+ expect(json_response[2]['target']['upvotes']).to eq(1)
+ expect(json_response[2]['target']['downvotes']).to eq(0)
+ expect(json_response[2]['target']['merge_requests_count']).to eq(0)
+
+ expect(json_response[3]['target_type']).to eq('MergeRequest')
+ # Only issues get a merge request count at the moment
+ expect(json_response[3]['target']['merge_requests_count']).to be_nil
+ expect(json_response[3]['target']['upvotes']).to eq(1)
+ expect(json_response[3]['target']['downvotes']).to eq(0)
end
context 'and using the author filter' do
@@ -54,7 +75,7 @@ describe API::Todos do
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)
+ expect(json_response.length).to eq(3)
end
end
@@ -67,7 +88,7 @@ describe API::Todos do
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(1)
+ expect(json_response.length).to eq(2)
end
end
@@ -100,7 +121,7 @@ describe API::Todos do
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)
+ expect(json_response.length).to eq(3)
end
end
@@ -115,6 +136,27 @@ describe API::Todos do
end
end
end
+
+ it 'avoids N+1 queries', :request_store do
+ create(:todo, project: project_1, author: author_2, user: john_doe, target: merge_request)
+
+ get api('/todos', john_doe)
+
+ control = ActiveRecord::QueryRecorder.new { get api('/todos', john_doe) }
+
+ merge_request_2 = create(:merge_request, source_project: project_2)
+ create(:todo, project: project_2, author: author_2, user: john_doe, target: merge_request_2)
+
+ project_3 = create(:project, :repository)
+ project_3.add_developer(john_doe)
+ merge_request_3 = create(:merge_request, source_project: project_3)
+ create(:todo, project: project_3, author: author_2, user: john_doe, target: merge_request_3)
+ create(:todo, :mentioned, project: project_1, author: author_1, user: john_doe)
+ create(:on_commit_todo, project: project_3, author: author_1, user: john_doe)
+
+ expect { get api('/todos', john_doe) }.not_to exceed_query_limit(control)
+ expect(response.status).to eq(200)
+ end
end
describe 'POST /todos/:id/mark_as_done' do
@@ -230,7 +272,7 @@ describe API::Todos do
context 'for a merge request' do
it_behaves_like 'an issuable', 'merge_requests' do
- let(:issuable) { merge_request }
+ let(:issuable) { create(:merge_request, :simple, source_project: project_1) }
end
end
end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index b381431306d..b84202364e1 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -68,6 +68,13 @@ describe API::Users do
expect(json_response.size).to eq(0)
end
+ it "does not return the highest role" do
+ get api("/users"), params: { username: user.username }
+
+ expect(response).to match_response_schema('public_api/v4/user/basics')
+ expect(json_response.first.keys).not_to include 'highest_role'
+ end
+
context "when public level is restricted" do
before do
stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
@@ -286,6 +293,13 @@ describe API::Users do
expect(json_response.keys).not_to include 'is_admin'
end
+ it "does not return the user's `highest_role`" do
+ get api("/users/#{user.id}", user)
+
+ expect(response).to match_response_schema('public_api/v4/user/basic')
+ expect(json_response.keys).not_to include 'highest_role'
+ end
+
context 'when authenticated as admin' do
it 'includes the `is_admin` field' do
get api("/users/#{user.id}", admin)
@@ -300,6 +314,12 @@ describe API::Users do
expect(response).to match_response_schema('public_api/v4/user/admin')
expect(json_response.keys).to include 'created_at'
end
+ it 'includes the `highest_role` field' do
+ get api("/users/#{user.id}", admin)
+
+ expect(response).to match_response_schema('public_api/v4/user/admin')
+ expect(json_response['highest_role']).to be(0)
+ end
end
context 'for an anonymous user' do
@@ -335,7 +355,7 @@ describe API::Users do
end
it "returns a 404 error if user id not found" do
- get api("/users/9999", user)
+ get api("/users/0", user)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
@@ -732,7 +752,7 @@ describe API::Users do
end
it "returns 404 for non-existing user" do
- put api("/users/999999", admin), params: { bio: 'update should fail' }
+ put api("/users/0", admin), params: { bio: 'update should fail' }
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
@@ -836,7 +856,7 @@ describe API::Users do
end
it "returns 400 for invalid ID" do
- post api("/users/999999/keys", admin)
+ post api("/users/0/keys", admin)
expect(response).to have_gitlab_http_status(400)
end
end
@@ -895,7 +915,7 @@ describe API::Users do
it 'returns 404 error if user not found' do
user.keys << key
user.save
- delete api("/users/999999/keys/#{key.id}", admin)
+ delete api("/users/0/keys/#{key.id}", admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
@@ -930,7 +950,7 @@ describe API::Users do
end
it 'returns 400 for invalid ID' do
- post api('/users/999999/gpg_keys', admin)
+ post api('/users/0/gpg_keys', admin)
expect(response).to have_gitlab_http_status(400)
end
@@ -951,7 +971,7 @@ describe API::Users do
context 'when authenticated' do
it 'returns 404 for non-existing user' do
- get api('/users/999999/gpg_keys', admin)
+ get api('/users/0/gpg_keys', admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
@@ -1007,7 +1027,7 @@ describe API::Users do
user.keys << key
user.save
- delete api("/users/999999/gpg_keys/#{gpg_key.id}", admin)
+ delete api("/users/0/gpg_keys/#{gpg_key.id}", admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
@@ -1051,7 +1071,7 @@ describe API::Users do
user.gpg_keys << gpg_key
user.save
- post api("/users/999999/gpg_keys/#{gpg_key.id}/revoke", admin)
+ post api("/users/0/gpg_keys/#{gpg_key.id}/revoke", admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
@@ -1089,7 +1109,7 @@ describe API::Users do
end
it "returns a 400 for invalid ID" do
- post api("/users/999999/emails", admin)
+ post api("/users/0/emails", admin)
expect(response).to have_gitlab_http_status(400)
end
@@ -1121,7 +1141,7 @@ describe API::Users do
context 'when authenticated' do
it 'returns 404 for non-existing user' do
- get api('/users/999999/emails', admin)
+ get api('/users/0/emails', admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
@@ -1177,7 +1197,7 @@ describe API::Users do
it 'returns 404 error if user not found' do
user.emails << email
user.save
- delete api("/users/999999/emails/#{email.id}", admin)
+ delete api("/users/0/emails/#{email.id}", admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
@@ -1227,7 +1247,7 @@ describe API::Users do
end
it "returns 404 for non-existing user" do
- perform_enqueued_jobs { delete api("/users/999999", admin) }
+ perform_enqueued_jobs { delete api("/users/0", admin) }
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
@@ -1778,7 +1798,7 @@ describe API::Users do
end
it 'returns a 404 error if user id not found' do
- post api('/users/9999/block', admin)
+ post api('/users/0/block', admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
@@ -1816,7 +1836,7 @@ describe API::Users do
end
it 'returns a 404 error if user id not found' do
- post api('/users/9999/block', admin)
+ post api('/users/0/block', admin)
expect(response).to have_gitlab_http_status(404)
expect(json_response['message']).to eq('404 User Not Found')
end
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
index cdac5b2f400..5df6baf0ddf 100644
--- a/spec/requests/api/variables_spec.rb
+++ b/spec/requests/api/variables_spec.rb
@@ -73,12 +73,12 @@ describe API::Variables do
context 'authorized user with proper permissions' do
it 'creates variable' do
expect do
- post api("/projects/#{project.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'VALUE_2', protected: true }
+ post api("/projects/#{project.id}/variables", user), params: { key: 'TEST_VARIABLE_2', value: 'PROTECTED_VALUE_2', protected: true }
end.to change {project.variables.count}.by(1)
expect(response).to have_gitlab_http_status(201)
expect(json_response['key']).to eq('TEST_VARIABLE_2')
- expect(json_response['value']).to eq('VALUE_2')
+ expect(json_response['value']).to eq('PROTECTED_VALUE_2')
expect(json_response['protected']).to be_truthy
end
diff --git a/spec/requests/api/version_spec.rb b/spec/requests/api/version_spec.rb
index 38b618191fb..e06f8bbc095 100644
--- a/spec/requests/api/version_spec.rb
+++ b/spec/requests/api/version_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
describe API::Version do
- describe 'GET /version' do
+ shared_examples_for 'GET /version' do
context 'when unauthenticated' do
it 'returns authentication error' do
get api('/version')
@@ -22,4 +22,20 @@ describe API::Version do
end
end
end
+
+ context 'with graphql enabled' do
+ before do
+ stub_feature_flags(graphql: true)
+ end
+
+ include_examples 'GET /version'
+ end
+
+ context 'with graphql disabled' do
+ before do
+ stub_feature_flags(graphql: false)
+ end
+
+ include_examples 'GET /version'
+ end
end
diff --git a/spec/requests/git_http_spec.rb b/spec/requests/git_http_spec.rb
index 5b625fd47be..bfa178f5cae 100644
--- a/spec/requests/git_http_spec.rb
+++ b/spec/requests/git_http_spec.rb
@@ -104,6 +104,70 @@ describe 'Git HTTP requests' do
end
end
+ shared_examples_for 'project path without .git suffix' do
+ context "GET info/refs" do
+ let(:path) { "/#{project_path}/info/refs" }
+
+ context "when no params are added" do
+ before do
+ get path
+ end
+
+ it "redirects to the .git suffix version" do
+ expect(response).to redirect_to("/#{project_path}.git/info/refs")
+ end
+ end
+
+ context "when the upload-pack service is requested" do
+ let(:params) { { service: 'git-upload-pack' } }
+
+ before do
+ get path, params: params
+ end
+
+ it "redirects to the .git suffix version" do
+ expect(response).to redirect_to("/#{project_path}.git/info/refs?service=#{params[:service]}")
+ end
+ end
+
+ context "when the receive-pack service is requested" do
+ let(:params) { { service: 'git-receive-pack' } }
+
+ before do
+ get path, params: params
+ end
+
+ it "redirects to the .git suffix version" do
+ expect(response).to redirect_to("/#{project_path}.git/info/refs?service=#{params[:service]}")
+ end
+ end
+
+ context "when the params are anything else" do
+ let(:params) { { service: 'git-implode-pack' } }
+
+ before do
+ get path, params: params
+ end
+
+ it "redirects to the sign-in page" do
+ expect(response).to redirect_to(new_user_session_path)
+ end
+ end
+ end
+
+ context "POST git-upload-pack" do
+ it "fails to find a route" do
+ expect { clone_post(project_path) }.to raise_error(ActionController::RoutingError)
+ end
+ end
+
+ context "POST git-receive-pack" do
+ it "fails to find a route" do
+ expect { push_post(project_path) }.to raise_error(ActionController::RoutingError)
+ end
+ end
+ end
+
describe "User with no identities" do
let(:user) { create(:user) }
@@ -143,6 +207,10 @@ describe 'Git HTTP requests' do
expect(response).to have_gitlab_http_status(:unprocessable_entity)
end
end
+
+ it_behaves_like 'project path without .git suffix' do
+ let(:project_path) { "#{user.namespace.path}/project.git-project" }
+ end
end
end
@@ -706,70 +774,8 @@ describe 'Git HTTP requests' do
end
end
- context "when the project path doesn't end in .git" do
- let(:project) { create(:project, :repository, :public, path: 'project.git-project') }
-
- context "GET info/refs" do
- let(:path) { "/#{project.full_path}/info/refs" }
-
- context "when no params are added" do
- before do
- get path
- end
-
- it "redirects to the .git suffix version" do
- expect(response).to redirect_to("/#{project.full_path}.git/info/refs")
- end
- end
-
- context "when the upload-pack service is requested" do
- let(:params) { { service: 'git-upload-pack' } }
-
- before do
- get path, params: params
- end
-
- it "redirects to the .git suffix version" do
- expect(response).to redirect_to("/#{project.full_path}.git/info/refs?service=#{params[:service]}")
- end
- end
-
- context "when the receive-pack service is requested" do
- let(:params) { { service: 'git-receive-pack' } }
-
- before do
- get path, params: params
- end
-
- it "redirects to the .git suffix version" do
- expect(response).to redirect_to("/#{project.full_path}.git/info/refs?service=#{params[:service]}")
- end
- end
-
- context "when the params are anything else" do
- let(:params) { { service: 'git-implode-pack' } }
-
- before do
- get path, params: params
- end
-
- it "redirects to the sign-in page" do
- expect(response).to redirect_to(new_user_session_path)
- end
- end
- end
-
- context "POST git-upload-pack" do
- it "fails to find a route" do
- expect { clone_post(project.full_path) }.to raise_error(ActionController::RoutingError)
- end
- end
-
- context "POST git-receive-pack" do
- it "fails to find a route" do
- expect { push_post(project.full_path) }.to raise_error(ActionController::RoutingError)
- end
- end
+ it_behaves_like 'project path without .git suffix' do
+ let(:project_path) { create(:project, :repository, :public, path: 'project.git-project').full_path }
end
context "retrieving an info/refs file" do
diff --git a/spec/routing/api_routing_spec.rb b/spec/routing/api_routing_spec.rb
index 5fde4bd885b..3c48ead4ff2 100644
--- a/spec/routing/api_routing_spec.rb
+++ b/spec/routing/api_routing_spec.rb
@@ -7,25 +7,17 @@ describe 'api', 'routing' do
end
it 'does not route to the GraphqlController' do
- expect(get('/api/graphql')).not_to route_to('graphql#execute')
- end
-
- it 'does not expose graphiql' do
- expect(get('/-/graphql-explorer')).not_to route_to('graphiql/rails/editors#show')
+ expect(post('/api/graphql')).not_to route_to('graphql#execute')
end
end
- context 'when graphql is disabled' do
+ context 'when graphql is enabled' do
before do
stub_feature_flags(graphql: true)
end
it 'routes to the GraphqlController' do
- expect(get('/api/graphql')).not_to route_to('graphql#execute')
- end
-
- it 'exposes graphiql' do
- expect(get('/-/graphql-explorer')).not_to route_to('graphiql/rails/editors#show')
+ expect(post('/api/graphql')).to route_to('graphql#execute')
end
end
end
diff --git a/spec/routing/group_routing_spec.rb b/spec/routing/group_routing_spec.rb
index 71788028cbf..53271550e8b 100644
--- a/spec/routing/group_routing_spec.rb
+++ b/spec/routing/group_routing_spec.rb
@@ -17,6 +17,10 @@ describe "Groups", "routing" do
expect(get("/#{group_path}")).to route_to('groups#show', id: group_path)
end
+ it "to #details" do
+ expect(get("/groups/#{group_path}/-/details")).to route_to('groups#details', id: group_path)
+ end
+
it "to #activity" do
expect(get("/groups/#{group_path}/-/activity")).to route_to('groups#activity', id: group_path)
end
diff --git a/spec/serializers/environment_serializer_spec.rb b/spec/serializers/environment_serializer_spec.rb
index 3541bd5f12e..375a28a8c72 100644
--- a/spec/serializers/environment_serializer_spec.rb
+++ b/spec/serializers/environment_serializer_spec.rb
@@ -124,10 +124,10 @@ describe EnvironmentSerializer do
end
context 'when used with pagination' do
- let(:request) { spy('request') }
+ let(:request) { double(url: "#{Gitlab.config.gitlab.url}:8080/api/v4/projects?#{query.to_query}", query_parameters: query) }
let(:response) { spy('response') }
let(:resource) { Environment.all }
- let(:pagination) { { page: 1, per_page: 2 } }
+ let(:query) { { page: 1, per_page: 2 } }
let(:serializer) do
described_class
@@ -135,11 +135,6 @@ describe EnvironmentSerializer do
.with_pagination(request, response)
end
- before do
- allow(request).to receive(:query_parameters)
- .and_return(pagination)
- end
-
subject { serializer.represent(resource) }
it 'creates a paginated serializer' do
diff --git a/spec/serializers/merge_request_for_pipeline_entity_spec.rb b/spec/serializers/merge_request_for_pipeline_entity_spec.rb
new file mode 100644
index 00000000000..e49b45bc7d7
--- /dev/null
+++ b/spec/serializers/merge_request_for_pipeline_entity_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe MergeRequestForPipelineEntity do
+ let(:project) { create(:project, :repository) }
+ let(:user) { create(:user) }
+ let(:request) { EntityRequest.new(project: project) }
+ let(:merge_request) { create(:merge_request, target_project: project, source_project: project) }
+ let(:presenter) { MergeRequestPresenter.new(merge_request, current_user: user) }
+
+ let(:entity) do
+ described_class.new(presenter, request: request)
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ context 'as json' do
+ subject { entity.as_json }
+
+ it 'exposes needed attributes' do
+ expect(subject).to include(
+ :iid, :path, :title,
+ :source_branch, :source_branch_path,
+ :target_branch, :target_branch_path
+ )
+ end
+ end
+end
diff --git a/spec/serializers/merge_request_widget_entity_spec.rb b/spec/serializers/merge_request_widget_entity_spec.rb
index 4dbd79f2fc0..727fd8951f2 100644
--- a/spec/serializers/merge_request_widget_entity_spec.rb
+++ b/spec/serializers/merge_request_widget_entity_spec.rb
@@ -279,13 +279,18 @@ describe MergeRequestWidgetEntity do
end
describe 'commits_without_merge_commits' do
+ def find_matching_commit(short_id)
+ resource.commits.find { |c| c.short_id == short_id }
+ end
+
it 'should not include merge commits' do
- # Mock all but the first 5 commits to be merge commits
- resource.commits.each_with_index do |commit, i|
- expect(commit).to receive(:merge_commit?).at_least(:once).and_return(i > 4)
- end
+ commits_in_widget = subject[:commits_without_merge_commits]
- expect(subject[:commits_without_merge_commits].size).to eq(5)
+ expect(commits_in_widget.length).to be < resource.commits.length
+ expect(commits_in_widget.length).to eq(resource.commits.without_merge_commits.length)
+ commits_in_widget.each do |c|
+ expect(find_matching_commit(c[:short_id]).merge_commit?).to eq(false)
+ end
end
end
end
diff --git a/spec/serializers/pipeline_entity_spec.rb b/spec/serializers/pipeline_entity_spec.rb
index 774486dcb6d..1d992e8a483 100644
--- a/spec/serializers/pipeline_entity_spec.rb
+++ b/spec/serializers/pipeline_entity_spec.rb
@@ -1,13 +1,18 @@
require 'spec_helper'
describe PipelineEntity do
+ include Gitlab::Routing
+
+ set(:project) { create(:project) }
set(:user) { create(:user) }
+ set(:project) { create(:project) }
let(:request) { double('request') }
before do
stub_not_protect_default_branch
allow(request).to receive(:current_user).and_return(user)
+ allow(request).to receive(:project).and_return(project)
end
let(:entity) do
@@ -128,5 +133,62 @@ describe PipelineEntity do
.to eq 'CI/CD YAML configuration error!'
end
end
+
+ context 'when pipeline is detached merge request pipeline' do
+ let(:merge_request) { create(:merge_request, :with_detached_merge_request_pipeline) }
+ let(:project) { merge_request.target_project }
+ let(:pipeline) { merge_request.merge_request_pipelines.first }
+
+ it 'makes detached flag true' do
+ expect(subject[:flags][:detached_merge_request_pipeline]).to be_truthy
+ end
+
+ context 'when user is a developer' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'has merge request information' do
+ expect(subject[:merge_request][:iid]).to eq(merge_request.iid)
+
+ expect(project_merge_request_path(project, merge_request))
+ .to include(subject[:merge_request][:path])
+
+ expect(subject[:merge_request][:title]).to eq(merge_request.title)
+
+ expect(subject[:merge_request][:source_branch])
+ .to eq(merge_request.source_branch)
+
+ expect(project_commits_path(project, merge_request.source_branch))
+ .to include(subject[:merge_request][:source_branch_path])
+
+ expect(subject[:merge_request][:target_branch])
+ .to eq(merge_request.target_branch)
+
+ expect(project_commits_path(project, merge_request.target_branch))
+ .to include(subject[:merge_request][:target_branch_path])
+ end
+ end
+
+ context 'when user is an external user' do
+ it 'has no merge request information' do
+ expect(subject[:merge_request]).to be_nil
+ end
+ end
+ end
+
+ context 'when pipeline is merge request pipeline' do
+ let(:merge_request) { create(:merge_request, :with_merge_request_pipeline, merge_sha: 'abc') }
+ let(:project) { merge_request.target_project }
+ let(:pipeline) { merge_request.merge_request_pipelines.first }
+
+ it 'makes detached flag false' do
+ expect(subject[:flags][:detached_merge_request_pipeline]).to be_falsy
+ end
+
+ it 'makes atached flag true' do
+ expect(subject[:flags][:merge_request_pipeline]).to be_truthy
+ end
+ end
end
end
diff --git a/spec/serializers/pipeline_serializer_spec.rb b/spec/serializers/pipeline_serializer_spec.rb
index 79aa32b29bb..0fdd675aa01 100644
--- a/spec/serializers/pipeline_serializer_spec.rb
+++ b/spec/serializers/pipeline_serializer_spec.rb
@@ -5,7 +5,7 @@ describe PipelineSerializer do
set(:user) { create(:user) }
let(:serializer) do
- described_class.new(current_user: user)
+ described_class.new(current_user: user, project: project)
end
before do
@@ -38,15 +38,9 @@ describe PipelineSerializer do
end
context 'when used with pagination' do
- let(:request) { spy('request') }
+ let(:request) { double(url: "#{Gitlab.config.gitlab.url}:8080/api/v4/projects?#{query.to_query}", query_parameters: query) }
let(:response) { spy('response') }
- let(:pagination) { {} }
-
- before do
- allow(request)
- .to receive(:query_parameters)
- .and_return(pagination)
- end
+ let(:query) { {} }
let(:serializer) do
described_class.new(current_user: user)
@@ -60,7 +54,7 @@ describe PipelineSerializer do
context 'when resource is not paginatable' do
context 'when a single pipeline object is being serialized' do
let(:resource) { create(:ci_empty_pipeline) }
- let(:pagination) { { page: 1, per_page: 1 } }
+ let(:query) { { page: 1, per_page: 1 } }
it 'raises error' do
expect { subject }.to raise_error(
@@ -71,7 +65,7 @@ describe PipelineSerializer do
context 'when resource is paginatable relation' do
let(:resource) { Ci::Pipeline.all }
- let(:pagination) { { page: 1, per_page: 2 } }
+ let(:query) { { page: 1, per_page: 2 } }
context 'when a single pipeline object is present in relation' do
before do
@@ -103,6 +97,44 @@ describe PipelineSerializer do
end
end
+ context 'when there are pipelines for merge requests' do
+ let(:resource) { Ci::Pipeline.all }
+
+ let!(:merge_request_1) do
+ create(:merge_request,
+ :with_detached_merge_request_pipeline,
+ target_project: project,
+ target_branch: 'master',
+ source_project: project,
+ source_branch: 'feature')
+ end
+
+ let!(:merge_request_2) do
+ create(:merge_request,
+ :with_detached_merge_request_pipeline,
+ target_project: project,
+ target_branch: 'master',
+ source_project: project,
+ source_branch: '2-mb-file')
+ end
+
+ before do
+ project.add_developer(user)
+ end
+
+ it 'includes merge requests information' do
+ expect(subject.all? { |entry| entry[:merge_request].present? }).to be_truthy
+ end
+
+ it 'preloads related merge requests', :postgresql do
+ recorded = ActiveRecord::QueryRecorder.new { subject }
+
+ expect(recorded.log)
+ .to include("SELECT \"merge_requests\".* FROM \"merge_requests\" " \
+ "WHERE \"merge_requests\".\"id\" IN (#{merge_request_1.id}, #{merge_request_2.id})")
+ end
+ end
+
describe 'number of queries when preloaded' do
subject { serializer.represent(resource, preload: true) }
let(:resource) { Ci::Pipeline.all }
diff --git a/spec/serializers/provider_repo_entity_spec.rb b/spec/serializers/provider_repo_entity_spec.rb
index b67115bab10..9a1160d16d5 100644
--- a/spec/serializers/provider_repo_entity_spec.rb
+++ b/spec/serializers/provider_repo_entity_spec.rb
@@ -13,7 +13,7 @@ describe ProviderRepoEntity do
describe '#as_json' do
subject { entity.as_json }
- it 'includes requried fields' do
+ it 'includes required fields' do
expect(subject[:id]).to eq(provider_repo[:id])
expect(subject[:full_name]).to eq(provider_repo[:full_name])
expect(subject[:owner_name]).to eq(provider_repo[:owner][:login])
diff --git a/spec/serializers/suggestion_entity_spec.rb b/spec/serializers/suggestion_entity_spec.rb
index 047571f161c..d38fc2b132b 100644
--- a/spec/serializers/suggestion_entity_spec.rb
+++ b/spec/serializers/suggestion_entity_spec.rb
@@ -13,8 +13,8 @@ describe SuggestionEntity do
subject { entity.as_json }
it 'exposes correct attributes' do
- expect(subject).to include(:id, :from_original_line, :to_original_line, :from_line,
- :to_line, :appliable, :applied, :from_content, :to_content)
+ expect(subject).to include(:id, :from_line, :to_line, :appliable,
+ :applied, :from_content, :to_content)
end
it 'exposes current user abilities' do
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
index 8021bd338e0..c9d85e96750 100644
--- a/spec/services/auth/container_registry_authentication_service_spec.rb
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -88,6 +88,12 @@ describe Auth::ContainerRegistryAuthenticationService do
end
end
+ shared_examples 'a deletable since registry 2.7' do
+ it_behaves_like 'an accessible' do
+ let(:actions) { ['delete'] }
+ end
+ end
+
shared_examples 'a pullable' do
it_behaves_like 'an accessible' do
let(:actions) { ['pull'] }
@@ -184,6 +190,19 @@ describe Auth::ContainerRegistryAuthenticationService do
it_behaves_like 'not a container repository factory'
end
+ context 'disallow developer to delete images since registry 2.7' do
+ before do
+ project.add_developer(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
context 'allow reporter to pull images' do
before do
project.add_reporter(current_user)
@@ -212,6 +231,19 @@ describe Auth::ContainerRegistryAuthenticationService do
it_behaves_like 'not a container repository factory'
end
+ context 'disallow reporter to delete images since registry 2.7' do
+ before do
+ project.add_reporter(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
context 'return a least of privileges' do
before do
project.add_reporter(current_user)
@@ -250,6 +282,19 @@ describe Auth::ContainerRegistryAuthenticationService do
it_behaves_like 'an inaccessible'
it_behaves_like 'not a container repository factory'
end
+
+ context 'disallow guest to delete images since regsitry 2.7' do
+ before do
+ project.add_guest(current_user)
+ end
+
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
end
context 'for public project' do
@@ -282,6 +327,15 @@ describe Auth::ContainerRegistryAuthenticationService do
it_behaves_like 'not a container repository factory'
end
+ context 'disallow anyone to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
+
context 'when repository name is invalid' do
let(:current_params) do
{ scopes: ['repository:invalid:push'] }
@@ -322,6 +376,15 @@ describe Auth::ContainerRegistryAuthenticationService do
it_behaves_like 'an inaccessible'
it_behaves_like 'not a container repository factory'
end
+
+ context 'disallow anyone to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
end
context 'for external user' do
@@ -344,6 +407,16 @@ describe Auth::ContainerRegistryAuthenticationService do
it_behaves_like 'an inaccessible'
it_behaves_like 'not a container repository factory'
end
+
+ context 'disallow anyone to delete images since registry 2.7' do
+ let(:current_user) { create(:user, external: true) }
+ let(:current_params) do
+ { scopes: ["repository:#{project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible'
+ it_behaves_like 'not a container repository factory'
+ end
end
end
end
@@ -371,6 +444,16 @@ describe Auth::ContainerRegistryAuthenticationService do
let(:project) { current_project }
end
end
+
+ context 'allow to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{current_project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'a deletable since registry 2.7' do
+ let(:project) { current_project }
+ end
+ end
end
context 'build authorized as user' do
@@ -419,6 +502,16 @@ describe Auth::ContainerRegistryAuthenticationService do
end
end
+ context 'disallow to delete images since registry 2.7' do
+ let(:current_params) do
+ { scopes: ["repository:#{current_project.full_path}:delete"] }
+ end
+
+ it_behaves_like 'an inaccessible' do
+ let(:project) { current_project }
+ end
+ end
+
context 'for other projects' do
context 'when pulling' do
let(:current_params) do
diff --git a/spec/services/boards/visits/latest_service_spec.rb b/spec/services/boards/visits/latest_service_spec.rb
index e55d599e2cc..c8a0a5e4243 100644
--- a/spec/services/boards/visits/latest_service_spec.rb
+++ b/spec/services/boards/visits/latest_service_spec.rb
@@ -23,6 +23,12 @@ describe Boards::Visits::LatestService do
service.execute
end
+
+ it 'queries for last N visits' do
+ expect(BoardProjectRecentVisit).to receive(:latest).with(user, project, count: 5).once
+
+ described_class.new(project_board.parent, user, count: 5).execute
+ end
end
context 'when a group board' do
@@ -42,6 +48,12 @@ describe Boards::Visits::LatestService do
service.execute
end
+
+ it 'queries for last N visits' do
+ expect(BoardGroupRecentVisit).to receive(:latest).with(user, group, count: 5).once
+
+ described_class.new(group_board.parent, user, count: 5).execute
+ end
end
end
end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index 8497e90bd8b..24707cd2d41 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -12,6 +12,7 @@ describe Ci::CreatePipelineService do
end
describe '#execute' do
+ # rubocop:disable Metrics/ParameterLists
def execute_service(
source: :push,
after: project.commit.id,
@@ -20,17 +21,22 @@ describe Ci::CreatePipelineService do
trigger_request: nil,
variables_attributes: nil,
merge_request: nil,
- push_options: nil)
+ push_options: nil,
+ source_sha: nil,
+ target_sha: nil)
params = { ref: ref,
before: '00000000',
after: after,
commits: [{ message: message }],
variables_attributes: variables_attributes,
- push_options: push_options }
+ push_options: push_options,
+ source_sha: source_sha,
+ target_sha: target_sha }
described_class.new(project, user, params).execute(
source, trigger_request: trigger_request, merge_request: merge_request)
end
+ # rubocop:enable Metrics/ParameterLists
context 'valid params' do
let(:pipeline) { execute_service }
@@ -679,7 +685,11 @@ describe Ci::CreatePipelineService do
describe 'Merge request pipelines' do
let(:pipeline) do
- execute_service(source: source, merge_request: merge_request, ref: ref_name)
+ execute_service(source: source,
+ merge_request: merge_request,
+ ref: ref_name,
+ source_sha: source_sha,
+ target_sha: target_sha)
end
before do
@@ -687,9 +697,11 @@ describe Ci::CreatePipelineService do
end
let(:ref_name) { 'refs/heads/feature' }
+ let(:source_sha) { project.commit(ref_name).id }
+ let(:target_sha) { nil }
context 'when source is merge request' do
- let(:source) { :merge_request }
+ let(:source) { :merge_request_event }
context "when config has merge_requests keywords" do
let(:config) do
@@ -722,11 +734,27 @@ describe Ci::CreatePipelineService do
it 'creates a merge request pipeline' do
expect(pipeline).to be_persisted
- expect(pipeline).to be_merge_request
+ expect(pipeline).to be_merge_request_event
expect(pipeline.merge_request).to eq(merge_request)
expect(pipeline.builds.order(:stage_id).map(&:name)).to eq(%w[test])
end
+ it 'persists the specified source sha' do
+ expect(pipeline.source_sha).to eq(source_sha)
+ end
+
+ it 'does not persist target sha for detached merge request pipeline' do
+ expect(pipeline.target_sha).to be_nil
+ end
+
+ context 'when target sha is specified' do
+ let(:target_sha) { merge_request.target_branch_sha }
+
+ it 'persists the target sha' do
+ expect(pipeline.target_sha).to eq(target_sha)
+ end
+ end
+
context 'when ref is tag' do
let(:ref_name) { 'refs/tags/v1.1.0' }
diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb
index d896f990470..bff2b3179fb 100644
--- a/spec/services/ci/destroy_pipeline_service_spec.rb
+++ b/spec/services/ci/destroy_pipeline_service_spec.rb
@@ -3,8 +3,8 @@
require 'spec_helper'
describe ::Ci::DestroyPipelineService do
- let(:project) { create(:project) }
- let!(:pipeline) { create(:ci_pipeline, project: project) }
+ let(:project) { create(:project, :repository) }
+ let!(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.id) }
subject { described_class.new(project, user).execute(pipeline) }
@@ -17,6 +17,17 @@ describe ::Ci::DestroyPipelineService do
expect { pipeline.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
+ it 'clears the cache', :use_clean_rails_memory_store_caching do
+ create(:commit_status, :success, pipeline: pipeline, ref: pipeline.ref)
+
+ expect(project.pipeline_status.has_status?).to be_truthy
+
+ subject
+
+ # Need to use find to avoid memoization
+ expect(Project.find(project.id).pipeline_status.has_status?).to be_falsey
+ end
+
it 'does not log an audit event' do
expect { subject }.not_to change { SecurityEvent.count }
end
diff --git a/spec/services/ci/prepare_build_service_spec.rb b/spec/services/ci/prepare_build_service_spec.rb
new file mode 100644
index 00000000000..1797f8f964f
--- /dev/null
+++ b/spec/services/ci/prepare_build_service_spec.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::PrepareBuildService do
+ describe '#execute' do
+ let(:build) { create(:ci_build, :preparing) }
+
+ subject { described_class.new(build).execute }
+
+ before do
+ allow(build).to receive(:prerequisites).and_return(prerequisites)
+ end
+
+ shared_examples 'build enqueueing' do
+ it 'enqueues the build' do
+ expect(build).to receive(:enqueue).once
+
+ subject
+ end
+ end
+
+ context 'build has unmet prerequisites' do
+ let(:prerequisite) { double(complete!: true) }
+ let(:prerequisites) { [prerequisite] }
+
+ it 'completes each prerequisite' do
+ expect(prerequisites).to all(receive(:complete!))
+
+ subject
+ end
+
+ include_examples 'build enqueueing'
+
+ context 'prerequisites fail to complete' do
+ before do
+ allow(build).to receive(:enqueue).and_return(false)
+ end
+
+ it 'drops the build' do
+ expect(build).to receive(:drop!).with(:unmet_prerequisites).once
+
+ subject
+ end
+ end
+ end
+
+ context 'build has no prerequisites' do
+ let(:prerequisites) { [] }
+
+ include_examples 'build enqueueing'
+ end
+ end
+end
diff --git a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb
index f3036fbcb0e..80fc48d1b07 100644
--- a/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb
+++ b/spec/services/clusters/applications/check_ingress_ip_address_service_spec.rb
@@ -6,9 +6,17 @@ describe Clusters::Applications::CheckIngressIpAddressService do
let(:application) { create(:clusters_applications_ingress, :installed) }
let(:service) { described_class.new(application) }
let(:kubeclient) { double(::Kubeclient::Client, get_service: kube_service) }
- let(:ingress) { [{ ip: '111.222.111.222' }] }
let(:lease_key) { "check_ingress_ip_address_service:#{application.id}" }
+ let(:ingress) do
+ [
+ {
+ ip: '111.222.111.222',
+ hostname: 'localhost.localdomain'
+ }
+ ]
+ end
+
let(:kube_service) do
::Kubeclient::Resource.new(
{
diff --git a/spec/services/clusters/applications/create_service_spec.rb b/spec/services/clusters/applications/create_service_spec.rb
index 3f621ed5944..20555873503 100644
--- a/spec/services/clusters/applications/create_service_spec.rb
+++ b/spec/services/clusters/applications/create_service_spec.rb
@@ -26,12 +26,6 @@ describe Clusters::Applications::CreateService do
end.to change(cluster, :application_helm)
end
- it 'schedules an install via worker' do
- expect(ClusterInstallAppWorker).to receive(:perform_async).with('helm', anything).once
-
- subject
- end
-
context 'application already installed' do
let!(:application) { create(:clusters_applications_helm, :installed, cluster: cluster) }
@@ -42,88 +36,101 @@ describe Clusters::Applications::CreateService do
end
it 'schedules an upgrade for the application' do
- expect(Clusters::Applications::ScheduleInstallationService).to receive(:new).with(application).and_call_original
+ expect(ClusterUpgradeAppWorker).to receive(:perform_async)
subject
end
end
- context 'cert manager application' do
- let(:params) do
- {
- application: 'cert_manager',
- email: 'test@example.com'
- }
- end
-
+ context 'known applications' do
before do
- allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute)
+ create(:clusters_applications_helm, :installed, cluster: cluster)
end
- it 'creates the application' do
- expect do
- subject
+ context 'cert manager application' do
+ let(:params) do
+ {
+ application: 'cert_manager',
+ email: 'test@example.com'
+ }
+ end
- cluster.reload
- end.to change(cluster, :application_cert_manager)
- end
+ before do
+ expect_any_instance_of(Clusters::Applications::CertManager)
+ .to receive(:make_scheduled!)
+ .and_call_original
+ end
- it 'sets the email' do
- expect(subject.email).to eq('test@example.com')
- end
- end
+ it 'creates the application' do
+ expect do
+ subject
- context 'jupyter application' do
- let(:params) do
- {
- application: 'jupyter',
- hostname: 'example.com'
- }
- end
+ cluster.reload
+ end.to change(cluster, :application_cert_manager)
+ end
- before do
- allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute)
+ it 'sets the email' do
+ expect(subject.email).to eq('test@example.com')
+ end
end
- it 'creates the application' do
- expect do
- subject
+ context 'jupyter application' do
+ let(:params) do
+ {
+ application: 'jupyter',
+ hostname: 'example.com'
+ }
+ end
- cluster.reload
- end.to change(cluster, :application_jupyter)
- end
+ before do
+ create(:clusters_applications_ingress, :installed, external_ip: "127.0.0.0", cluster: cluster)
+ expect_any_instance_of(Clusters::Applications::Jupyter)
+ .to receive(:make_scheduled!)
+ .and_call_original
+ end
- it 'sets the hostname' do
- expect(subject.hostname).to eq('example.com')
- end
+ it 'creates the application' do
+ expect do
+ subject
- it 'sets the oauth_application' do
- expect(subject.oauth_application).to be_present
- end
- end
+ cluster.reload
+ end.to change(cluster, :application_jupyter)
+ end
- context 'knative application' do
- let(:params) do
- {
- application: 'knative',
- hostname: 'example.com'
- }
- end
+ it 'sets the hostname' do
+ expect(subject.hostname).to eq('example.com')
+ end
- before do
- allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute)
+ it 'sets the oauth_application' do
+ expect(subject.oauth_application).to be_present
+ end
end
- it 'creates the application' do
- expect do
- subject
+ context 'knative application' do
+ let(:params) do
+ {
+ application: 'knative',
+ hostname: 'example.com'
+ }
+ end
- cluster.reload
- end.to change(cluster, :application_knative)
- end
+ before do
+ expect_any_instance_of(Clusters::Applications::Knative)
+ .to receive(:make_scheduled!)
+ .and_call_original
+ end
- it 'sets the hostname' do
- expect(subject.hostname).to eq('example.com')
+ it 'creates the application' do
+ expect do
+ subject
+
+ cluster.reload
+ end.to change(cluster, :application_knative)
+ end
+
+ it 'sets the hostname' do
+ expect(subject.hostname).to eq('example.com')
+ end
end
end
@@ -140,19 +147,21 @@ describe Clusters::Applications::CreateService do
using RSpec::Parameterized::TableSyntax
- before do
- allow_any_instance_of(Clusters::Applications::ScheduleInstallationService).to receive(:execute)
- end
-
- where(:application, :association, :allowed) do
- 'helm' | :application_helm | true
- 'ingress' | :application_ingress | true
- 'runner' | :application_runner | false
- 'jupyter' | :application_jupyter | false
- 'prometheus' | :application_prometheus | false
+ where(:application, :association, :allowed, :pre_create_helm) do
+ 'helm' | :application_helm | true | false
+ 'ingress' | :application_ingress | true | true
+ 'runner' | :application_runner | true | true
+ 'jupyter' | :application_jupyter | false | true
+ 'prometheus' | :application_prometheus | false | true
end
with_them do
+ before do
+ klass = "Clusters::Applications::#{application.titleize}"
+ allow_any_instance_of(klass.constantize).to receive(:make_scheduled!).and_call_original
+ create(:clusters_applications_helm, :installed, cluster: cluster) if pre_create_helm
+ end
+
let(:params) { { application: application } }
it 'executes for each application' do
@@ -168,5 +177,68 @@ describe Clusters::Applications::CreateService do
end
end
end
+
+ context 'when application is installable' do
+ shared_examples 'installable applications' do
+ it 'makes the application scheduled' do
+ expect do
+ subject
+ end.to change { Clusters::Applications::Helm.with_status(:scheduled).count }.by(1)
+ end
+
+ it 'schedules an install via worker' do
+ expect(ClusterInstallAppWorker)
+ .to receive(:perform_async)
+ .with(*worker_arguments)
+ .once
+
+ subject
+ end
+ end
+
+ context 'when application is associated with a cluster' do
+ let(:application) { create(:clusters_applications_helm, :installable, cluster: cluster) }
+ let(:worker_arguments) { [application.name, application.id] }
+
+ it_behaves_like 'installable applications'
+ end
+
+ context 'when application is not associated with a cluster' do
+ let(:worker_arguments) { [params[:application], kind_of(Numeric)] }
+
+ it_behaves_like 'installable applications'
+ end
+ end
+
+ context 'when installation is already in progress' do
+ let!(:application) { create(:clusters_applications_helm, :installing, cluster: cluster) }
+
+ it 'raises an exception' do
+ expect { subject }
+ .to raise_exception(StateMachines::InvalidTransition)
+ .and not_change(application.class.with_status(:scheduled), :count)
+ end
+
+ it 'does not schedule a cluster worker' do
+ expect(ClusterInstallAppWorker).not_to receive(:perform_async)
+ end
+ end
+
+ context 'when application is installed' do
+ %i(installed updated).each do |status|
+ let(:application) { create(:clusters_applications_helm, status, cluster: cluster) }
+
+ it 'schedules an upgrade via worker' do
+ expect(ClusterUpgradeAppWorker)
+ .to receive(:perform_async)
+ .with(application.name, application.id)
+ .once
+
+ subject
+
+ expect(application.reload).to be_scheduled
+ end
+ end
+ end
end
end
diff --git a/spec/services/clusters/applications/patch_service_spec.rb b/spec/services/clusters/applications/patch_service_spec.rb
new file mode 100644
index 00000000000..d4ee3243b84
--- /dev/null
+++ b/spec/services/clusters/applications/patch_service_spec.rb
@@ -0,0 +1,128 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::PatchService do
+ describe '#execute' do
+ let(:application) { create(:clusters_applications_knative, :scheduled) }
+ let!(:update_command) { application.update_command }
+ let(:service) { described_class.new(application) }
+ let(:helm_client) { instance_double(Gitlab::Kubernetes::Helm::Api) }
+
+ before do
+ allow(service).to receive(:update_command).and_return(update_command)
+ allow(service).to receive(:helm_api).and_return(helm_client)
+ end
+
+ context 'when there are no errors' do
+ before do
+ expect(helm_client).to receive(:update).with(update_command)
+ allow(ClusterWaitForAppInstallationWorker).to receive(:perform_in).and_return(nil)
+ end
+
+ it 'make the application updating' do
+ expect(application.cluster).not_to be_nil
+ service.execute
+
+ expect(application).to be_updating
+ end
+
+ it 'schedule async installation status check' do
+ expect(ClusterWaitForAppInstallationWorker).to receive(:perform_in).once
+
+ service.execute
+ end
+ end
+
+ context 'when kubernetes cluster communication fails' do
+ let(:error) { Kubeclient::HttpError.new(500, 'system failure', nil) }
+
+ before do
+ expect(helm_client).to receive(:update).with(update_command).and_raise(error)
+ end
+
+ it 'make the application errored' do
+ service.execute
+
+ expect(application).to be_update_errored
+ expect(application.status_reason).to match('Kubernetes error: 500')
+ end
+
+ it 'logs errors' do
+ expect(service.send(:logger)).to receive(:error).with(
+ {
+ exception: 'Kubeclient::HttpError',
+ message: 'system failure',
+ service: 'Clusters::Applications::PatchService',
+ app_id: application.id,
+ project_ids: application.cluster.project_ids,
+ group_ids: [],
+ error_code: 500
+ }
+ )
+
+ expect(Gitlab::Sentry).to receive(:track_acceptable_exception).with(
+ error,
+ extra: {
+ exception: 'Kubeclient::HttpError',
+ message: 'system failure',
+ service: 'Clusters::Applications::PatchService',
+ app_id: application.id,
+ project_ids: application.cluster.project_ids,
+ group_ids: [],
+ error_code: 500
+ }
+ )
+
+ service.execute
+ end
+ end
+
+ context 'a non kubernetes error happens' do
+ let(:application) { create(:clusters_applications_knative, :scheduled) }
+ let(:error) { StandardError.new('something bad happened') }
+
+ before do
+ expect(application).to receive(:make_updating!).once.and_raise(error)
+ end
+
+ it 'make the application errored' do
+ expect(helm_client).not_to receive(:update)
+
+ service.execute
+
+ expect(application).to be_update_errored
+ expect(application.status_reason).to eq("Can't start update process.")
+ end
+
+ it 'logs errors' do
+ expect(service.send(:logger)).to receive(:error).with(
+ {
+ exception: 'StandardError',
+ error_code: nil,
+ message: 'something bad happened',
+ service: 'Clusters::Applications::PatchService',
+ app_id: application.id,
+ project_ids: application.cluster.projects.pluck(:id),
+ group_ids: []
+ }
+ )
+
+ expect(Gitlab::Sentry).to receive(:track_acceptable_exception).with(
+ error,
+ extra: {
+ exception: 'StandardError',
+ error_code: nil,
+ message: 'something bad happened',
+ service: 'Clusters::Applications::PatchService',
+ app_id: application.id,
+ project_ids: application.cluster.projects.pluck(:id),
+ group_ids: []
+ }
+ )
+
+ service.execute
+ end
+ end
+ end
+end
diff --git a/spec/services/clusters/applications/schedule_installation_service_spec.rb b/spec/services/clusters/applications/schedule_installation_service_spec.rb
deleted file mode 100644
index 8380932dfaa..00000000000
--- a/spec/services/clusters/applications/schedule_installation_service_spec.rb
+++ /dev/null
@@ -1,77 +0,0 @@
-require 'spec_helper'
-
-describe Clusters::Applications::ScheduleInstallationService do
- def count_scheduled
- application&.class&.with_status(:scheduled)&.count || 0
- end
-
- shared_examples 'a failing service' do
- it 'raise an exception' do
- expect(ClusterInstallAppWorker).not_to receive(:perform_async)
- count_before = count_scheduled
-
- expect { service.execute }.to raise_error(StandardError)
- expect(count_scheduled).to eq(count_before)
- end
- end
-
- describe '#execute' do
- let(:service) { described_class.new(application) }
-
- context 'when application is installable' do
- let(:application) { create(:clusters_applications_helm, :installable) }
-
- it 'make the application scheduled' do
- expect(ClusterInstallAppWorker).to receive(:perform_async).with(application.name, kind_of(Numeric)).once
-
- expect { service.execute }.to change { application.class.with_status(:scheduled).count }.by(1)
- end
- end
-
- context 'when installation is already in progress' do
- let(:application) { create(:clusters_applications_helm, :installing) }
-
- it_behaves_like 'a failing service'
- end
-
- context 'when application is nil' do
- let(:application) { nil }
-
- it_behaves_like 'a failing service'
- end
-
- context 'when application cannot be persisted' do
- let(:application) { create(:clusters_applications_helm) }
-
- before do
- expect(application).to receive(:make_scheduled!).once.and_raise(ActiveRecord::RecordInvalid)
- end
-
- it_behaves_like 'a failing service'
- end
-
- context 'when application is installed' do
- let(:application) { create(:clusters_applications_helm, :installed) }
-
- it 'schedules an upgrade via worker' do
- expect(ClusterUpgradeAppWorker).to receive(:perform_async).with(application.name, application.id).once
-
- service.execute
-
- expect(application).to be_scheduled
- end
- end
-
- context 'when application is updated' do
- let(:application) { create(:clusters_applications_helm, :updated) }
-
- it 'schedules an upgrade via worker' do
- expect(ClusterUpgradeAppWorker).to receive(:perform_async).with(application.name, application.id).once
-
- service.execute
-
- expect(application).to be_scheduled
- end
- end
- end
-end
diff --git a/spec/services/clusters/applications/update_service_spec.rb b/spec/services/clusters/applications/update_service_spec.rb
new file mode 100644
index 00000000000..2d299882af0
--- /dev/null
+++ b/spec/services/clusters/applications/update_service_spec.rb
@@ -0,0 +1,72 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Clusters::Applications::UpdateService do
+ include TestRequestHelpers
+
+ let(:cluster) { create(:cluster, :project, :provided_by_gcp) }
+ let(:user) { create(:user) }
+ let(:params) { { application: 'knative', hostname: 'udpate.example.com' } }
+ let(:service) { described_class.new(cluster, user, params) }
+
+ subject { service.execute(test_request) }
+
+ describe '#execute' do
+ before do
+ allow(ClusterPatchAppWorker).to receive(:perform_async)
+ end
+
+ context 'application is not installed' do
+ it 'raises Clusters::Applications::BaseService::InvalidApplicationError' do
+ expect(ClusterPatchAppWorker).not_to receive(:perform_async)
+
+ expect { subject }
+ .to raise_exception { Clusters::Applications::BaseService::InvalidApplicationError }
+ .and not_change { Clusters::Applications::Knative.count }
+ .and not_change { Clusters::Applications::Knative.with_status(:scheduled).count }
+ end
+ end
+
+ context 'application is installed' do
+ context 'application is schedulable' do
+ let!(:application) do
+ create(:clusters_applications_knative, status: 3, cluster: cluster)
+ end
+
+ it 'updates the application data' do
+ expect do
+ subject
+ end.to change { application.reload.hostname }.to(params[:hostname])
+ end
+
+ it 'makes application scheduled!' do
+ subject
+
+ expect(application.reload).to be_scheduled
+ end
+
+ it 'schedules ClusterPatchAppWorker' do
+ expect(ClusterPatchAppWorker).to receive(:perform_async)
+
+ subject
+ end
+ end
+
+ context 'application is not schedulable' do
+ let!(:application) do
+ create(:clusters_applications_knative, status: 4, cluster: cluster)
+ end
+
+ it 'raises StateMachines::InvalidTransition' do
+ expect(ClusterPatchAppWorker).not_to receive(:perform_async)
+
+ expect { subject }
+ .to raise_exception { StateMachines::InvalidTransition }
+ .and not_change { application.reload.hostname }
+ .and not_change { Clusters::Applications::Knative.with_status(:scheduled).count }
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/emails/create_service_spec.rb b/spec/services/emails/create_service_spec.rb
index 54692c88623..87f93ec97c9 100644
--- a/spec/services/emails/create_service_spec.rb
+++ b/spec/services/emails/create_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Emails::CreateService do
diff --git a/spec/services/emails/destroy_service_spec.rb b/spec/services/emails/destroy_service_spec.rb
index c3204fac3df..5abe8da2529 100644
--- a/spec/services/emails/destroy_service_spec.rb
+++ b/spec/services/emails/destroy_service_spec.rb
@@ -1,3 +1,5 @@
+# frozen_string_literal: true
+
require 'spec_helper'
describe Emails::DestroyService do
diff --git a/spec/services/error_tracking/list_issues_service_spec.rb b/spec/services/error_tracking/list_issues_service_spec.rb
index d9dab1d705c..9d4fc62f923 100644
--- a/spec/services/error_tracking/list_issues_service_spec.rb
+++ b/spec/services/error_tracking/list_issues_service_spec.rb
@@ -45,7 +45,23 @@ describe ErrorTracking::ListIssuesService do
it 'result is not ready' do
expect(result).to eq(
- status: :error, http_status: :no_content, message: 'not ready')
+ status: :error, http_status: :no_content, message: 'Not ready. Try again later')
+ end
+ end
+
+ context 'when list_sentry_issues returns error' do
+ before do
+ allow(error_tracking_setting)
+ .to receive(:list_sentry_issues)
+ .and_return(error: 'Sentry response status code: 401')
+ end
+
+ it 'returns the error' do
+ expect(result).to eq(
+ status: :error,
+ http_status: :bad_request,
+ message: 'Sentry response status code: 401'
+ )
end
end
end
@@ -58,7 +74,11 @@ describe ErrorTracking::ListIssuesService do
it 'returns error' do
result = subject.execute
- expect(result).to include(status: :error, message: 'access denied')
+ expect(result).to include(
+ status: :error,
+ message: 'Access denied',
+ http_status: :unauthorized
+ )
end
end
@@ -70,7 +90,7 @@ describe ErrorTracking::ListIssuesService do
it 'raises error' do
result = subject.execute
- expect(result).to include(status: :error, message: 'not enabled')
+ expect(result).to include(status: :error, message: 'Error Tracking is not enabled')
end
end
end
diff --git a/spec/services/error_tracking/list_projects_service_spec.rb b/spec/services/error_tracking/list_projects_service_spec.rb
index ee9c59e3f65..a92d3376f7b 100644
--- a/spec/services/error_tracking/list_projects_service_spec.rb
+++ b/spec/services/error_tracking/list_projects_service_spec.rb
@@ -32,7 +32,7 @@ describe ErrorTracking::ListProjectsService do
end
context 'set model attributes to new values' do
- let(:new_api_url) { new_api_host + 'api/0/projects/' }
+ let(:new_api_url) { new_api_host + 'api/0/projects/org/proj/' }
before do
expect(error_tracking_setting).to receive(:list_sentry_projects)
@@ -53,11 +53,11 @@ describe ErrorTracking::ListProjectsService do
context 'sentry client raises exception' do
before do
expect(error_tracking_setting).to receive(:list_sentry_projects)
- .and_raise(Sentry::Client::Error, 'Sentry response error: 500')
+ .and_raise(Sentry::Client::Error, 'Sentry response status code: 500')
end
it 'returns error response' do
- expect(result[:message]).to eq('Sentry response error: 500')
+ expect(result[:message]).to eq('Sentry response status code: 500')
expect(result[:http_status]).to eq(:bad_request)
end
end
@@ -121,7 +121,7 @@ describe ErrorTracking::ListProjectsService do
context 'error_tracking_setting is nil' do
let(:error_tracking_setting) { build(:project_error_tracking_setting) }
- let(:new_api_url) { new_api_host + 'api/0/projects/' }
+ let(:new_api_url) { new_api_host + 'api/0/projects/org/proj/' }
before do
expect(project).to receive(:build_error_tracking_setting).once
diff --git a/spec/services/files/multi_service_spec.rb b/spec/services/files/multi_service_spec.rb
index 84c48d63c64..6842fa9f435 100644
--- a/spec/services/files/multi_service_spec.rb
+++ b/spec/services/files/multi_service_spec.rb
@@ -235,6 +235,22 @@ describe Files::MultiService do
expect(blob).to be_present
end
end
+
+ context 'when force is set to true and branch already exists' do
+ let(:commit_params) do
+ {
+ commit_message: commit_message,
+ branch_name: 'feature',
+ start_branch: 'master',
+ actions: actions,
+ force: true
+ }
+ end
+
+ it 'is still a success' do
+ expect(subject.execute[:status]).to eq(:success)
+ end
+ end
end
def update_file(path)
diff --git a/spec/services/git/branch_push_service_spec.rb b/spec/services/git/branch_push_service_spec.rb
new file mode 100644
index 00000000000..d0e2169b4a6
--- /dev/null
+++ b/spec/services/git/branch_push_service_spec.rb
@@ -0,0 +1,837 @@
+require 'spec_helper'
+
+describe Git::BranchPushService, services: true do
+ include RepoHelpers
+
+ 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_maintainer(user)
+ end
+
+ describe 'with remote mirrors' do
+ let(:project) { create(:project, :repository, :remote_mirror) }
+
+ subject do
+ described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref)
+ end
+
+ context 'when remote mirror feature is enabled' do
+ it 'fails stuck remote mirrors' do
+ allow(project).to receive(:update_remote_mirrors).and_return(project.remote_mirrors)
+ expect(project).to receive(:mark_stuck_remote_mirrors_as_failed!)
+
+ subject.execute
+ end
+
+ it 'updates remote mirrors' do
+ expect(project).to receive(:update_remote_mirrors)
+
+ subject.execute
+ end
+ end
+
+ context 'when remote mirror feature is disabled' do
+ before do
+ stub_application_setting(mirror_available: false)
+ end
+
+ context 'with remote mirrors global setting overridden' do
+ before do
+ project.remote_mirror_available_overridden = true
+ end
+
+ it 'fails stuck remote mirrors' do
+ allow(project).to receive(:update_remote_mirrors).and_return(project.remote_mirrors)
+ expect(project).to receive(:mark_stuck_remote_mirrors_as_failed!)
+
+ subject.execute
+ end
+
+ it 'updates remote mirrors' do
+ expect(project).to receive(:update_remote_mirrors)
+
+ subject.execute
+ end
+ end
+
+ context 'without remote mirrors global setting overridden' do
+ before do
+ project.remote_mirror_available_overridden = false
+ end
+
+ it 'does not fails stuck remote mirrors' do
+ expect(project).not_to receive(:mark_stuck_remote_mirrors_as_failed!)
+
+ subject.execute
+ end
+
+ it 'does not updates remote mirrors' do
+ expect(project).not_to receive(:update_remote_mirrors)
+
+ subject.execute
+ end
+ end
+ end
+ end
+
+ describe 'Push branches' do
+ subject do
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+
+ context 'new branch' do
+ let(:oldrev) { blankrev }
+
+ it { is_expected.to be_truthy }
+
+ it 'calls the after_push_commit hook' do
+ expect(project.repository).to receive(:after_push_commit).with('master')
+
+ subject
+ end
+
+ it 'calls the after_create_branch hook' do
+ expect(project.repository).to receive(:after_create_branch)
+
+ subject
+ end
+ end
+
+ context 'existing branch' do
+ it { is_expected.to be_truthy }
+
+ it 'calls the after_push_commit hook' do
+ expect(project.repository).to receive(:after_push_commit).with('master')
+
+ subject
+ end
+ end
+
+ context 'rm branch' do
+ let(:newrev) { blankrev }
+
+ it { is_expected.to be_truthy }
+
+ it 'calls the after_push_commit hook' do
+ expect(project.repository).to receive(:after_push_commit).with('master')
+
+ subject
+ end
+
+ it 'calls the after_remove_branch hook' do
+ expect(project.repository).to receive(:after_remove_branch)
+
+ subject
+ end
+ end
+ end
+
+ describe "Git Push Data" do
+ let(:commit) { project.commit(newrev) }
+
+ subject { push_data_from_service(project, user, oldrev, newrev, ref) }
+
+ it { is_expected.to include(object_kind: 'push') }
+ it { is_expected.to include(before: oldrev) }
+ it { is_expected.to include(after: newrev) }
+ it { is_expected.to include(ref: ref) }
+ it { is_expected.to include(user_id: user.id) }
+ it { is_expected.to include(user_name: user.name) }
+ it { is_expected.to include(project_id: project.id) }
+
+ context "with repository data" do
+ subject { push_data_from_service(project, user, oldrev, newrev, ref)[:repository] }
+
+ it { is_expected.to include(name: project.name) }
+ it { is_expected.to include(url: project.url_to_repo) }
+ it { is_expected.to include(description: project.description) }
+ it { is_expected.to include(homepage: project.web_url) }
+ end
+
+ context "with commits" do
+ subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits] }
+
+ it { is_expected.to be_an(Array) }
+ it 'has 1 element' do
+ expect(subject.size).to eq(1)
+ end
+
+ context "the commit" do
+ subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first }
+
+ it { is_expected.to include(id: commit.id) }
+ it { is_expected.to include(message: commit.safe_message) }
+ it { expect(subject[:timestamp].in_time_zone).to eq(commit.date.in_time_zone) }
+ it do
+ is_expected.to include(
+ url: [
+ Gitlab.config.gitlab.url,
+ project.namespace.to_param,
+ project.to_param,
+ 'commit',
+ commit.id
+ ].join('/')
+ )
+ end
+
+ context "with a author" do
+ subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first[:author] }
+
+ it { is_expected.to include(name: commit.author_name) }
+ it { is_expected.to include(email: commit.author_email) }
+ end
+ end
+ end
+ end
+
+ describe "Pipelines" do
+ subject { execute_service(project, user, oldrev, newrev, ref) }
+
+ before do
+ stub_ci_pipeline_to_return_yaml_file
+ end
+
+ it "creates a new pipeline" do
+ expect { subject }.to change { Ci::Pipeline.count }
+ expect(Ci::Pipeline.last).to be_push
+ end
+ end
+
+ describe "Push Event" do
+ context "with an existing branch" do
+ let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) }
+ let(:event) { Event.find_by_action(Event::PUSHED) }
+
+ it 'generates a push event with one commit' do
+ expect(event).to be_an_instance_of(PushEvent)
+ expect(event.project).to eq(project)
+ expect(event.action).to eq(Event::PUSHED)
+ expect(event.push_event_payload).to be_an_instance_of(PushEventPayload)
+ expect(event.push_event_payload.commit_from).to eq(oldrev)
+ expect(event.push_event_payload.commit_to).to eq(newrev)
+ expect(event.push_event_payload.ref).to eq('master')
+ expect(event.push_event_payload.commit_count).to eq(1)
+ end
+ end
+
+ context "with a new branch" do
+ let!(:new_branch_data) { push_data_from_service(project, user, Gitlab::Git::BLANK_SHA, newrev, ref) }
+ let(:event) { Event.find_by_action(Event::PUSHED) }
+
+ it 'generates a push event with more than one commit' do
+ expect(event).to be_an_instance_of(PushEvent)
+ expect(event.project).to eq(project)
+ expect(event.action).to eq(Event::PUSHED)
+ expect(event.push_event_payload).to be_an_instance_of(PushEventPayload)
+ expect(event.push_event_payload.commit_from).to be_nil
+ expect(event.push_event_payload.commit_to).to eq(newrev)
+ expect(event.push_event_payload.ref).to eq('master')
+ expect(event.push_event_payload.commit_count).to be > 1
+ end
+ end
+
+ context "Updates merge requests" do
+ it "when pushing a new branch for the first time" do
+ expect(UpdateMergeRequestsWorker).to receive(:perform_async)
+ .with(project.id, user.id, blankrev, 'newrev', ref)
+ execute_service(project, user, blankrev, 'newrev', ref )
+ end
+ end
+
+ describe 'system hooks' do
+ let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) }
+ let!(:system_hooks_service) { SystemHooksService.new }
+
+ it "sends a system hook after pushing a branch" do
+ allow(SystemHooksService).to receive(:new).and_return(system_hooks_service)
+ allow(system_hooks_service).to receive(:execute_hooks)
+
+ execute_service(project, user, oldrev, newrev, ref)
+
+ expect(system_hooks_service).to have_received(:execute_hooks).with(push_data, :push_hooks)
+ end
+ end
+ end
+
+ describe "Updates git attributes" do
+ context "for default branch" do
+ it "calls the copy attributes method for the first push to the default branch" do
+ expect(project.repository).to receive(:copy_gitattributes).with('master')
+
+ execute_service(project, user, blankrev, 'newrev', ref)
+ end
+
+ it "calls the copy attributes method for changes to the default branch" do
+ expect(project.repository).to receive(:copy_gitattributes).with(ref)
+
+ execute_service(project, user, 'oldrev', 'newrev', ref)
+ end
+ end
+
+ context "for non-default branch" do
+ before do
+ # Make sure the "default" branch is different
+ allow(project).to receive(:default_branch).and_return('not-master')
+ end
+
+ it "does not call copy attributes method" do
+ expect(project.repository).not_to receive(:copy_gitattributes)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+ end
+ end
+
+ describe "Webhooks" do
+ context "execute webhooks" do
+ it "when pushing a branch for the first time" do
+ expect(project).to receive(:execute_hooks)
+ 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::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
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
+
+ expect(project).to receive(:execute_hooks)
+ expect(project.default_branch).to eq("master")
+ execute_service(project, user, blankrev, 'newrev', ref)
+ expect(project.protected_branches).to be_empty
+ end
+
+ it "when pushing a branch for the first time with default branch protection set to 'developers can push'" do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
+
+ expect(project).to receive(:execute_hooks)
+ 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.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::MAINTAINER])
+ end
+
+ it "when pushing a branch for the first time with an existing branch permission configured" do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
+
+ create(:protected_branch, :no_one_can_push, :developers_can_merge, project: project, name: 'master')
+ expect(project).to receive(:execute_hooks)
+ expect(project.default_branch).to eq("master")
+ expect_any_instance_of(ProtectedBranches::CreateService).not_to receive(:execute)
+
+ execute_service(project, user, blankrev, 'newrev', ref)
+
+ expect(project.protected_branches).not_to be_empty
+ expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::NO_ACCESS])
+ expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
+ end
+
+ it "when pushing a branch for the first time with default branch protection set to 'developers can merge'" do
+ stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
+
+ expect(project).to receive(:execute_hooks)
+ 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::MAINTAINER])
+ expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
+ end
+
+ it "when pushing new commits to existing branch" do
+ expect(project).to receive(:execute_hooks)
+ execute_service(project, user, 'oldrev', 'newrev', ref)
+ end
+ end
+ end
+
+ describe "cross-reference notes" do
+ let(:issue) { create :issue, project: project }
+ let(:commit_author) { create :user }
+ let(:commit) { project.commit }
+
+ before do
+ project.add_developer(commit_author)
+ project.add_developer(user)
+
+ allow(commit).to receive_messages(
+ safe_message: "this commit \n mentions #{issue.to_reference}",
+ references: [issue],
+ author_name: commit_author.name,
+ author_email: commit_author.email
+ )
+
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ .and_return(commit)
+
+ allow(project.repository).to receive(:commits_between).and_return([commit])
+ end
+
+ it "creates a note if a pushed commit mentions an issue" do
+ expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+
+ it "only creates a cross-reference note if one doesn't already exist" do
+ SystemNoteService.cross_reference(issue, commit, user)
+
+ expect(SystemNoteService).not_to receive(:cross_reference).with(issue, commit, commit_author)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+
+ it "defaults to the pushing user if the commit's author is not known" do
+ allow(commit).to receive_messages(
+ author_name: 'unknown name',
+ author_email: 'unknown@email.com'
+ )
+ expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, user)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+
+ it "finds references in the first push to a non-default branch" do
+ allow(project.repository).to receive(:commits_between).with(blankrev, newrev).and_return([])
+ allow(project.repository).to receive(:commits_between).with("master", newrev).and_return([commit])
+
+ expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author)
+
+ execute_service(project, user, blankrev, newrev, 'refs/heads/other')
+ end
+ end
+
+ describe "issue metrics" do
+ let(:issue) { create :issue, project: project }
+ let(:commit_author) { create :user }
+ let(:commit) { project.commit }
+ let(:commit_time) { Time.now }
+
+ before do
+ project.add_developer(commit_author)
+ project.add_developer(user)
+
+ allow(commit).to receive_messages(
+ safe_message: "this commit \n mentions #{issue.to_reference}",
+ references: [issue],
+ author_name: commit_author.name,
+ author_email: commit_author.email,
+ committed_date: commit_time
+ )
+
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ .and_return(commit)
+
+ allow(project.repository).to receive(:commits_between).and_return([commit])
+ end
+
+ context "while saving the 'first_mentioned_in_commit_at' metric for an issue" do
+ it 'sets the metric for referenced issues' do
+ execute_service(project, user, oldrev, newrev, ref)
+
+ expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_like_time(commit_time)
+ end
+
+ it 'does not set the metric for non-referenced issues' do
+ non_referenced_issue = create(:issue, project: project)
+ execute_service(project, user, oldrev, newrev, ref)
+
+ expect(non_referenced_issue.reload.metrics.first_mentioned_in_commit_at).to be_nil
+ end
+ end
+ end
+
+ describe "closing issues from pushed commits containing a closing reference" do
+ let(:issue) { create :issue, project: project }
+ let(:other_issue) { create :issue, project: project }
+ let(:commit_author) { create :user }
+ let(:closing_commit) { project.commit }
+
+ before do
+ allow(closing_commit).to receive_messages(
+ issue_closing_regex: /^([Cc]loses|[Ff]ixes) #\d+/,
+ safe_message: "this is some work.\n\ncloses ##{issue.iid}",
+ author_name: commit_author.name,
+ author_email: commit_author.email
+ )
+
+ allow(project.repository).to receive(:commits_between)
+ .and_return([closing_commit])
+
+ allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
+ .and_return(closing_commit)
+
+ project.add_maintainer(commit_author)
+ end
+
+ context "to default branches" do
+ it "closes issues" do
+ execute_service(project, commit_author, oldrev, newrev, ref)
+ expect(Issue.find(issue.id)).to be_closed
+ end
+
+ it "adds a note indicating that the issue is now closed" do
+ expect(SystemNoteService).to receive(:change_status).with(issue, project, commit_author, "closed", closing_commit)
+ execute_service(project, commit_author, oldrev, newrev, ref)
+ end
+
+ it "doesn't create additional cross-reference notes" do
+ expect(SystemNoteService).not_to receive(:cross_reference)
+ execute_service(project, commit_author, oldrev, newrev, ref)
+ end
+ end
+
+ context "to non-default branches" do
+ before do
+ # Make sure the "default" branch is different
+ allow(project).to receive(:default_branch).and_return('not-master')
+ end
+
+ it "creates cross-reference notes" do
+ expect(SystemNoteService).to receive(:cross_reference).with(issue, closing_commit, commit_author)
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+
+ it "doesn't close issues" do
+ execute_service(project, user, oldrev, newrev, ref)
+ expect(Issue.find(issue.id)).to be_opened
+ end
+ end
+
+ context "for jira issue tracker" do
+ include JiraServiceHelper
+
+ let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? }
+
+ before do
+ # project.create_jira_service doesn't seem to invalidate the cache here
+ project.has_external_issue_tracker = true
+ jira_service_settings
+ stub_jira_urls("JIRA-1")
+
+ allow(closing_commit).to receive_messages({
+ issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern),
+ safe_message: message,
+ author_name: commit_author.name,
+ author_email: commit_author.email
+ })
+
+ allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
+
+ allow(project.repository).to receive_messages(commits_between: [closing_commit])
+ end
+
+ after do
+ jira_tracker.destroy!
+ end
+
+ context "mentioning an issue" do
+ let(:message) { "this is some work.\n\nrelated to JIRA-1" }
+
+ it "initiates one api call to jira server to mention the issue" do
+ execute_service(project, user, oldrev, newrev, ref)
+
+ expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
+ body: /mentioned this issue in/
+ ).once
+ end
+ end
+
+ context "closing an issue" do
+ let(:message) { "this is some work.\n\ncloses JIRA-1" }
+ let(:comment_body) do
+ {
+ body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.full_path}/commit/#{closing_commit.id}]."
+ }.to_json
+ end
+
+ before do
+ open_issue = JIRA::Resource::Issue.new(jira_tracker.client, attrs: { "id" => "JIRA-1" })
+ closed_issue = open_issue.dup
+ allow(open_issue).to receive(:resolution).and_return(false)
+ allow(closed_issue).to receive(:resolution).and_return(true)
+ allow(JIRA::Resource::Issue).to receive(:find).and_return(open_issue, closed_issue)
+
+ allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return("JIRA-1")
+ end
+
+ context "using right markdown" do
+ it "initiates one api call to jira server to close the issue" do
+ execute_service(project, commit_author, oldrev, newrev, ref)
+
+ expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once
+ end
+
+ it "initiates one api call to jira server to comment on the issue" do
+ execute_service(project, commit_author, oldrev, newrev, ref)
+
+ expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
+ body: comment_body
+ ).once
+ end
+ end
+
+ context "using internal issue reference" do
+ context 'when internal issues are disabled' do
+ before do
+ project.issues_enabled = false
+ project.save!
+ end
+ let(:message) { "this is some work.\n\ncloses #1" }
+
+ it "does not initiates one api call to jira server to close the issue" do
+ execute_service(project, commit_author, oldrev, newrev, ref)
+
+ expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1'))
+ end
+
+ it "does not initiates one api call to jira server to comment on the issue" do
+ execute_service(project, commit_author, oldrev, newrev, ref)
+
+ expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
+ body: comment_body
+ ).once
+ end
+ end
+
+ context 'when internal issues are enabled' do
+ let(:issue) { create(:issue, project: project) }
+ let(:message) { "this is some work.\n\ncloses JIRA-1 \n\n closes #{issue.to_reference}" }
+
+ it "initiates one api call to jira server to close the jira issue" do
+ execute_service(project, commit_author, oldrev, newrev, ref)
+
+ expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once
+ end
+
+ it "initiates one api call to jira server to comment on the jira issue" do
+ execute_service(project, commit_author, oldrev, newrev, ref)
+
+ expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
+ body: comment_body
+ ).once
+ end
+
+ it "closes the internal issue" do
+ execute_service(project, commit_author, oldrev, newrev, ref)
+ expect(issue.reload).to be_closed
+ end
+
+ it "adds a note indicating that the issue is now closed" do
+ expect(SystemNoteService).to receive(:change_status)
+ .with(issue, project, commit_author, "closed", closing_commit)
+ execute_service(project, commit_author, oldrev, newrev, ref)
+ end
+ end
+ end
+ end
+ end
+ end
+
+ describe "empty project" do
+ let(:project) { create(:project_empty_repo) }
+ let(:new_ref) { 'refs/heads/feature' }
+
+ before do
+ allow(project).to receive(:default_branch).and_return('feature')
+ expect(project).to receive(:change_head) { 'feature'}
+ end
+
+ it 'push to first branch updates HEAD' do
+ execute_service(project, user, blankrev, newrev, new_ref)
+ end
+ end
+
+ describe "housekeeping" do
+ let(:housekeeping) { Projects::HousekeepingService.new(project) }
+
+ before do
+ # Flush any raw key-value data stored by the housekeeping code.
+ Gitlab::Redis::Cache.with { |conn| conn.flushall }
+ Gitlab::Redis::Queues.with { |conn| conn.flushall }
+ Gitlab::Redis::SharedState.with { |conn| conn.flushall }
+
+ allow(Projects::HousekeepingService).to receive(:new).and_return(housekeeping)
+ end
+
+ after do
+ Gitlab::Redis::Cache.with { |conn| conn.flushall }
+ Gitlab::Redis::Queues.with { |conn| conn.flushall }
+ Gitlab::Redis::SharedState.with { |conn| conn.flushall }
+ end
+
+ it 'does not perform housekeeping when not needed' do
+ expect(housekeeping).not_to receive(:execute)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+
+ context 'when housekeeping is needed' do
+ before do
+ allow(housekeeping).to receive(:needed?).and_return(true)
+ end
+
+ it 'performs housekeeping' do
+ expect(housekeeping).to receive(:execute)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+
+ it 'does not raise an exception' do
+ allow(housekeeping).to receive(:try_obtain_lease).and_return(false)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+ end
+
+ it 'increments the push counter' do
+ expect(housekeeping).to receive(:increment!)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+ end
+
+ describe '#update_caches' do
+ let(:service) do
+ described_class.new(project,
+ user,
+ oldrev: oldrev,
+ newrev: newrev,
+ ref: ref)
+ end
+
+ context 'on the default branch' do
+ before do
+ allow(service).to receive(:default_branch?).and_return(true)
+ end
+
+ it 'flushes the caches of any special files that have been changed' do
+ commit = double(:commit)
+ diff = double(:diff, new_path: 'README.md')
+
+ expect(commit).to receive(:raw_deltas)
+ .and_return([diff])
+
+ service.push_commits = [commit]
+
+ expect(ProjectCacheWorker).to receive(:perform_async)
+ .with(project.id, %i(readme), %i(commit_count repository_size))
+
+ service.update_caches
+ end
+ end
+
+ context 'on a non-default branch' do
+ before do
+ allow(service).to receive(:default_branch?).and_return(false)
+ end
+
+ it 'does not flush any conditional caches' do
+ expect(ProjectCacheWorker).to receive(:perform_async)
+ .with(project.id, [], %i(commit_count repository_size))
+ .and_call_original
+
+ service.update_caches
+ end
+ end
+ end
+
+ describe '#process_commit_messages' do
+ let(:service) do
+ described_class.new(project,
+ user,
+ oldrev: oldrev,
+ newrev: newrev,
+ ref: ref)
+ end
+
+ it 'only schedules a limited number of commits' do
+ service.push_commits = Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true))
+
+ expect(ProcessCommitWorker).to receive(:perform_async).exactly(100).times
+
+ service.process_commit_messages
+ end
+
+ it "skips commits which don't include cross-references" do
+ service.push_commits = [double(:commit, to_hash: {}, matches_cross_reference_regex?: false)]
+
+ expect(ProcessCommitWorker).not_to receive(:perform_async)
+
+ service.process_commit_messages
+ end
+ end
+
+ describe '#update_signatures' do
+ let(:service) do
+ described_class.new(
+ project,
+ user,
+ oldrev: oldrev,
+ newrev: newrev,
+ ref: 'refs/heads/master'
+ )
+ end
+
+ context 'when the commit has a signature' do
+ context 'when the signature is already cached' do
+ before do
+ create(:gpg_signature, commit_sha: sample_commit.id)
+ end
+
+ it 'does not queue a CreateGpgSignatureWorker' do
+ expect(CreateGpgSignatureWorker).not_to receive(:perform_async)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+ end
+
+ 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)
+
+ 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
+ end
+ end
+
+ context 'when the commit does not have a signature' do
+ before do
+ allow(Gitlab::Git::Commit).to receive(:shas_with_signatures).with(project.repository, [sample_commit.id]).and_return([])
+ end
+
+ it 'does not queue a CreateGpgSignatureWorker' do
+ expect(CreateGpgSignatureWorker).not_to receive(:perform_async).with(sample_commit.id, project.id)
+
+ execute_service(project, user, oldrev, newrev, ref)
+ end
+ end
+ end
+
+ def execute_service(project, user, oldrev, newrev, ref)
+ service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref)
+ service.execute
+ service
+ end
+
+ def push_data_from_service(project, user, oldrev, newrev, ref)
+ execute_service(project, user, oldrev, newrev, ref).push_data
+ end
+end
diff --git a/spec/services/git/tag_push_service_spec.rb b/spec/services/git/tag_push_service_spec.rb
new file mode 100644
index 00000000000..e151db5827f
--- /dev/null
+++ b/spec/services/git/tag_push_service_spec.rb
@@ -0,0 +1,196 @@
+require 'spec_helper'
+
+describe Git::TagPushService do
+ include RepoHelpers
+ include GitHelpers
+
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :repository) }
+ let(:service) { described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) }
+
+ let(:oldrev) { Gitlab::Git::BLANK_SHA }
+ let(:newrev) { "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" } # gitlab-test: git rev-parse refs/tags/v1.1.0
+ let(:ref) { 'refs/tags/v1.1.0' }
+
+ describe "Push tags" do
+ subject do
+ service.execute
+ service
+ end
+
+ it 'flushes general cached data' do
+ expect(project.repository).to receive(:before_push_tag)
+
+ subject
+ end
+
+ it 'flushes the tags cache' do
+ expect(project.repository).to receive(:expire_tags_cache)
+
+ subject
+ end
+ end
+
+ describe "Pipelines" do
+ subject { service.execute }
+
+ before do
+ stub_ci_pipeline_to_return_yaml_file
+ project.add_developer(user)
+ end
+
+ it "creates a new pipeline" do
+ expect { subject }.to change { Ci::Pipeline.count }
+ expect(Ci::Pipeline.last).to be_push
+ end
+ end
+
+ describe "Git Tag Push Data" do
+ subject { @push_data }
+ let(:tag) { project.repository.find_tag(tag_name) }
+ let(:commit) { tag.dereferenced_target }
+
+ context 'annotated tag' do
+ let(:tag_name) { Gitlab::Git.ref_name(ref) }
+
+ before do
+ service.execute
+ @push_data = service.push_data
+ end
+
+ it { is_expected.to include(object_kind: 'tag_push') }
+ it { is_expected.to include(ref: ref) }
+ it { is_expected.to include(before: oldrev) }
+ it { is_expected.to include(after: newrev) }
+ it { is_expected.to include(message: tag.message) }
+ it { is_expected.to include(user_id: user.id) }
+ it { is_expected.to include(user_name: user.name) }
+ it { is_expected.to include(project_id: project.id) }
+
+ context "with repository data" do
+ subject { @push_data[:repository] }
+
+ it { is_expected.to include(name: project.name) }
+ it { is_expected.to include(url: project.url_to_repo) }
+ it { is_expected.to include(description: project.description) }
+ it { is_expected.to include(homepage: project.web_url) }
+ end
+
+ context "with commits" do
+ subject { @push_data[:commits] }
+
+ it { is_expected.to be_an(Array) }
+ it 'has 1 element' do
+ expect(subject.size).to eq(1)
+ end
+
+ context "the commit" do
+ subject { @push_data[:commits].first }
+
+ it { is_expected.to include(id: commit.id) }
+ it { is_expected.to include(message: commit.safe_message) }
+ it { is_expected.to include(timestamp: commit.date.xmlschema) }
+ it do
+ is_expected.to include(
+ url: [
+ Gitlab.config.gitlab.url,
+ project.namespace.to_param,
+ project.to_param,
+ 'commit',
+ commit.id
+ ].join('/')
+ )
+ end
+
+ context "with a author" do
+ subject { @push_data[:commits].first[:author] }
+
+ it { is_expected.to include(name: commit.author_name) }
+ it { is_expected.to include(email: commit.author_email) }
+ end
+ end
+ end
+ end
+
+ context 'lightweight tag' do
+ let(:tag_name) { 'light-tag' }
+ let(:newrev) { '5937ac0a7beb003549fc5fd26fc247adbce4a52e' }
+ let(:ref) { "refs/tags/light-tag" }
+
+ before do
+ # Create the lightweight tag
+ rugged_repo(project.repository).tags.create(tag_name, newrev)
+
+ # Clear tag list cache
+ project.repository.expire_tags_cache
+
+ service.execute
+ @push_data = service.push_data
+ end
+
+ it { is_expected.to include(object_kind: 'tag_push') }
+ it { is_expected.to include(ref: ref) }
+ it { is_expected.to include(before: oldrev) }
+ it { is_expected.to include(after: newrev) }
+ it { is_expected.to include(message: tag.message) }
+ it { is_expected.to include(user_id: user.id) }
+ it { is_expected.to include(user_name: user.name) }
+ it { is_expected.to include(project_id: project.id) }
+
+ context "with repository data" do
+ subject { @push_data[:repository] }
+
+ it { is_expected.to include(name: project.name) }
+ it { is_expected.to include(url: project.url_to_repo) }
+ it { is_expected.to include(description: project.description) }
+ it { is_expected.to include(homepage: project.web_url) }
+ end
+
+ context "with commits" do
+ subject { @push_data[:commits] }
+
+ it { is_expected.to be_an(Array) }
+ it 'has 1 element' do
+ expect(subject.size).to eq(1)
+ end
+
+ context "the commit" do
+ subject { @push_data[:commits].first }
+
+ it { is_expected.to include(id: commit.id) }
+ it { is_expected.to include(message: commit.safe_message) }
+ it { is_expected.to include(timestamp: commit.date.xmlschema) }
+ it do
+ is_expected.to include(
+ url: [
+ Gitlab.config.gitlab.url,
+ project.namespace.to_param,
+ project.to_param,
+ 'commit',
+ commit.id
+ ].join('/')
+ )
+ end
+
+ context "with a author" do
+ subject { @push_data[:commits].first[:author] }
+
+ it { is_expected.to include(name: commit.author_name) }
+ it { is_expected.to include(email: commit.author_email) }
+ end
+ end
+ end
+ end
+ end
+
+ describe "Webhooks" do
+ context "execute webhooks" do
+ let(:service) { described_class.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') }
+
+ it "when pushing tags" do
+ expect(project).to receive(:execute_hooks)
+ service.execute
+ end
+ end
+ end
+end
diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb
deleted file mode 100644
index e8fce951155..00000000000
--- a/spec/services/git_push_service_spec.rb
+++ /dev/null
@@ -1,837 +0,0 @@
-require 'spec_helper'
-
-describe GitPushService, services: true do
- include RepoHelpers
-
- 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_maintainer(user)
- end
-
- describe 'with remote mirrors' do
- let(:project) { create(:project, :repository, :remote_mirror) }
-
- subject do
- described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref)
- end
-
- context 'when remote mirror feature is enabled' do
- it 'fails stuck remote mirrors' do
- allow(project).to receive(:update_remote_mirrors).and_return(project.remote_mirrors)
- expect(project).to receive(:mark_stuck_remote_mirrors_as_failed!)
-
- subject.execute
- end
-
- it 'updates remote mirrors' do
- expect(project).to receive(:update_remote_mirrors)
-
- subject.execute
- end
- end
-
- context 'when remote mirror feature is disabled' do
- before do
- stub_application_setting(mirror_available: false)
- end
-
- context 'with remote mirrors global setting overridden' do
- before do
- project.remote_mirror_available_overridden = true
- end
-
- it 'fails stuck remote mirrors' do
- allow(project).to receive(:update_remote_mirrors).and_return(project.remote_mirrors)
- expect(project).to receive(:mark_stuck_remote_mirrors_as_failed!)
-
- subject.execute
- end
-
- it 'updates remote mirrors' do
- expect(project).to receive(:update_remote_mirrors)
-
- subject.execute
- end
- end
-
- context 'without remote mirrors global setting overridden' do
- before do
- project.remote_mirror_available_overridden = false
- end
-
- it 'does not fails stuck remote mirrors' do
- expect(project).not_to receive(:mark_stuck_remote_mirrors_as_failed!)
-
- subject.execute
- end
-
- it 'does not updates remote mirrors' do
- expect(project).not_to receive(:update_remote_mirrors)
-
- subject.execute
- end
- end
- end
- end
-
- describe 'Push branches' do
- subject do
- execute_service(project, user, oldrev, newrev, ref)
- end
-
- context 'new branch' do
- let(:oldrev) { blankrev }
-
- it { is_expected.to be_truthy }
-
- it 'calls the after_push_commit hook' do
- expect(project.repository).to receive(:after_push_commit).with('master')
-
- subject
- end
-
- it 'calls the after_create_branch hook' do
- expect(project.repository).to receive(:after_create_branch)
-
- subject
- end
- end
-
- context 'existing branch' do
- it { is_expected.to be_truthy }
-
- it 'calls the after_push_commit hook' do
- expect(project.repository).to receive(:after_push_commit).with('master')
-
- subject
- end
- end
-
- context 'rm branch' do
- let(:newrev) { blankrev }
-
- it { is_expected.to be_truthy }
-
- it 'calls the after_push_commit hook' do
- expect(project.repository).to receive(:after_push_commit).with('master')
-
- subject
- end
-
- it 'calls the after_remove_branch hook' do
- expect(project.repository).to receive(:after_remove_branch)
-
- subject
- end
- end
- end
-
- describe "Git Push Data" do
- let(:commit) { project.commit(newrev) }
-
- subject { push_data_from_service(project, user, oldrev, newrev, ref) }
-
- it { is_expected.to include(object_kind: 'push') }
- it { is_expected.to include(before: oldrev) }
- it { is_expected.to include(after: newrev) }
- it { is_expected.to include(ref: ref) }
- it { is_expected.to include(user_id: user.id) }
- it { is_expected.to include(user_name: user.name) }
- it { is_expected.to include(project_id: project.id) }
-
- context "with repository data" do
- subject { push_data_from_service(project, user, oldrev, newrev, ref)[:repository] }
-
- it { is_expected.to include(name: project.name) }
- it { is_expected.to include(url: project.url_to_repo) }
- it { is_expected.to include(description: project.description) }
- it { is_expected.to include(homepage: project.web_url) }
- end
-
- context "with commits" do
- subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits] }
-
- it { is_expected.to be_an(Array) }
- it 'has 1 element' do
- expect(subject.size).to eq(1)
- end
-
- context "the commit" do
- subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first }
-
- it { is_expected.to include(id: commit.id) }
- it { is_expected.to include(message: commit.safe_message) }
- it { expect(subject[:timestamp].in_time_zone).to eq(commit.date.in_time_zone) }
- it do
- is_expected.to include(
- url: [
- Gitlab.config.gitlab.url,
- project.namespace.to_param,
- project.to_param,
- 'commit',
- commit.id
- ].join('/')
- )
- end
-
- context "with a author" do
- subject { push_data_from_service(project, user, oldrev, newrev, ref)[:commits].first[:author] }
-
- it { is_expected.to include(name: commit.author_name) }
- it { is_expected.to include(email: commit.author_email) }
- end
- end
- end
- end
-
- describe "Pipelines" do
- subject { execute_service(project, user, oldrev, newrev, ref) }
-
- before do
- stub_ci_pipeline_to_return_yaml_file
- end
-
- it "creates a new pipeline" do
- expect { subject }.to change { Ci::Pipeline.count }
- expect(Ci::Pipeline.last).to be_push
- end
- end
-
- describe "Push Event" do
- context "with an existing branch" do
- let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) }
- let(:event) { Event.find_by_action(Event::PUSHED) }
-
- it 'generates a push event with one commit' do
- expect(event).to be_an_instance_of(PushEvent)
- expect(event.project).to eq(project)
- expect(event.action).to eq(Event::PUSHED)
- expect(event.push_event_payload).to be_an_instance_of(PushEventPayload)
- expect(event.push_event_payload.commit_from).to eq(oldrev)
- expect(event.push_event_payload.commit_to).to eq(newrev)
- expect(event.push_event_payload.ref).to eq('master')
- expect(event.push_event_payload.commit_count).to eq(1)
- end
- end
-
- context "with a new branch" do
- let!(:new_branch_data) { push_data_from_service(project, user, Gitlab::Git::BLANK_SHA, newrev, ref) }
- let(:event) { Event.find_by_action(Event::PUSHED) }
-
- it 'generates a push event with more than one commit' do
- expect(event).to be_an_instance_of(PushEvent)
- expect(event.project).to eq(project)
- expect(event.action).to eq(Event::PUSHED)
- expect(event.push_event_payload).to be_an_instance_of(PushEventPayload)
- expect(event.push_event_payload.commit_from).to be_nil
- expect(event.push_event_payload.commit_to).to eq(newrev)
- expect(event.push_event_payload.ref).to eq('master')
- expect(event.push_event_payload.commit_count).to be > 1
- end
- end
-
- context "Updates merge requests" do
- it "when pushing a new branch for the first time" do
- expect(UpdateMergeRequestsWorker).to receive(:perform_async)
- .with(project.id, user.id, blankrev, 'newrev', ref)
- execute_service(project, user, blankrev, 'newrev', ref )
- end
- end
-
- describe 'system hooks' do
- let!(:push_data) { push_data_from_service(project, user, oldrev, newrev, ref) }
- let!(:system_hooks_service) { SystemHooksService.new }
-
- it "sends a system hook after pushing a branch" do
- allow(SystemHooksService).to receive(:new).and_return(system_hooks_service)
- allow(system_hooks_service).to receive(:execute_hooks)
-
- execute_service(project, user, oldrev, newrev, ref)
-
- expect(system_hooks_service).to have_received(:execute_hooks).with(push_data, :push_hooks)
- end
- end
- end
-
- describe "Updates git attributes" do
- context "for default branch" do
- it "calls the copy attributes method for the first push to the default branch" do
- expect(project.repository).to receive(:copy_gitattributes).with('master')
-
- execute_service(project, user, blankrev, 'newrev', ref)
- end
-
- it "calls the copy attributes method for changes to the default branch" do
- expect(project.repository).to receive(:copy_gitattributes).with(ref)
-
- execute_service(project, user, 'oldrev', 'newrev', ref)
- end
- end
-
- context "for non-default branch" do
- before do
- # Make sure the "default" branch is different
- allow(project).to receive(:default_branch).and_return('not-master')
- end
-
- it "does not call copy attributes method" do
- expect(project.repository).not_to receive(:copy_gitattributes)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
- end
- end
-
- describe "Webhooks" do
- context "execute webhooks" do
- it "when pushing a branch for the first time" do
- expect(project).to receive(:execute_hooks)
- 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::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
- stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_NONE)
-
- expect(project).to receive(:execute_hooks)
- expect(project.default_branch).to eq("master")
- execute_service(project, user, blankrev, 'newrev', ref)
- expect(project.protected_branches).to be_empty
- end
-
- it "when pushing a branch for the first time with default branch protection set to 'developers can push'" do
- stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
-
- expect(project).to receive(:execute_hooks)
- 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.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::MAINTAINER])
- end
-
- it "when pushing a branch for the first time with an existing branch permission configured" do
- stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_PUSH)
-
- create(:protected_branch, :no_one_can_push, :developers_can_merge, project: project, name: 'master')
- expect(project).to receive(:execute_hooks)
- expect(project.default_branch).to eq("master")
- expect_any_instance_of(ProtectedBranches::CreateService).not_to receive(:execute)
-
- execute_service(project, user, blankrev, 'newrev', ref)
-
- expect(project.protected_branches).not_to be_empty
- expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::NO_ACCESS])
- expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
- end
-
- it "when pushing a branch for the first time with default branch protection set to 'developers can merge'" do
- stub_application_setting(default_branch_protection: Gitlab::Access::PROTECTION_DEV_CAN_MERGE)
-
- expect(project).to receive(:execute_hooks)
- 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::MAINTAINER])
- expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
- end
-
- it "when pushing new commits to existing branch" do
- expect(project).to receive(:execute_hooks)
- execute_service(project, user, 'oldrev', 'newrev', ref)
- end
- end
- end
-
- describe "cross-reference notes" do
- let(:issue) { create :issue, project: project }
- let(:commit_author) { create :user }
- let(:commit) { project.commit }
-
- before do
- project.add_developer(commit_author)
- project.add_developer(user)
-
- allow(commit).to receive_messages(
- safe_message: "this commit \n mentions #{issue.to_reference}",
- references: [issue],
- author_name: commit_author.name,
- author_email: commit_author.email
- )
-
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
- .and_return(commit)
-
- allow(project.repository).to receive(:commits_between).and_return([commit])
- end
-
- it "creates a note if a pushed commit mentions an issue" do
- expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
-
- it "only creates a cross-reference note if one doesn't already exist" do
- SystemNoteService.cross_reference(issue, commit, user)
-
- expect(SystemNoteService).not_to receive(:cross_reference).with(issue, commit, commit_author)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
-
- it "defaults to the pushing user if the commit's author is not known" do
- allow(commit).to receive_messages(
- author_name: 'unknown name',
- author_email: 'unknown@email.com'
- )
- expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, user)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
-
- it "finds references in the first push to a non-default branch" do
- allow(project.repository).to receive(:commits_between).with(blankrev, newrev).and_return([])
- allow(project.repository).to receive(:commits_between).with("master", newrev).and_return([commit])
-
- expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author)
-
- execute_service(project, user, blankrev, newrev, 'refs/heads/other')
- end
- end
-
- describe "issue metrics" do
- let(:issue) { create :issue, project: project }
- let(:commit_author) { create :user }
- let(:commit) { project.commit }
- let(:commit_time) { Time.now }
-
- before do
- project.add_developer(commit_author)
- project.add_developer(user)
-
- allow(commit).to receive_messages(
- safe_message: "this commit \n mentions #{issue.to_reference}",
- references: [issue],
- author_name: commit_author.name,
- author_email: commit_author.email,
- committed_date: commit_time
- )
-
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
- .and_return(commit)
-
- allow(project.repository).to receive(:commits_between).and_return([commit])
- end
-
- context "while saving the 'first_mentioned_in_commit_at' metric for an issue" do
- it 'sets the metric for referenced issues' do
- execute_service(project, user, oldrev, newrev, ref)
-
- expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_like_time(commit_time)
- end
-
- it 'does not set the metric for non-referenced issues' do
- non_referenced_issue = create(:issue, project: project)
- execute_service(project, user, oldrev, newrev, ref)
-
- expect(non_referenced_issue.reload.metrics.first_mentioned_in_commit_at).to be_nil
- end
- end
- end
-
- describe "closing issues from pushed commits containing a closing reference" do
- let(:issue) { create :issue, project: project }
- let(:other_issue) { create :issue, project: project }
- let(:commit_author) { create :user }
- let(:closing_commit) { project.commit }
-
- before do
- allow(closing_commit).to receive_messages(
- issue_closing_regex: /^([Cc]loses|[Ff]ixes) #\d+/,
- safe_message: "this is some work.\n\ncloses ##{issue.iid}",
- author_name: commit_author.name,
- author_email: commit_author.email
- )
-
- allow(project.repository).to receive(:commits_between)
- .and_return([closing_commit])
-
- allow_any_instance_of(ProcessCommitWorker).to receive(:build_commit)
- .and_return(closing_commit)
-
- project.add_maintainer(commit_author)
- end
-
- context "to default branches" do
- it "closes issues" do
- execute_service(project, commit_author, oldrev, newrev, ref)
- expect(Issue.find(issue.id)).to be_closed
- end
-
- it "adds a note indicating that the issue is now closed" do
- expect(SystemNoteService).to receive(:change_status).with(issue, project, commit_author, "closed", closing_commit)
- execute_service(project, commit_author, oldrev, newrev, ref)
- end
-
- it "doesn't create additional cross-reference notes" do
- expect(SystemNoteService).not_to receive(:cross_reference)
- execute_service(project, commit_author, oldrev, newrev, ref)
- end
- end
-
- context "to non-default branches" do
- before do
- # Make sure the "default" branch is different
- allow(project).to receive(:default_branch).and_return('not-master')
- end
-
- it "creates cross-reference notes" do
- expect(SystemNoteService).to receive(:cross_reference).with(issue, closing_commit, commit_author)
- execute_service(project, user, oldrev, newrev, ref)
- end
-
- it "doesn't close issues" do
- execute_service(project, user, oldrev, newrev, ref)
- expect(Issue.find(issue.id)).to be_opened
- end
- end
-
- context "for jira issue tracker" do
- include JiraServiceHelper
-
- let(:jira_tracker) { project.create_jira_service if project.jira_service.nil? }
-
- before do
- # project.create_jira_service doesn't seem to invalidate the cache here
- project.has_external_issue_tracker = true
- jira_service_settings
- stub_jira_urls("JIRA-1")
-
- allow(closing_commit).to receive_messages({
- issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern),
- safe_message: message,
- author_name: commit_author.name,
- author_email: commit_author.email
- })
-
- allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])
-
- allow(project.repository).to receive_messages(commits_between: [closing_commit])
- end
-
- after do
- jira_tracker.destroy!
- end
-
- context "mentioning an issue" do
- let(:message) { "this is some work.\n\nrelated to JIRA-1" }
-
- it "initiates one api call to jira server to mention the issue" do
- execute_service(project, user, oldrev, newrev, ref)
-
- expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
- body: /mentioned this issue in/
- ).once
- end
- end
-
- context "closing an issue" do
- let(:message) { "this is some work.\n\ncloses JIRA-1" }
- let(:comment_body) do
- {
- body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.full_path}/commit/#{closing_commit.id}]."
- }.to_json
- end
-
- before do
- open_issue = JIRA::Resource::Issue.new(jira_tracker.client, attrs: { "id" => "JIRA-1" })
- closed_issue = open_issue.dup
- allow(open_issue).to receive(:resolution).and_return(false)
- allow(closed_issue).to receive(:resolution).and_return(true)
- allow(JIRA::Resource::Issue).to receive(:find).and_return(open_issue, closed_issue)
-
- allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return("JIRA-1")
- end
-
- context "using right markdown" do
- it "initiates one api call to jira server to close the issue" do
- execute_service(project, commit_author, oldrev, newrev, ref)
-
- expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once
- end
-
- it "initiates one api call to jira server to comment on the issue" do
- execute_service(project, commit_author, oldrev, newrev, ref)
-
- expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
- body: comment_body
- ).once
- end
- end
-
- context "using internal issue reference" do
- context 'when internal issues are disabled' do
- before do
- project.issues_enabled = false
- project.save!
- end
- let(:message) { "this is some work.\n\ncloses #1" }
-
- it "does not initiates one api call to jira server to close the issue" do
- execute_service(project, commit_author, oldrev, newrev, ref)
-
- expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1'))
- end
-
- it "does not initiates one api call to jira server to comment on the issue" do
- execute_service(project, commit_author, oldrev, newrev, ref)
-
- expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
- body: comment_body
- ).once
- end
- end
-
- context 'when internal issues are enabled' do
- let(:issue) { create(:issue, project: project) }
- let(:message) { "this is some work.\n\ncloses JIRA-1 \n\n closes #{issue.to_reference}" }
-
- it "initiates one api call to jira server to close the jira issue" do
- execute_service(project, commit_author, oldrev, newrev, ref)
-
- expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once
- end
-
- it "initiates one api call to jira server to comment on the jira issue" do
- execute_service(project, commit_author, oldrev, newrev, ref)
-
- expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
- body: comment_body
- ).once
- end
-
- it "closes the internal issue" do
- execute_service(project, commit_author, oldrev, newrev, ref)
- expect(issue.reload).to be_closed
- end
-
- it "adds a note indicating that the issue is now closed" do
- expect(SystemNoteService).to receive(:change_status)
- .with(issue, project, commit_author, "closed", closing_commit)
- execute_service(project, commit_author, oldrev, newrev, ref)
- end
- end
- end
- end
- end
- end
-
- describe "empty project" do
- let(:project) { create(:project_empty_repo) }
- let(:new_ref) { 'refs/heads/feature' }
-
- before do
- allow(project).to receive(:default_branch).and_return('feature')
- expect(project).to receive(:change_head) { 'feature'}
- end
-
- it 'push to first branch updates HEAD' do
- execute_service(project, user, blankrev, newrev, new_ref)
- end
- end
-
- describe "housekeeping" do
- let(:housekeeping) { Projects::HousekeepingService.new(project) }
-
- before do
- # Flush any raw key-value data stored by the housekeeping code.
- Gitlab::Redis::Cache.with { |conn| conn.flushall }
- Gitlab::Redis::Queues.with { |conn| conn.flushall }
- Gitlab::Redis::SharedState.with { |conn| conn.flushall }
-
- allow(Projects::HousekeepingService).to receive(:new).and_return(housekeeping)
- end
-
- after do
- Gitlab::Redis::Cache.with { |conn| conn.flushall }
- Gitlab::Redis::Queues.with { |conn| conn.flushall }
- Gitlab::Redis::SharedState.with { |conn| conn.flushall }
- end
-
- it 'does not perform housekeeping when not needed' do
- expect(housekeeping).not_to receive(:execute)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
-
- context 'when housekeeping is needed' do
- before do
- allow(housekeeping).to receive(:needed?).and_return(true)
- end
-
- it 'performs housekeeping' do
- expect(housekeeping).to receive(:execute)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
-
- it 'does not raise an exception' do
- allow(housekeeping).to receive(:try_obtain_lease).and_return(false)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
- end
-
- it 'increments the push counter' do
- expect(housekeeping).to receive(:increment!)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
- end
-
- describe '#update_caches' do
- let(:service) do
- described_class.new(project,
- user,
- oldrev: oldrev,
- newrev: newrev,
- ref: ref)
- end
-
- context 'on the default branch' do
- before do
- allow(service).to receive(:default_branch?).and_return(true)
- end
-
- it 'flushes the caches of any special files that have been changed' do
- commit = double(:commit)
- diff = double(:diff, new_path: 'README.md')
-
- expect(commit).to receive(:raw_deltas)
- .and_return([diff])
-
- service.push_commits = [commit]
-
- expect(ProjectCacheWorker).to receive(:perform_async)
- .with(project.id, %i(readme), %i(commit_count repository_size))
-
- service.update_caches
- end
- end
-
- context 'on a non-default branch' do
- before do
- allow(service).to receive(:default_branch?).and_return(false)
- end
-
- it 'does not flush any conditional caches' do
- expect(ProjectCacheWorker).to receive(:perform_async)
- .with(project.id, [], %i(commit_count repository_size))
- .and_call_original
-
- service.update_caches
- end
- end
- end
-
- describe '#process_commit_messages' do
- let(:service) do
- described_class.new(project,
- user,
- oldrev: oldrev,
- newrev: newrev,
- ref: ref)
- end
-
- it 'only schedules a limited number of commits' do
- service.push_commits = Array.new(1000, double(:commit, to_hash: {}, matches_cross_reference_regex?: true))
-
- expect(ProcessCommitWorker).to receive(:perform_async).exactly(100).times
-
- service.process_commit_messages
- end
-
- it "skips commits which don't include cross-references" do
- service.push_commits = [double(:commit, to_hash: {}, matches_cross_reference_regex?: false)]
-
- expect(ProcessCommitWorker).not_to receive(:perform_async)
-
- service.process_commit_messages
- end
- end
-
- describe '#update_signatures' do
- let(:service) do
- described_class.new(
- project,
- user,
- oldrev: oldrev,
- newrev: newrev,
- ref: 'refs/heads/master'
- )
- end
-
- context 'when the commit has a signature' do
- context 'when the signature is already cached' do
- before do
- create(:gpg_signature, commit_sha: sample_commit.id)
- end
-
- it 'does not queue a CreateGpgSignatureWorker' do
- expect(CreateGpgSignatureWorker).not_to receive(:perform_async)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
- end
-
- 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)
-
- 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
- end
- end
-
- context 'when the commit does not have a signature' do
- before do
- allow(Gitlab::Git::Commit).to receive(:shas_with_signatures).with(project.repository, [sample_commit.id]).and_return([])
- end
-
- it 'does not queue a CreateGpgSignatureWorker' do
- expect(CreateGpgSignatureWorker).not_to receive(:perform_async).with(sample_commit.id, project.id)
-
- execute_service(project, user, oldrev, newrev, ref)
- end
- end
- end
-
- def execute_service(project, user, oldrev, newrev, ref)
- service = described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref)
- service.execute
- service
- end
-
- def push_data_from_service(project, user, oldrev, newrev, ref)
- execute_service(project, user, oldrev, newrev, ref).push_data
- end
-end
diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb
deleted file mode 100644
index 2699f6e7bcd..00000000000
--- a/spec/services/git_tag_push_service_spec.rb
+++ /dev/null
@@ -1,196 +0,0 @@
-require 'spec_helper'
-
-describe GitTagPushService do
- include RepoHelpers
- include GitHelpers
-
- let(:user) { create(:user) }
- let(:project) { create(:project, :repository) }
- let(:service) { described_class.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) }
-
- let(:oldrev) { Gitlab::Git::BLANK_SHA }
- let(:newrev) { "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" } # gitlab-test: git rev-parse refs/tags/v1.1.0
- let(:ref) { 'refs/tags/v1.1.0' }
-
- describe "Push tags" do
- subject do
- service.execute
- service
- end
-
- it 'flushes general cached data' do
- expect(project.repository).to receive(:before_push_tag)
-
- subject
- end
-
- it 'flushes the tags cache' do
- expect(project.repository).to receive(:expire_tags_cache)
-
- subject
- end
- end
-
- describe "Pipelines" do
- subject { service.execute }
-
- before do
- stub_ci_pipeline_to_return_yaml_file
- project.add_developer(user)
- end
-
- it "creates a new pipeline" do
- expect { subject }.to change { Ci::Pipeline.count }
- expect(Ci::Pipeline.last).to be_push
- end
- end
-
- describe "Git Tag Push Data" do
- subject { @push_data }
- let(:tag) { project.repository.find_tag(tag_name) }
- let(:commit) { tag.dereferenced_target }
-
- context 'annotated tag' do
- let(:tag_name) { Gitlab::Git.ref_name(ref) }
-
- before do
- service.execute
- @push_data = service.push_data
- end
-
- it { is_expected.to include(object_kind: 'tag_push') }
- it { is_expected.to include(ref: ref) }
- it { is_expected.to include(before: oldrev) }
- it { is_expected.to include(after: newrev) }
- it { is_expected.to include(message: tag.message) }
- it { is_expected.to include(user_id: user.id) }
- it { is_expected.to include(user_name: user.name) }
- it { is_expected.to include(project_id: project.id) }
-
- context "with repository data" do
- subject { @push_data[:repository] }
-
- it { is_expected.to include(name: project.name) }
- it { is_expected.to include(url: project.url_to_repo) }
- it { is_expected.to include(description: project.description) }
- it { is_expected.to include(homepage: project.web_url) }
- end
-
- context "with commits" do
- subject { @push_data[:commits] }
-
- it { is_expected.to be_an(Array) }
- it 'has 1 element' do
- expect(subject.size).to eq(1)
- end
-
- context "the commit" do
- subject { @push_data[:commits].first }
-
- it { is_expected.to include(id: commit.id) }
- it { is_expected.to include(message: commit.safe_message) }
- it { is_expected.to include(timestamp: commit.date.xmlschema) }
- it do
- is_expected.to include(
- url: [
- Gitlab.config.gitlab.url,
- project.namespace.to_param,
- project.to_param,
- 'commit',
- commit.id
- ].join('/')
- )
- end
-
- context "with a author" do
- subject { @push_data[:commits].first[:author] }
-
- it { is_expected.to include(name: commit.author_name) }
- it { is_expected.to include(email: commit.author_email) }
- end
- end
- end
- end
-
- context 'lightweight tag' do
- let(:tag_name) { 'light-tag' }
- let(:newrev) { '5937ac0a7beb003549fc5fd26fc247adbce4a52e' }
- let(:ref) { "refs/tags/light-tag" }
-
- before do
- # Create the lightweight tag
- rugged_repo(project.repository).tags.create(tag_name, newrev)
-
- # Clear tag list cache
- project.repository.expire_tags_cache
-
- service.execute
- @push_data = service.push_data
- end
-
- it { is_expected.to include(object_kind: 'tag_push') }
- it { is_expected.to include(ref: ref) }
- it { is_expected.to include(before: oldrev) }
- it { is_expected.to include(after: newrev) }
- it { is_expected.to include(message: tag.message) }
- it { is_expected.to include(user_id: user.id) }
- it { is_expected.to include(user_name: user.name) }
- it { is_expected.to include(project_id: project.id) }
-
- context "with repository data" do
- subject { @push_data[:repository] }
-
- it { is_expected.to include(name: project.name) }
- it { is_expected.to include(url: project.url_to_repo) }
- it { is_expected.to include(description: project.description) }
- it { is_expected.to include(homepage: project.web_url) }
- end
-
- context "with commits" do
- subject { @push_data[:commits] }
-
- it { is_expected.to be_an(Array) }
- it 'has 1 element' do
- expect(subject.size).to eq(1)
- end
-
- context "the commit" do
- subject { @push_data[:commits].first }
-
- it { is_expected.to include(id: commit.id) }
- it { is_expected.to include(message: commit.safe_message) }
- it { is_expected.to include(timestamp: commit.date.xmlschema) }
- it do
- is_expected.to include(
- url: [
- Gitlab.config.gitlab.url,
- project.namespace.to_param,
- project.to_param,
- 'commit',
- commit.id
- ].join('/')
- )
- end
-
- context "with a author" do
- subject { @push_data[:commits].first[:author] }
-
- it { is_expected.to include(name: commit.author_name) }
- it { is_expected.to include(email: commit.author_email) }
- end
- end
- end
- end
- end
-
- describe "Webhooks" do
- context "execute webhooks" do
- let(:service) { described_class.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') }
-
- it "when pushing tags" do
- expect(project).to receive(:execute_hooks)
- service.execute
- end
- end
- end
-end
diff --git a/spec/services/groups/auto_devops_service_spec.rb b/spec/services/groups/auto_devops_service_spec.rb
new file mode 100644
index 00000000000..7f8ab517cef
--- /dev/null
+++ b/spec/services/groups/auto_devops_service_spec.rb
@@ -0,0 +1,62 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe Groups::AutoDevopsService, '#execute' do
+ set(:group) { create(:group) }
+ set(:user) { create(:user) }
+ let(:group_params) { { auto_devops_enabled: '0' } }
+ let(:service) { described_class.new(group, user, group_params) }
+
+ context 'when user does not have enough privileges' do
+ it 'raises exception' do
+ group.add_developer(user)
+
+ expect do
+ service.execute
+ end.to raise_exception(Gitlab::Access::AccessDeniedError)
+ end
+ end
+
+ context 'when user has enough privileges' do
+ before do
+ group.add_owner(user)
+ end
+
+ it 'updates group auto devops enabled accordingly' do
+ service.execute
+
+ expect(group.auto_devops_enabled).to eq(false)
+ end
+
+ context 'when group has projects' do
+ it 'reflects changes on projects' do
+ project_1 = create(:project, namespace: group)
+
+ service.execute
+
+ expect(project_1).not_to have_auto_devops_implicitly_enabled
+ end
+ end
+
+ context 'when group has subgroups' do
+ it 'reflects changes on subgroups' do
+ subgroup_1 = create(:group, parent: group)
+
+ service.execute
+
+ expect(subgroup_1.auto_devops_enabled?).to eq(false)
+ end
+
+ context 'when subgroups have projects', :nested_groups do
+ it 'reflects changes on projects' do
+ subgroup_1 = create(:group, parent: group)
+ project_1 = create(:project, namespace: subgroup_1)
+
+ service.execute
+
+ expect(project_1).not_to have_auto_devops_implicitly_enabled
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/groups/transfer_service_spec.rb b/spec/services/groups/transfer_service_spec.rb
index 6b48c993c57..79d504b9b45 100644
--- a/spec/services/groups/transfer_service_spec.rb
+++ b/spec/services/groups/transfer_service_spec.rb
@@ -410,5 +410,34 @@ describe Groups::TransferService, :postgresql do
end
end
end
+
+ context 'when transferring a subgroup into root group' do
+ let(:group) { create(:group, :public, :nested) }
+ let(:subgroup) { create(:group, :public, parent: group) }
+ let(:transfer_service) { described_class.new(subgroup, user) }
+
+ it 'ensures there is still an owner for the transferred group' do
+ expect(subgroup.owners).to be_empty
+
+ transfer_service.execute(nil)
+ subgroup.reload
+
+ expect(subgroup.owners).to match_array(user)
+ end
+
+ context 'when group has explicit owner' do
+ let(:another_owner) { create(:user) }
+ let!(:another_member) { create(:group_member, :owner, group: subgroup, user: another_owner) }
+
+ it 'does not add additional owner' do
+ expect(subgroup.owners).to match_array(another_owner)
+
+ transfer_service.execute(nil)
+ subgroup.reload
+
+ expect(subgroup.owners).to match_array(another_owner)
+ end
+ end
+ end
end
end
diff --git a/spec/services/issuable/common_system_notes_service_spec.rb b/spec/services/issuable/common_system_notes_service_spec.rb
index fa5d5ebac5c..0edc9016c96 100644
--- a/spec/services/issuable/common_system_notes_service_spec.rb
+++ b/spec/services/issuable/common_system_notes_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe Issuable::CommonSystemNotesService do
let(:user) { create(:user) }
let(:project) { create(:project) }
- let(:issuable) { create(:issue) }
+ let(:issuable) { create(:issue, project: project) }
context 'on issuable update' do
it_behaves_like 'system note creation', { title: 'New title' }, 'changed title'
@@ -70,7 +70,7 @@ describe Issuable::CommonSystemNotesService do
end
context 'on issuable create' do
- let(:issuable) { build(:issue) }
+ let(:issuable) { build(:issue, project: project) }
subject { described_class.new(project, user).execute(issuable, old_labels: [], is_update: false) }
diff --git a/spec/services/issues/build_service_spec.rb b/spec/services/issues/build_service_spec.rb
index 248e7d5a389..86e58fe06b9 100644
--- a/spec/services/issues/build_service_spec.rb
+++ b/spec/services/issues/build_service_spec.rb
@@ -8,29 +8,29 @@ describe Issues::BuildService do
project.add_developer(user)
end
+ def build_issue(issue_params = {})
+ described_class.new(project, user, issue_params).execute
+ end
+
context 'for a single discussion' do
describe '#execute' do
let(:merge_request) { create(:merge_request, title: "Hello world", source_project: project) }
let(:discussion) { create(:diff_note_on_merge_request, project: project, noteable: merge_request, note: "Almost done").to_discussion }
- let(:service) { described_class.new(project, user, merge_request_to_resolve_discussions_of: merge_request.iid, discussion_to_resolve: discussion.id) }
- it 'references the noteable title in the issue title' do
- issue = service.execute
+ subject { build_issue(merge_request_to_resolve_discussions_of: merge_request.iid, discussion_to_resolve: discussion.id) }
- expect(issue.title).to include('Hello world')
+ it 'references the noteable title in the issue title' do
+ expect(subject.title).to include('Hello world')
end
it 'adds the note content to the description' do
- issue = service.execute
-
- expect(issue.description).to include('Almost done')
+ expect(subject.description).to include('Almost done')
end
end
end
context 'for discussions in a merge request' do
let(:merge_request) { create(:merge_request_with_diff_notes, source_project: project) }
- let(:issue) { described_class.new(project, user, merge_request_to_resolve_discussions_of: merge_request.iid).execute }
describe '#items_for_discussions' do
it 'has an item for each discussion' do
@@ -66,28 +66,30 @@ describe Issues::BuildService do
end
describe '#execute' do
- it 'has the merge request reference in the title' do
- expect(issue.title).to include(merge_request.title)
- end
+ let(:base_params) { { merge_request_to_resolve_discussions_of: merge_request.iid } }
- it 'has the reference of the merge request in the description' do
- expect(issue.description).to include(merge_request.to_reference)
+ context 'without additional params' do
+ subject { build_issue(base_params) }
+
+ it 'has the merge request reference in the title' do
+ expect(subject.title).to include(merge_request.title)
+ end
+
+ it 'has the reference of the merge request in the description' do
+ expect(subject.description).to include(merge_request.to_reference)
+ end
end
- it 'does not assign title when a title was given' do
- issue = described_class.new(project, user,
- merge_request_to_resolve_discussions_of: merge_request,
- title: 'What an issue').execute
+ it 'uses provided title if title param given' do
+ issue = build_issue(base_params.merge(title: 'What an issue'))
expect(issue.title).to eq('What an issue')
end
- it 'does not assign description when a description was given' do
- issue = described_class.new(project, user,
- merge_request_to_resolve_discussions_of: merge_request,
- description: 'Fix at your earliest conveignance').execute
+ it 'uses provided description if description param given' do
+ issue = build_issue(base_params.merge(description: 'Fix at your earliest convenience'))
- expect(issue.description).to eq('Fix at your earliest conveignance')
+ expect(issue.description).to eq('Fix at your earliest convenience')
end
describe 'with multiple discussions' do
@@ -96,20 +98,20 @@ describe Issues::BuildService do
it 'mentions all the authors in the description' do
authors = merge_request.resolvable_discussions.map(&:author)
- expect(issue.description).to include(*authors.map(&:to_reference))
+ expect(build_issue(base_params).description).to include(*authors.map(&:to_reference))
end
it 'has a link for each unresolved discussion in the description' do
notes = merge_request.resolvable_discussions.map(&:first_note)
links = notes.map { |note| Gitlab::UrlBuilder.build(note) }
- expect(issue.description).to include(*links)
+ expect(build_issue(base_params).description).to include(*links)
end
it 'mentions additional notes' do
create_list(:diff_note_on_merge_request, 2, noteable: merge_request, project: merge_request.target_project, in_reply_to: diff_note)
- expect(issue.description).to include('(+2 comments)')
+ expect(build_issue(base_params).description).to include('(+2 comments)')
end
end
end
@@ -120,7 +122,7 @@ describe Issues::BuildService do
describe '#execute' do
it 'mentions the merge request in the description' do
- issue = described_class.new(project, user, merge_request_to_resolve_discussions_of: merge_request.iid).execute
+ issue = build_issue(merge_request_to_resolve_discussions_of: merge_request.iid)
expect(issue.description).to include("Review the conversation in #{merge_request.to_reference}")
end
@@ -128,20 +130,18 @@ describe Issues::BuildService do
end
describe '#execute' do
- let(:milestone) { create(:milestone, project: project) }
-
it 'builds a new issues with given params' do
- issue = described_class.new(
- project,
- user,
- title: 'Issue #1',
- description: 'Issue description',
- milestone_id: milestone.id
- ).execute
-
- expect(issue.title).to eq('Issue #1')
- expect(issue.description).to eq('Issue description')
+ milestone = create(:milestone, project: project)
+ issue = build_issue(milestone_id: milestone.id)
+
expect(issue.milestone).to eq(milestone)
end
+
+ it 'sets milestone to nil if it is not available for the project' do
+ milestone = create(:milestone, project: create(:project))
+ issue = build_issue(milestone_id: milestone.id)
+
+ expect(issue.milestone).to be_nil
+ end
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 931e47d3a77..f1684209729 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -356,7 +356,7 @@ describe Issues::UpdateService, :mailer do
it_behaves_like 'system notes for milestones'
it 'sends notifications for subscribers of changed milestone' do
- issue.milestone = create(:milestone)
+ issue.milestone = create(:milestone, project: project)
issue.save
@@ -380,7 +380,7 @@ describe Issues::UpdateService, :mailer do
end
it 'marks todos as done' do
- update_issue(milestone: create(:milestone))
+ update_issue(milestone: create(:milestone, project: project))
expect(todo.reload.done?).to eq true
end
@@ -389,7 +389,7 @@ describe Issues::UpdateService, :mailer do
it 'sends notifications for subscribers of changed milestone' do
perform_enqueued_jobs do
- update_issue(milestone: create(:milestone))
+ update_issue(milestone: create(:milestone, project: project))
end
should_email(subscriber)
diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb
index 536d0d345a4..057e8137a4e 100644
--- a/spec/services/merge_requests/build_service_spec.rb
+++ b/spec/services/merge_requests/build_service_spec.rb
@@ -229,6 +229,15 @@ describe MergeRequests::BuildService do
end
end
end
+
+ context 'when a milestone is from another project' do
+ let(:milestone) { create(:milestone, project: create(:project)) }
+ let(:milestone_id) { milestone.id }
+
+ it 'sets milestone to nil' do
+ expect(merge_request.milestone).to be_nil
+ 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 b46aa65818d..a04a4d5fc36 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -194,7 +194,7 @@ describe MergeRequests::CreateService do
merge_request.reload
expect(merge_request.merge_request_pipelines.count).to eq(1)
- expect(merge_request.actual_head_pipeline).to be_merge_request
+ expect(merge_request.actual_head_pipeline).to be_merge_request_event
end
context 'when there are no commits between source branch and target branch' do
@@ -226,7 +226,7 @@ describe MergeRequests::CreateService do
end
it 'sets the latest merge request pipeline as the head pipeline' do
- expect(merge_request.actual_head_pipeline).to be_merge_request
+ expect(merge_request.actual_head_pipeline).to be_merge_request_event
end
end
diff --git a/spec/services/merge_requests/ff_merge_service_spec.rb b/spec/services/merge_requests/ff_merge_service_spec.rb
index fe673de46aa..1430e12a07e 100644
--- a/spec/services/merge_requests/ff_merge_service_spec.rb
+++ b/spec/services/merge_requests/ff_merge_service_spec.rb
@@ -72,7 +72,7 @@ describe MergeRequests::FfMergeService do
it 'logs and saves error if there is an PreReceiveError exception' do
error_message = 'error message'
- allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, error_message)
+ allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, "GitLab: #{error_message}")
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 04a62aa454d..887ec17171e 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -224,10 +224,22 @@ describe MergeRequests::MergeService do
expect(Rails.logger).to have_received(:error).with(a_string_matching(error_message))
end
+ it 'logs and saves error if user is not authorized' do
+ unauthorized_user = create(:user)
+ project.add_reporter(unauthorized_user)
+
+ service = described_class.new(project, unauthorized_user)
+
+ service.execute(merge_request)
+
+ expect(merge_request.merge_error)
+ .to eq('You are not allowed to merge this merge request')
+ end
+
it 'logs and saves error if there is an PreReceiveError exception' do
error_message = 'error message'
- allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, error_message)
+ allow(service).to receive(:repository).and_raise(Gitlab::Git::PreReceiveError, "GitLab: #{error_message}")
allow(service).to receive(:execute_hooks)
service.execute(merge_request)
diff --git a/spec/services/merge_requests/merge_to_ref_service_spec.rb b/spec/services/merge_requests/merge_to_ref_service_spec.rb
new file mode 100644
index 00000000000..fabca8f6b4a
--- /dev/null
+++ b/spec/services/merge_requests/merge_to_ref_service_spec.rb
@@ -0,0 +1,195 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe MergeRequests::MergeToRefService do
+ shared_examples_for 'MergeService for target ref' do
+ it 'target_ref has the same state of target branch' do
+ repo = merge_request.target_project.repository
+
+ process_merge_to_ref
+ merge_service.execute(merge_request)
+
+ ref_commits = repo.commits(merge_request.merge_ref_path, limit: 3)
+ target_branch_commits = repo.commits(merge_request.target_branch, limit: 3)
+
+ ref_commits.zip(target_branch_commits).each do |ref_commit, target_branch_commit|
+ expect(ref_commit.parents).to eq(target_branch_commit.parents)
+ end
+ end
+ end
+
+ shared_examples_for 'successfully merges to ref with merge method' do
+ it 'writes commit to merge ref' do
+ repository = project.repository
+ target_ref = merge_request.merge_ref_path
+
+ expect(repository.ref_exists?(target_ref)).to be(false)
+
+ result = service.execute(merge_request)
+
+ ref_head = repository.commit(target_ref)
+
+ expect(result[:status]).to eq(:success)
+ expect(result[:commit_id]).to be_present
+ expect(result[:source_id]).to eq(merge_request.source_branch_sha)
+ expect(result[:target_id]).to eq(merge_request.target_branch_sha)
+ expect(repository.ref_exists?(target_ref)).to be(true)
+ expect(ref_head.id).to eq(result[:commit_id])
+ end
+ end
+
+ shared_examples_for 'successfully evaluates pre-condition checks' do
+ it 'returns error when feature is disabled' do
+ stub_feature_flags(merge_to_tmp_merge_ref_path: false)
+
+ result = service.execute(merge_request)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq('Feature is not enabled')
+ end
+
+ it 'returns an error when the failing to process the merge' do
+ allow(project.repository).to receive(:merge_to_ref).and_return(nil)
+
+ result = service.execute(merge_request)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq('Conflicts detected during merge')
+ end
+
+ it 'does not send any mail' do
+ expect { process_merge_to_ref }.not_to change { ActionMailer::Base.deliveries.count }
+ end
+
+ it 'does not change the MR state' do
+ expect { process_merge_to_ref }.not_to change { merge_request.state }
+ end
+
+ it 'does not create notes' do
+ expect { process_merge_to_ref }.not_to change { merge_request.notes.count }
+ end
+
+ it 'does not delete the source branch' do
+ expect(DeleteBranchService).not_to receive(:new)
+
+ process_merge_to_ref
+ end
+ end
+
+ set(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request, :simple) }
+ let(:project) { merge_request.project }
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ describe '#execute' do
+ let(:service) do
+ described_class.new(project, user, commit_message: 'Awesome message',
+ should_remove_source_branch: true)
+ end
+
+ def process_merge_to_ref
+ perform_enqueued_jobs do
+ service.execute(merge_request)
+ end
+ end
+
+ it_behaves_like 'successfully merges to ref with merge method'
+ it_behaves_like 'successfully evaluates pre-condition checks'
+
+ context 'commit history comparison with regular MergeService' do
+ let(:merge_ref_service) do
+ described_class.new(project, user, {})
+ end
+
+ let(:merge_service) do
+ MergeRequests::MergeService.new(project, user, {})
+ end
+
+ context 'when merge commit' do
+ it_behaves_like 'MergeService for target ref'
+ end
+
+ context 'when merge commit with squash' do
+ before do
+ merge_request.update!(squash: true, source_branch: 'master', target_branch: 'feature')
+ end
+
+ it_behaves_like 'MergeService for target ref'
+ end
+ end
+
+ context 'merge pre-condition checks' do
+ before do
+ merge_request.project.update!(merge_method: merge_method)
+ end
+
+ context 'when semi-linear merge method' do
+ let(:merge_method) { :rebase_merge }
+
+ it_behaves_like 'successfully merges to ref with merge method'
+ it_behaves_like 'successfully evaluates pre-condition checks'
+ end
+
+ context 'when fast-forward merge method' do
+ let(:merge_method) { :ff }
+
+ it_behaves_like 'successfully merges to ref with merge method'
+ it_behaves_like 'successfully evaluates pre-condition checks'
+ end
+
+ context 'when MR is not mergeable to ref' do
+ let(:merge_method) { :merge }
+
+ it 'returns error' do
+ allow(merge_request).to receive(:mergeable_to_ref?) { false }
+
+ error_message = "Merge request is not mergeable to #{merge_request.merge_ref_path}"
+
+ result = service.execute(merge_request)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq(error_message)
+ end
+ end
+ end
+
+ context 'does not close related todos' do
+ let(:merge_request) { create(:merge_request, assignee: user, author: user) }
+ let(:project) { merge_request.project }
+ let!(:todo) do
+ create(:todo, :assigned,
+ project: project,
+ author: user,
+ user: user,
+ target: merge_request)
+ end
+
+ before do
+ allow(service).to receive(:execute_hooks)
+
+ perform_enqueued_jobs do
+ service.execute(merge_request)
+ todo.reload
+ end
+ end
+
+ it { expect(todo).not_to be_done }
+ end
+
+ it 'returns error when user has no authorization to admin the merge request' do
+ unauthorized_user = create(:user)
+ project.add_reporter(unauthorized_user)
+
+ service = described_class.new(project, unauthorized_user)
+
+ result = service.execute(merge_request)
+
+ expect(result[:status]).to eq(:error)
+ expect(result[:message]).to eq('You are not allowed to merge to this ref')
+ end
+ end
+end
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 9e9dc5a576c..6c8ff163692 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -97,6 +97,15 @@ describe MergeRequests::RefreshService do
}
end
+ it 'outdates MR suggestions' do
+ expect_next_instance_of(Suggestions::OutdateService) do |service|
+ expect(service).to receive(:execute).with(@merge_request).and_call_original
+ expect(service).to receive(:execute).with(@another_merge_request).and_call_original
+ end
+
+ refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
+ end
+
context 'when source branch ref does not exists' do
before do
DeleteBranchService.new(@project, @user).execute(@merge_request.source_branch)
@@ -173,12 +182,12 @@ describe MergeRequests::RefreshService do
it 'sets the latest merge request pipeline as a head pipeline' do
@merge_request.reload
- expect(@merge_request.actual_head_pipeline).to be_merge_request
+ expect(@merge_request.actual_head_pipeline).to be_merge_request_event
end
it 'returns pipelines in correct order' do
@merge_request.reload
- expect(@merge_request.all_pipelines.first).to be_merge_request
+ expect(@merge_request.all_pipelines.first).to be_merge_request_event
expect(@merge_request.all_pipelines.second).to be_push
end
end
@@ -329,14 +338,16 @@ describe MergeRequests::RefreshService do
context 'push to fork repo source branch' do
let(:refresh_service) { service.new(@fork_project, @user) }
- context 'open fork merge request' do
- before do
- allow(refresh_service).to receive(:execute_hooks)
- refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
- reload_mrs
- end
+ def refresh
+ allow(refresh_service).to receive(:execute_hooks)
+ refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
+ reload_mrs
+ end
+ context 'open fork merge request' do
it 'executes hooks with update action' do
+ refresh
+
expect(refresh_service).to have_received(:execute_hooks)
.with(@fork_merge_request, 'update', old_rev: @oldrev)
@@ -347,21 +358,30 @@ describe MergeRequests::RefreshService do
expect(@build_failed_todo).to be_pending
expect(@fork_build_failed_todo).to be_pending
end
+
+ it 'outdates opened forked MR suggestions' do
+ expect_next_instance_of(Suggestions::OutdateService) do |service|
+ expect(service).to receive(:execute).with(@fork_merge_request).and_call_original
+ end
+
+ refresh
+ end
end
context 'closed fork merge request' do
before do
@fork_merge_request.close!
- allow(refresh_service).to receive(:execute_hooks)
- refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
- reload_mrs
end
it 'do not execute hooks with update action' do
+ refresh
+
expect(refresh_service).not_to have_received(:execute_hooks)
end
it 'updates merge request to closed state' do
+ refresh
+
expect(@merge_request.notes).to be_empty
expect(@merge_request).to be_open
expect(@fork_merge_request.notes).to be_empty
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 20580bf14b9..8e367db031c 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -328,7 +328,7 @@ describe MergeRequests::UpdateService, :mailer do
it_behaves_like 'system notes for milestones'
it 'sends notifications for subscribers of changed milestone' do
- merge_request.milestone = create(:milestone)
+ merge_request.milestone = create(:milestone, project: project)
merge_request.save
@@ -352,7 +352,7 @@ describe MergeRequests::UpdateService, :mailer do
end
it 'marks pending todos as done' do
- update_merge_request({ milestone: create(:milestone) })
+ update_merge_request({ milestone: create(:milestone, project: project) })
expect(pending_todo.reload).to be_done
end
@@ -361,7 +361,7 @@ describe MergeRequests::UpdateService, :mailer do
it 'sends notifications for subscribers of changed milestone' do
perform_enqueued_jobs do
- update_merge_request(milestone: create(:milestone))
+ update_merge_request(milestone: create(:milestone, project: project))
end
should_email(subscriber)
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index 1645b67c329..8d8e81173ff 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -220,6 +220,19 @@ describe Notes::CreateService do
expect(note.note).to eq "HELLO\nWORLD"
end
end
+
+ context 'when note only have commands' do
+ it 'adds commands applied message to note errors' do
+ note_text = %(/close)
+ 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
+
+ expect(note.errors[:commands_only]).to be_present
+ end
+ end
end
context 'as a user who cannot update the target' do
diff --git a/spec/services/notes/quick_actions_service_spec.rb b/spec/services/notes/quick_actions_service_spec.rb
index 14d62763a5b..7d2b6d5b8a7 100644
--- a/spec/services/notes/quick_actions_service_spec.rb
+++ b/spec/services/notes/quick_actions_service_spec.rb
@@ -28,8 +28,8 @@ describe Notes::QuickActionsService do
end
it 'closes noteable, sets labels, assigns, and sets milestone to noteable, and leave no note' do
- content, command_params = service.extract_commands(note)
- service.execute(command_params, note)
+ content, update_params = service.execute(note)
+ service.apply_updates(update_params, note)
expect(content).to eq ''
expect(note.noteable).to be_closed
@@ -47,8 +47,8 @@ describe Notes::QuickActionsService do
let(:note_text) { '/reopen' }
it 'opens the noteable, and leave no note' do
- content, command_params = service.extract_commands(note)
- service.execute(command_params, note)
+ content, update_params = service.execute(note)
+ service.apply_updates(update_params, note)
expect(content).to eq ''
expect(note.noteable).to be_open
@@ -59,8 +59,8 @@ describe Notes::QuickActionsService do
let(:note_text) { '/spend 1h' }
it 'updates the spent time on the noteable' do
- content, command_params = service.extract_commands(note)
- service.execute(command_params, note)
+ content, update_params = service.execute(note)
+ service.apply_updates(update_params, note)
expect(content).to eq ''
expect(note.noteable.time_spent).to eq(3600)
@@ -75,8 +75,8 @@ describe Notes::QuickActionsService do
end
it 'closes noteable, sets labels, assigns, and sets milestone to noteable' do
- content, command_params = service.extract_commands(note)
- service.execute(command_params, note)
+ content, update_params = service.execute(note)
+ service.apply_updates(update_params, note)
expect(content).to eq "HELLO\nWORLD"
expect(note.noteable).to be_closed
@@ -94,8 +94,8 @@ describe Notes::QuickActionsService do
let(:note_text) { "HELLO\n/reopen\nWORLD" }
it 'opens the noteable' do
- content, command_params = service.extract_commands(note)
- service.execute(command_params, note)
+ content, update_params = service.execute(note)
+ service.apply_updates(update_params, note)
expect(content).to eq "HELLO\nWORLD"
expect(note.noteable).to be_open
@@ -190,8 +190,8 @@ describe Notes::QuickActionsService do
end
it 'adds only one assignee from the list' do
- _, command_params = service.extract_commands(note)
- service.execute(command_params, note)
+ _, update_params = service.execute(note)
+ service.apply_updates(update_params, note)
expect(note.noteable.assignees.count).to eq(1)
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 6a5a6989607..9ba4a11104a 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -177,7 +177,7 @@ describe NotificationService, :mailer do
end
end
- context 'when recieving a non-existent method' do
+ context 'when receiving a non-existent method' do
it 'raises NoMethodError' do
expect { async.foo(key) }.to raise_error(NoMethodError)
end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index d1b110b9806..e8418b09dc2 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -276,6 +276,7 @@ describe Projects::CreateService, '#execute' do
before do
group.add_owner(user)
+ stub_feature_flags(ci_preparing_state: false)
expect(Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService).to receive(:namespace_creator).and_return(service_account_creator)
expect(Clusters::Gcp::Kubernetes::FetchKubernetesTokenService).to receive(:new).and_return(secrets_fetcher)
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index dfbdfa2ab69..d3a8ee46f85 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -128,10 +128,8 @@ describe Projects::DestroyService do
it 'keeps project team intact upon an error' do
perform_enqueued_jobs do
- begin
- destroy_project(project, user, {})
- rescue ::Redis::CannotConnectError
- end
+ destroy_project(project, user, {})
+ rescue ::Redis::CannotConnectError
end
expect(project.team.members.count).to eq 2
diff --git a/spec/services/projects/fetch_statistics_increment_service_spec.rb b/spec/services/projects/fetch_statistics_increment_service_spec.rb
new file mode 100644
index 00000000000..fcfb138aad6
--- /dev/null
+++ b/spec/services/projects/fetch_statistics_increment_service_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+module Projects
+ describe FetchStatisticsIncrementService do
+ let(:project) { create(:project) }
+
+ describe '#execute' do
+ subject { described_class.new(project).execute }
+
+ it 'creates a new record for today with count == 1' do
+ expect { subject }.to change { ProjectDailyStatistic.count }.by(1)
+ created_stat = ProjectDailyStatistic.last
+
+ expect(created_stat.fetch_count).to eq(1)
+ expect(created_stat.project).to eq(project)
+ expect(created_stat.date).to eq(Date.today)
+ end
+
+ it "doesn't increment previous days statistics" do
+ yesterday_stat = create(:project_daily_statistic, fetch_count: 5, project: project, date: 1.day.ago)
+
+ expect { subject }.not_to change { yesterday_stat.reload.fetch_count }
+ end
+
+ context 'when the record already exists for today' do
+ let!(:project_daily_stat) { create(:project_daily_statistic, fetch_count: 5, project: project, date: Date.today) }
+
+ it 'increments the today record count by 1' do
+ expect { subject }.to change { project_daily_stat.reload.fetch_count }.to(6)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/group_links/create_service_spec.rb b/spec/services/projects/group_links/create_service_spec.rb
index ffb270d277e..68fd82b4cbe 100644
--- a/spec/services/projects/group_links/create_service_spec.rb
+++ b/spec/services/projects/group_links/create_service_spec.rb
@@ -12,6 +12,10 @@ describe Projects::GroupLinks::CreateService, '#execute' do
end
let(:subject) { described_class.new(project, user, opts) }
+ before do
+ group.add_developer(user)
+ end
+
it 'adds group to project' do
expect { subject.execute(group) }.to change { project.project_group_links.count }.from(0).to(1)
end
@@ -19,4 +23,8 @@ describe Projects::GroupLinks::CreateService, '#execute' do
it 'returns false if group is blank' do
expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
end
+
+ it 'returns error if user is not allowed to share with a group' do
+ expect { subject.execute(create :group) }.not_to change { project.project_group_links.count }
+ 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 61dbb57ec08..efe15139717 100644
--- a/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_attachments_service_spec.rb
@@ -70,12 +70,18 @@ describe Projects::HashedStorage::MigrateAttachmentsService do
FileUtils.mkdir_p(base_path(hashed_storage))
end
- it 'raises AttachmentMigrationError' do
+ it 'raises AttachmentCannotMoveError' do
expect(FileUtils).not_to receive(:mv).with(base_path(legacy_storage), base_path(hashed_storage))
- expect { service.execute }.to raise_error(Projects::HashedStorage::AttachmentMigrationError)
+ expect { service.execute }.to raise_error(Projects::HashedStorage::AttachmentCannotMoveError)
end
end
+
+ it 'works even when project validation fails' do
+ allow(project).to receive(:valid?) { false }
+
+ expect { service.execute }.to change { project.hashed_storage?(:attachments) }.to(true)
+ end
end
context '#old_disk_path' do
@@ -86,6 +92,8 @@ describe Projects::HashedStorage::MigrateAttachmentsService do
context '#new_disk_path' do
it 'returns new disk_path for project' do
+ service.execute
+
expect(service.new_disk_path).to eq(project.disk_path)
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 0772dc4b85b..42b0d256cbf 100644
--- a/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
+++ b/spec/services/projects/hashed_storage/migrate_repository_service_spec.rb
@@ -28,7 +28,17 @@ describe Projects::HashedStorage::MigrateRepositoryService do
it 'fails when a git operation is in progress' do
allow(project).to receive(:repo_reference_count) { 1 }
- expect { service.execute }.to raise_error(Projects::HashedStorage::RepositoryMigrationError)
+ expect { service.execute }.to raise_error(Projects::HashedStorage::RepositoryInUseError)
+ end
+ end
+
+ context 'when repository doesnt exist on disk' do
+ let(:project) { create(:project, :legacy_storage) }
+
+ it 'skips the disk change but increase the version' do
+ service.execute
+
+ expect(project.hashed_storage?(:repository)).to be_truthy
end
end
@@ -92,6 +102,12 @@ describe Projects::HashedStorage::MigrateRepositoryService do
end
end
+ it 'works even when project validation fails' do
+ allow(project).to receive(:valid?) { false }
+
+ expect { service.execute }.to change { project.hashed_storage?(:repository) }.to(true)
+ end
+
def expect_move_repository(from_name, to_name)
expect(gitlab_shell).to receive(:mv_repository).with(project.repository_storage, from_name, to_name).and_call_original
end
diff --git a/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb b/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb
new file mode 100644
index 00000000000..815c85e0866
--- /dev/null
+++ b/spec/services/projects/hashed_storage/rollback_attachments_service_spec.rb
@@ -0,0 +1,106 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::HashedStorage::RollbackAttachmentsService do
+ subject(:service) { described_class.new(project, logger: nil) }
+
+ let(:project) { create(:project, :repository, skip_disk_validation: true) }
+ 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_disk_path) { File.join(base_path(hashed_storage), upload.path) }
+ let(:new_disk_path) { File.join(base_path(legacy_storage), upload.path) }
+
+ context '#execute' do
+ context 'when succeeds' do
+ it 'moves attachments to legacy storage layout' do
+ expect(File.file?(old_disk_path)).to be_truthy
+ expect(File.file?(new_disk_path)).to be_falsey
+ expect(File.exist?(base_path(hashed_storage))).to be_truthy
+ expect(File.exist?(base_path(legacy_storage))).to be_falsey
+ expect(FileUtils).to receive(:mv).with(base_path(hashed_storage), base_path(legacy_storage)).and_call_original
+
+ service.execute
+
+ expect(File.exist?(base_path(legacy_storage))).to be_truthy
+ expect(File.exist?(base_path(hashed_storage))).to be_falsey
+ expect(File.file?(old_disk_path)).to be_falsey
+ expect(File.file?(new_disk_path)).to be_truthy
+ end
+
+ it 'returns true' do
+ expect(service.execute).to be_truthy
+ end
+
+ it 'sets skipped to false' do
+ service.execute
+
+ expect(service.skipped?).to be_falsey
+ end
+ end
+
+ context 'when original folder does not exist anymore' do
+ before do
+ FileUtils.rm_rf(base_path(hashed_storage))
+ end
+
+ it 'skips moving folders and go to next' do
+ expect(FileUtils).not_to receive(:mv).with(base_path(hashed_storage), base_path(legacy_storage))
+
+ service.execute
+
+ expect(File.exist?(base_path(legacy_storage))).to be_falsey
+ expect(File.file?(new_disk_path)).to be_falsey
+ end
+
+ it 'returns true' do
+ expect(service.execute).to be_truthy
+ end
+
+ it 'sets skipped to true' do
+ service.execute
+
+ expect(service.skipped?).to be_truthy
+ end
+ end
+
+ context 'when target folder already exists' do
+ before do
+ FileUtils.mkdir_p(base_path(legacy_storage))
+ end
+
+ it 'raises AttachmentCannotMoveError' do
+ expect(FileUtils).not_to receive(:mv).with(base_path(legacy_storage), base_path(hashed_storage))
+
+ expect { service.execute }.to raise_error(Projects::HashedStorage::AttachmentCannotMoveError)
+ end
+ end
+
+ it 'works even when project validation fails' do
+ allow(project).to receive(:valid?) { false }
+
+ expect { service.execute }.to change { project.hashed_storage?(:attachments) }.to(false)
+ end
+ end
+
+ context '#old_disk_path' do
+ it 'returns old disk_path for project' do
+ expect(service.old_disk_path).to eq(project.disk_path)
+ end
+ end
+
+ context '#new_disk_path' do
+ it 'returns new disk_path for project' do
+ service.execute
+
+ expect(service.new_disk_path).to eq(project.full_path)
+ end
+ end
+
+ def base_path(storage)
+ File.join(FileUploader.root, storage.disk_path)
+ end
+end
diff --git a/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
new file mode 100644
index 00000000000..bd4354a7df3
--- /dev/null
+++ b/spec/services/projects/hashed_storage/rollback_repository_service_spec.rb
@@ -0,0 +1,117 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::HashedStorage::RollbackRepositoryService, :clean_gitlab_redis_shared_state do
+ include GitHelpers
+
+ let(:gitlab_shell) { Gitlab::Shell.new }
+ let(:project) { create(:project, :repository, :wiki_repo, storage_version: ::Project::HASHED_STORAGE_FEATURES[:repository]) }
+ let(:legacy_storage) { Storage::LegacyProject.new(project) }
+ let(:hashed_storage) { Storage::HashedProject.new(project) }
+
+ subject(:service) { described_class.new(project, project.disk_path) }
+
+ describe '#execute' do
+ let(:old_disk_path) { hashed_storage.disk_path }
+ let(:new_disk_path) { legacy_storage.disk_path }
+
+ before do
+ allow(service).to receive(:gitlab_shell) { gitlab_shell }
+ end
+
+ context 'repository lock' do
+ it 'tries to lock the repository' do
+ expect(service).to receive(:try_to_set_repository_read_only!)
+
+ service.execute
+ end
+
+ it 'fails when a git operation is in progress' do
+ allow(project).to receive(:repo_reference_count) { 1 }
+
+ expect { service.execute }.to raise_error(Projects::HashedStorage::RepositoryInUseError)
+ end
+ end
+
+ context 'when repository doesnt exist on disk' do
+ let(:project) { create(:project) }
+
+ it 'skips the disk change but decrease the version' do
+ service.execute
+
+ expect(project.legacy_storage?).to be_truthy
+ end
+ end
+
+ context 'when succeeds' do
+ it 'renames project and wiki repositories' do
+ service.execute
+
+ expect(gitlab_shell.exists?(project.repository_storage, "#{new_disk_path}.git")).to be_truthy
+ expect(gitlab_shell.exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_truthy
+ end
+
+ it 'updates project to be legacy and not read-only' do
+ service.execute
+
+ expect(project.legacy_storage?).to be_truthy
+ expect(project.repository_read_only).to be_falsey
+ end
+
+ it 'move operation is called for both repositories' do
+ expect_move_repository(old_disk_path, new_disk_path)
+ expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
+
+ service.execute
+ end
+
+ it 'writes project full path to .git/config' do
+ service.execute
+
+ rugged_config = rugged_repo(project.repository).config['gitlab.fullpath']
+
+ expect(rugged_config).to eq project.full_path
+ end
+ end
+
+ context 'when one move fails' do
+ it 'rolls repositories back to original name' do
+ allow(service).to receive(:move_repository).and_call_original
+ allow(service).to receive(:move_repository).with(old_disk_path, new_disk_path).once { false } # will disable first move only
+
+ expect(service).to receive(:rollback_folder_move).and_call_original
+
+ service.execute
+
+ expect(gitlab_shell.exists?(project.repository_storage, "#{new_disk_path}.git")).to be_falsey
+ expect(gitlab_shell.exists?(project.repository_storage, "#{new_disk_path}.wiki.git")).to be_falsey
+ expect(project.repository_read_only?).to be_falsey
+ end
+
+ context 'when rollback fails' do
+ before do
+ legacy_storage.ensure_storage_path_exists
+ gitlab_shell.mv_repository(project.repository_storage, old_disk_path, new_disk_path)
+ end
+
+ it 'does not try to move nil repository over existing' do
+ expect(gitlab_shell).not_to receive(:mv_repository).with(project.repository_storage, old_disk_path, new_disk_path)
+ expect_move_repository("#{old_disk_path}.wiki", "#{new_disk_path}.wiki")
+
+ service.execute
+ end
+ end
+ end
+
+ it 'works even when project validation fails' do
+ allow(project).to receive(:valid?) { false }
+
+ expect { service.execute }.to change { project.legacy_storage? }.to(true)
+ end
+
+ def expect_move_repository(from_name, to_name)
+ expect(gitlab_shell).to receive(:mv_repository).with(project.repository_storage, from_name, to_name).and_call_original
+ end
+ end
+end
diff --git a/spec/services/projects/hashed_storage/rollback_service_spec.rb b/spec/services/projects/hashed_storage/rollback_service_spec.rb
new file mode 100644
index 00000000000..427d1535559
--- /dev/null
+++ b/spec/services/projects/hashed_storage/rollback_service_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Projects::HashedStorage::RollbackService do
+ let(:project) { create(:project, :empty_repo, :wiki_repo) }
+ let(:logger) { double }
+
+ subject(:service) { described_class.new(project, project.full_path, logger: logger) }
+
+ describe '#execute' do
+ context 'attachments rollback' do
+ let(:attachments_service_class) { Projects::HashedStorage::RollbackAttachmentsService }
+ let(:attachments_service) { attachments_service_class.new(project, logger: logger) }
+
+ it 'delegates rollback to Projects::HashedStorage::RollbackAttachmentsService' do
+ expect(attachments_service_class).to receive(:new)
+ .with(project, logger: logger)
+ .and_return(attachments_service)
+ expect(attachments_service).to receive(:execute)
+
+ service.execute
+ end
+
+ it 'does not delegate rollback if repository is in legacy storage already' do
+ project.storage_version = nil
+ expect(attachments_service_class).not_to receive(:new)
+
+ service.execute
+ end
+ end
+
+ context 'repository rollback' do
+ let(:repository_service_class) { Projects::HashedStorage::RollbackRepositoryService }
+ let(:repository_service) { repository_service_class.new(project, project.full_path, logger: logger) }
+
+ it 'delegates rollback to RollbackRepositoryService' do
+ project.storage_version = ::Project::HASHED_STORAGE_FEATURES[:repository]
+
+ expect(repository_service_class).to receive(:new)
+ .with(project, project.full_path, logger: logger)
+ .and_return(repository_service)
+ expect(repository_service).to receive(:execute)
+
+ service.execute
+ end
+
+ it 'does not delegate rollback if repository is in legacy storage already' do
+ project.storage_version = nil
+
+ expect(repository_service_class).not_to receive(:new)
+
+ service.execute
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/operations/update_service_spec.rb b/spec/services/projects/operations/update_service_spec.rb
index 6afae3da80c..86b1ec83f50 100644
--- a/spec/services/projects/operations/update_service_spec.rb
+++ b/spec/services/projects/operations/update_service_spec.rb
@@ -17,8 +17,14 @@ describe Projects::Operations::UpdateService do
{
error_tracking_setting_attributes: {
enabled: false,
- api_url: 'http://gitlab.com/api/0/projects/org/project',
- token: 'token'
+ api_host: 'http://gitlab.com/',
+ token: 'token',
+ project: {
+ slug: 'project',
+ name: 'Project',
+ organization_slug: 'org',
+ organization_name: 'Org'
+ }
}
}
end
@@ -32,8 +38,30 @@ describe Projects::Operations::UpdateService do
project.reload
expect(project.error_tracking_setting).not_to be_enabled
- expect(project.error_tracking_setting.api_url).to eq('http://gitlab.com/api/0/projects/org/project')
+ expect(project.error_tracking_setting.api_url).to eq(
+ 'http://gitlab.com/api/0/projects/org/project/'
+ )
expect(project.error_tracking_setting.token).to eq('token')
+ expect(project.error_tracking_setting[:project_name]).to eq('Project')
+ expect(project.error_tracking_setting[:organization_name]).to eq('Org')
+ end
+
+ context 'disable error tracking' do
+ before do
+ params[:error_tracking_setting_attributes][:api_host] = ''
+ params[:error_tracking_setting_attributes][:enabled] = false
+ end
+
+ it 'can set api_url to nil' do
+ expect(result[:status]).to eq(:success)
+
+ project.reload
+ expect(project.error_tracking_setting).not_to be_enabled
+ expect(project.error_tracking_setting.api_url).to be_nil
+ expect(project.error_tracking_setting.token).to eq('token')
+ expect(project.error_tracking_setting[:project_name]).to eq('Project')
+ expect(project.error_tracking_setting[:organization_name]).to eq('Org')
+ end
end
end
@@ -42,8 +70,14 @@ describe Projects::Operations::UpdateService do
{
error_tracking_setting_attributes: {
enabled: true,
- api_url: 'http://gitlab.com/api/0/projects/org/project',
- token: 'token'
+ api_host: 'http://gitlab.com/',
+ token: 'token',
+ project: {
+ slug: 'project',
+ name: 'Project',
+ organization_slug: 'org',
+ organization_name: 'Org'
+ }
}
}
end
@@ -52,8 +86,12 @@ describe Projects::Operations::UpdateService do
expect(result[:status]).to eq(:success)
expect(project.error_tracking_setting).to be_enabled
- expect(project.error_tracking_setting.api_url).to eq('http://gitlab.com/api/0/projects/org/project')
+ expect(project.error_tracking_setting.api_url).to eq(
+ 'http://gitlab.com/api/0/projects/org/project/'
+ )
expect(project.error_tracking_setting.token).to eq('token')
+ expect(project.error_tracking_setting[:project_name]).to eq('Project')
+ expect(project.error_tracking_setting[:organization_name]).to eq('Org')
end
end
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index aae50d5307f..4efd360cb30 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -83,6 +83,7 @@ describe Projects::TransferService do
subject { transfer_project(project, user, group) }
before do
+ stub_feature_flags(ci_preparing_state: false)
expect(Clusters::Gcp::Kubernetes::CreateOrUpdateServiceAccountService).to receive(:namespace_creator).and_return(service_account_creator)
expect(Clusters::Gcp::Kubernetes::FetchKubernetesTokenService).to receive(:new).and_return(secrets_fetcher)
end
diff --git a/spec/services/prometheus/adapter_service_spec.rb b/spec/services/prometheus/adapter_service_spec.rb
index 335fc5844aa..505e2935e93 100644
--- a/spec/services/prometheus/adapter_service_spec.rb
+++ b/spec/services/prometheus/adapter_service_spec.rb
@@ -22,7 +22,15 @@ describe Prometheus::AdapterService do
context "prometheus service can't execute queries" do
let(:prometheus_service) { double(:prometheus_service, can_query?: false) }
- context 'with cluster with prometheus installed' do
+ context 'with cluster with prometheus not available' do
+ let!(:prometheus) { create(:clusters_applications_prometheus, :installable, cluster: cluster) }
+
+ it 'returns nil' do
+ expect(subject.prometheus_adapter).to be_nil
+ end
+ end
+
+ context 'with cluster with prometheus available' do
let!(:prometheus) { create(:clusters_applications_prometheus, :installed, cluster: cluster) }
it 'returns application handling all environments' do
diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb
index 938764f40b0..ea33d156c8a 100644
--- a/spec/services/quick_actions/interpret_service_spec.rb
+++ b/spec/services/quick_actions/interpret_service_spec.rb
@@ -1526,5 +1526,15 @@ describe QuickActions::InterpretService do
end
end
end
+
+ context "#commands_executed_count" do
+ it 'counts commands executed' do
+ content = "/close and \n/assign me and \n/title new title"
+
+ service.execute(content, issue)
+
+ expect(service.commands_executed_count).to eq(3)
+ end
+ end
end
end
diff --git a/spec/services/releases/destroy_service_spec.rb b/spec/services/releases/destroy_service_spec.rb
index dd5b8708f36..28663ca8853 100644
--- a/spec/services/releases/destroy_service_spec.rb
+++ b/spec/services/releases/destroy_service_spec.rb
@@ -28,13 +28,11 @@ describe Releases::DestroyService do
end
end
- context 'when tag is not found' do
+ context 'when tag does not exist in the repository' do
let(:tag) { 'v1.1.1' }
- it 'returns an error' do
- is_expected.to include(status: :error,
- message: 'Tag does not exist',
- http_status: 404)
+ it 'removes the orphaned release' do
+ expect { subject }.to change { project.releases.count }.by(-1)
end
end
diff --git a/spec/services/suggestions/apply_service_spec.rb b/spec/services/suggestions/apply_service_spec.rb
index 8e77d582eb4..80b5dcac6c7 100644
--- a/spec/services/suggestions/apply_service_spec.rb
+++ b/spec/services/suggestions/apply_service_spec.rb
@@ -5,6 +5,41 @@ require 'spec_helper'
describe Suggestions::ApplyService do
include ProjectForksHelper
+ shared_examples 'successfully creates commit and updates suggestion' do
+ def apply(suggestion)
+ result = subject.execute(suggestion)
+ expect(result[:status]).to eq(:success)
+ end
+
+ it 'updates the file with the new contents' do
+ apply(suggestion)
+
+ blob = project.repository.blob_at_branch(merge_request.source_branch,
+ position.new_path)
+
+ expect(blob.data).to eq(expected_content)
+ end
+
+ it 'updates suggestion applied and commit_id columns' do
+ expect { apply(suggestion) }
+ .to change(suggestion, :applied)
+ .from(false).to(true)
+ .and change(suggestion, :commit_id)
+ .from(nil)
+ end
+
+ it 'created commit has users email and name' do
+ apply(suggestion)
+
+ commit = project.repository.commit
+
+ expect(user.commit_email).not_to eq(user.email)
+ expect(commit.author_email).to eq(user.commit_email)
+ expect(commit.committer_email).to eq(user.commit_email)
+ expect(commit.author_name).to eq(user.name)
+ end
+ end
+
let(:project) { create(:project, :repository) }
let(:user) { create(:user, :commit_email) }
@@ -17,9 +52,8 @@ describe Suggestions::ApplyService do
end
let(:suggestion) do
- create(:suggestion, note: diff_note,
- from_content: " raise RuntimeError, \"System commands must be given as an array of strings\"\n",
- to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
+ create(:suggestion, :content_from_repo, note: diff_note,
+ to_content: " raise RuntimeError, 'Explosion'\n # explosion?\n")
end
subject { described_class.new(user) }
@@ -84,39 +118,7 @@ describe Suggestions::ApplyService do
project.add_maintainer(user)
end
- it 'updates the file with the new contents' do
- subject.execute(suggestion)
-
- blob = project.repository.blob_at_branch(merge_request.source_branch,
- position.new_path)
-
- expect(blob.data).to eq(expected_content)
- end
-
- it 'returns success status' do
- result = subject.execute(suggestion)
-
- expect(result[:status]).to eq(:success)
- end
-
- it 'updates suggestion applied and commit_id columns' do
- expect { subject.execute(suggestion) }
- .to change(suggestion, :applied)
- .from(false).to(true)
- .and change(suggestion, :commit_id)
- .from(nil)
- end
-
- it 'created commit has users email and name' do
- subject.execute(suggestion)
-
- commit = project.repository.commit
-
- expect(user.commit_email).not_to eq(user.email)
- expect(commit.author_email).to eq(user.commit_email)
- expect(commit.committer_email).to eq(user.commit_email)
- expect(commit.author_name).to eq(user.name)
- end
+ it_behaves_like 'successfully creates commit and updates suggestion'
context 'when it fails to apply because the file was changed' do
it 'returns error message' do
@@ -212,11 +214,13 @@ describe Suggestions::ApplyService do
end
def apply_suggestion(suggestion)
- suggestion.note.reload
+ suggestion.reload
merge_request.reload
merge_request.clear_memoized_shas
result = subject.execute(suggestion)
+ expect(result[:status]).to eq(:success)
+
refresh = MergeRequests::RefreshService.new(project, user)
refresh.execute(merge_request.diff_head_sha,
suggestion.commit_id,
@@ -241,7 +245,7 @@ describe Suggestions::ApplyService do
suggestion_2_changes = { old_line: 24,
new_line: 31,
- from_content: " @cmd_output << stderr.read\n",
+ from_content: " @cmd_output << stderr.read\n",
to_content: "# v2 change\n",
path: path }
@@ -362,6 +366,28 @@ describe Suggestions::ApplyService do
project.add_maintainer(user)
end
+ context 'diff file was not found' do
+ it 'returns error message' do
+ expect(suggestion.note).to receive(:latest_diff_file) { nil }
+
+ result = subject.execute(suggestion)
+
+ expect(result).to eq(message: 'Suggestion is not appliable',
+ status: :error)
+ end
+ end
+
+ context 'suggestion is eligible to be outdated' do
+ it 'returns error message' do
+ expect(suggestion).to receive(:outdated?) { true }
+
+ result = subject.execute(suggestion)
+
+ expect(result).to eq(message: 'Suggestion is not appliable',
+ status: :error)
+ end
+ end
+
context 'suggestion was already applied' do
it 'returns success status' do
result = subject.execute(suggestion)
diff --git a/spec/services/suggestions/create_service_spec.rb b/spec/services/suggestions/create_service_spec.rb
index f1142c88a69..ce4990a34a4 100644
--- a/spec/services/suggestions/create_service_spec.rb
+++ b/spec/services/suggestions/create_service_spec.rb
@@ -9,14 +9,18 @@ describe Suggestions::CreateService do
target_project: project_with_repo)
end
- let(:position) do
- 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)
+ def build_position(args = {})
+ default_args = { old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ diff_refs: merge_request.diff_refs }
+
+ Gitlab::Diff::Position.new(default_args.merge(args))
end
+ let(:position) { build_position }
+
let(:markdown) do
<<-MARKDOWN.strip_heredoc
```suggestion
@@ -36,6 +40,14 @@ describe Suggestions::CreateService do
```thing
this is not a suggestion, it's a thing
```
+
+ ```suggestion:-3+2
+ # multi-line suggestion 1
+ ```
+
+ ```suggestion:-5
+ # multi-line suggestion 1
+ ```
MARKDOWN
end
@@ -50,7 +62,7 @@ describe Suggestions::CreateService do
end
it 'does not try to parse suggestions' do
- expect(Banzai::SuggestionsParser).not_to receive(:parse)
+ expect(Gitlab::Diff::SuggestionsParser).not_to receive(:parse)
subject.execute
end
@@ -67,14 +79,14 @@ describe Suggestions::CreateService do
it 'does not try to parse suggestions' do
allow(note).to receive(:on_text?) { false }
- expect(Banzai::SuggestionsParser).not_to receive(:parse)
+ expect(Gitlab::Diff::SuggestionsParser).not_to receive(:parse)
subject.execute
end
end
end
- context 'should create suggestions' do
+ context 'should not create suggestions' do
let(:note) do
create(:diff_note_on_merge_request, project: project_with_repo,
noteable: merge_request,
@@ -82,27 +94,61 @@ describe Suggestions::CreateService do
note: markdown)
end
- context 'single line suggestions' do
- it 'persists suggestion records' do
- expect { subject.execute }
- .to change { note.suggestions.count }
- .from(0)
- .to(2)
+ it 'creates no suggestion when diff file is not found' do
+ expect_next_instance_of(DiffNote) do |diff_note|
+ expect(diff_note).to receive(:latest_diff_file).twice { nil }
end
- it 'persists original from_content lines and suggested lines' do
- subject.execute
+ expect { subject.execute }.not_to change(Suggestion, :count)
+ end
+ end
- suggestions = note.suggestions.order(:relative_order)
+ context 'should create suggestions' do
+ let(:note) do
+ create(:diff_note_on_merge_request, project: project_with_repo,
+ noteable: merge_request,
+ position: position,
+ note: markdown)
+ end
+
+ let(:expected_suggestions) do
+ Gitlab::Diff::SuggestionsParser.parse(markdown,
+ project: note.project,
+ position: note.position)
+ end
+
+ it 'persists suggestion records' do
+ expect { subject.execute }.to change { note.suggestions.count }
+ .from(0).to(expected_suggestions.size)
+ end
+
+ it 'persists suggestions data correctly' do
+ subject.execute
- suggestion_1 = suggestions.first
- suggestion_2 = suggestions.last
+ suggestions = note.suggestions.order(:relative_order)
- expect(suggestion_1).to have_attributes(from_content: " vars = {\n",
- to_content: " foo\n bar\n")
+ suggestions.zip(expected_suggestions) do |suggestion, expected_suggestion|
+ expected_data = expected_suggestion.to_hash
- expect(suggestion_2).to have_attributes(from_content: " vars = {\n",
- to_content: " xpto\n baz\n")
+ expect(suggestion.from_content).to eq(expected_data[:from_content])
+ expect(suggestion.to_content).to eq(expected_data[:to_content])
+ expect(suggestion.lines_above).to eq(expected_data[:lines_above])
+ expect(suggestion.lines_below).to eq(expected_data[:lines_below])
+ end
+ end
+
+ context 'outdated position note' do
+ let!(:outdated_diff) { merge_request.merge_request_diff }
+ let!(:latest_diff) { merge_request.create_merge_request_diff }
+ let(:outdated_position) { build_position(diff_refs: outdated_diff.diff_refs) }
+ let(:position) { build_position(diff_refs: latest_diff.diff_refs) }
+
+ it 'uses the correct position when creating the suggestion' do
+ expect(Gitlab::Diff::SuggestionsParser).to receive(:parse)
+ .with(note.note, project: note.project, position: note.position)
+ .and_call_original
+
+ subject.execute
end
end
end
diff --git a/spec/services/suggestions/outdate_service_spec.rb b/spec/services/suggestions/outdate_service_spec.rb
new file mode 100644
index 00000000000..bcc627013d8
--- /dev/null
+++ b/spec/services/suggestions/outdate_service_spec.rb
@@ -0,0 +1,102 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Suggestions::OutdateService do
+ describe '#execute' do
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { merge_request.target_project }
+ let(:user) { merge_request.author }
+ let(:file_path) { 'files/ruby/popen.rb' }
+ let(:branch_name) { project.default_branch }
+ let(:diff_file) { suggestion.diff_file }
+ let(:position) { build_position(file_path, comment_line) }
+ let(:note) do
+ create(:diff_note_on_merge_request, noteable: merge_request,
+ position: position,
+ project: project)
+ end
+
+ def build_position(path, line)
+ Gitlab::Diff::Position.new(old_path: path,
+ new_path: path,
+ old_line: nil,
+ new_line: line,
+ diff_refs: merge_request.diff_refs)
+ end
+
+ def commit_changes(file_path, new_content)
+ params = {
+ file_path: file_path,
+ commit_message: "Update File",
+ file_content: new_content,
+ start_project: project,
+ start_branch: project.default_branch,
+ branch_name: branch_name
+ }
+
+ Files::UpdateService.new(project, user, params).execute
+ end
+
+ def update_file_line(diff_file, change_line, content)
+ new_lines = diff_file.new_blob.data.lines
+ new_lines[change_line..change_line] = content
+ result = commit_changes(diff_file.file_path, new_lines.join)
+ newrev = result[:result]
+
+ expect(result[:status]).to eq(:success)
+ expect(newrev).to be_present
+
+ # Ensure all memoized data is cleared in order
+ # to generate the new merge_request_diff.
+ MergeRequest.find(merge_request.id).reload_diff(user)
+
+ note.reload
+ end
+
+ before do
+ project.add_maintainer(user)
+ end
+
+ subject { described_class.new.execute(merge_request) }
+
+ context 'when there is a change within multi-line suggestion range' do
+ let(:comment_line) { 9 }
+ let(:lines_above) { 8 } # suggesting to change lines 1..9
+ let(:change_line) { 2 } # line 2 is within the range
+ let!(:suggestion) do
+ create(:suggestion, :content_from_repo, note: note, lines_above: lines_above)
+ end
+
+ it 'updates the outdatable suggestion record' do
+ update_file_line(diff_file, change_line, "# foo\nbar\n")
+
+ # Make sure note is still active
+ expect(note.active?).to be(true)
+
+ expect { subject }.to change { suggestion.reload.outdated }
+ .from(false).to(true)
+ end
+ end
+
+ context 'when there is no change within multi-line suggestion range' do
+ let(:comment_line) { 9 }
+ let(:lines_above) { 3 } # suggesting to change lines 6..9
+ let(:change_line) { 2 } # line 2 is not within the range
+ let!(:suggestion) do
+ create(:suggestion, :content_from_repo, note: note, lines_above: lines_above)
+ end
+
+ subject { described_class.new.execute(merge_request) }
+
+ it 'does not outdates suggestion record' do
+ update_file_line(diff_file, change_line, "# foo\nbar\n")
+
+ # Make sure note is still active
+ expect(note.active?).to be(true)
+
+ expect { subject }.not_to change { suggestion.reload.outdated }.from(false)
+ end
+ end
+ end
+end
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 82544ab0413..b917de14b2e 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -807,9 +807,10 @@ describe SystemNoteService do
expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with(
body: hash_including(
GlobalID: "GitLab",
+ relationship: 'mentioned on',
object: {
url: project_commit_url(project, commit),
- title: "GitLab: Mentioned on commit - #{commit.title}",
+ title: "Commit - #{commit.title}",
icon: { title: "GitLab", url16x16: favicon_path },
status: { resolved: false }
}
@@ -833,9 +834,10 @@ describe SystemNoteService do
expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with(
body: hash_including(
GlobalID: "GitLab",
+ relationship: 'mentioned on',
object: {
url: project_issue_url(project, issue),
- title: "GitLab: Mentioned on issue - #{issue.title}",
+ title: "Issue - #{issue.title}",
icon: { title: "GitLab", url16x16: favicon_path },
status: { resolved: false }
}
@@ -859,9 +861,10 @@ describe SystemNoteService do
expect(WebMock).to have_requested(:post, jira_api_remote_link_url(jira_issue)).with(
body: hash_including(
GlobalID: "GitLab",
+ relationship: 'mentioned on',
object: {
url: project_snippet_url(project, snippet),
- title: "GitLab: Mentioned on snippet - #{snippet.title}",
+ title: "Snippet - #{snippet.title}",
icon: { title: "GitLab", url16x16: favicon_path },
status: { resolved: false }
}
diff --git a/spec/services/tags/create_service_spec.rb b/spec/services/tags/create_service_spec.rb
index 0cbe57352be..e112cdc8881 100644
--- a/spec/services/tags/create_service_spec.rb
+++ b/spec/services/tags/create_service_spec.rb
@@ -41,7 +41,7 @@ describe Tags::CreateService do
it 'returns an error' do
expect(repository).to receive(:add_tag)
.with(user, 'v1.1.0', 'master', 'Foo')
- .and_raise(Gitlab::Git::PreReceiveError, 'something went wrong')
+ .and_raise(Gitlab::Git::PreReceiveError, 'GitLab: something went wrong')
response = service.execute('v1.1.0', 'master', 'Foo')
diff --git a/spec/services/tags/destroy_service_spec.rb b/spec/services/tags/destroy_service_spec.rb
index 7c8c1dd0d3a..a541d300595 100644
--- a/spec/services/tags/destroy_service_spec.rb
+++ b/spec/services/tags/destroy_service_spec.rb
@@ -7,11 +7,27 @@ describe Tags::DestroyService do
let(:service) { described_class.new(project, user) }
describe '#execute' do
+ subject { service.execute(tag_name) }
+
it 'removes the tag' do
expect(repository).to receive(:before_remove_tag)
expect(service).to receive(:success)
service.execute('v1.1.0')
end
+
+ context 'when there is an associated release on the tag' do
+ let(:tag) { repository.tags.first }
+ let(:tag_name) { tag.name }
+
+ before do
+ project.add_maintainer(user)
+ create(:release, tag: tag_name, project: project)
+ end
+
+ it 'destroys the release' do
+ expect { subject }.to change { project.releases.count }.by(-1)
+ end
+ end
end
end
diff --git a/spec/services/web_hook_service_spec.rb b/spec/services/web_hook_service_spec.rb
index 5945a7dc0ad..747e04fb18c 100644
--- a/spec/services/web_hook_service_spec.rb
+++ b/spec/services/web_hook_service_spec.rb
@@ -102,7 +102,7 @@ describe WebHookService do
exception = exception_class.new('Exception message')
WebMock.stub_request(:post, project_hook.url).to_raise(exception)
- expect(service_instance.execute).to eq({ status: :error, message: exception.message })
+ expect(service_instance.execute).to eq({ status: :error, message: exception.to_s })
expect { service_instance.execute }.not_to raise_error
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 97e7a019222..e8d7b18bf04 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -115,10 +115,17 @@ RSpec.configure do |config|
TestEnv.clean_test_path
end
- config.before do
+ config.before do |example|
# Enable all features by default for testing
allow(Feature).to receive(:enabled?) { true }
+ enabled = example.metadata[:enable_rugged].present?
+
+ # Disable Rugged features by default
+ Gitlab::Git::RuggedImpl::Repository::FEATURE_FLAGS.each do |flag|
+ allow(Feature).to receive(:enabled?).with(flag).and_return(enabled)
+ end
+
# The following can be removed when we remove the staged rollout strategy
# and we can just enable it using instance wide settings
# (ie. ApplicationSetting#auto_devops_enabled)
diff --git a/spec/support/api/milestones_shared_examples.rb b/spec/support/api/milestones_shared_examples.rb
index 5f709831ce1..63b719be03e 100644
--- a/spec/support/api/milestones_shared_examples.rb
+++ b/spec/support/api/milestones_shared_examples.rb
@@ -72,6 +72,15 @@ shared_examples_for 'group and project milestones' do |route_definition|
expect(json_response.first['id']).to eq closed_milestone.id
end
+ it 'returns a milestone by title' do
+ get api(route, user), params: { title: 'version2' }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to eq(1)
+ expect(json_response.first['title']).to eq milestone.title
+ expect(json_response.first['id']).to eq milestone.id
+ end
+
it 'returns a milestone by searching for title' do
get api(route, user), params: { search: 'version2' }
diff --git a/spec/support/api/schema_matcher.rb b/spec/support/api/schema_matcher.rb
index 6591d56e473..4cf34d43117 100644
--- a/spec/support/api/schema_matcher.rb
+++ b/spec/support/api/schema_matcher.rb
@@ -1,10 +1,16 @@
module SchemaPath
- def self.expand(schema, dir = '')
- Rails.root.join('spec', dir, "fixtures/api/schemas/#{schema}.json").to_s
+ def self.expand(schema, dir = nil)
+ if Gitlab.ee? && dir.nil?
+ ee_path = expand(schema, 'ee')
+
+ return ee_path if File.exist?(ee_path)
+ end
+
+ Rails.root.join(dir.to_s, 'spec', "fixtures/api/schemas/#{schema}.json").to_s
end
end
-RSpec::Matchers.define :match_response_schema do |schema, dir: '', **options|
+RSpec::Matchers.define :match_response_schema do |schema, dir: nil, **options|
match do |response|
@errors = JSON::Validator.fully_validate(
SchemaPath.expand(schema, dir), response.body, options)
@@ -18,8 +24,16 @@ RSpec::Matchers.define :match_response_schema do |schema, dir: '', **options|
end
end
-RSpec::Matchers.define :match_schema do |schema, dir: '', **options|
+RSpec::Matchers.define :match_schema do |schema, dir: nil, **options|
match do |data|
- JSON::Validator.validate!(SchemaPath.expand(schema, dir), data, options)
+ @errors = JSON::Validator.fully_validate(
+ SchemaPath.expand(schema, dir), data, options)
+
+ @errors.empty?
+ end
+
+ failure_message do |response|
+ "didn't match the schema defined by #{SchemaPath.expand(schema, dir)}" \
+ " The validation errors were:\n#{@errors.join("\n")}"
end
end
diff --git a/spec/support/api/time_tracking_shared_examples.rb b/spec/support/api/time_tracking_shared_examples.rb
index e883d33f671..15037222630 100644
--- a/spec/support/api/time_tracking_shared_examples.rb
+++ b/spec/support/api/time_tracking_shared_examples.rb
@@ -3,6 +3,8 @@ shared_examples 'an unauthorized API user' do
end
shared_examples 'time tracking endpoints' do |issuable_name|
+ let(:non_member) { create(:user) }
+
issuable_collection_name = issuable_name.pluralize
describe "POST /projects/:id/#{issuable_collection_name}/:#{issuable_name}_id/time_estimate" do
diff --git a/spec/support/database_cleaner.rb b/spec/support/database_cleaner.rb
new file mode 100644
index 00000000000..edd7de94203
--- /dev/null
+++ b/spec/support/database_cleaner.rb
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+
+require 'database_cleaner/active_record/deletion'
+require_relative 'db_cleaner'
+
+module FakeInformationSchema
+ # Work around a bug in DatabaseCleaner when using the deletion strategy:
+ # https://github.com/DatabaseCleaner/database_cleaner/issues/347
+ #
+ # On MySQL, if the information schema is said to exist, we use an inaccurate
+ # row count leading to some tables not being cleaned when they should
+ def information_schema_exists?(_connection)
+ false
+ end
+end
+
+DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema)
+
+RSpec.configure do |config|
+ include DbCleaner
+
+ # Ensure all sequences are reset at the start of the suite run
+ config.before(:suite) do
+ setup_database_cleaner
+ DatabaseCleaner.clean_with(:truncation)
+ end
+
+ config.append_after(:context) do
+ DatabaseCleaner.clean_with(:deletion, cache_tables: false)
+ end
+
+ config.before do
+ setup_database_cleaner
+ DatabaseCleaner.strategy = :transaction
+ end
+
+ config.before(:each, :js) do
+ DatabaseCleaner.strategy = :deletion, { except: deletion_except_tables, cache_tables: false }
+ end
+
+ config.before(:each, :delete) do
+ DatabaseCleaner.strategy = :deletion, { except: deletion_except_tables, cache_tables: false }
+ end
+
+ config.before(:each, :migration) do
+ DatabaseCleaner.strategy = :deletion, { cache_tables: false }
+ end
+
+ config.before do
+ DatabaseCleaner.start
+ end
+
+ config.append_after do
+ DatabaseCleaner.clean
+ end
+end
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index 34b9efaaecd..c69fa322073 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -1,49 +1,9 @@
-require 'database_cleaner/active_record/deletion'
-
-module FakeInformationSchema
- # Work around a bug in DatabaseCleaner when using the deletion strategy:
- # https://github.com/DatabaseCleaner/database_cleaner/issues/347
- #
- # On MySQL, if the information schema is said to exist, we use an inaccurate
- # row count leading to some tables not being cleaned when they should
- def information_schema_exists?(_connection)
- false
- end
-end
-
-DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema)
-
-RSpec.configure do |config|
- # Ensure all sequences are reset at the start of the suite run
- config.before(:suite) do
- DatabaseCleaner.clean_with(:truncation)
- end
-
- config.append_after(:context) do
- DatabaseCleaner.clean_with(:deletion, cache_tables: false)
- end
-
- config.before do
- DatabaseCleaner.strategy = :transaction
- end
-
- config.before(:each, :js) do
- DatabaseCleaner.strategy = :deletion, { cache_tables: false }
- end
-
- config.before(:each, :delete) do
- DatabaseCleaner.strategy = :deletion, { cache_tables: false }
- end
-
- config.before(:each, :migration) do
- DatabaseCleaner.strategy = :deletion, { cache_tables: false }
- end
-
- config.before do
- DatabaseCleaner.start
+module DbCleaner
+ def deletion_except_tables
+ []
end
- config.append_after do
- DatabaseCleaner.clean
+ def setup_database_cleaner
+ DatabaseCleaner[:active_record, { connection: ActiveRecord::Base }]
end
end
diff --git a/spec/support/features/reportable_note_shared_examples.rb b/spec/support/features/reportable_note_shared_examples.rb
index 8cfce49da8a..89dfbf931d2 100644
--- a/spec/support/features/reportable_note_shared_examples.rb
+++ b/spec/support/features/reportable_note_shared_examples.rb
@@ -41,7 +41,7 @@ shared_examples 'reportable note' do |type|
def open_dropdown(dropdown)
# make window wide enough that tooltip doesn't trigger horizontal scrollbar
- resize_window(1200, 800)
+ restore_window_size
dropdown.find('.more-actions-toggle').click
dropdown.find('.dropdown-menu li', match: :first)
diff --git a/spec/support/features/variable_list_shared_examples.rb b/spec/support/features/variable_list_shared_examples.rb
index 0a464d77cb7..73156d18c1b 100644
--- a/spec/support/features/variable_list_shared_examples.rb
+++ b/spec/support/features/variable_list_shared_examples.rb
@@ -8,7 +8,7 @@ shared_examples 'variable list' do
it 'adds new CI variable' do
page.within('.js-ci-variable-list-section .js-row:last-child') do
find('.js-ci-variable-input-key').set('key')
- find('.js-ci-variable-input-value').set('key value')
+ find('.js-ci-variable-input-value').set('key_value')
end
click_button('Save variables')
@@ -19,7 +19,7 @@ shared_examples 'variable list' do
# We check the first row because it re-sorts to alphabetical order on refresh
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
expect(find('.js-ci-variable-input-key').value).to eq('key')
- expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key value')
+ expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value')
end
end
@@ -44,7 +44,7 @@ shared_examples 'variable list' do
it 'adds new protected variable' do
page.within('.js-ci-variable-list-section .js-row:last-child') do
find('.js-ci-variable-input-key').set('key')
- find('.js-ci-variable-input-value').set('key value')
+ find('.js-ci-variable-input-value').set('key_value')
find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
@@ -58,7 +58,7 @@ shared_examples 'variable list' do
# We check the first row because it re-sorts to alphabetical order on refresh
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
expect(find('.js-ci-variable-input-key').value).to eq('key')
- expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key value')
+ expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key_value')
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
end
end
diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb
index ecefdc23811..33648292037 100644
--- a/spec/support/helpers/cycle_analytics_helpers.rb
+++ b/spec/support/helpers/cycle_analytics_helpers.rb
@@ -23,7 +23,7 @@ module CycleAnalyticsHelpers
return if skip_push_handler
- GitPushService.new(project,
+ Git::BranchPushService.new(project,
user,
oldrev: oldrev,
newrev: commit_shas.last,
diff --git a/spec/support/helpers/file_mover_helpers.rb b/spec/support/helpers/file_mover_helpers.rb
new file mode 100644
index 00000000000..1ba7cc03354
--- /dev/null
+++ b/spec/support/helpers/file_mover_helpers.rb
@@ -0,0 +1,12 @@
+# frozen_string_literal: true
+
+module FileMoverHelpers
+ def stub_file_mover(file_path, stub_real_path: nil)
+ file_name = File.basename(file_path)
+ allow(Pathname).to receive(:new).and_call_original
+
+ expect_next_instance_of(Pathname, a_string_including(file_name)) do |pathname|
+ allow(pathname).to receive(:realpath) { stub_real_path || pathname.cleanpath }
+ end
+ end
+end
diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb
index e468ee4676d..ca28325eab9 100644
--- a/spec/support/helpers/graphql_helpers.rb
+++ b/spec/support/helpers/graphql_helpers.rb
@@ -18,12 +18,10 @@ module GraphqlHelpers
# Runs a block inside a BatchLoader::Executor wrapper
def batch(max_queries: nil, &blk)
wrapper = proc do
- begin
- BatchLoader::Executor.ensure_current
- yield
- ensure
- BatchLoader::Executor.clear_current
- end
+ BatchLoader::Executor.ensure_current
+ yield
+ ensure
+ BatchLoader::Executor.clear_current
end
if max_queries
@@ -77,13 +75,23 @@ module GraphqlHelpers
def query_graphql_field(name, attributes = {}, fields = nil)
fields ||= all_graphql_fields_for(name.classify)
attributes = attributes_to_graphql(attributes)
+ attributes = "(#{attributes})" if attributes.present?
<<~QUERY
- #{name}(#{attributes}) {
- #{fields}
- }
+ #{name}#{attributes}
+ #{wrap_fields(fields)}
QUERY
end
+ def wrap_fields(fields)
+ return unless fields.strip.present?
+
+ <<~FIELDS
+ {
+ #{fields}
+ }
+ FIELDS
+ end
+
def all_graphql_fields_for(class_name, parent_types = Set.new)
type = GitlabSchema.types[class_name.to_s]
return "" unless type
@@ -115,8 +123,8 @@ module GraphqlHelpers
end.join(", ")
end
- def post_graphql(query, current_user: nil, variables: nil)
- post api('/', current_user, version: 'graphql'), params: { query: query, variables: variables }
+ def post_graphql(query, current_user: nil, variables: nil, headers: {})
+ post api('/', current_user, version: 'graphql'), params: { query: query, variables: variables }, headers: headers
end
def post_graphql_mutation(mutation, current_user: nil)
diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb
index 89c5ec7a718..9cae8f934db 100644
--- a/spec/support/helpers/javascript_fixtures_helpers.rb
+++ b/spec/support/helpers/javascript_fixtures_helpers.rb
@@ -2,24 +2,29 @@ require 'action_dispatch/testing/test_request'
require 'fileutils'
module JavaScriptFixturesHelpers
+ extend ActiveSupport::Concern
include Gitlab::Popen
- FIXTURE_PATH = 'spec/javascripts/fixtures'.freeze
+ extend self
- def self.included(base)
+ included do |base|
base.around do |example|
# pick an arbitrary date from the past, so tests are not time dependent
Timecop.freeze(Time.utc(2015, 7, 3, 10)) { example.run }
end
end
+ def fixture_root_path
+ 'spec/javascripts/fixtures'
+ end
+
# Public: Removes all fixture files from given directory
#
- # directory_name - directory of the fixtures (relative to FIXTURE_PATH)
+ # directory_name - directory of the fixtures (relative to .fixture_root_path)
#
def clean_frontend_fixtures(directory_name)
- directory_name = File.expand_path(directory_name, FIXTURE_PATH)
- Dir[File.expand_path('*.html.raw', directory_name)].each do |file_name|
+ full_directory_name = File.expand_path(directory_name, fixture_root_path)
+ Dir[File.expand_path('*.html', full_directory_name)].each do |file_name|
FileUtils.rm(file_name)
end
end
@@ -27,14 +32,14 @@ module JavaScriptFixturesHelpers
# Public: Store a response object as fixture file
#
# response - string or response object to store
- # fixture_file_name - file name to store the fixture in (relative to FIXTURE_PATH)
+ # fixture_file_name - file name to store the fixture in (relative to .fixture_root_path)
#
def store_frontend_fixture(response, fixture_file_name)
- fixture_file_name = File.expand_path(fixture_file_name, FIXTURE_PATH)
+ full_fixture_path = File.expand_path(fixture_file_name, fixture_root_path)
fixture = response.respond_to?(:body) ? parse_response(response) : response
- FileUtils.mkdir_p(File.dirname(fixture_file_name))
- File.write(fixture_file_name, fixture)
+ FileUtils.mkdir_p(File.dirname(full_fixture_path))
+ File.write(full_fixture_path, fixture)
end
def remove_repository(project)
diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb
index 9dc89b483b2..ac52acb6570 100644
--- a/spec/support/helpers/kubernetes_helpers.rb
+++ b/spec/support/helpers/kubernetes_helpers.rb
@@ -9,6 +9,10 @@ module KubernetesHelpers
kube_response(kube_pods_body)
end
+ def kube_logs_response
+ kube_response(kube_logs_body)
+ end
+
def kube_deployments_response
kube_response(kube_deployments_body)
end
@@ -34,6 +38,13 @@ module KubernetesHelpers
WebMock.stub_request(:get, pods_url).to_return(response || kube_pods_response)
end
+ def stub_kubeclient_logs(pod_name, response = nil)
+ stub_kubeclient_discover(service.api_url)
+ logs_url = service.api_url + "/api/v1/namespaces/#{service.actual_namespace}/pods/#{pod_name}/log?tailLines=#{Clusters::Platforms::Kubernetes::LOGS_LIMIT}"
+
+ WebMock.stub_request(:get, logs_url).to_return(response || kube_logs_response)
+ end
+
def stub_kubeclient_deployments(response = nil)
stub_kubeclient_discover(service.api_url)
deployments_url = service.api_url + "/apis/extensions/v1beta1/namespaces/#{service.actual_namespace}/deployments"
@@ -212,6 +223,10 @@ module KubernetesHelpers
}
end
+ def kube_logs_body
+ "Log 1\nLog 2\nLog 3"
+ end
+
def kube_deployments_body
{
"kind" => "DeploymentList",
@@ -235,16 +250,19 @@ module KubernetesHelpers
# This is a partial response, it will have many more elements in reality but
# these are the ones we care about at the moment
- def kube_pod(name: "kube-pod", app: "valid-pod-label", status: "Running", track: nil)
+ def kube_pod(name: "kube-pod", environment_slug: "production", project_slug: "project-path-slug", status: "Running", track: nil)
{
"metadata" => {
"name" => name,
"generate_name" => "generated-name-with-suffix",
"creationTimestamp" => "2016-11-25T19:55:19Z",
+ "annotations" => {
+ "app.gitlab.com/env" => environment_slug,
+ "app.gitlab.com/app" => project_slug
+ },
"labels" => {
- "app" => app,
"track" => track
- }
+ }.compact
},
"spec" => {
"containers" => [
@@ -278,13 +296,16 @@ module KubernetesHelpers
}
end
- def kube_deployment(name: "kube-deployment", app: "valid-deployment-label", track: nil)
+ def kube_deployment(name: "kube-deployment", environment_slug: "production", project_slug: "project-path-slug", track: nil)
{
"metadata" => {
"name" => name,
"generation" => 4,
+ "annotations" => {
+ "app.gitlab.com/env" => environment_slug,
+ "app.gitlab.com/app" => project_slug
+ },
"labels" => {
- "app" => app,
"track" => track
}.compact
},
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 3fee6872498..4a0cf62a661 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -47,7 +47,7 @@ module LoginHelpers
end
def gitlab_sign_in_via(provider, user, uid, saml_response = nil)
- mock_auth_hash(provider, uid, user.email, saml_response)
+ mock_auth_hash_with_saml_xml(provider, uid, user.email, saml_response)
visit new_user_session_path
click_link provider
end
@@ -87,7 +87,12 @@ module LoginHelpers
click_link "oauth-login-#{provider}"
end
- def mock_auth_hash(provider, uid, email, saml_response = nil)
+ def mock_auth_hash_with_saml_xml(provider, uid, email, saml_response)
+ response_object = { document: saml_xml(saml_response) }
+ mock_auth_hash(provider, uid, email, response_object: response_object)
+ end
+
+ def mock_auth_hash(provider, uid, email, response_object: nil)
# The mock_auth configuration allows you to set per-provider (or default)
# authentication hashes to return during integration testing.
OmniAuth.config.mock_auth[provider.to_sym] = OmniAuth::AuthHash.new({
@@ -110,9 +115,7 @@ module LoginHelpers
image: 'mock_user_thumbnail_url'
}
},
- response_object: {
- document: saml_xml(saml_response)
- }
+ response_object: response_object
}
})
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym]
diff --git a/spec/support/helpers/repo_helpers.rb b/spec/support/helpers/repo_helpers.rb
index 3c6956cf5e0..4af90f4af79 100644
--- a/spec/support/helpers/repo_helpers.rb
+++ b/spec/support/helpers/repo_helpers.rb
@@ -115,4 +115,18 @@ eos
commits: commits
)
end
+
+ def create_file_in_repo(
+ project, start_branch, branch_name, filename, content,
+ commit_message: 'Add new content')
+ Files::CreateService.new(
+ project,
+ project.owner,
+ commit_message: commit_message,
+ start_branch: start_branch,
+ branch_name: branch_name,
+ file_path: filename,
+ file_content: content
+ ).execute
+ end
end
diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb
index ff21bbe28ca..cfa9151b2d7 100644
--- a/spec/support/helpers/stub_configuration.rb
+++ b/spec/support/helpers/stub_configuration.rb
@@ -84,6 +84,10 @@ module StubConfiguration
allow(Gitlab.config.kerberos).to receive_messages(to_settings(messages))
end
+ def stub_gitlab_shell_setting(messages)
+ allow(Gitlab.config.gitlab_shell).to receive_messages(to_settings(messages))
+ end
+
private
# Modifies stubbed messages to also stub possible predicate versions
diff --git a/spec/support/helpers/stub_object_storage.rb b/spec/support/helpers/stub_object_storage.rb
index e0c50e533a6..30c8477f16a 100644
--- a/spec/support/helpers/stub_object_storage.rb
+++ b/spec/support/helpers/stub_object_storage.rb
@@ -23,15 +23,13 @@ module StubObjectStorage
Fog.mock!
::Fog::Storage.new(connection_params).tap do |connection|
- begin
- connection.directories.create(key: remote_directory)
+ connection.directories.create(key: remote_directory)
- # Cleanup remaining files
- connection.directories.each do |directory|
- directory.files.map(&:destroy)
- end
- rescue Excon::Error::Conflict
+ # 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 f485eb7b0eb..dc902d373b8 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -63,7 +63,8 @@ module TestEnv
'after-create-delete-modify-move' => 'ba3faa7',
'with-codeowners' => '219560e',
'submodule_inside_folder' => 'b491b92',
- 'png-lfs' => 'fe42f41'
+ 'png-lfs' => 'fe42f41',
+ 'sha-starting-with-large-number' => '8426165'
}.freeze
# gitlab-test-fork is a fork of gitlab-fork, but we don't necessarily
@@ -146,12 +147,15 @@ module TestEnv
version: Gitlab::Shell.version_required,
task: 'gitlab:shell:install')
- create_fake_git_hooks
+ # gitlab-shell hooks don't work in our test environment because they try to make internal API calls
+ sabotage_gitlab_shell_hooks
end
- def create_fake_git_hooks
- # gitlab-shell hooks don't work in our test environment because they try to make internal API calls
- hooks_dir = File.join(Gitlab.config.gitlab_shell.path, 'hooks')
+ def sabotage_gitlab_shell_hooks
+ create_fake_git_hooks(Gitlab::Shell.new.hooks_path)
+ end
+
+ def create_fake_git_hooks(hooks_dir)
%w[pre-receive post-receive update].each do |hook|
File.open(File.join(hooks_dir, hook), 'w', 0755) { |f| f.puts '#!/bin/sh' }
end
@@ -168,6 +172,7 @@ module TestEnv
task: "gitlab:gitaly:install[#{install_gitaly_args}]") do
Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
+ create_fake_git_hooks(File.join(gitaly_dir, 'ruby/git-hooks'))
start_gitaly(gitaly_dir)
end
end
@@ -197,12 +202,10 @@ module TestEnv
socket = Gitlab::GitalyClient.address('default').sub('unix:', '')
Integer(sleep_time / sleep_interval).times do
- begin
- Socket.unix(socket)
- return
- rescue
- sleep sleep_interval
- end
+ Socket.unix(socket)
+ return
+ rescue
+ sleep sleep_interval
end
raise "could not connect to gitaly at #{socket.inspect} after #{sleep_time} seconds"
diff --git a/spec/support/import_export/export_file_helper.rb b/spec/support/import_export/export_file_helper.rb
index ac320934f5a..388b88f0331 100644
--- a/spec/support/import_export/export_file_helper.rb
+++ b/spec/support/import_export/export_file_helper.rb
@@ -91,7 +91,7 @@ module ExportFileHelper
loop do
object_with_parent = deep_find_with_parent(sensitive_word, project_hash)
- return nil unless object_with_parent && object_with_parent.object
+ return unless object_with_parent && object_with_parent.object
if is_safe_hash?(object_with_parent.parent, sensitive_word)
# It's in the safe list, remove hash and keep looking
diff --git a/spec/support/matchers/access_matchers.rb b/spec/support/matchers/access_matchers.rb
index 3e4ca8b7ab0..e6899e2d23c 100644
--- a/spec/support/matchers/access_matchers.rb
+++ b/spec/support/matchers/access_matchers.rb
@@ -7,29 +7,28 @@ module AccessMatchers
extend RSpec::Matchers::DSL
include Warden::Test::Helpers
- def emulate_user(user, membership = nil)
- case user
- when :user
- login_as(create(:user))
+ def emulate_user(user_type_or_trait, membership = nil)
+ case user_type_or_trait
+ when :user, :admin
+ login_as(create(user_type_or_trait))
+ when :external, :auditor
+ login_as(create(:user, user_type_or_trait))
when :visitor
logout
- when :admin
- login_as(create(:admin))
- when :external
- login_as(create(:user, external: true))
when User
- login_as(user)
+ login_as(user_type_or_trait)
when *Gitlab::Access.sym_options_with_owner.keys
- raise ArgumentError, "cannot emulate #{user} without membership parent" unless membership
-
- role = user
+ raise ArgumentError, "cannot emulate #{user_type_or_trait} without membership parent" unless membership
- if role == :owner && membership.owner
- user = membership.owner
- else
- user = create(:user)
- membership.public_send(:"add_#{role}", user)
- end
+ role = user_type_or_trait
+ user =
+ if role == :owner && membership.owner
+ membership.owner
+ else
+ create(:user).tap do |new_user|
+ membership.public_send(:"add_#{role}", new_user)
+ end
+ end
login_as(user)
else
diff --git a/spec/support/matchers/graphql_matchers.rb b/spec/support/matchers/graphql_matchers.rb
index 7be84838e00..7894484f590 100644
--- a/spec/support/matchers/graphql_matchers.rb
+++ b/spec/support/matchers/graphql_matchers.rb
@@ -1,8 +1,6 @@
RSpec::Matchers.define :require_graphql_authorizations do |*expected|
match do |field|
- field_definition = field.metadata[:type_class]
- expect(field_definition).to respond_to(:required_permissions)
- expect(field_definition.required_permissions).to contain_exactly(*expected)
+ expect(field.metadata[:authorize]).to eq(*expected)
end
end
diff --git a/spec/support/matchers/issuable_matchers.rb b/spec/support/matchers/issuable_matchers.rb
index f5d9a97051a..62f510b0fbd 100644
--- a/spec/support/matchers/issuable_matchers.rb
+++ b/spec/support/matchers/issuable_matchers.rb
@@ -1,4 +1,4 @@
-RSpec::Matchers.define :have_header_with_correct_id_and_link do |level, text, id, parent = ".wiki"|
+RSpec::Matchers.define :have_header_with_correct_id_and_link do |level, text, id, parent = ".md"|
match do |actual|
node = find("#{parent} h#{level} a#user-content-#{id}")
diff --git a/spec/support/matchers/not_changed_matcher.rb b/spec/support/matchers/not_changed_matcher.rb
new file mode 100644
index 00000000000..8ef4694982d
--- /dev/null
+++ b/spec/support/matchers/not_changed_matcher.rb
@@ -0,0 +1,3 @@
+# frozen_string_literal: true
+
+RSpec::Matchers.define_negated_matcher :not_change, :change
diff --git a/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
new file mode 100644
index 00000000000..a0d994c4d8d
--- /dev/null
+++ b/spec/support/shared_contexts/finders/group_projects_finder_shared_contexts.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+RSpec.shared_context 'GroupProjectsFinder context' do
+ let(:group) { create(:group) }
+ let(:subgroup) { create(:group, parent: group) }
+ let(:current_user) { create(:user) }
+ let(:options) { {} }
+
+ let(:finder) { described_class.new(group: group, current_user: current_user, options: options) }
+
+ let!(:public_project) { create(:project, :public, group: group, path: '1') }
+ let!(:private_project) { create(:project, :private, group: group, path: '2') }
+ let!(:shared_project_1) { create(:project, :public, path: '3') }
+ let!(:shared_project_2) { create(:project, :private, path: '4') }
+ let!(:shared_project_3) { create(:project, :internal, path: '5') }
+ let!(:subgroup_project) { create(:project, :public, path: '6', group: subgroup) }
+ let!(:subgroup_private_project) { create(:project, :private, path: '7', group: subgroup) }
+
+ before do
+ 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
+end
diff --git a/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb
new file mode 100644
index 00000000000..b8a9554f55f
--- /dev/null
+++ b/spec/support/shared_contexts/finders/issues_finder_shared_contexts.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+RSpec.shared_context 'IssuesFinder context' do
+ set(:user) { create(:user) }
+ set(:user2) { create(:user) }
+ set(:group) { create(:group) }
+ set(:subgroup) { create(:group, parent: group) }
+ set(:project1) { create(:project, group: group) }
+ set(:project2) { create(:project) }
+ set(:project3) { create(:project, group: subgroup) }
+ set(:milestone) { create(:milestone, project: project1) }
+ set(:label) { create(:label, project: project2) }
+ set(:issue1) { create(:issue, author: user, assignees: [user], project: project1, milestone: milestone, title: 'gitlab', created_at: 1.week.ago, updated_at: 1.week.ago) }
+ set(:issue2) { create(:issue, author: user, assignees: [user], project: project2, description: 'gitlab', created_at: 1.week.from_now, updated_at: 1.week.from_now) }
+ set(:issue3) { create(:issue, author: user2, assignees: [user2], project: project2, title: 'tanuki', description: 'tanuki', created_at: 2.weeks.from_now, updated_at: 2.weeks.from_now) }
+ set(:issue4) { create(:issue, project: project3) }
+ set(:award_emoji1) { create(:award_emoji, name: 'thumbsup', user: user, awardable: issue1) }
+ set(:award_emoji2) { create(:award_emoji, name: 'thumbsup', user: user2, awardable: issue2) }
+ set(:award_emoji3) { create(:award_emoji, name: 'thumbsdown', user: user, awardable: issue3) }
+end
+
+RSpec.shared_context 'IssuesFinder#execute context' do
+ let!(:closed_issue) { create(:issue, author: user2, assignees: [user2], project: project2, state: 'closed') }
+ let!(:label_link) { create(:label_link, label: label, target: issue2) }
+ let(:search_user) { user }
+ let(:params) { {} }
+ let(:issues) { described_class.new(search_user, params.reverse_merge(scope: scope, state: 'opened')).execute }
+
+ before(:context) do
+ project1.add_maintainer(user)
+ project2.add_developer(user)
+ project2.add_developer(user2)
+ project3.add_developer(user)
+
+ issue1
+ issue2
+ issue3
+ issue4
+
+ award_emoji1
+ award_emoji2
+ award_emoji3
+ end
+end
diff --git a/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
new file mode 100644
index 00000000000..4df80b4168a
--- /dev/null
+++ b/spec/support/shared_contexts/finders/merge_requests_finder_shared_contexts.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+RSpec.shared_context 'MergeRequestsFinder multiple projects with merge requests context' do
+ include ProjectForksHelper
+
+ # We need to explicitly permit Gitaly N+1s because of the specs that use
+ # :request_store. Gitaly N+1 detection is only enabled when :request_store is,
+ # but we don't care about potential N+1s when we're just creating several
+ # projects in the setup phase.
+ def allow_gitaly_n_plus_1
+ Gitlab::GitalyClient.allow_n_plus_1_calls do
+ yield
+ end
+ end
+
+ set(:user) { create(:user) }
+ set(:user2) { create(:user) }
+
+ set(:group) { create(:group) }
+ set(:subgroup) { create(:group, parent: group) }
+ set(:project1) do
+ allow_gitaly_n_plus_1 { create(:project, :public, group: group) }
+ end
+ # We cannot use `set` here otherwise we get:
+ # Failure/Error: allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
+ # The use of doubles or partial doubles from rspec-mocks outside of the per-test lifecycle is not supported.
+ let(:project2) do
+ allow_gitaly_n_plus_1 do
+ fork_project(project1, user)
+ end
+ end
+ let(:project3) do
+ allow_gitaly_n_plus_1 do
+ fork_project(project1, user).tap do |project|
+ project.update!(archived: true)
+ end
+ end
+ end
+ set(:project4) do
+ allow_gitaly_n_plus_1 { create(:project, :repository, group: subgroup) }
+ end
+ set(:project5) do
+ allow_gitaly_n_plus_1 { create(:project, group: subgroup) }
+ end
+ set(:project6) do
+ allow_gitaly_n_plus_1 { create(:project, group: subgroup) }
+ end
+
+ let!(:merge_request1) { create(:merge_request, author: user, source_project: project2, target_project: project1, target_branch: 'merged-target') }
+ 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, state: 'locked', title: 'thing WIP thing') }
+ let!(:merge_request4) { create(:merge_request, :simple, author: user, source_project: project3, target_project: project3, title: 'WIP thing') }
+ let!(:merge_request5) { create(:merge_request, :simple, author: user, source_project: project4, target_project: project4, title: '[WIP]') }
+
+ before do
+ project1.add_maintainer(user)
+ project2.add_developer(user)
+ project3.add_developer(user)
+ project4.add_developer(user)
+ project5.add_developer(user)
+ project6.add_developer(user)
+
+ project2.add_developer(user2)
+ end
+end
diff --git a/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb b/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb
new file mode 100644
index 00000000000..9e1f89ee0ed
--- /dev/null
+++ b/spec/support/shared_contexts/finders/users_finder_shared_contexts.rb
@@ -0,0 +1,8 @@
+require 'spec_helper'
+
+RSpec.shared_context 'UsersFinder#execute filter by project context' do
+ set(:normal_user) { create(:user, username: 'johndoe') }
+ set(:blocked_user) { create(:user, :blocked, username: 'notsorandom') }
+ set(:external_user) { create(:user, :external) }
+ set(:omniauth_user) { create(:omniauth_user, provider: 'twitter', extern_uid: '123456') }
+end
diff --git a/spec/support/shared_contexts/services_shared_context.rb b/spec/support/shared_contexts/services_shared_context.rb
index d92e8318fa0..089f1798cd2 100644
--- a/spec/support/shared_contexts/services_shared_context.rb
+++ b/spec/support/shared_contexts/services_shared_context.rb
@@ -26,6 +26,14 @@ Service.available_services_names.each do |service|
end
end
+ before do
+ if service == 'github' && respond_to?(:stub_licensed_features)
+ stub_licensed_features(github_project_service_integration: true)
+ project.clear_memoization(:disabled_services)
+ project.clear_memoization(:licensed_feature_available)
+ end
+ end
+
def initialize_service(service)
service_item = project.find_or_initialize_service(service)
service_item.properties = service_attrs
diff --git a/spec/support/shared_examples/application_setting_examples.rb b/spec/support/shared_examples/application_setting_examples.rb
new file mode 100644
index 00000000000..e7ec24c5b7e
--- /dev/null
+++ b/spec/support/shared_examples/application_setting_examples.rb
@@ -0,0 +1,252 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples 'application settings examples' do
+ context 'restricted signup domains' do
+ it 'sets single domain' do
+ setting.domain_whitelist_raw = 'example.com'
+ expect(setting.domain_whitelist).to eq(['example.com'])
+ end
+
+ it 'sets multiple domains with spaces' do
+ setting.domain_whitelist_raw = 'example.com *.example.com'
+ expect(setting.domain_whitelist).to eq(['example.com', '*.example.com'])
+ end
+
+ it 'sets multiple domains with newlines and a space' do
+ setting.domain_whitelist_raw = "example.com\n *.example.com"
+ expect(setting.domain_whitelist).to eq(['example.com', '*.example.com'])
+ end
+
+ it 'sets multiple domains with commas' do
+ setting.domain_whitelist_raw = "example.com, *.example.com"
+ expect(setting.domain_whitelist).to eq(['example.com', '*.example.com'])
+ end
+ end
+
+ context 'blacklisted signup domains' do
+ it 'sets single domain' do
+ setting.domain_blacklist_raw = 'example.com'
+ expect(setting.domain_blacklist).to contain_exactly('example.com')
+ end
+
+ it 'sets multiple domains with spaces' do
+ setting.domain_blacklist_raw = 'example.com *.example.com'
+ expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com')
+ end
+
+ it 'sets multiple domains with newlines and a space' do
+ setting.domain_blacklist_raw = "example.com\n *.example.com"
+ expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com')
+ end
+
+ it 'sets multiple domains with commas' do
+ setting.domain_blacklist_raw = "example.com, *.example.com"
+ expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com')
+ end
+
+ it 'sets multiple domains with semicolon' do
+ setting.domain_blacklist_raw = "example.com; *.example.com"
+ expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com')
+ end
+
+ it 'sets multiple domains with mixture of everything' do
+ setting.domain_blacklist_raw = "example.com; *.example.com\n test.com\sblock.com yes.com"
+ expect(setting.domain_blacklist).to contain_exactly('example.com', '*.example.com', 'test.com', 'block.com', 'yes.com')
+ end
+
+ it 'sets multiple domain with file' do
+ setting.domain_blacklist_file = File.open(Rails.root.join('spec/fixtures/', 'domain_blacklist.txt'))
+ expect(setting.domain_blacklist).to contain_exactly('example.com', 'test.com', 'foo.bar')
+ end
+ end
+
+ describe 'usage ping settings' do
+ context 'when the usage ping is disabled in gitlab.yml' do
+ before do
+ allow(Settings.gitlab).to receive(:usage_ping_enabled).and_return(false)
+ end
+
+ it 'does not allow the usage ping to be configured' do
+ expect(setting.usage_ping_can_be_configured?).to be_falsey
+ end
+
+ context 'when the usage ping is disabled in the DB' do
+ before do
+ setting.usage_ping_enabled = false
+ end
+
+ it 'returns false for usage_ping_enabled' do
+ expect(setting.usage_ping_enabled).to be_falsey
+ end
+ end
+
+ context 'when the usage ping is enabled in the DB' do
+ before do
+ setting.usage_ping_enabled = true
+ end
+
+ it 'returns false for usage_ping_enabled' do
+ expect(setting.usage_ping_enabled).to be_falsey
+ end
+ end
+ end
+
+ context 'when the usage ping is enabled in gitlab.yml' do
+ before do
+ allow(Settings.gitlab).to receive(:usage_ping_enabled).and_return(true)
+ end
+
+ it 'allows the usage ping to be configured' do
+ expect(setting.usage_ping_can_be_configured?).to be_truthy
+ end
+
+ context 'when the usage ping is disabled in the DB' do
+ before do
+ setting.usage_ping_enabled = false
+ end
+
+ it 'returns false for usage_ping_enabled' do
+ expect(setting.usage_ping_enabled).to be_falsey
+ end
+ end
+
+ context 'when the usage ping is enabled in the DB' do
+ before do
+ setting.usage_ping_enabled = true
+ end
+
+ it 'returns true for usage_ping_enabled' do
+ expect(setting.usage_ping_enabled).to be_truthy
+ end
+ end
+ end
+ end
+
+ describe '#allowed_key_types' do
+ it 'includes all key types by default' do
+ expect(setting.allowed_key_types).to contain_exactly(*described_class::SUPPORTED_KEY_TYPES)
+ end
+
+ it 'excludes disabled key types' do
+ expect(setting.allowed_key_types).to include(:ed25519)
+
+ setting.ed25519_key_restriction = described_class::FORBIDDEN_KEY_VALUE
+
+ expect(setting.allowed_key_types).not_to include(:ed25519)
+ end
+ end
+
+ describe '#key_restriction_for' do
+ it 'returns the restriction value for recognised types' do
+ setting.rsa_key_restriction = 1024
+
+ expect(setting.key_restriction_for(:rsa)).to eq(1024)
+ end
+
+ it 'allows types to be passed as a string' do
+ setting.rsa_key_restriction = 1024
+
+ expect(setting.key_restriction_for('rsa')).to eq(1024)
+ end
+
+ it 'returns forbidden for unrecognised type' do
+ expect(setting.key_restriction_for(:foo)).to eq(described_class::FORBIDDEN_KEY_VALUE)
+ end
+ end
+
+ describe '#allow_signup?' do
+ it 'returns true' do
+ expect(setting.allow_signup?).to be_truthy
+ end
+
+ it 'returns false if signup is disabled' do
+ allow(setting).to receive(:signup_enabled?).and_return(false)
+
+ expect(setting.allow_signup?).to be_falsey
+ end
+
+ it 'returns false if password authentication is disabled for the web interface' do
+ allow(setting).to receive(:password_authentication_enabled_for_web?).and_return(false)
+
+ expect(setting.allow_signup?).to be_falsey
+ end
+ end
+
+ describe '#pick_repository_storage' do
+ it 'uses Array#sample to pick a random storage' do
+ array = double('array', sample: 'random')
+ expect(setting).to receive(:repository_storages).and_return(array)
+
+ expect(setting.pick_repository_storage).to eq('random')
+ end
+ end
+
+ describe '#user_default_internal_regex_enabled?' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_default_external, :user_default_internal_regex, :result) do
+ false | nil | false
+ false | '' | false
+ false | '^(?:(?!\.ext@).)*$\r?\n?' | false
+ true | '' | false
+ true | nil | false
+ true | '^(?:(?!\.ext@).)*$\r?\n?' | true
+ end
+
+ with_them do
+ before do
+ setting.user_default_external = user_default_external
+ setting.user_default_internal_regex = user_default_internal_regex
+ end
+
+ subject { setting.user_default_internal_regex_enabled? }
+
+ it { is_expected.to eq(result) }
+ end
+ end
+
+ describe '#archive_builds_older_than' do
+ subject { setting.archive_builds_older_than }
+
+ context 'when the archive_builds_in_seconds is set' do
+ before do
+ setting.archive_builds_in_seconds = 3600
+ end
+
+ it { is_expected.to be_within(1.minute).of(1.hour.ago) }
+ end
+
+ context 'when the archive_builds_in_seconds is set' do
+ before do
+ setting.archive_builds_in_seconds = nil
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#commit_email_hostname' do
+ context 'when the value is provided' do
+ before do
+ setting.commit_email_hostname = 'localhost'
+ end
+
+ it 'returns the provided value' do
+ expect(setting.commit_email_hostname).to eq('localhost')
+ end
+ end
+
+ context 'when the value is not provided' do
+ it 'returns the default from the class' do
+ expect(setting.commit_email_hostname)
+ .to eq(described_class.default_commit_email_hostname)
+ end
+ end
+ end
+
+ it 'predicate method changes when value is updated' do
+ setting.password_authentication_enabled_for_web = false
+
+ expect(setting.password_authentication_enabled_for_web?).to be_falsey
+ end
+end
diff --git a/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb b/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb
new file mode 100644
index 00000000000..713f0a879c1
--- /dev/null
+++ b/spec/support/shared_examples/graphql/issuable_state_shared_examples.rb
@@ -0,0 +1,5 @@
+RSpec.shared_examples 'issuable state' do
+ it 'exposes all the existing issuable states' do
+ expect(described_class.values.keys).to include(*%w[opened closed locked])
+ end
+end
diff --git a/spec/support/shared_examples/issuable_shared_examples.rb b/spec/support/shared_examples/issuable_shared_examples.rb
index c3d40c5b231..d97b21f71cd 100644
--- a/spec/support/shared_examples/issuable_shared_examples.rb
+++ b/spec/support/shared_examples/issuable_shared_examples.rb
@@ -31,7 +31,7 @@ shared_examples 'system notes for milestones' do
context 'project milestones' do
it 'creates a system note' do
expect do
- update_issuable(milestone: create(:milestone))
+ update_issuable(milestone: create(:milestone, project: project))
end.to change { Note.system.count }.by(1)
end
end
diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb
index a38354060cf..4fff1c4e228 100644
--- a/spec/support/shared_examples/notify_shared_examples.rb
+++ b/spec/support/shared_examples/notify_shared_examples.rb
@@ -252,3 +252,31 @@ shared_examples 'a note email' do
end
end
end
+
+shared_examples 'appearance header and footer enabled' do
+ it "contains header and footer" do
+ create :appearance, header_message: "Foo", footer_message: "Bar", email_header_and_footer_enabled: true
+
+ aggregate_failures do
+ expect(subject.html_part).to have_body_text("<div class=\"header-message\" style=\"\"><p>Foo</p></div>")
+ expect(subject.html_part).to have_body_text("<div class=\"footer-message\" style=\"\"><p>Bar</p></div>")
+
+ expect(subject.text_part).to have_body_text(/^Foo/)
+ expect(subject.text_part).to have_body_text(/Bar$/)
+ end
+ end
+end
+
+shared_examples 'appearance header and footer not enabled' do
+ it "does not contain header and footer" do
+ create :appearance, header_message: "Foo", footer_message: "Bar", email_header_and_footer_enabled: false
+
+ aggregate_failures do
+ expect(subject.html_part).not_to have_body_text("<div class=\"header-message\" style=\"\"><p>Foo</p></div>")
+ expect(subject.html_part).not_to have_body_text("<div class=\"footer-message\" style=\"\"><p>Bar</p></div>")
+
+ expect(subject.text_part).not_to have_body_text(/^Foo/)
+ expect(subject.text_part).not_to have_body_text(/Bar$/)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/discussions.rb b/spec/support/shared_examples/requests/api/discussions.rb
index e44da4faa5a..eff8e401bad 100644
--- a/spec/support/shared_examples/requests/api/discussions.rb
+++ b/spec/support/shared_examples/requests/api/discussions.rb
@@ -86,6 +86,37 @@ shared_examples 'discussions API' do |parent_type, noteable_type, id_name|
expect(response).to have_gitlab_http_status(404)
end
end
+
+ context 'when a project is public with private repo access' do
+ let!(:parent) { create(:project, :public, :repository, :repository_private, :snippets_private) }
+ let!(:user_without_access) { create(:user) }
+
+ context 'when user is not a team member of private repo' do
+ before do
+ project.team.truncate
+ end
+
+ context "creating a new note" do
+ before do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user_without_access), params: { body: 'hi!' }
+ end
+
+ it 'raises 404 error' do
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+
+ context "fetching a discussion" do
+ before do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions/#{note.discussion_id}", user_without_access)
+ end
+
+ it 'raises 404 error' do
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+ end
end
describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id/notes" do
diff --git a/spec/support/shared_examples/requests/api/merge_requests_list.rb b/spec/support/shared_examples/requests/api/merge_requests_list.rb
deleted file mode 100644
index 6713ec47ace..00000000000
--- a/spec/support/shared_examples/requests/api/merge_requests_list.rb
+++ /dev/null
@@ -1,335 +0,0 @@
-shared_examples 'merge requests list' do
- context 'when unauthenticated' do
- it 'returns merge requests for public projects' do
- get api(endpoint_path)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to be_an Array
- end
- end
-
- context 'when authenticated' do
- it 'avoids N+1 queries' do
- control = ActiveRecord::QueryRecorder.new do
- get api(endpoint_path, user)
- end
-
- create(:merge_request, state: 'closed', milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: 'Test', created_at: base_time)
-
- merge_request = create(:merge_request, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: 'Test', created_at: base_time)
-
- merge_request.metrics.update!(merged_by: user,
- latest_closed_by: user,
- latest_closed_at: 1.hour.ago,
- merged_at: 2.hours.ago)
-
- expect do
- get api(endpoint_path, user)
- end.not_to exceed_query_limit(control)
- end
-
- it 'returns an array of all merge_requests' do
- get api(endpoint_path, 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(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)
- expect(json_response.last['merge_commit_sha']).to be_nil
- expect(json_response.last['merge_commit_sha']).to eq(merge_request.merge_commit_sha)
- expect(json_response.last['downvotes']).to eq(1)
- expect(json_response.last['upvotes']).to eq(1)
- expect(json_response.last['labels']).to eq([label2.title, label.title])
- expect(json_response.first['title']).to eq(merge_request_merged.title)
- expect(json_response.first['sha']).to eq(merge_request_merged.diff_head_sha)
- expect(json_response.first['merge_commit_sha']).not_to be_nil
- expect(json_response.first['merge_commit_sha']).to eq(merge_request_merged.merge_commit_sha)
- end
-
- it 'returns an array of all merge_requests using simple mode' do
- path = endpoint_path + '?view=simple'
-
- get api(path, user)
-
- expect(response).to have_gitlab_http_status(200)
- 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(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')
- expect(json_response.first['iid']).to eq(merge_request_merged.iid)
- expect(json_response.first['title']).to eq(merge_request_merged.title)
- expect(json_response.first).to have_key('web_url')
- end
-
- it 'returns an array of all merge_requests' do
- path = endpoint_path + '?state'
-
- get api(path, 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(4)
- expect(json_response.last['title']).to eq(merge_request.title)
- end
-
- it 'returns an array of open merge_requests' do
- path = endpoint_path + '?state=opened'
-
- get api(path, 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(1)
- expect(json_response.last['title']).to eq(merge_request.title)
- end
-
- it 'returns an array of closed merge_requests' do
- path = endpoint_path + '?state=closed'
-
- get api(path, 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(1)
- expect(json_response.first['title']).to eq(merge_request_closed.title)
- end
-
- it 'returns an array of merged merge_requests' do
- path = endpoint_path + '?state=merged'
-
- get api(path, 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(1)
- expect(json_response.first['title']).to eq(merge_request_merged.title)
- end
-
- it 'matches V4 response schema' do
- get api(endpoint_path, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to match_response_schema('public_api/v4/merge_requests')
- end
-
- it 'returns an empty array if no issue matches milestone' do
- get api(endpoint_path, user), params: { milestone: '1.0.0' }
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(0)
- end
-
- it 'returns an empty array if milestone does not exist' do
- get api(endpoint_path, user), params: { milestone: 'foo' }
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(0)
- end
-
- it 'returns an array of merge requests in given milestone' do
- get api(endpoint_path, user), params: { milestone: '0.9' }
-
- 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
- get api(endpoint_path, user), params: { milestone: '0.9', state: 'closed' }
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(1)
- expect(json_response.first['id']).to eq(merge_request_closed.id)
- end
-
- it 'returns an array of labeled merge requests' do
- path = endpoint_path + "?labels=#{label.title}"
-
- get api(path, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(1)
- expect(json_response.first['labels']).to eq([label2.title, label.title])
- end
-
- it 'returns an array of labeled merge requests where all labels match' do
- path = endpoint_path + "?labels=#{label.title},foo,bar"
-
- get api(path, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(0)
- end
-
- it 'returns an empty array if no merge request matches labels' do
- path = endpoint_path + '?labels=foo,bar'
-
- get api(path, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(0)
- end
-
- it 'returns an array of merge requests with any label when filtering by any label' do
- get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_ANY }
-
- expect_paginated_array_response
- expect(json_response.length).to eq(1)
- expect(json_response.first['id']).to eq(merge_request.id)
- end
-
- it 'returns an array of merge requests without a label when filtering by no label' do
- get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_NONE }
-
- response_ids = json_response.map { |merge_request| merge_request['id'] }
-
- expect_paginated_array_response
- expect(response_ids).to contain_exactly(merge_request_closed.id, merge_request_merged.id, merge_request_locked.id)
- end
-
- it 'returns an array of labeled merge requests that are merged for a milestone' do
- bug_label = create(:label, title: 'bug', color: '#FFAABB', project: project)
-
- mr1 = create(:merge_request, state: 'merged', source_project: project, target_project: project, milestone: milestone)
- mr2 = create(:merge_request, state: 'merged', source_project: project, target_project: project, milestone: milestone1)
- mr3 = create(:merge_request, state: 'closed', source_project: project, target_project: project, milestone: milestone1)
- _mr = create(:merge_request, state: 'merged', source_project: project, target_project: project, milestone: milestone1)
-
- create(:label_link, label: bug_label, target: mr1)
- create(:label_link, label: bug_label, target: mr2)
- create(:label_link, label: bug_label, target: mr3)
-
- path = endpoint_path + "?labels=#{bug_label.title}&milestone=#{milestone1.title}&state=merged"
-
- get api(path, user)
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to be_an Array
- expect(json_response.length).to eq(1)
- expect(json_response.first['id']).to eq(mr2.id)
- end
-
- context 'with ordering' do
- before do
- @mr_later = mr_with_later_created_and_updated_at_time
- @mr_earlier = mr_with_earlier_created_and_updated_at_time
- end
-
- it 'returns an array of merge_requests in ascending order' do
- path = endpoint_path + '?sort=asc'
-
- get api(path, 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(4)
- response_dates = json_response.map { |merge_request| merge_request['created_at'] }
- expect(response_dates).to eq(response_dates.sort)
- end
-
- it 'returns an array of merge_requests in descending order' do
- path = endpoint_path + '?sort=desc'
-
- get api(path, 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(4)
- response_dates = json_response.map { |merge_request| merge_request['created_at'] }
- expect(response_dates).to eq(response_dates.sort.reverse)
- end
-
- context '2 merge requests with equal created_at' do
- let!(:closed_mr2) do
- create :merge_request,
- state: 'closed',
- milestone: milestone1,
- author: user,
- assignee: user,
- source_project: project,
- target_project: project,
- title: "Test",
- created_at: @mr_earlier.created_at
- end
-
- it 'page breaks first page correctly' do
- get api("#{endpoint_path}?sort=desc&per_page=4", user)
-
- response_ids = json_response.map { |merge_request| merge_request['id'] }
-
- expect(response_ids).to include(closed_mr2.id)
- expect(response_ids).not_to include(@mr_earlier.id)
- end
-
- it 'page breaks second page correctly' do
- get api("#{endpoint_path}?sort=desc&per_page=4&page=2", user)
-
- response_ids = json_response.map { |merge_request| merge_request['id'] }
-
- expect(response_ids).not_to include(closed_mr2.id)
- expect(response_ids).to include(@mr_earlier.id)
- end
- end
-
- it 'returns an array of merge_requests ordered by updated_at' do
- path = endpoint_path + '?order_by=updated_at'
-
- get api(path, 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(4)
- response_dates = json_response.map { |merge_request| merge_request['updated_at'] }
- expect(response_dates).to eq(response_dates.sort.reverse)
- end
-
- it 'returns an array of merge_requests ordered by created_at' do
- path = endpoint_path + '?order_by=created_at&sort=asc'
-
- get api(path, 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(4)
- response_dates = json_response.map { |merge_request| merge_request['created_at'] }
- expect(response_dates).to eq(response_dates.sort)
- end
- end
-
- context 'source_branch param' do
- it 'returns merge requests with the given source branch' do
- get api(endpoint_path, user), params: { source_branch: merge_request_closed.source_branch, state: 'all' }
-
- expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked)
- end
- end
-
- context 'target_branch param' do
- it 'returns merge requests with the given target branch' do
- get api(endpoint_path, user), params: { target_branch: merge_request_closed.target_branch, state: 'all' }
-
- expect_response_contain_exactly(merge_request_closed, merge_request_merged, merge_request_locked)
- end
- end
- end
-end
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 ec44b99d10e..9dbd1d8e867 100644
--- a/spec/support/shared_examples/services/boards/issues_move_service.rb
+++ b/spec/support/shared_examples/services/boards/issues_move_service.rb
@@ -39,6 +39,22 @@ shared_examples 'issues move service' do |group|
end
end
+ context 'when moving to backlog' do
+ let(:milestone) { create(:milestone, project: project) }
+ let!(:backlog) { create(:backlog_list, board: board1) }
+
+ let(:issue) { create(:labeled_issue, project: project, labels: [bug, development, testing, regression], milestone: milestone) }
+ let(:params) { { board_id: board1.id, from_list_id: list2.id, to_list_id: backlog.id } }
+
+ it 'keeps labels and milestone' do
+ described_class.new(parent, user, params).execute(issue)
+ issue.reload
+
+ expect(issue.labels).to contain_exactly(bug, regression)
+ expect(issue.milestone).to eq(milestone)
+ end
+ end
+
context 'when moving from closed' do
let(:issue) { create(:labeled_issue, :closed, project: project, labels: [bug]) }
let(:params) { { board_id: board1.id, from_list_id: closed.id, to_list_id: list2.id } }
diff --git a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb
index 14638a574a5..02de47a96dd 100644
--- a/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb
+++ b/spec/support/shared_examples/services/check_ingress_ip_address_service_shared_examples.rb
@@ -12,6 +12,14 @@ shared_examples 'check ingress ip executions' do |app_name|
end
end
+ context 'when the ingress external hostname is available' do
+ it 'updates the external_hostname for the app' do
+ subject
+
+ expect(application.external_hostname).to eq('localhost.localdomain')
+ end
+ end
+
context 'when the ingress ip address is not available' do
let(:ingress) { nil }
diff --git a/spec/support/shared_examples/snippet_visibility.rb b/spec/support/shared_examples/snippet_visibility.rb
deleted file mode 100644
index 3a7c69b7877..00000000000
--- a/spec/support/shared_examples/snippet_visibility.rb
+++ /dev/null
@@ -1,322 +0,0 @@
-RSpec.shared_examples 'snippet visibility' do
- let!(:author) { create(:user) }
- let!(:member) { create(:user) }
- let!(:external) { create(:user, :external) }
-
- let!(:snippet_type_visibilities) do
- {
- public: Snippet::PUBLIC,
- internal: Snippet::INTERNAL,
- private: Snippet::PRIVATE
- }
- end
-
- context "For project snippets" do
- let!(:users) do
- {
- unauthenticated: nil,
- external: external,
- non_member: create(:user),
- member: member,
- author: author
- }
- end
-
- let!(:project_type_visibilities) do
- {
- public: Gitlab::VisibilityLevel::PUBLIC,
- internal: Gitlab::VisibilityLevel::INTERNAL,
- private: Gitlab::VisibilityLevel::PRIVATE
- }
- end
-
- let(:project_feature_visibilities) do
- {
- enabled: ProjectFeature::ENABLED,
- private: ProjectFeature::PRIVATE,
- disabled: ProjectFeature::DISABLED
- }
- end
-
- where(:project_type, :feature_visibility, :user_type, :snippet_type, :outcome) do
- [
- # Public projects
- [:public, :enabled, :unauthenticated, :public, true],
- [:public, :enabled, :unauthenticated, :internal, false],
- [:public, :enabled, :unauthenticated, :private, false],
-
- [:public, :enabled, :external, :public, true],
- [:public, :enabled, :external, :internal, false],
- [:public, :enabled, :external, :private, false],
-
- [:public, :enabled, :non_member, :public, true],
- [:public, :enabled, :non_member, :internal, true],
- [:public, :enabled, :non_member, :private, false],
-
- [:public, :enabled, :member, :public, true],
- [:public, :enabled, :member, :internal, true],
- [:public, :enabled, :member, :private, true],
-
- [:public, :enabled, :author, :public, true],
- [:public, :enabled, :author, :internal, true],
- [:public, :enabled, :author, :private, true],
-
- [:public, :private, :unauthenticated, :public, false],
- [:public, :private, :unauthenticated, :internal, false],
- [:public, :private, :unauthenticated, :private, false],
-
- [:public, :private, :external, :public, false],
- [:public, :private, :external, :internal, false],
- [:public, :private, :external, :private, false],
-
- [:public, :private, :non_member, :public, false],
- [:public, :private, :non_member, :internal, false],
- [:public, :private, :non_member, :private, false],
-
- [:public, :private, :member, :public, true],
- [:public, :private, :member, :internal, true],
- [:public, :private, :member, :private, true],
-
- [:public, :private, :author, :public, true],
- [:public, :private, :author, :internal, true],
- [:public, :private, :author, :private, true],
-
- [:public, :disabled, :unauthenticated, :public, false],
- [:public, :disabled, :unauthenticated, :internal, false],
- [:public, :disabled, :unauthenticated, :private, false],
-
- [:public, :disabled, :external, :public, false],
- [:public, :disabled, :external, :internal, false],
- [:public, :disabled, :external, :private, false],
-
- [:public, :disabled, :non_member, :public, false],
- [:public, :disabled, :non_member, :internal, false],
- [:public, :disabled, :non_member, :private, false],
-
- [:public, :disabled, :member, :public, false],
- [:public, :disabled, :member, :internal, false],
- [:public, :disabled, :member, :private, false],
-
- [:public, :disabled, :author, :public, false],
- [:public, :disabled, :author, :internal, false],
- [:public, :disabled, :author, :private, false],
-
- # Internal projects
- [:internal, :enabled, :unauthenticated, :public, false],
- [:internal, :enabled, :unauthenticated, :internal, false],
- [:internal, :enabled, :unauthenticated, :private, false],
-
- [:internal, :enabled, :external, :public, false],
- [:internal, :enabled, :external, :internal, false],
- [:internal, :enabled, :external, :private, false],
-
- [:internal, :enabled, :non_member, :public, true],
- [:internal, :enabled, :non_member, :internal, true],
- [:internal, :enabled, :non_member, :private, false],
-
- [:internal, :enabled, :member, :public, true],
- [:internal, :enabled, :member, :internal, true],
- [:internal, :enabled, :member, :private, true],
-
- [:internal, :enabled, :author, :public, true],
- [:internal, :enabled, :author, :internal, true],
- [:internal, :enabled, :author, :private, true],
-
- [:internal, :private, :unauthenticated, :public, false],
- [:internal, :private, :unauthenticated, :internal, false],
- [:internal, :private, :unauthenticated, :private, false],
-
- [:internal, :private, :external, :public, false],
- [:internal, :private, :external, :internal, false],
- [:internal, :private, :external, :private, false],
-
- [:internal, :private, :non_member, :public, false],
- [:internal, :private, :non_member, :internal, false],
- [:internal, :private, :non_member, :private, false],
-
- [:internal, :private, :member, :public, true],
- [:internal, :private, :member, :internal, true],
- [:internal, :private, :member, :private, true],
-
- [:internal, :private, :author, :public, true],
- [:internal, :private, :author, :internal, true],
- [:internal, :private, :author, :private, true],
-
- [:internal, :disabled, :unauthenticated, :public, false],
- [:internal, :disabled, :unauthenticated, :internal, false],
- [:internal, :disabled, :unauthenticated, :private, false],
-
- [:internal, :disabled, :external, :public, false],
- [:internal, :disabled, :external, :internal, false],
- [:internal, :disabled, :external, :private, false],
-
- [:internal, :disabled, :non_member, :public, false],
- [:internal, :disabled, :non_member, :internal, false],
- [:internal, :disabled, :non_member, :private, false],
-
- [:internal, :disabled, :member, :public, false],
- [:internal, :disabled, :member, :internal, false],
- [:internal, :disabled, :member, :private, false],
-
- [:internal, :disabled, :author, :public, false],
- [:internal, :disabled, :author, :internal, false],
- [:internal, :disabled, :author, :private, false],
-
- # Private projects
- [:private, :enabled, :unauthenticated, :public, false],
- [:private, :enabled, :unauthenticated, :internal, false],
- [:private, :enabled, :unauthenticated, :private, false],
-
- [:private, :enabled, :external, :public, true],
- [:private, :enabled, :external, :internal, true],
- [:private, :enabled, :external, :private, true],
-
- [:private, :enabled, :non_member, :public, false],
- [:private, :enabled, :non_member, :internal, false],
- [:private, :enabled, :non_member, :private, false],
-
- [:private, :enabled, :member, :public, true],
- [:private, :enabled, :member, :internal, true],
- [:private, :enabled, :member, :private, true],
-
- [:private, :enabled, :author, :public, true],
- [:private, :enabled, :author, :internal, true],
- [:private, :enabled, :author, :private, true],
-
- [:private, :private, :unauthenticated, :public, false],
- [:private, :private, :unauthenticated, :internal, false],
- [:private, :private, :unauthenticated, :private, false],
-
- [:private, :private, :external, :public, true],
- [:private, :private, :external, :internal, true],
- [:private, :private, :external, :private, true],
-
- [:private, :private, :non_member, :public, false],
- [:private, :private, :non_member, :internal, false],
- [:private, :private, :non_member, :private, false],
-
- [:private, :private, :member, :public, true],
- [:private, :private, :member, :internal, true],
- [:private, :private, :member, :private, true],
-
- [:private, :private, :author, :public, true],
- [:private, :private, :author, :internal, true],
- [:private, :private, :author, :private, true],
-
- [:private, :disabled, :unauthenticated, :public, false],
- [:private, :disabled, :unauthenticated, :internal, false],
- [:private, :disabled, :unauthenticated, :private, false],
-
- [:private, :disabled, :external, :public, false],
- [:private, :disabled, :external, :internal, false],
- [:private, :disabled, :external, :private, false],
-
- [:private, :disabled, :non_member, :public, false],
- [:private, :disabled, :non_member, :internal, false],
- [:private, :disabled, :non_member, :private, false],
-
- [:private, :disabled, :member, :public, false],
- [:private, :disabled, :member, :internal, false],
- [:private, :disabled, :member, :private, false],
-
- [:private, :disabled, :author, :public, false],
- [:private, :disabled, :author, :internal, false],
- [:private, :disabled, :author, :private, false]
- ]
- end
-
- with_them do
- let!(:project) { create(:project, visibility_level: project_type_visibilities[project_type]) }
- let!(:project_feature) { project.project_feature.update_column(:snippets_access_level, project_feature_visibilities[feature_visibility]) }
- let!(:user) { users[user_type] }
- let!(:snippet) { create(:project_snippet, visibility_level: snippet_type_visibilities[snippet_type], project: project, author: author) }
- let!(:members) do
- project.add_developer(author)
- project.add_developer(member)
- project.add_developer(external) if project.private?
- end
-
- context "For #{params[:project_type]} project and #{params[:user_type]} users" do
- it 'should agree with the read_project_snippet policy' do
- expect(can?(user, :read_project_snippet, snippet)).to eq(outcome)
- end
-
- it 'should return proper outcome' do
- results = described_class.new(user, project: project).execute
- expect(results.include?(snippet)).to eq(outcome)
- end
- end
-
- context "Without a given project and #{params[:user_type]} users" do
- it 'should return proper outcome' do
- results = described_class.new(user).execute
- expect(results.include?(snippet)).to eq(outcome)
- end
-
- it 'returns no snippets when the user cannot read cross project' do
- allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
-
- snippets = described_class.new(user).execute
-
- expect(snippets).to be_empty
- end
- end
- end
- end
-
- context 'For personal snippets' do
- let!(:users) do
- {
- unauthenticated: nil,
- external: external,
- non_member: create(:user),
- author: author
- }
- end
-
- where(:snippet_visibility, :user_type, :outcome) do
- [
- [:public, :unauthenticated, true],
- [:public, :external, true],
- [:public, :non_member, true],
- [:public, :author, true],
-
- [:internal, :unauthenticated, false],
- [:internal, :external, false],
- [:internal, :non_member, true],
- [:internal, :author, true],
-
- [:private, :unauthenticated, false],
- [:private, :external, false],
- [:private, :non_member, false],
- [:private, :author, true]
- ]
- end
-
- with_them do
- let!(:user) { users[user_type] }
- let!(:snippet) { create(:personal_snippet, visibility_level: snippet_type_visibilities[snippet_visibility], author: author) }
-
- context "For personal and #{params[:snippet_visibility]} snippets with #{params[:user_type]} user" do
- it 'should agree with read_personal_snippet policy' do
- expect(can?(user, :read_personal_snippet, snippet)).to eq(outcome)
- end
-
- it 'should return proper outcome' do
- results = described_class.new(user).execute
- expect(results.include?(snippet)).to eq(outcome)
- end
-
- it 'should return personal snippets when the user cannot read cross project' do
- allow(Ability).to receive(:allowed?).and_call_original
- allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
-
- results = described_class.new(user).execute
-
- expect(results.include?(snippet)).to eq(outcome)
- end
- end
- end
- end
-end
diff --git a/spec/support/shared_examples/snippet_visibility_shared_examples.rb b/spec/support/shared_examples/snippet_visibility_shared_examples.rb
new file mode 100644
index 00000000000..4f662db2120
--- /dev/null
+++ b/spec/support/shared_examples/snippet_visibility_shared_examples.rb
@@ -0,0 +1,306 @@
+RSpec.shared_examples 'snippet visibility' do
+ using RSpec::Parameterized::TableSyntax
+
+ # Make sure no snippets exist prior to running the test matrix
+ before(:context) do
+ DatabaseCleaner.clean_with(:truncation)
+ end
+
+ set(:author) { create(:user) }
+ set(:member) { create(:user) }
+ set(:external) { create(:user, :external) }
+
+ context "For project snippets" do
+ let!(:users) do
+ {
+ unauthenticated: nil,
+ external: external,
+ non_member: create(:user),
+ member: member,
+ author: author
+ }
+ end
+
+ where(:project_type, :feature_visibility, :user_type, :snippet_type, :outcome) do
+ [
+ # Public projects
+ [:public, ProjectFeature::ENABLED, :unauthenticated, Snippet::PUBLIC, true],
+ [:public, ProjectFeature::ENABLED, :unauthenticated, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::ENABLED, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::ENABLED, :external, Snippet::PUBLIC, true],
+ [:public, ProjectFeature::ENABLED, :external, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::ENABLED, :external, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::ENABLED, :non_member, Snippet::PUBLIC, true],
+ [:public, ProjectFeature::ENABLED, :non_member, Snippet::INTERNAL, true],
+ [:public, ProjectFeature::ENABLED, :non_member, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::ENABLED, :member, Snippet::PUBLIC, true],
+ [:public, ProjectFeature::ENABLED, :member, Snippet::INTERNAL, true],
+ [:public, ProjectFeature::ENABLED, :member, Snippet::PRIVATE, true],
+
+ [:public, ProjectFeature::ENABLED, :author, Snippet::PUBLIC, true],
+ [:public, ProjectFeature::ENABLED, :author, Snippet::INTERNAL, true],
+ [:public, ProjectFeature::ENABLED, :author, Snippet::PRIVATE, true],
+
+ [:public, ProjectFeature::PRIVATE, :unauthenticated, Snippet::PUBLIC, false],
+ [:public, ProjectFeature::PRIVATE, :unauthenticated, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::PRIVATE, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::PRIVATE, :external, Snippet::PUBLIC, false],
+ [:public, ProjectFeature::PRIVATE, :external, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::PRIVATE, :external, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::PRIVATE, :non_member, Snippet::PUBLIC, false],
+ [:public, ProjectFeature::PRIVATE, :non_member, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::PRIVATE, :non_member, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::PRIVATE, :member, Snippet::PUBLIC, true],
+ [:public, ProjectFeature::PRIVATE, :member, Snippet::INTERNAL, true],
+ [:public, ProjectFeature::PRIVATE, :member, Snippet::PRIVATE, true],
+
+ [:public, ProjectFeature::PRIVATE, :author, Snippet::PUBLIC, true],
+ [:public, ProjectFeature::PRIVATE, :author, Snippet::INTERNAL, true],
+ [:public, ProjectFeature::PRIVATE, :author, Snippet::PRIVATE, true],
+
+ [:public, ProjectFeature::DISABLED, :unauthenticated, Snippet::PUBLIC, false],
+ [:public, ProjectFeature::DISABLED, :unauthenticated, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::DISABLED, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::DISABLED, :external, Snippet::PUBLIC, false],
+ [:public, ProjectFeature::DISABLED, :external, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::DISABLED, :external, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::DISABLED, :non_member, Snippet::PUBLIC, false],
+ [:public, ProjectFeature::DISABLED, :non_member, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::DISABLED, :non_member, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::DISABLED, :member, Snippet::PUBLIC, false],
+ [:public, ProjectFeature::DISABLED, :member, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::DISABLED, :member, Snippet::PRIVATE, false],
+
+ [:public, ProjectFeature::DISABLED, :author, Snippet::PUBLIC, false],
+ [:public, ProjectFeature::DISABLED, :author, Snippet::INTERNAL, false],
+ [:public, ProjectFeature::DISABLED, :author, Snippet::PRIVATE, false],
+
+ # Internal projects
+ [:internal, ProjectFeature::ENABLED, :unauthenticated, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::ENABLED, :unauthenticated, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::ENABLED, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::ENABLED, :external, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::ENABLED, :external, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::ENABLED, :external, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::ENABLED, :non_member, Snippet::PUBLIC, true],
+ [:internal, ProjectFeature::ENABLED, :non_member, Snippet::INTERNAL, true],
+ [:internal, ProjectFeature::ENABLED, :non_member, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::ENABLED, :member, Snippet::PUBLIC, true],
+ [:internal, ProjectFeature::ENABLED, :member, Snippet::INTERNAL, true],
+ [:internal, ProjectFeature::ENABLED, :member, Snippet::PRIVATE, true],
+
+ [:internal, ProjectFeature::ENABLED, :author, Snippet::PUBLIC, true],
+ [:internal, ProjectFeature::ENABLED, :author, Snippet::INTERNAL, true],
+ [:internal, ProjectFeature::ENABLED, :author, Snippet::PRIVATE, true],
+
+ [:internal, ProjectFeature::PRIVATE, :unauthenticated, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::PRIVATE, :unauthenticated, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::PRIVATE, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::PRIVATE, :external, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::PRIVATE, :external, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::PRIVATE, :external, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::PRIVATE, :non_member, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::PRIVATE, :non_member, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::PRIVATE, :non_member, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::PRIVATE, :member, Snippet::PUBLIC, true],
+ [:internal, ProjectFeature::PRIVATE, :member, Snippet::INTERNAL, true],
+ [:internal, ProjectFeature::PRIVATE, :member, Snippet::PRIVATE, true],
+
+ [:internal, ProjectFeature::PRIVATE, :author, Snippet::PUBLIC, true],
+ [:internal, ProjectFeature::PRIVATE, :author, Snippet::INTERNAL, true],
+ [:internal, ProjectFeature::PRIVATE, :author, Snippet::PRIVATE, true],
+
+ [:internal, ProjectFeature::DISABLED, :unauthenticated, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::DISABLED, :unauthenticated, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::DISABLED, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::DISABLED, :external, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::DISABLED, :external, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::DISABLED, :external, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::DISABLED, :non_member, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::DISABLED, :non_member, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::DISABLED, :non_member, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::DISABLED, :member, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::DISABLED, :member, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::DISABLED, :member, Snippet::PRIVATE, false],
+
+ [:internal, ProjectFeature::DISABLED, :author, Snippet::PUBLIC, false],
+ [:internal, ProjectFeature::DISABLED, :author, Snippet::INTERNAL, false],
+ [:internal, ProjectFeature::DISABLED, :author, Snippet::PRIVATE, false],
+
+ # Private projects
+ [:private, ProjectFeature::ENABLED, :unauthenticated, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::ENABLED, :unauthenticated, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::ENABLED, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:private, ProjectFeature::ENABLED, :external, Snippet::PUBLIC, true],
+ [:private, ProjectFeature::ENABLED, :external, Snippet::INTERNAL, true],
+ [:private, ProjectFeature::ENABLED, :external, Snippet::PRIVATE, true],
+
+ [:private, ProjectFeature::ENABLED, :non_member, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::ENABLED, :non_member, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::ENABLED, :non_member, Snippet::PRIVATE, false],
+
+ [:private, ProjectFeature::ENABLED, :member, Snippet::PUBLIC, true],
+ [:private, ProjectFeature::ENABLED, :member, Snippet::INTERNAL, true],
+ [:private, ProjectFeature::ENABLED, :member, Snippet::PRIVATE, true],
+
+ [:private, ProjectFeature::ENABLED, :author, Snippet::PUBLIC, true],
+ [:private, ProjectFeature::ENABLED, :author, Snippet::INTERNAL, true],
+ [:private, ProjectFeature::ENABLED, :author, Snippet::PRIVATE, true],
+
+ [:private, ProjectFeature::PRIVATE, :unauthenticated, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::PRIVATE, :unauthenticated, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::PRIVATE, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:private, ProjectFeature::PRIVATE, :external, Snippet::PUBLIC, true],
+ [:private, ProjectFeature::PRIVATE, :external, Snippet::INTERNAL, true],
+ [:private, ProjectFeature::PRIVATE, :external, Snippet::PRIVATE, true],
+
+ [:private, ProjectFeature::PRIVATE, :non_member, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::PRIVATE, :non_member, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::PRIVATE, :non_member, Snippet::PRIVATE, false],
+
+ [:private, ProjectFeature::PRIVATE, :member, Snippet::PUBLIC, true],
+ [:private, ProjectFeature::PRIVATE, :member, Snippet::INTERNAL, true],
+ [:private, ProjectFeature::PRIVATE, :member, Snippet::PRIVATE, true],
+
+ [:private, ProjectFeature::PRIVATE, :author, Snippet::PUBLIC, true],
+ [:private, ProjectFeature::PRIVATE, :author, Snippet::INTERNAL, true],
+ [:private, ProjectFeature::PRIVATE, :author, Snippet::PRIVATE, true],
+
+ [:private, ProjectFeature::DISABLED, :unauthenticated, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::DISABLED, :unauthenticated, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::DISABLED, :unauthenticated, Snippet::PRIVATE, false],
+
+ [:private, ProjectFeature::DISABLED, :external, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::DISABLED, :external, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::DISABLED, :external, Snippet::PRIVATE, false],
+
+ [:private, ProjectFeature::DISABLED, :non_member, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::DISABLED, :non_member, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::DISABLED, :non_member, Snippet::PRIVATE, false],
+
+ [:private, ProjectFeature::DISABLED, :member, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::DISABLED, :member, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::DISABLED, :member, Snippet::PRIVATE, false],
+
+ [:private, ProjectFeature::DISABLED, :author, Snippet::PUBLIC, false],
+ [:private, ProjectFeature::DISABLED, :author, Snippet::INTERNAL, false],
+ [:private, ProjectFeature::DISABLED, :author, Snippet::PRIVATE, false]
+ ]
+ end
+
+ with_them do
+ let!(:project) { create(:project, visibility_level: Gitlab::VisibilityLevel.level_value(project_type.to_s)) }
+ let!(:project_feature) { project.project_feature.update_column(:snippets_access_level, feature_visibility) }
+ let!(:user) { users[user_type] }
+ let!(:snippet) { create(:project_snippet, visibility_level: snippet_type, project: project, author: author) }
+ let!(:members) do
+ project.add_developer(author)
+ project.add_developer(member)
+ project.add_developer(external) if project.private?
+ end
+
+ context "For #{params[:project_type]} project and #{params[:user_type]} users" do
+ it 'should agree with the read_project_snippet policy' do
+ expect(can?(user, :read_project_snippet, snippet)).to eq(outcome)
+ end
+
+ it 'should return proper outcome' do
+ results = described_class.new(user, project: project).execute
+
+ expect(results.include?(snippet)).to eq(outcome)
+ end
+ end
+
+ context "Without a given project and #{params[:user_type]} users" do
+ it 'should return proper outcome' do
+ results = described_class.new(user).execute
+ expect(results.include?(snippet)).to eq(outcome)
+ end
+
+ it 'returns no snippets when the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ snippets = described_class.new(user).execute
+
+ expect(snippets).to be_empty
+ end
+ end
+ end
+ end
+
+ context 'For personal snippets' do
+ let!(:users) do
+ {
+ unauthenticated: nil,
+ external: external,
+ non_member: create(:user),
+ author: author
+ }
+ end
+
+ where(:snippet_visibility, :user_type, :outcome) do
+ [
+ [Snippet::PUBLIC, :unauthenticated, true],
+ [Snippet::PUBLIC, :external, true],
+ [Snippet::PUBLIC, :non_member, true],
+ [Snippet::PUBLIC, :author, true],
+
+ [Snippet::INTERNAL, :unauthenticated, false],
+ [Snippet::INTERNAL, :external, false],
+ [Snippet::INTERNAL, :non_member, true],
+ [Snippet::INTERNAL, :author, true],
+
+ [Snippet::PRIVATE, :unauthenticated, false],
+ [Snippet::PRIVATE, :external, false],
+ [Snippet::PRIVATE, :non_member, false],
+ [Snippet::PRIVATE, :author, true]
+ ]
+ end
+
+ with_them do
+ let!(:user) { users[user_type] }
+ let!(:snippet) { create(:personal_snippet, visibility_level: snippet_visibility, author: author) }
+
+ context "For personal and #{params[:snippet_visibility]} snippets with #{params[:user_type]} user" do
+ it 'should agree with read_personal_snippet policy' do
+ expect(can?(user, :read_personal_snippet, snippet)).to eq(outcome)
+ end
+
+ it 'should return proper outcome' do
+ results = described_class.new(user).execute
+ expect(results.include?(snippet)).to eq(outcome)
+ end
+
+ it 'should return personal snippets when the user cannot read cross project' do
+ allow(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
+
+ results = described_class.new(user).execute
+
+ expect(results.include?(snippet)).to eq(outcome)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/support/shared_examples/views/nav_sidebar.rb b/spec/support/shared_examples/views/nav_sidebar.rb
new file mode 100644
index 00000000000..6ac5abe275d
--- /dev/null
+++ b/spec/support/shared_examples/views/nav_sidebar.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+shared_examples 'has nav sidebar' do
+ it 'has collapsed nav sidebar on mobile' do
+ render
+
+ expect(rendered).to have_selector('.nav-sidebar')
+ expect(rendered).not_to have_selector('.sidebar-collapsed-desktop')
+ expect(rendered).not_to have_selector('.sidebar-expanded-mobile')
+ end
+end
diff --git a/spec/support/webmock.rb b/spec/support/webmock.rb
index af2906b7568..9ac7e7fc515 100644
--- a/spec/support/webmock.rb
+++ b/spec/support/webmock.rb
@@ -1,4 +1,12 @@
require 'webmock'
require 'webmock/rspec'
-WebMock.disable_net_connect!(allow_localhost: true)
+def webmock_allowed_hosts
+ %w[elasticsearch registry.gitlab.com-gitlab-org-test-elastic-image].tap do |hosts|
+ if ENV.key?('ELASTIC_URL')
+ hosts << URI.parse(ENV['ELASTIC_URL']).host
+ end
+ end.uniq
+end
+
+WebMock.disable_net_connect!(allow_localhost: true, allow: webmock_allowed_hosts)
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index a8fae4a88a3..bdbd39475b9 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -21,9 +21,6 @@ describe 'gitlab:app namespace rake task' do
# empty task as env is already loaded
Rake::Task.define_task :environment
-
- # We need this directory to run `gitlab:backup:create` task
- FileUtils.mkdir_p('public/uploads')
end
before do
@@ -38,6 +35,7 @@ describe 'gitlab:app namespace rake task' do
end
def run_rake_task(task_name)
+ FileUtils.mkdir_p('tmp/tests/public/uploads')
Rake::Task[task_name].reenable
Rake.application.invoke_task task_name
end
diff --git a/spec/tasks/gitlab/shell_rake_spec.rb b/spec/tasks/gitlab/shell_rake_spec.rb
index 0ed5d3e27b9..a9d14070177 100644
--- a/spec/tasks/gitlab/shell_rake_spec.rb
+++ b/spec/tasks/gitlab/shell_rake_spec.rb
@@ -8,7 +8,7 @@ describe 'gitlab:shell rake tasks' do
end
after do
- TestEnv.create_fake_git_hooks
+ TestEnv.sabotage_gitlab_shell_hooks
end
describe 'install task' do
diff --git a/spec/tasks/gitlab/storage_rake_spec.rb b/spec/tasks/gitlab/storage_rake_spec.rb
index 6b50670c3c0..4b04d9cec39 100644
--- a/spec/tasks/gitlab/storage_rake_spec.rb
+++ b/spec/tasks/gitlab/storage_rake_spec.rb
@@ -1,6 +1,6 @@
require 'rake_helper'
-describe 'rake gitlab:storage:*' do
+describe 'rake gitlab:storage:*', :sidekiq do
before do
Rake.application.rake_require 'tasks/gitlab/storage'
@@ -43,9 +43,7 @@ describe 'rake gitlab:storage:*' do
end
end
- describe 'gitlab:storage:migrate_to_hashed' do
- let(:task) { 'gitlab:storage:migrate_to_hashed' }
-
+ shared_examples "make sure database is writable" do
context 'read-only database' do
it 'does nothing' do
expect(Gitlab::Database).to receive(:read_only?).and_return(true)
@@ -55,48 +53,68 @@ describe 'rake gitlab:storage:*' do
expect { run_rake_task(task) }.to output(/This task requires database write access. Exiting./).to_stderr
end
end
+ end
- context '0 legacy projects' do
- it 'does nothing' do
- expect(::HashedStorage::MigratorWorker).not_to receive(:perform_async)
+ shared_examples "handles custom BATCH env var" do |worker_klass|
+ context 'in batches of 1' do
+ before do
+ stub_env('BATCH' => 1)
+ end
+
+ it "enqueues one #{worker_klass} per project" do
+ projects.each do |project|
+ expect(worker_klass).to receive(:perform_async).with(project.id, project.id)
+ end
run_rake_task(task)
end
end
- context '3 legacy projects' do
- let(:projects) { create_list(:project, 3, :legacy_storage) }
+ context 'in batches of 2' do
+ before do
+ stub_env('BATCH' => 2)
+ end
- context 'in batches of 1' do
- before do
- stub_env('BATCH' => 1)
+ it "enqueues one #{worker_klass} per 2 projects" do
+ projects.map(&:id).sort.each_slice(2) do |first, last|
+ last ||= first
+ expect(worker_klass).to receive(:perform_async).with(first, last)
end
- it 'enqueues one HashedStorage::MigratorWorker per project' do
- projects.each do |project|
- expect(::HashedStorage::MigratorWorker).to receive(:perform_async).with(project.id, project.id)
- end
-
- run_rake_task(task)
- end
+ run_rake_task(task)
end
+ end
+ end
- context 'in batches of 2' do
- before do
- stub_env('BATCH' => 2)
- end
+ describe 'gitlab:storage:migrate_to_hashed' do
+ let(:task) { 'gitlab:storage:migrate_to_hashed' }
- it 'enqueues one HashedStorage::MigratorWorker per 2 projects' do
- projects.map(&:id).sort.each_slice(2) do |first, last|
- last ||= first
- expect(::HashedStorage::MigratorWorker).to receive(:perform_async).with(first, last)
- end
+ context 'with rollback already scheduled', :redis do
+ it 'does nothing' do
+ Sidekiq::Testing.disable! do
+ ::HashedStorage::RollbackerWorker.perform_async(1, 5)
+
+ expect(Project).not_to receive(:with_unmigrated_storage)
- run_rake_task(task)
+ expect { run_rake_task(task) }.to output(/There is already a rollback operation in progress/).to_stderr
end
end
end
+ context 'with 0 legacy projects' do
+ it 'does nothing' do
+ expect(::HashedStorage::MigratorWorker).not_to receive(:perform_async)
+
+ run_rake_task(task)
+ end
+ end
+
+ context 'with 3 legacy projects' do
+ let(:projects) { create_list(:project, 3, :legacy_storage) }
+
+ it_behaves_like "handles custom BATCH env var", ::HashedStorage::MigratorWorker
+ end
+
context 'with same id in range' do
it 'displays message when project cant be found' do
stub_env('ID_FROM', 99999)
@@ -123,6 +141,38 @@ describe 'rake gitlab:storage:*' do
end
end
+ describe 'gitlab:storage:rollback_to_legacy' do
+ let(:task) { 'gitlab:storage:rollback_to_legacy' }
+
+ it_behaves_like 'make sure database is writable'
+
+ context 'with migration already scheduled', :redis do
+ it 'does nothing' do
+ Sidekiq::Testing.disable! do
+ ::HashedStorage::MigratorWorker.perform_async(1, 5)
+
+ expect(Project).not_to receive(:with_unmigrated_storage)
+
+ expect { run_rake_task(task) }.to output(/There is already a migration operation in progress/).to_stderr
+ end
+ end
+ end
+
+ context 'with 0 hashed projects' do
+ it 'does nothing' do
+ expect(::HashedStorage::RollbackerWorker).not_to receive(:perform_async)
+
+ run_rake_task(task)
+ end
+ end
+
+ context 'with 3 hashed projects' do
+ let(:projects) { create_list(:project, 3) }
+
+ it_behaves_like "handles custom BATCH env var", ::HashedStorage::RollbackerWorker
+ end
+ end
+
describe 'gitlab:storage:legacy_projects' do
it_behaves_like 'rake entities summary', 'projects', 'Legacy' do
let(:task) { 'gitlab:storage:legacy_projects' }
diff --git a/spec/uploaders/file_mover_spec.rb b/spec/uploaders/file_mover_spec.rb
index de29d0c943f..e474a714b10 100644
--- a/spec/uploaders/file_mover_spec.rb
+++ b/spec/uploaders/file_mover_spec.rb
@@ -1,8 +1,9 @@
require 'spec_helper'
describe FileMover do
+ include FileMoverHelpers
+
let(:filename) { 'banana_sample.gif' }
- let(:file) { fixture_file_upload(File.join('spec', 'fixtures', filename)) }
let(:temp_file_path) { File.join('uploads/-/system/temp', 'secret55', filename) }
let(:temp_description) do
@@ -12,7 +13,7 @@ describe FileMover do
let(:file_path) { File.join('uploads/-/system/personal_snippet', snippet.id.to_s, 'secret55', filename) }
let(:snippet) { create(:personal_snippet, description: temp_description) }
- subject { described_class.new(file_path, snippet).execute }
+ subject { described_class.new(temp_file_path, snippet).execute }
describe '#execute' do
before do
@@ -20,6 +21,8 @@ describe FileMover do
expect(FileUtils).to receive(:move).with(a_string_including(temp_file_path), a_string_including(file_path))
allow_any_instance_of(CarrierWave::SanitizedFile).to receive(:exists?).and_return(true)
allow_any_instance_of(CarrierWave::SanitizedFile).to receive(:size).and_return(10)
+
+ stub_file_mover(temp_file_path)
end
context 'when move and field update successful' do
@@ -66,4 +69,30 @@ describe FileMover do
end
end
end
+
+ context 'security' do
+ context 'when relative path is involved' do
+ let(:temp_file_path) { File.join('uploads/-/system/temp', '..', 'another_subdir_of_temp') }
+
+ it 'does not trigger move if path is outside designated directory' do
+ stub_file_mover('uploads/-/system/another_subdir_of_temp')
+ expect(FileUtils).not_to receive(:move)
+
+ subject
+
+ expect(snippet.reload.description).to eq(temp_description)
+ end
+ end
+
+ context 'when symlink is involved' do
+ it 'does not trigger move if path is outside designated directory' do
+ stub_file_mover(temp_file_path, stub_real_path: Pathname('/etc'))
+ expect(FileUtils).not_to receive(:move)
+
+ subject
+
+ expect(snippet.reload.description).to eq(temp_description)
+ end
+ end
+ end
end
diff --git a/spec/validators/devise_email_validator_spec.rb b/spec/validators/devise_email_validator_spec.rb
new file mode 100644
index 00000000000..7860b659bd3
--- /dev/null
+++ b/spec/validators/devise_email_validator_spec.rb
@@ -0,0 +1,94 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe DeviseEmailValidator do
+ let!(:user) { build(:user, public_email: 'test@example.com') }
+ subject { validator.validate(user) }
+
+ describe 'validations' do
+ context 'by default' do
+ let(:validator) { described_class.new(attributes: [:public_email]) }
+
+ it 'allows when email is valid' do
+ subject
+
+ expect(user.errors).to be_empty
+ end
+
+ it 'returns error when email is invalid' do
+ user.public_email = 'invalid'
+
+ subject
+
+ expect(user.errors).to be_present
+ expect(user.errors.first[1]).to eq 'is invalid'
+ end
+
+ it 'returns error when email is nil' do
+ user.public_email = nil
+
+ subject
+
+ expect(user.errors).to be_present
+ end
+
+ it 'returns error when email is blank' do
+ user.public_email = ''
+
+ subject
+
+ expect(user.errors).to be_present
+ expect(user.errors.first[1]).to eq 'is invalid'
+ end
+ end
+ end
+
+ context 'when regexp is set as Regexp' do
+ let(:validator) { described_class.new(attributes: [:public_email], regexp: /[0-9]/) }
+
+ it 'allows when value match' do
+ user.public_email = '1'
+
+ subject
+
+ expect(user.errors).to be_empty
+ end
+
+ it 'returns error when value does not match' do
+ subject
+
+ expect(user.errors).to be_present
+ end
+ end
+
+ context 'when regexp is set as String' do
+ it 'raise argument error' do
+ expect { described_class.new( { regexp: 'something' } ) }.to raise_error ArgumentError
+ end
+ end
+
+ context 'when allow_nil is set to true' do
+ let(:validator) { described_class.new(attributes: [:public_email], allow_nil: true) }
+
+ it 'allows when email is nil' do
+ user.public_email = nil
+
+ subject
+
+ expect(user.errors).to be_empty
+ end
+ end
+
+ context 'when allow_blank is set to true' do
+ let(:validator) { described_class.new(attributes: [:public_email], allow_blank: true) }
+
+ it 'allows when email is blank' do
+ user.public_email = ''
+
+ subject
+
+ expect(user.errors).to be_empty
+ end
+ end
+end
diff --git a/spec/validators/sha_validator_spec.rb b/spec/validators/sha_validator_spec.rb
new file mode 100644
index 00000000000..0a76570f65e
--- /dev/null
+++ b/spec/validators/sha_validator_spec.rb
@@ -0,0 +1,47 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ShaValidator do
+ let(:validator) { described_class.new(attributes: [:base_commit_sha]) }
+ let!(:merge_diff) { build(:merge_request_diff) }
+
+ subject { validator.validate_each(merge_diff, :base_commit_sha, value) }
+
+ context 'with empty value' do
+ let(:value) { nil }
+
+ it 'does not add any error if value is empty' do
+ expect(Commit).not_to receive(:valid_hash?)
+
+ subject
+
+ expect(merge_diff.errors).to be_empty
+ end
+ end
+
+ context 'with valid sha' do
+ let(:value) { Digest::SHA1.hexdigest(SecureRandom.hex) }
+
+ it 'does not add any error' do
+ expect(Commit).to receive(:valid_hash?).and_call_original
+
+ subject
+
+ expect(merge_diff.errors).to be_empty
+ end
+ end
+
+ context 'with invalid sha' do
+ let(:value) { 'foo' }
+
+ it 'adds error to the record' do
+ expect(Commit).to receive(:valid_hash?).and_call_original
+ expect(merge_diff.errors).to be_empty
+
+ subject
+
+ expect(merge_diff.errors).not_to be_empty
+ end
+ end
+end
diff --git a/spec/views/ci/status/_icon.html.haml_spec.rb b/spec/views/ci/status/_icon.html.haml_spec.rb
new file mode 100644
index 00000000000..626159fc512
--- /dev/null
+++ b/spec/views/ci/status/_icon.html.haml_spec.rb
@@ -0,0 +1,89 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe 'ci/status/_icon' do
+ let(:user) { create(:user) }
+ let(:project) { create(:project, :private) }
+ let(:pipeline) { create(:ci_pipeline, project: project) }
+
+ context 'when rendering status for build' do
+ let(:build) do
+ create(:ci_build, :success, pipeline: pipeline)
+ end
+
+ context 'when user has ability to see details' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'has link to build details page' do
+ details_path = project_job_path(project, build)
+
+ render_status(build)
+
+ expect(rendered).to have_link(href: details_path)
+ end
+ end
+
+ context 'when user do not have ability to see build details' do
+ before do
+ render_status(build)
+ end
+
+ it 'contains build status text' do
+ expect(rendered).to have_css('.ci-status-icon.ci-status-icon-success')
+ end
+
+ it 'does not contain links' do
+ expect(rendered).not_to have_link
+ end
+ end
+ end
+
+ context 'when rendering status for external job' do
+ context 'when user has ability to see commit status details' do
+ before do
+ project.add_developer(user)
+ end
+
+ context 'status has external target url' do
+ before do
+ external_job = create(:generic_commit_status,
+ status: :running,
+ pipeline: pipeline,
+ target_url: 'http://gitlab.com')
+
+ render_status(external_job)
+ end
+
+ it 'contains valid commit status text' do
+ expect(rendered).to have_css('.ci-status-icon.ci-status-icon-running')
+ end
+
+ it 'has link to external status page' do
+ expect(rendered).to have_link(href: 'http://gitlab.com')
+ end
+ end
+
+ context 'status do not have external target url' do
+ before do
+ external_job = create(:generic_commit_status, status: :canceled)
+
+ render_status(external_job)
+ end
+
+ it 'contains valid commit status text' do
+ expect(rendered).to have_css('.ci-status-icon.ci-status-icon-canceled')
+ end
+
+ it 'has link to external status page' do
+ expect(rendered).not_to have_link
+ end
+ end
+ end
+ end
+
+ def render_status(resource)
+ render 'ci/status/icon', status: resource.detailed_status(user)
+ end
+end
diff --git a/spec/views/groups/_home_panel.html.haml_spec.rb b/spec/views/groups/_home_panel.html.haml_spec.rb
new file mode 100644
index 00000000000..91c5ca261b9
--- /dev/null
+++ b/spec/views/groups/_home_panel.html.haml_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe 'groups/_home_panel' do
+ let(:group) { create(:group) }
+
+ before do
+ assign(:group, group)
+ end
+
+ it 'renders the group ID' do
+ render
+
+ expect(rendered).to have_content("Group ID: #{group.id}")
+ end
+end
diff --git a/spec/views/layouts/_head.html.haml_spec.rb b/spec/views/layouts/_head.html.haml_spec.rb
index 9d1efcabb80..cbb4199954a 100644
--- a/spec/views/layouts/_head.html.haml_spec.rb
+++ b/spec/views/layouts/_head.html.haml_spec.rb
@@ -62,6 +62,14 @@ describe 'layouts/_head' do
end
end
+ it 'adds selected syntax highlight stylesheet' do
+ allow_any_instance_of(PreferencesHelper).to receive(:user_color_scheme).and_return("solarised-light")
+
+ render
+
+ expect(rendered).to match('<link rel="stylesheet" media="all" href="/stylesheets/highlight/themes/solarised-light.css" />')
+ 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/layouts/nav/sidebar/_admin.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb
index 05c2f61a606..bf63021a7fa 100644
--- a/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_admin.html.haml_spec.rb
@@ -26,6 +26,8 @@ describe 'layouts/nav/sidebar/_admin' do
it_behaves_like 'page has active tab', 'Overview'
end
+ it_behaves_like 'has nav sidebar'
+
context 'on projects' do
before do
allow(controller).to receive(:controller_name).and_return('projects')
diff --git a/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb
new file mode 100644
index 00000000000..24b66a0e767
--- /dev/null
+++ b/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'layouts/nav/sidebar/_group' do
+ let(:group) { create(:group) }
+
+ before do
+ assign(:group, group)
+ end
+
+ it_behaves_like 'has nav sidebar'
+end
diff --git a/spec/views/layouts/nav/sidebar/_instance_statistics.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_instance_statistics.html.haml_spec.rb
new file mode 100644
index 00000000000..7f7f5637035
--- /dev/null
+++ b/spec/views/layouts/nav/sidebar/_instance_statistics.html.haml_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'layouts/nav/sidebar/_instance_statistics' do
+ it_behaves_like 'has nav sidebar'
+end
diff --git a/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb
new file mode 100644
index 00000000000..6b820ab0b4c
--- /dev/null
+++ b/spec/views/layouts/nav/sidebar/_profile.html.haml_spec.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'layouts/nav/sidebar/_profile' do
+ let(:user) { create(:user) }
+
+ before do
+ allow(view).to receive(:current_user).and_return(user)
+ end
+
+ it_behaves_like 'has nav sidebar'
+end
diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
index d9f05e5f94f..2c60ccfb754 100644
--- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
+++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb
@@ -11,6 +11,8 @@ describe 'layouts/nav/sidebar/_project' do
allow(view).to receive(:can?).and_return(true)
end
+ it_behaves_like 'has nav sidebar'
+
describe 'issue boards' do
it 'has board tab' do
render
diff --git a/spec/views/projects/commits/_commit.html.haml_spec.rb b/spec/views/projects/commits/_commit.html.haml_spec.rb
index 00547e433c4..6bf1b5fd2d0 100644
--- a/spec/views/projects/commits/_commit.html.haml_spec.rb
+++ b/spec/views/projects/commits/_commit.html.haml_spec.rb
@@ -1,15 +1,15 @@
require 'spec_helper'
describe 'projects/commits/_commit.html.haml' do
+ let(:project) { create(:project, :repository) }
+ let(:commit) { project.repository.commit(ref) }
+
before do
allow(view).to receive(:current_application_settings).and_return(Gitlab::CurrentSettings.current_application_settings)
end
- context 'with a singed commit' do
- let(:project) { create(:project, :repository) }
- let(:repository) { project.repository }
+ context 'with a signed commit' do
let(:ref) { GpgHelpers::SIGNED_COMMIT_SHA }
- let(:commit) { repository.commit(ref) }
it 'does not display a loading spinner for GPG status' do
render partial: 'projects/commits/commit', locals: {
@@ -23,4 +23,55 @@ describe 'projects/commits/_commit.html.haml' do
end
end
end
+
+ context 'with ci status' do
+ let(:ref) { 'master' }
+ let(:user) { create(:user) }
+
+ before do
+ allow(view).to receive(:current_user).and_return(user)
+
+ project.add_developer(user)
+
+ create(
+ :ci_empty_pipeline,
+ ref: 'master',
+ sha: commit.id,
+ status: 'success',
+ project: project
+ )
+ end
+
+ context 'when pipelines are disabled' do
+ before do
+ allow(project).to receive(:builds_enabled?).and_return(false)
+ end
+
+ it 'does not display a ci status icon' do
+ render partial: 'projects/commits/commit', locals: {
+ project: project,
+ ref: ref,
+ commit: commit
+ }
+
+ expect(rendered).not_to have_css('.ci-status-link')
+ end
+ end
+
+ context 'when pipelines are enabled' do
+ before do
+ allow(project).to receive(:builds_enabled?).and_return(true)
+ end
+
+ it 'does display a ci status icon when pipelines are enabled' do
+ render partial: 'projects/commits/commit', locals: {
+ project: project,
+ ref: ref,
+ commit: commit
+ }
+
+ expect(rendered).to have_css('.ci-status-link')
+ end
+ end
+ end
end
diff --git a/spec/views/projects/deployments/_confirm_rollback_modal_spec.html.rb b/spec/views/projects/deployments/_confirm_rollback_modal_spec.html.rb
new file mode 100644
index 00000000000..54ec4f32856
--- /dev/null
+++ b/spec/views/projects/deployments/_confirm_rollback_modal_spec.html.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe 'projects/deployments/_confirm_rollback_modal' do
+ let(:environment) { create(:environment, :with_review_app) }
+ let(:deployments) { environment.deployments }
+ let(:project) { environment.project }
+
+ before do
+ assign(:environment, environment)
+ assign(:deployments, deployments)
+ assign(:project, project)
+ end
+
+ context 'when re-deploying last deployment' do
+ let(:deployment) { deployments.first }
+
+ before do
+ allow(view).to receive(:deployment).and_return(deployment)
+ end
+
+ it 'shows "re-deploy"' do
+ render
+
+ expect(rendered).to have_selector('h4', text: "Re-deploy environment #{environment.name}?")
+ expect(rendered).to have_selector('p', text: "This action will relaunch the job for commit #{deployment.short_sha}, putting the environment in a previous version. Are you sure you want to continue?")
+ expect(rendered).to have_selector('a.btn-danger', text: 'Re-deploy')
+ end
+
+ it 'links to re-deploying the environment' do
+ expected_link = retry_project_job_path(environment.project, deployment.deployable)
+
+ render
+
+ expect(rendered).to have_selector("a[href='#{expected_link}']", text: 'Re-deploy')
+ end
+ end
+
+ context 'when rolling back to previous deployment' do
+ let(:deployment) { create(:deployment, environment: environment) }
+
+ before do
+ allow(view).to receive(:deployment).and_return(deployment)
+ end
+
+ it 'shows "rollback"' do
+ render
+
+ expect(rendered).to have_selector('h4', text: "Rollback environment #{environment.name}?")
+ expect(rendered).to have_selector('p', text: "This action will run the job defined by staging for commit #{deployment.short_sha}, putting the environment in a previous version. You can revert it by re-deploying the latest version of your application. Are you sure you want to continue?")
+ expect(rendered).to have_selector('a.btn-danger', text: 'Rollback')
+ end
+
+ it 'links to re-deploying the environment' do
+ expected_link = retry_project_job_path(environment.project, deployment.deployable)
+
+ render
+
+ expect(rendered).to have_selector("a[href='#{expected_link}']", text: 'Rollback')
+ end
+ end
+end
diff --git a/spec/views/projects/issues/_merge_requests_status.html.haml_spec.rb b/spec/views/projects/issues/_merge_requests_status.html.haml_spec.rb
index 02c225292ce..9424795749d 100644
--- a/spec/views/projects/issues/_merge_requests_status.html.haml_spec.rb
+++ b/spec/views/projects/issues/_merge_requests_status.html.haml_spec.rb
@@ -2,6 +2,12 @@
require 'spec_helper'
describe 'projects/issues/_merge_requests_status.html.haml' do
+ around do |ex|
+ Timecop.freeze(Date.new(2018, 7, 22)) do
+ ex.run
+ end
+ end
+
it 'shows date of status change in tooltip' do
merge_request = create(:merge_request, created_at: 1.month.ago)
diff --git a/spec/views/projects/issues/show.html.haml_spec.rb b/spec/views/projects/issues/show.html.haml_spec.rb
index ff88efd0e31..1d9c6d36ad7 100644
--- a/spec/views/projects/issues/show.html.haml_spec.rb
+++ b/spec/views/projects/issues/show.html.haml_spec.rb
@@ -21,12 +21,24 @@ describe 'projects/issues/show' do
allow(issue).to receive(:closed?).and_return(true)
end
- it 'shows "Closed (moved)" if an issue has been moved' do
- allow(issue).to receive(:moved?).and_return(true)
+ context 'when the issue was moved' do
+ let(:new_issue) { create(:issue, project: project, author: user) }
- render
+ before do
+ issue.moved_to = new_issue
+ end
+
+ it 'shows "Closed (moved)" if an issue has been moved' do
+ render
+
+ expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
+ end
+
+ it 'links "moved" to the new issue the original issue was moved to' do
+ render
- expect(rendered).to have_selector('.status-box-issue-closed:not(.hidden)', text: 'Closed (moved)')
+ expect(rendered).to have_selector("a[href=\"#{issue_path(new_issue)}\"]", text: 'moved')
+ end
end
it 'shows "Closed" if an issue has not been moved' do
diff --git a/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb
index 2a2539c80b5..b52fc719a64 100644
--- a/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb
+++ b/spec/views/projects/settings/ci_cd/_autodevops_form.html.haml_spec.rb
@@ -5,6 +5,7 @@ describe 'projects/settings/ci_cd/_autodevops_form' do
before do
assign :project, project
+ allow(view).to receive(:auto_devops_enabled) { true }
end
it 'shows a warning message about Kubernetes cluster' do
diff --git a/spec/views/projects/settings/operations/show.html.haml_spec.rb b/spec/views/projects/settings/operations/show.html.haml_spec.rb
index 8e34521c7c8..6762fe3759b 100644
--- a/spec/views/projects/settings/operations/show.html.haml_spec.rb
+++ b/spec/views/projects/settings/operations/show.html.haml_spec.rb
@@ -18,6 +18,7 @@ describe 'projects/settings/operations/show' do
allow(view).to receive(:error_tracking_setting)
.and_return(error_tracking_setting)
allow(view).to receive(:current_user).and_return(user)
+ allow(view).to receive(:incident_management_available?) { false }
end
let!(:error_tracking_setting) do
@@ -30,7 +31,6 @@ describe 'projects/settings/operations/show' do
expect(rendered).to have_content _('Error Tracking')
expect(rendered).to have_content _('To link Sentry to GitLab, enter your Sentry URL and Auth Token')
- expect(rendered).to have_content _('Active')
end
end
end
diff --git a/spec/workers/build_finished_worker_spec.rb b/spec/workers/build_finished_worker_spec.rb
index acd8da11d8d..ccb26849e67 100644
--- a/spec/workers/build_finished_worker_spec.rb
+++ b/spec/workers/build_finished_worker_spec.rb
@@ -26,5 +26,24 @@ describe BuildFinishedWorker do
.not_to raise_error
end
end
+
+ it 'schedules a ChatNotification job for a chat build' do
+ build = create(:ci_build, :success, pipeline: create(:ci_pipeline, source: :chat))
+
+ expect(ChatNotificationWorker)
+ .to receive(:perform_async)
+ .with(build.id)
+
+ described_class.new.perform(build.id)
+ end
+
+ it 'does not schedule a ChatNotification job for a regular build' do
+ build = create(:ci_build, :success, pipeline: create(:ci_pipeline))
+
+ expect(ChatNotificationWorker)
+ .not_to receive(:perform_async)
+
+ described_class.new.perform(build.id)
+ end
end
end
diff --git a/spec/workers/chat_notification_worker_spec.rb b/spec/workers/chat_notification_worker_spec.rb
new file mode 100644
index 00000000000..91695674f5d
--- /dev/null
+++ b/spec/workers/chat_notification_worker_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ChatNotificationWorker do
+ let(:worker) { described_class.new }
+ let(:chat_build) do
+ create(:ci_build, pipeline: create(:ci_pipeline, source: :chat))
+ end
+
+ describe '#perform' do
+ it 'does nothing when the build no longer exists' do
+ expect(worker).not_to receive(:send_response)
+
+ worker.perform(-1)
+ end
+
+ it 'sends a response for an existing build' do
+ expect(worker)
+ .to receive(:send_response)
+ .with(an_instance_of(Ci::Build))
+
+ worker.perform(chat_build.id)
+ end
+
+ it 'reschedules the job if the trace sections could not be found' do
+ expect(worker)
+ .to receive(:send_response)
+ .and_raise(Gitlab::Chat::Output::MissingBuildSectionError)
+
+ expect(described_class)
+ .to receive(:perform_in)
+ .with(described_class::RESCHEDULE_INTERVAL, chat_build.id)
+
+ worker.perform(chat_build.id)
+ end
+ end
+
+ describe '#send_response' do
+ context 'when a responder could not be found' do
+ it 'does nothing' do
+ expect(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(chat_build)
+ .and_return(nil)
+
+ expect(worker.send_response(chat_build)).to be_nil
+ end
+ end
+
+ context 'when a responder could be found' do
+ let(:responder) { double(:responder) }
+
+ before do
+ allow(Gitlab::Chat::Responder)
+ .to receive(:responder_for)
+ .with(chat_build)
+ .and_return(responder)
+ end
+
+ it 'sends the response for a succeeded build' do
+ output = double(:output, to_s: 'this is the build output')
+
+ expect(chat_build)
+ .to receive(:success?)
+ .and_return(true)
+
+ expect(responder)
+ .to receive(:success)
+ .with(an_instance_of(String))
+
+ expect(Gitlab::Chat::Output)
+ .to receive(:new)
+ .with(chat_build)
+ .and_return(output)
+
+ worker.send_response(chat_build)
+ end
+
+ it 'sends the response for a failed build' do
+ expect(chat_build)
+ .to receive(:success?)
+ .and_return(false)
+
+ expect(responder).to receive(:failure)
+
+ worker.send_response(chat_build)
+ end
+ end
+ end
+end
diff --git a/spec/workers/ci/build_prepare_worker_spec.rb b/spec/workers/ci/build_prepare_worker_spec.rb
new file mode 100644
index 00000000000..9f76696ee66
--- /dev/null
+++ b/spec/workers/ci/build_prepare_worker_spec.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe Ci::BuildPrepareWorker do
+ subject { described_class.new.perform(build_id) }
+
+ context 'build exists' do
+ let(:build) { create(:ci_build) }
+ let(:build_id) { build.id }
+ let(:service) { double(execute: true) }
+
+ it 'calls the prepare build service' do
+ expect(Ci::PrepareBuildService).to receive(:new).with(build).and_return(service)
+ expect(service).to receive(:execute).once
+
+ subject
+ end
+ end
+
+ context 'build does not exist' do
+ let(:build_id) { -1 }
+
+ it 'does not attempt to prepare the build' do
+ expect(Ci::PrepareBuildService).not_to receive(:new)
+
+ subject
+ end
+ end
+end
diff --git a/spec/workers/cluster_configure_worker_spec.rb b/spec/workers/cluster_configure_worker_spec.rb
index 6918ee3d7d8..83f76809435 100644
--- a/spec/workers/cluster_configure_worker_spec.rb
+++ b/spec/workers/cluster_configure_worker_spec.rb
@@ -4,6 +4,11 @@ require 'spec_helper'
describe ClusterConfigureWorker, '#perform' do
let(:worker) { described_class.new }
+ let(:ci_preparing_state_enabled) { false }
+
+ before do
+ stub_feature_flags(ci_preparing_state: ci_preparing_state_enabled)
+ end
context 'when group cluster' do
let(:cluster) { create(:cluster, :group, :provided_by_gcp) }
@@ -66,4 +71,15 @@ describe ClusterConfigureWorker, '#perform' do
described_class.new.perform(123)
end
end
+
+ context 'ci_preparing_state feature is enabled' do
+ let(:cluster) { create(:cluster) }
+ let(:ci_preparing_state_enabled) { true }
+
+ it 'does not configure the cluster' do
+ expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_cluster)
+
+ described_class.new.perform(cluster.id)
+ end
+ end
end
diff --git a/spec/workers/cluster_project_configure_worker_spec.rb b/spec/workers/cluster_project_configure_worker_spec.rb
new file mode 100644
index 00000000000..afdea55adf4
--- /dev/null
+++ b/spec/workers/cluster_project_configure_worker_spec.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe ClusterProjectConfigureWorker, '#perform' do
+ let(:worker) { described_class.new }
+
+ context 'ci_preparing_state feature is enabled' do
+ let(:cluster) { create(:cluster) }
+
+ before do
+ stub_feature_flags(ci_preparing_state: true)
+ end
+
+ it 'does not configure the cluster' do
+ expect(Clusters::RefreshService).not_to receive(:create_or_update_namespaces_for_project)
+
+ described_class.new.perform(cluster.id)
+ end
+ end
+end
diff --git a/spec/workers/hashed_storage/project_migrate_worker_spec.rb b/spec/workers/hashed_storage/project_migrate_worker_spec.rb
new file mode 100644
index 00000000000..340e722aa7e
--- /dev/null
+++ b/spec/workers/hashed_storage/project_migrate_worker_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe HashedStorage::ProjectMigrateWorker, :clean_gitlab_redis_shared_state do
+ include ExclusiveLeaseHelpers
+
+ describe '#perform' do
+ let(:project) { create(:project, :empty_repo, :legacy_storage) }
+ let(:lease_key) { "project_migrate_hashed_storage_worker:#{project.id}" }
+ let(:lease_timeout) { described_class::LEASE_TIMEOUT }
+ let(:migration_service) { ::Projects::HashedStorage::MigrationService }
+
+ it 'skips when project no longer exists' do
+ expect(migration_service).not_to receive(:new)
+
+ subject.perform(-1)
+ end
+
+ it 'skips when project is pending delete' do
+ pending_delete_project = create(:project, :empty_repo, pending_delete: true)
+
+ expect(migration_service).not_to receive(:new)
+
+ subject.perform(pending_delete_project.id)
+ end
+
+ it 'delegates migration to service class when we have exclusive lease' do
+ stub_exclusive_lease(lease_key, 'uuid', timeout: lease_timeout)
+
+ service_spy = spy
+
+ allow(migration_service)
+ .to receive(:new).with(project, project.full_path, logger: subject.logger)
+ .and_return(service_spy)
+
+ subject.perform(project.id)
+
+ expect(service_spy).to have_received(:execute)
+ end
+
+ it 'skips when it cant acquire the exclusive lease' do
+ stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+
+ expect(migration_service).not_to receive(:new)
+
+ subject.perform(project.id)
+ end
+ end
+end
diff --git a/spec/workers/hashed_storage/project_rollback_worker_spec.rb b/spec/workers/hashed_storage/project_rollback_worker_spec.rb
new file mode 100644
index 00000000000..d833553c0ec
--- /dev/null
+++ b/spec/workers/hashed_storage/project_rollback_worker_spec.rb
@@ -0,0 +1,50 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe HashedStorage::ProjectRollbackWorker, :clean_gitlab_redis_shared_state do
+ include ExclusiveLeaseHelpers
+
+ describe '#perform' do
+ let(:project) { create(:project, :empty_repo) }
+ let(:lease_key) { "project_migrate_hashed_storage_worker:#{project.id}" }
+ let(:lease_timeout) { described_class::LEASE_TIMEOUT }
+ let(:rollback_service) { ::Projects::HashedStorage::RollbackService }
+
+ it 'skips when project no longer exists' do
+ expect(rollback_service).not_to receive(:new)
+
+ subject.perform(-1)
+ end
+
+ it 'skips when project is pending delete' do
+ pending_delete_project = create(:project, :empty_repo, pending_delete: true)
+
+ expect(rollback_service).not_to receive(:new)
+
+ subject.perform(pending_delete_project.id)
+ end
+
+ it 'delegates rollback to service class when have exclusive lease' do
+ stub_exclusive_lease(lease_key, 'uuid', timeout: lease_timeout)
+
+ service_spy = spy
+
+ allow(rollback_service)
+ .to receive(:new).with(project, project.disk_path, logger: subject.logger)
+ .and_return(service_spy)
+
+ subject.perform(project.id)
+
+ expect(service_spy).to have_received(:execute)
+ end
+
+ it 'skips when it cant acquire the exclusive lease' do
+ stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
+
+ expect(rollback_service).not_to receive(:new)
+
+ subject.perform(project.id)
+ end
+ end
+end
diff --git a/spec/workers/hashed_storage/rollbacker_worker_spec.rb b/spec/workers/hashed_storage/rollbacker_worker_spec.rb
new file mode 100644
index 00000000000..4055f380978
--- /dev/null
+++ b/spec/workers/hashed_storage/rollbacker_worker_spec.rb
@@ -0,0 +1,27 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe HashedStorage::RollbackerWorker do
+ subject(:worker) { described_class.new }
+ let(:projects) { create_list(:project, 2, :empty_repo) }
+ let(:ids) { projects.map(&:id) }
+
+ describe '#perform' do
+ it 'delegates to MigratorService' do
+ expect_any_instance_of(Gitlab::HashedStorage::Migrator).to receive(:bulk_rollback).with(start: 5, finish: 10)
+
+ worker.perform(5, 10)
+ end
+
+ it 'rollsback projects in the specified range' do
+ perform_enqueued_jobs do
+ worker.perform(ids.min, ids.max)
+ end
+
+ projects.each do |project|
+ expect(project.reload.legacy_storage?).to be_truthy
+ end
+ end
+ end
+end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index caae46a3175..9cddad71a51 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -33,8 +33,8 @@ describe PostReceive do
describe "#process_project_changes" do
context 'empty changes' do
it "does not call any PushService but runs after project hooks" do
- expect(GitPushService).not_to receive(:new)
- expect(GitTagPushService).not_to receive(:new)
+ expect(Git::BranchPushService).not_to receive(:new)
+ expect(Git::TagPushService).not_to receive(:new)
expect_next_instance_of(SystemHooksService) { |service| expect(service).to receive(:execute_hooks) }
described_class.new.perform(gl_repository, key_id, "")
@@ -45,8 +45,8 @@ describe PostReceive do
let!(:key_id) { "" }
it 'returns false' do
- expect(GitPushService).not_to receive(:new)
- expect(GitTagPushService).not_to receive(:new)
+ expect(Git::BranchPushService).not_to receive(:new)
+ expect(Git::TagPushService).not_to receive(:new)
expect(described_class.new.perform(gl_repository, key_id, base64_changes)).to be false
end
@@ -60,9 +60,9 @@ describe PostReceive do
context "branches" do
let(:changes) { "123456 789012 refs/heads/tést" }
- it "calls GitPushService" do
- expect_any_instance_of(GitPushService).to receive(:execute).and_return(true)
- expect_any_instance_of(GitTagPushService).not_to receive(:execute)
+ it "calls Git::BranchPushService" do
+ expect_any_instance_of(Git::BranchPushService).to receive(:execute).and_return(true)
+ expect_any_instance_of(Git::TagPushService).not_to receive(:execute)
described_class.new.perform(gl_repository, key_id, base64_changes)
end
end
@@ -70,9 +70,9 @@ describe PostReceive do
context "tags" do
let(:changes) { "123456 789012 refs/tags/tag" }
- it "calls GitTagPushService" do
- expect_any_instance_of(GitPushService).not_to receive(:execute)
- expect_any_instance_of(GitTagPushService).to receive(:execute).and_return(true)
+ it "calls Git::TagPushService" do
+ expect_any_instance_of(Git::BranchPushService).not_to receive(:execute)
+ expect_any_instance_of(Git::TagPushService).to receive(:execute).and_return(true)
described_class.new.perform(gl_repository, key_id, base64_changes)
end
end
@@ -81,8 +81,8 @@ describe PostReceive do
let(:changes) { "123456 789012 refs/merge-requests/123" }
it "does not call any of the services" do
- expect_any_instance_of(GitPushService).not_to receive(:execute)
- expect_any_instance_of(GitTagPushService).not_to receive(:execute)
+ expect_any_instance_of(Git::BranchPushService).not_to receive(:execute)
+ expect_any_instance_of(Git::TagPushService).not_to receive(:execute)
described_class.new.perform(gl_repository, key_id, base64_changes)
end
end
@@ -125,7 +125,7 @@ describe PostReceive do
allow_any_instance_of(Gitlab::DataBuilder::Repository).to receive(:update).and_return(fake_hook_data)
# silence hooks so we can isolate
allow_any_instance_of(Key).to receive(:post_create_hook).and_return(true)
- allow_any_instance_of(GitPushService).to receive(:execute).and_return(true)
+ allow_any_instance_of(Git::BranchPushService).to receive(:execute).and_return(true)
end
it 'calls SystemHooksService' do
diff --git a/spec/workers/project_daily_statistics_worker_spec.rb b/spec/workers/project_daily_statistics_worker_spec.rb
new file mode 100644
index 00000000000..8640add99e5
--- /dev/null
+++ b/spec/workers/project_daily_statistics_worker_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+describe ProjectDailyStatisticsWorker, '#perform' do
+ let(:worker) { described_class.new }
+ let(:project) { create(:project) }
+
+ describe '#perform' do
+ context 'with a non-existing project' do
+ it 'does nothing' do
+ expect(Projects::FetchStatisticsIncrementService).not_to receive(:new)
+
+ worker.perform(-1)
+ end
+ end
+
+ context 'with an existing project without a repository' do
+ it 'does nothing' do
+ expect(Projects::FetchStatisticsIncrementService).not_to receive(:new)
+
+ worker.perform(project.id)
+ end
+ end
+
+ it 'calls daily_statistics_service with the given project' do
+ project = create(:project, :repository)
+
+ expect_next_instance_of(Projects::FetchStatisticsIncrementService, project) do |service|
+ expect(service).to receive(:execute)
+ end
+
+ worker.perform(project.id)
+ 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
deleted file mode 100644
index 333eb6a0569..00000000000
--- a/spec/workers/project_migrate_hashed_storage_worker_spec.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-require 'spec_helper'
-
-describe ProjectMigrateHashedStorageWorker, :clean_gitlab_redis_shared_state do
- include ExclusiveLeaseHelpers
-
- describe '#perform' do
- let(:project) { create(:project, :empty_repo, :legacy_storage) }
- let(:lease_key) { "project_migrate_hashed_storage_worker:#{project.id}" }
- let(:lease_timeout) { described_class::LEASE_TIMEOUT }
- let(:migration_service) { ::Projects::HashedStorage::MigrationService }
-
- it 'skips when project no longer exists' do
- expect(migration_service).not_to receive(:new)
-
- subject.perform(-1)
- end
-
- it 'skips when project is pending delete' do
- pending_delete_project = create(:project, :empty_repo, pending_delete: true)
-
- expect(migration_service).not_to receive(:new)
-
- subject.perform(pending_delete_project.id)
- end
-
- it 'delegates migration to service class when we have exclusive lease' do
- stub_exclusive_lease(lease_key, 'uuid', timeout: lease_timeout)
-
- service_spy = spy
-
- allow(migration_service)
- .to receive(:new).with(project, project.full_path, logger: subject.logger)
- .and_return(service_spy)
-
- subject.perform(project.id)
-
- expect(service_spy).to have_received(:execute)
- end
-
- it 'skips when it cant acquire the exclusive lease' do
- stub_exclusive_lease_taken(lease_key, timeout: lease_timeout)
-
- expect(migration_service).not_to receive(:new)
-
- subject.perform(project.id)
- end
- end
-end
diff --git a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
index 963237ceadf..d20d926f5a0 100644
--- a/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
+++ b/spec/workers/update_head_pipeline_for_merge_request_worker_spec.rb
@@ -18,7 +18,7 @@ describe UpdateHeadPipelineForMergeRequestWorker do
context 'when merge request sha does not equal pipeline sha' do
before do
- merge_request.merge_request_diff.update(head_commit_sha: 'different_sha')
+ merge_request.merge_request_diff.update(head_commit_sha: Digest::SHA1.hexdigest(SecureRandom.hex))
end
it 'does not update head pipeline' do
@@ -39,7 +39,7 @@ describe UpdateHeadPipelineForMergeRequestWorker do
let!(:merge_request_pipeline) do
create(:ci_pipeline,
project: project,
- source: :merge_request,
+ source: :merge_request_event,
sha: latest_sha,
merge_request: merge_request)
end
diff --git a/vendor/assets/javascripts/jquery.atwho.js b/vendor/assets/javascripts/jquery.atwho.js
deleted file mode 100644
index e058e13303a..00000000000
--- a/vendor/assets/javascripts/jquery.atwho.js
+++ /dev/null
@@ -1,1202 +0,0 @@
-/**
- * at.js - 1.5.1
- * Copyright (c) 2016 chord.luo <chord.luo@gmail.com>;
- * Homepage: http://ichord.github.com/At.js
- * License: MIT
- */
-(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module unless amdModuleId is set
- define(["jquery"], function (a0) {
- return (factory(a0));
- });
- } else if (typeof exports === 'object') {
- // Node. Does not work with strict CommonJS, but
- // only CommonJS-like environments that support module.exports,
- // like Node.
- module.exports = factory(require("jquery"));
- } else {
- factory(jQuery);
- }
-}(this, function ($) {
-var DEFAULT_CALLBACKS, KEY_CODE;
-
-KEY_CODE = {
- DOWN: 40,
- UP: 38,
- ESC: 27,
- TAB: 9,
- ENTER: 13,
- CTRL: 17,
- A: 65,
- P: 80,
- N: 78,
- LEFT: 37,
- UP: 38,
- RIGHT: 39,
- DOWN: 40,
- BACKSPACE: 8,
- SPACE: 32
-};
-
-DEFAULT_CALLBACKS = {
- beforeSave: function(data) {
- return Controller.arrayToDefaultHash(data);
- },
- matcher: function(flag, subtext, should_startWithSpace, acceptSpaceBar) {
- var _a, _y, match, regexp, space;
- flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
- if (should_startWithSpace) {
- flag = '(?:^|\\s)' + flag;
- }
- _a = decodeURI("%C3%80");
- _y = decodeURI("%C3%BF");
- space = acceptSpaceBar ? "\ " : "";
- regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi');
- match = regexp.exec(subtext);
- if (match) {
- return match[2] || match[1];
- } else {
- return null;
- }
- },
- filter: function(query, data, searchKey) {
- var _results, i, item, len;
- _results = [];
- for (i = 0, len = data.length; i < len; i++) {
- item = data[i];
- if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) {
- _results.push(item);
- }
- }
- return _results;
- },
- remoteFilter: null,
- sorter: function(query, items, searchKey) {
- var _results, i, item, len;
- if (!query) {
- return items;
- }
- _results = [];
- for (i = 0, len = items.length; i < len; i++) {
- item = items[i];
- item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase());
- if (item.atwho_order > -1) {
- _results.push(item);
- }
- }
- return _results.sort(function(a, b) {
- return a.atwho_order - b.atwho_order;
- });
- },
- tplEval: function(tpl, map) {
- var error, error1, template;
- template = tpl;
- try {
- if (typeof tpl !== 'string') {
- template = tpl(map);
- }
- return template.replace(/\$\{([^\}]*)\}/g, function(tag, key, pos) {
- return map[key];
- });
- } catch (error1) {
- error = error1;
- return "";
- }
- },
- highlighter: function(li, query) {
- var regexp;
- if (!query) {
- return li;
- }
- regexp = new RegExp(">\\s*(\\w*?)(" + query.replace("+", "\\+") + ")(\\w*)\\s*<", 'ig');
- return li.replace(regexp, function(str, $1, $2, $3) {
- return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <';
- });
- },
- beforeInsert: function(value, $li, e) {
- return value;
- },
- beforeReposition: function(offset) {
- return offset;
- },
- afterMatchFailed: function(at, el) {}
-};
-
-var App;
-
-App = (function() {
- function App(inputor) {
- this.currentFlag = null;
- this.controllers = {};
- this.aliasMaps = {};
- this.$inputor = $(inputor);
- this.setupRootElement();
- this.listen();
- }
-
- App.prototype.createContainer = function(doc) {
- var ref;
- if ((ref = this.$el) != null) {
- ref.remove();
- }
- return $(doc.body).append(this.$el = $("<div class='atwho-container'></div>"));
- };
-
- App.prototype.setupRootElement = function(iframe, asRoot) {
- var error, error1;
- if (asRoot == null) {
- asRoot = false;
- }
- if (iframe) {
- this.window = iframe.contentWindow;
- this.document = iframe.contentDocument || this.window.document;
- this.iframe = iframe;
- } else {
- this.document = this.$inputor[0].ownerDocument;
- this.window = this.document.defaultView || this.document.parentWindow;
- try {
- this.iframe = this.window.frameElement;
- } catch (error1) {
- error = error1;
- this.iframe = null;
- if ($.fn.atwho.debug) {
- throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error);
- }
- }
- }
- return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document);
- };
-
- App.prototype.controller = function(at) {
- var c, current, currentFlag, ref;
- if (this.aliasMaps[at]) {
- current = this.controllers[this.aliasMaps[at]];
- } else {
- ref = this.controllers;
- for (currentFlag in ref) {
- c = ref[currentFlag];
- if (currentFlag === at) {
- current = c;
- break;
- }
- }
- }
- if (current) {
- return current;
- } else {
- return this.controllers[this.currentFlag];
- }
- };
-
- App.prototype.setContextFor = function(at) {
- this.currentFlag = at;
- return this;
- };
-
- App.prototype.reg = function(flag, setting) {
- var base, controller;
- controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag));
- if (setting.alias) {
- this.aliasMaps[setting.alias] = flag;
- }
- controller.init(setting);
- return this;
- };
-
- App.prototype.listen = function() {
- return this.$inputor.on('compositionstart', (function(_this) {
- return function(e) {
- var ref;
- if ((ref = _this.controller()) != null) {
- ref.view.hide();
- }
- _this.isComposing = true;
- return null;
- };
- })(this)).on('compositionend', (function(_this) {
- return function(e) {
- _this.isComposing = false;
- setTimeout(function(e) {
- return _this.dispatch(e);
- });
- return null;
- };
- })(this)).on('keyup.atwhoInner', (function(_this) {
- return function(e) {
- return _this.onKeyup(e);
- };
- })(this)).on('keydown.atwhoInner', (function(_this) {
- return function(e) {
- return _this.onKeydown(e);
- };
- })(this)).on('blur.atwhoInner', (function(_this) {
- return function(e) {
- var c;
- if (c = _this.controller()) {
- c.expectedQueryCBId = null;
- return c.view.hide(e, c.getOpt("displayTimeout"));
- }
- };
- })(this)).on('click.atwhoInner', (function(_this) {
- return function(e) {
- return _this.dispatch(e);
- };
- })(this)).on('scroll.atwhoInner', (function(_this) {
- return function() {
- var lastScrollTop;
- lastScrollTop = _this.$inputor.scrollTop();
- return function(e) {
- var currentScrollTop, ref;
- currentScrollTop = e.target.scrollTop;
- if (lastScrollTop !== currentScrollTop) {
- if ((ref = _this.controller()) != null) {
- ref.view.hide(e);
- }
- }
- lastScrollTop = currentScrollTop;
- return true;
- };
- };
- })(this)());
- };
-
- App.prototype.shutdown = function() {
- var _, c, ref;
- ref = this.controllers;
- for (_ in ref) {
- c = ref[_];
- c.destroy();
- delete this.controllers[_];
- }
- this.$inputor.off('.atwhoInner');
- return this.$el.remove();
- };
-
- App.prototype.dispatch = function(e) {
- var _, c, ref, results;
- ref = this.controllers;
- results = [];
- for (_ in ref) {
- c = ref[_];
- results.push(c.lookUp(e));
- }
- return results;
- };
-
- App.prototype.onKeyup = function(e) {
- var ref;
- switch (e.keyCode) {
- case KEY_CODE.ESC:
- e.preventDefault();
- if ((ref = this.controller()) != null) {
- ref.view.hide();
- }
- break;
- case KEY_CODE.DOWN:
- case KEY_CODE.UP:
- case KEY_CODE.CTRL:
- case KEY_CODE.ENTER:
- $.noop();
- break;
- case KEY_CODE.P:
- case KEY_CODE.N:
- if (!e.ctrlKey) {
- this.dispatch(e);
- }
- break;
- default:
- this.dispatch(e);
- }
- };
-
- App.prototype.onKeydown = function(e) {
- var ref, view;
- view = (ref = this.controller()) != null ? ref.view : void 0;
- if (!(view && view.visible())) {
- return;
- }
- switch (e.keyCode) {
- case KEY_CODE.ESC:
- e.preventDefault();
- view.hide(e);
- break;
- case KEY_CODE.UP:
- e.preventDefault();
- view.prev();
- break;
- case KEY_CODE.DOWN:
- e.preventDefault();
- view.next();
- break;
- case KEY_CODE.P:
- if (!e.ctrlKey) {
- return;
- }
- e.preventDefault();
- view.prev();
- break;
- case KEY_CODE.N:
- if (!e.ctrlKey) {
- return;
- }
- e.preventDefault();
- view.next();
- break;
- case KEY_CODE.TAB:
- case KEY_CODE.ENTER:
- case KEY_CODE.SPACE:
- if (!view.visible()) {
- return;
- }
- if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) {
- return;
- }
- if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) {
- return;
- }
- if (view.highlighted()) {
- e.preventDefault();
- view.choose(e);
- } else {
- view.hide(e);
- }
- break;
- default:
- $.noop();
- }
- };
-
- return App;
-
-})();
-
-var Controller,
- slice = [].slice;
-
-Controller = (function() {
- Controller.prototype.uid = function() {
- return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime());
- };
-
- function Controller(app, at1) {
- this.app = app;
- this.at = at1;
- this.$inputor = this.app.$inputor;
- this.id = this.$inputor[0].id || this.uid();
- this.expectedQueryCBId = null;
- this.setting = null;
- this.query = null;
- this.pos = 0;
- this.range = null;
- if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) {
- this.app.$el.append(this.$el = $("<div id='atwho-ground-" + this.id + "'></div>"));
- }
- this.model = new Model(this);
- this.view = new View(this);
- }
-
- Controller.prototype.init = function(setting) {
- this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting);
- this.view.init();
- return this.model.reload(this.setting.data);
- };
-
- Controller.prototype.destroy = function() {
- this.trigger('beforeDestroy');
- this.model.destroy();
- this.view.destroy();
- return this.$el.remove();
- };
-
- Controller.prototype.callDefault = function() {
- var args, error, error1, funcName;
- funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
- try {
- return DEFAULT_CALLBACKS[funcName].apply(this, args);
- } catch (error1) {
- error = error1;
- return $.error(error + " Or maybe At.js doesn't have function " + funcName);
- }
- };
-
- Controller.prototype.trigger = function(name, data) {
- var alias, eventName;
- if (data == null) {
- data = [];
- }
- data.push(this);
- alias = this.getOpt('alias');
- eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho";
- return this.$inputor.trigger(eventName, data);
- };
-
- Controller.prototype.callbacks = function(funcName) {
- return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName];
- };
-
- Controller.prototype.getOpt = function(at, default_value) {
- var e, error1;
- try {
- return this.setting[at];
- } catch (error1) {
- e = error1;
- return null;
- }
- };
-
- Controller.prototype.insertContentFor = function($li) {
- var data, tpl;
- tpl = this.getOpt('insertTpl');
- data = $.extend({}, $li.data('itemData'), {
- 'atwho-at': this.at
- });
- return this.callbacks("tplEval").call(this, tpl, data, "onInsert");
- };
-
- Controller.prototype.renderView = function(data) {
- var searchKey;
- searchKey = this.getOpt("searchKey");
- data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey);
- return this.view.render(data.slice(0, this.getOpt('limit')));
- };
-
- Controller.arrayToDefaultHash = function(data) {
- var i, item, len, results;
- if (!$.isArray(data)) {
- return data;
- }
- results = [];
- for (i = 0, len = data.length; i < len; i++) {
- item = data[i];
- if ($.isPlainObject(item)) {
- results.push(item);
- } else {
- results.push({
- name: item
- });
- }
- }
- return results;
- };
-
- Controller.prototype.lookUp = function(e) {
- var query, wait;
- if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {
- return;
- }
- if (this.getOpt('suspendOnComposing') && this.app.isComposing) {
- return;
- }
- query = this.catchQuery(e);
- if (!query) {
- this.expectedQueryCBId = null;
- return query;
- }
- this.app.setContextFor(this.at);
- if (wait = this.getOpt('delay')) {
- this._delayLookUp(query, wait);
- } else {
- this._lookUp(query);
- }
- return query;
- };
-
- Controller.prototype._delayLookUp = function(query, wait) {
- var now, remaining;
- now = Date.now ? Date.now() : new Date().getTime();
- this.previousCallTime || (this.previousCallTime = now);
- remaining = wait - (now - this.previousCallTime);
- if ((0 < remaining && remaining < wait)) {
- this.previousCallTime = now;
- this._stopDelayedCall();
- return this.delayedCallTimeout = setTimeout((function(_this) {
- return function() {
- _this.previousCallTime = 0;
- _this.delayedCallTimeout = null;
- return _this._lookUp(query);
- };
- })(this), wait);
- } else {
- this._stopDelayedCall();
- if (this.previousCallTime !== now) {
- this.previousCallTime = 0;
- }
- return this._lookUp(query);
- }
- };
-
- Controller.prototype._stopDelayedCall = function() {
- if (this.delayedCallTimeout) {
- clearTimeout(this.delayedCallTimeout);
- return this.delayedCallTimeout = null;
- }
- };
-
- Controller.prototype._generateQueryCBId = function() {
- return {};
- };
-
- Controller.prototype._lookUp = function(query) {
- var _callback;
- _callback = function(queryCBId, data) {
- if (queryCBId !== this.expectedQueryCBId) {
- return;
- }
- if (data && data.length > 0) {
- return this.renderView(this.constructor.arrayToDefaultHash(data));
- } else {
- return this.view.hide();
- }
- };
- this.expectedQueryCBId = this._generateQueryCBId();
- return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId));
- };
-
- return Controller;
-
-})();
-
-var TextareaController,
- extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- hasProp = {}.hasOwnProperty;
-
-TextareaController = (function(superClass) {
- extend(TextareaController, superClass);
-
- function TextareaController() {
- return TextareaController.__super__.constructor.apply(this, arguments);
- }
-
- TextareaController.prototype.catchQuery = function() {
- var caretPos, content, end, isString, query, start, subtext;
- content = this.$inputor.val();
- caretPos = this.$inputor.caret('pos', {
- iframe: this.app.iframe
- });
- subtext = content.slice(0, caretPos);
- query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
- isString = typeof query === 'string';
- if (isString && query.length < this.getOpt('minLen', 0)) {
- return;
- }
- if (isString && query.length <= this.getOpt('maxLen', 20)) {
- start = caretPos - query.length;
- end = start + query.length;
- this.pos = start;
- query = {
- 'text': query,
- 'headPos': start,
- 'endPos': end
- };
- this.trigger("matched", [this.at, query.text]);
- } else {
- query = null;
- this.view.hide();
- }
- return this.query = query;
- };
-
- TextareaController.prototype.rect = function() {
- var c, iframeOffset, scaleBottom;
- if (!(c = this.$inputor.caret('offset', this.pos - 1, {
- iframe: this.app.iframe
- }))) {
- return;
- }
- if (this.app.iframe && !this.app.iframeAsRoot) {
- iframeOffset = $(this.app.iframe).offset();
- c.left += iframeOffset.left;
- c.top += iframeOffset.top;
- }
- scaleBottom = this.app.document.selection ? 0 : 2;
- return {
- left: c.left,
- top: c.top,
- bottom: c.top + c.height + scaleBottom
- };
- };
-
- TextareaController.prototype.insert = function(content, $li) {
- var $inputor, source, startStr, suffix, text;
- $inputor = this.$inputor;
- source = $inputor.val();
- startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0));
- suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " ";
- content += suffix;
- text = "" + startStr + content + (source.slice(this.query['endPos'] || 0));
- $inputor.val(text);
- $inputor.caret('pos', startStr.length + content.length, {
- iframe: this.app.iframe
- });
- if (!$inputor.is(':focus')) {
- $inputor.focus();
- }
- return $inputor.change();
- };
-
- return TextareaController;
-
-})(Controller);
-
-var EditableController,
- extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
- hasProp = {}.hasOwnProperty;
-
-EditableController = (function(superClass) {
- extend(EditableController, superClass);
-
- function EditableController() {
- return EditableController.__super__.constructor.apply(this, arguments);
- }
-
- EditableController.prototype._getRange = function() {
- var sel;
- sel = this.app.window.getSelection();
- if (sel.rangeCount > 0) {
- return sel.getRangeAt(0);
- }
- };
-
- EditableController.prototype._setRange = function(position, node, range) {
- if (range == null) {
- range = this._getRange();
- }
- if (!range) {
- return;
- }
- node = $(node)[0];
- if (position === 'after') {
- range.setEndAfter(node);
- range.setStartAfter(node);
- } else {
- range.setEndBefore(node);
- range.setStartBefore(node);
- }
- range.collapse(false);
- return this._clearRange(range);
- };
-
- EditableController.prototype._clearRange = function(range) {
- var sel;
- if (range == null) {
- range = this._getRange();
- }
- sel = this.app.window.getSelection();
- if (this.ctrl_a_pressed == null) {
- sel.removeAllRanges();
- return sel.addRange(range);
- }
- };
-
- EditableController.prototype._movingEvent = function(e) {
- var ref;
- return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN);
- };
-
- EditableController.prototype._unwrap = function(node) {
- var next;
- node = $(node).unwrap().get(0);
- if ((next = node.nextSibling) && next.nodeValue) {
- node.nodeValue += next.nodeValue;
- $(next).remove();
- }
- return node;
- };
-
- EditableController.prototype.catchQuery = function(e) {
- var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range;
- if (!(range = this._getRange())) {
- return;
- }
- if (!range.collapsed) {
- return;
- }
- if (e.which === KEY_CODE.ENTER) {
- ($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap();
- if ($query.is(':empty')) {
- $query.remove();
- }
- ($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap();
- this._clearRange();
- return;
- }
- if (/firefox/i.test(navigator.userAgent)) {
- if ($(range.startContainer).is(this.$inputor)) {
- this._clearRange();
- return;
- }
- if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) {
- _range = range.cloneRange();
- _range.setStart(range.startContainer, offset);
- if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) {
- inserted = $(range.startContainer).contents().get(offset);
- this._setRange('after', $(inserted).contents().last());
- }
- } else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) {
- $inserted = $(range.startContainer.previousSibling);
- if ($inserted.is('.atwho-inserted') && range.startOffset === 0) {
- this._setRange('after', $inserted.contents().last());
- }
- }
- }
- $(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query');
- if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) {
- $query.remove();
- }
- if (!this._movingEvent(e)) {
- $query.removeClass('atwho-inserted');
- }
- if ($query.length > 0) {
- switch (e.which) {
- case KEY_CODE.LEFT:
- this._setRange('before', $query.get(0), range);
- $query.removeClass('atwho-query');
- return;
- case KEY_CODE.RIGHT:
- this._setRange('after', $query.get(0).nextSibling, range);
- $query.removeClass('atwho-query');
- return;
- }
- }
- if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {
- $query.empty().html(query_content).attr('data-atwho-at-query', null);
- this._setRange('after', $query.get(0), range);
- }
- _range = range.cloneRange();
- _range.setStart(range.startContainer, 0);
- matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
- isString = typeof matched === 'string';
- if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) {
- range.setStart(range.startContainer, index);
- $query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query');
- range.surroundContents($query.get(0));
- lastNode = $query.contents().last().get(0);
- if (/firefox/i.test(navigator.userAgent)) {
- range.setStart(lastNode, lastNode.length);
- range.setEnd(lastNode, lastNode.length);
- this._clearRange(range);
- } else {
- this._setRange('after', lastNode, range);
- }
- }
- if (isString && matched.length < this.getOpt('minLen', 0)) {
- return;
- }
- if (isString && matched.length <= this.getOpt('maxLen', 20)) {
- query = {
- text: matched,
- el: $query
- };
- this.trigger("matched", [this.at, query.text]);
- return this.query = query;
- } else {
- this.view.hide();
- this.query = {
- el: $query
- };
- if ($query.text().indexOf(this.at) >= 0) {
- if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) {
- $query.removeClass('atwho-query');
- } else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) {
- this._setRange("after", this._unwrap($query.text($query.text()).contents().first()));
- }
- }
- return null;
- }
- };
-
- EditableController.prototype.rect = function() {
- var $iframe, iframeOffset, rect;
- rect = this.query.el.offset();
- if (this.app.iframe && !this.app.iframeAsRoot) {
- iframeOffset = ($iframe = $(this.app.iframe)).offset();
- rect.left += iframeOffset.left - this.$inputor.scrollLeft();
- rect.top += iframeOffset.top - this.$inputor.scrollTop();
- }
- rect.bottom = rect.top + this.query.el.height();
- return rect;
- };
-
- EditableController.prototype.insert = function(content, $li) {
- var data, range, suffix, suffixNode;
- if (!this.$inputor.is(':focus')) {
- this.$inputor.focus();
- }
- suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0";
- data = $li.data('itemData');
- this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text);
- if (range = this._getRange()) {
- range.setEndAfter(this.query.el[0]);
- range.collapse(false);
- range.insertNode(suffixNode = this.app.document.createTextNode("\u200D" + suffix));
- this._setRange('after', suffixNode, range);
- }
- if (!this.$inputor.is(':focus')) {
- this.$inputor.focus();
- }
- return this.$inputor.change();
- };
-
- return EditableController;
-
-})(Controller);
-
-var Model;
-
-Model = (function() {
- function Model(context) {
- this.context = context;
- this.at = this.context.at;
- this.storage = this.context.$inputor;
- }
-
- Model.prototype.destroy = function() {
- return this.storage.data(this.at, null);
- };
-
- Model.prototype.saved = function() {
- return this.fetch() > 0;
- };
-
- Model.prototype.query = function(query, callback) {
- var _remoteFilter, data, searchKey;
- data = this.fetch();
- searchKey = this.context.getOpt("searchKey");
- data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || [];
- _remoteFilter = this.context.callbacks('remoteFilter');
- if (data.length > 0 || (!_remoteFilter && data.length === 0)) {
- return callback(data);
- } else {
- return _remoteFilter.call(this.context, query, callback);
- }
- };
-
- Model.prototype.fetch = function() {
- return this.storage.data(this.at) || [];
- };
-
- Model.prototype.save = function(data) {
- return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || []));
- };
-
- Model.prototype.load = function(data) {
- if (!(this.saved() || !data)) {
- return this._load(data);
- }
- };
-
- Model.prototype.reload = function(data) {
- return this._load(data);
- };
-
- Model.prototype._load = function(data) {
- if (typeof data === "string") {
- return $.ajax(data, {
- dataType: "json"
- }).done((function(_this) {
- return function(data) {
- return _this.save(data);
- };
- })(this));
- } else {
- return this.save(data);
- }
- };
-
- return Model;
-
-})();
-
-var View;
-
-View = (function() {
- function View(context) {
- this.context = context;
- this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");
- this.$elUl = this.$el.children();
- this.timeoutID = null;
- this.context.$el.append(this.$el);
- this.bindEvent();
- }
-
- View.prototype.init = function() {
- var header_tpl, id;
- id = this.context.getOpt("alias") || this.context.at.charCodeAt(0);
- header_tpl = this.context.getOpt("headerTpl");
- if (header_tpl && this.$el.children().length === 1) {
- this.$el.prepend(header_tpl);
- }
- return this.$el.attr({
- 'id': "at-view-" + id
- });
- };
-
- View.prototype.destroy = function() {
- return this.$el.remove();
- };
-
- View.prototype.bindEvent = function() {
- var $menu, lastCoordX, lastCoordY;
- $menu = this.$el.find('ul');
- lastCoordX = 0;
- lastCoordY = 0;
- return $menu.on('mousemove.atwho-view', 'li', (function(_this) {
- return function(e) {
- var $cur;
- if (lastCoordX === e.clientX && lastCoordY === e.clientY) {
- return;
- }
- lastCoordX = e.clientX;
- lastCoordY = e.clientY;
- $cur = $(e.currentTarget);
- if ($cur.hasClass('cur')) {
- return;
- }
- $menu.find('.cur').removeClass('cur');
- return $cur.addClass('cur');
- };
- })(this)).on('click.atwho-view', 'li', (function(_this) {
- return function(e) {
- $menu.find('.cur').removeClass('cur');
- $(e.currentTarget).addClass('cur');
- _this.choose(e);
- return e.preventDefault();
- };
- })(this));
- };
-
- View.prototype.visible = function() {
- return this.$el.is(":visible");
- };
-
- View.prototype.highlighted = function() {
- return this.$el.find(".cur").length > 0;
- };
-
- View.prototype.choose = function(e) {
- var $li, content;
- if (($li = this.$el.find(".cur")).length) {
- content = this.context.insertContentFor($li);
- this.context._stopDelayedCall();
- this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li);
- this.context.trigger("inserted", [$li, e]);
- this.hide(e);
- }
- if (this.context.getOpt("hideWithoutSuffix")) {
- return this.stopShowing = true;
- }
- };
-
- View.prototype.reposition = function(rect) {
- var _window, offset, overflowOffset, ref;
- _window = this.context.app.iframeAsRoot ? this.context.app.window : window;
- if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {
- rect.bottom = rect.top - this.$el.height();
- }
- if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) {
- rect.left = overflowOffset;
- }
- offset = {
- left: rect.left,
- top: rect.bottom
- };
- if ((ref = this.context.callbacks("beforeReposition")) != null) {
- ref.call(this.context, offset);
- }
- this.$el.offset(offset);
- return this.context.trigger("reposition", [offset]);
- };
-
- View.prototype.next = function() {
- var cur, next, nextEl, offset;
- cur = this.$el.find('.cur').removeClass('cur');
- next = cur.next();
- if (!next.length) {
- next = this.$el.find('li:first');
- }
- next.addClass('cur');
- nextEl = next[0];
- offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0);
- return this.scrollTop(Math.max(0, offset - this.$el.height()));
- };
-
- View.prototype.prev = function() {
- var cur, offset, prev, prevEl;
- cur = this.$el.find('.cur').removeClass('cur');
- prev = cur.prev();
- if (!prev.length) {
- prev = this.$el.find('li:last');
- }
- prev.addClass('cur');
- prevEl = prev[0];
- offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0);
- return this.scrollTop(Math.max(0, offset - this.$el.height()));
- };
-
- View.prototype.scrollTop = function(scrollTop) {
- var scrollDuration;
- scrollDuration = this.context.getOpt('scrollDuration');
- if (scrollDuration) {
- return this.$elUl.animate({
- scrollTop: scrollTop
- }, scrollDuration);
- } else {
- return this.$elUl.scrollTop(scrollTop);
- }
- };
-
- View.prototype.show = function() {
- var rect;
- if (this.stopShowing) {
- this.stopShowing = false;
- return;
- }
- if (!this.visible()) {
- this.$el.show();
- this.$el.scrollTop(0);
- this.context.trigger('shown');
- }
- if (rect = this.context.rect()) {
- return this.reposition(rect);
- }
- };
-
- View.prototype.hide = function(e, time) {
- var callback;
- if (!this.visible()) {
- return;
- }
- if (isNaN(time)) {
- this.$el.hide();
- return this.context.trigger('hidden', [e]);
- } else {
- callback = (function(_this) {
- return function() {
- return _this.hide();
- };
- })(this);
- clearTimeout(this.timeoutID);
- return this.timeoutID = setTimeout(callback, time);
- }
- };
-
- View.prototype.render = function(list) {
- var $li, $ul, i, item, len, li, tpl;
- if (!($.isArray(list) && list.length > 0)) {
- this.hide();
- return;
- }
- this.$el.find('ul').empty();
- $ul = this.$el.find('ul');
- tpl = this.context.getOpt('displayTpl');
- for (i = 0, len = list.length; i < len; i++) {
- item = list[i];
- item = $.extend({}, item, {
- 'atwho-at': this.context.at
- });
- li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay");
- $li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text));
- $li.data("item-data", item);
- $ul.append($li);
- }
- this.show();
- if (this.context.getOpt('highlightFirst')) {
- return $ul.find("li:first").addClass("cur");
- }
- };
-
- return View;
-
-})();
-
-var Api;
-
-Api = {
- load: function(at, data) {
- var c;
- if (c = this.controller(at)) {
- return c.model.load(data);
- }
- },
- isSelecting: function() {
- var ref;
- return !!((ref = this.controller()) != null ? ref.view.visible() : void 0);
- },
- hide: function() {
- var ref;
- return (ref = this.controller()) != null ? ref.view.hide() : void 0;
- },
- reposition: function() {
- var c;
- if (c = this.controller()) {
- return c.view.reposition(c.rect());
- }
- },
- setIframe: function(iframe, asRoot) {
- this.setupRootElement(iframe, asRoot);
- return null;
- },
- run: function() {
- return this.dispatch();
- },
- destroy: function() {
- this.shutdown();
- return this.$inputor.data('atwho', null);
- }
-};
-
-$.fn.atwho = function(method) {
- var _args, result;
- _args = arguments;
- result = null;
- this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function() {
- var $this, app;
- if (!(app = ($this = $(this)).data("atwho"))) {
- $this.data('atwho', (app = new App(this)));
- }
- if (typeof method === 'object' || !method) {
- return app.reg(method.at, method);
- } else if (Api[method] && app) {
- return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));
- } else {
- return $.error("Method " + method + " does not exist on jQuery.atwho");
- }
- });
- if (result != null) {
- return result;
- } else {
- return this;
- }
-};
-
-$.fn.atwho["default"] = {
- at: void 0,
- alias: void 0,
- data: null,
- displayTpl: "<li>${name}</li>",
- insertTpl: "${atwho-at}${name}",
- headerTpl: null,
- callbacks: DEFAULT_CALLBACKS,
- searchKey: "name",
- suffix: void 0,
- hideWithoutSuffix: false,
- startWithSpace: true,
- acceptSpaceBar: false,
- highlightFirst: true,
- limit: 5,
- maxLen: 20,
- minLen: 0,
- displayTimeout: 300,
- delay: null,
- spaceSelectsMatch: false,
- tabSelectsMatch: true,
- editableAtwhoQueryAttrs: {},
- scrollDuration: 150,
- suspendOnComposing: true,
- lookUpOnClick: true
-};
-
-$.fn.atwho.debug = false;
-
-}));
diff --git a/vendor/assets/javascripts/jquery.caret.js b/vendor/assets/javascripts/jquery.caret.js
deleted file mode 100644
index 811ec63ee47..00000000000
--- a/vendor/assets/javascripts/jquery.caret.js
+++ /dev/null
@@ -1,436 +0,0 @@
-(function (root, factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(["jquery"], function ($) {
- return (root.returnExportsGlobal = factory($));
- });
- } else if (typeof exports === 'object') {
- // Node. Does not work with strict CommonJS, but
- // only CommonJS-like enviroments that support module.exports,
- // like Node.
- module.exports = factory(require("jquery"));
- } else {
- factory(jQuery);
- }
-}(this, function ($) {
-
-/*
- Implement Github like autocomplete mentions
- http://ichord.github.com/At.js
-
- Copyright (c) 2013 chord.luo@gmail.com
- Licensed under the MIT license.
-*/
-
-/*
-本æ’件æ“作 textarea 或者 input 内的æ’入符
-åªå®žçŽ°äº†èŽ·å¾—æ’入符在文本框中的ä½ç½®ï¼Œæˆ‘设置
-æ’入符的ä½ç½®.
-*/
-
-"use strict";
-var EditableCaret, InputCaret, Mirror, Utils, discoveryIframeOf, methods, oDocument, oFrame, oWindow, pluginName, setContextBy;
-
-pluginName = 'caret';
-
-EditableCaret = (function() {
- function EditableCaret($inputor) {
- this.$inputor = $inputor;
- this.domInputor = this.$inputor[0];
- }
-
- EditableCaret.prototype.setPos = function(pos) {
- var fn, found, offset, sel;
- if (sel = oWindow.getSelection()) {
- offset = 0;
- found = false;
- (fn = function(pos, parent) {
- var node, range, _i, _len, _ref, _results;
- _ref = parent.childNodes;
- _results = [];
- for (_i = 0, _len = _ref.length; _i < _len; _i++) {
- node = _ref[_i];
- if (found) {
- break;
- }
- if (node.nodeType === 3) {
- if (offset + node.length >= pos) {
- found = true;
- range = oDocument.createRange();
- range.setStart(node, pos - offset);
- sel.removeAllRanges();
- sel.addRange(range);
- break;
- } else {
- _results.push(offset += node.length);
- }
- } else {
- _results.push(fn(pos, node));
- }
- }
- return _results;
- })(pos, this.domInputor);
- }
- return this.domInputor;
- };
-
- EditableCaret.prototype.getIEPosition = function() {
- return this.getPosition();
- };
-
- EditableCaret.prototype.getPosition = function() {
- var inputor_offset, offset;
- offset = this.getOffset();
- inputor_offset = this.$inputor.offset();
- offset.left -= inputor_offset.left;
- offset.top -= inputor_offset.top;
- return offset;
- };
-
- EditableCaret.prototype.getOldIEPos = function() {
- var preCaretTextRange, textRange;
- textRange = oDocument.selection.createRange();
- preCaretTextRange = oDocument.body.createTextRange();
- preCaretTextRange.moveToElementText(this.domInputor);
- preCaretTextRange.setEndPoint("EndToEnd", textRange);
- return preCaretTextRange.text.length;
- };
-
- EditableCaret.prototype.getPos = function() {
- var clonedRange, pos, range;
- if (range = this.range()) {
- clonedRange = range.cloneRange();
- clonedRange.selectNodeContents(this.domInputor);
- clonedRange.setEnd(range.endContainer, range.endOffset);
- pos = clonedRange.toString().length;
- clonedRange.detach();
- return pos;
- } else if (oDocument.selection) {
- return this.getOldIEPos();
- }
- };
-
- EditableCaret.prototype.getOldIEOffset = function() {
- var range, rect;
- range = oDocument.selection.createRange().duplicate();
- range.moveStart("character", -1);
- rect = range.getBoundingClientRect();
- return {
- height: rect.bottom - rect.top,
- left: rect.left,
- top: rect.top
- };
- };
-
- EditableCaret.prototype.getOffset = function(pos) {
- var clonedRange, offset, range, rect, shadowCaret;
- if (oWindow.getSelection && (range = this.range())) {
- if (range.endOffset - 1 > 0 && range.endContainer !== this.domInputor) {
- clonedRange = range.cloneRange();
- clonedRange.setStart(range.endContainer, range.endOffset - 1);
- clonedRange.setEnd(range.endContainer, range.endOffset);
- rect = clonedRange.getBoundingClientRect();
- offset = {
- height: rect.height,
- left: rect.left + rect.width,
- top: rect.top
- };
- clonedRange.detach();
- }
- if (!offset || (offset != null ? offset.height : void 0) === 0) {
- clonedRange = range.cloneRange();
- shadowCaret = $(oDocument.createTextNode("|"));
- clonedRange.insertNode(shadowCaret[0]);
- clonedRange.selectNode(shadowCaret[0]);
- rect = clonedRange.getBoundingClientRect();
- offset = {
- height: rect.height,
- left: rect.left,
- top: rect.top
- };
- shadowCaret.remove();
- clonedRange.detach();
- }
- } else if (oDocument.selection) {
- offset = this.getOldIEOffset();
- }
- if (offset) {
- offset.top += $(oWindow).scrollTop();
- offset.left += $(oWindow).scrollLeft();
- }
- return offset;
- };
-
- EditableCaret.prototype.range = function() {
- var sel;
- if (!oWindow.getSelection) {
- return;
- }
- sel = oWindow.getSelection();
- if (sel.rangeCount > 0) {
- return sel.getRangeAt(0);
- } else {
- return null;
- }
- };
-
- return EditableCaret;
-
-})();
-
-InputCaret = (function() {
- function InputCaret($inputor) {
- this.$inputor = $inputor;
- this.domInputor = this.$inputor[0];
- }
-
- InputCaret.prototype.getIEPos = function() {
- var endRange, inputor, len, normalizedValue, pos, range, textInputRange;
- inputor = this.domInputor;
- range = oDocument.selection.createRange();
- pos = 0;
- if (range && range.parentElement() === inputor) {
- normalizedValue = inputor.value.replace(/\r\n/g, "\n");
- len = normalizedValue.length;
- textInputRange = inputor.createTextRange();
- textInputRange.moveToBookmark(range.getBookmark());
- endRange = inputor.createTextRange();
- endRange.collapse(false);
- if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) {
- pos = len;
- } else {
- pos = -textInputRange.moveStart("character", -len);
- }
- }
- return pos;
- };
-
- InputCaret.prototype.getPos = function() {
- if (oDocument.selection) {
- return this.getIEPos();
- } else {
- return this.domInputor.selectionStart;
- }
- };
-
- InputCaret.prototype.setPos = function(pos) {
- var inputor, range;
- inputor = this.domInputor;
- if (oDocument.selection) {
- range = inputor.createTextRange();
- range.move("character", pos);
- range.select();
- } else if (inputor.setSelectionRange) {
- inputor.setSelectionRange(pos, pos);
- }
- return inputor;
- };
-
- InputCaret.prototype.getIEOffset = function(pos) {
- var h, textRange, x, y;
- textRange = this.domInputor.createTextRange();
- pos || (pos = this.getPos());
- textRange.move('character', pos);
- x = textRange.boundingLeft;
- y = textRange.boundingTop;
- h = textRange.boundingHeight;
- return {
- left: x,
- top: y,
- height: h
- };
- };
-
- InputCaret.prototype.getOffset = function(pos) {
- var $inputor, offset, position;
- $inputor = this.$inputor;
- if (oDocument.selection) {
- offset = this.getIEOffset(pos);
- offset.top += $(oWindow).scrollTop() + $inputor.scrollTop();
- offset.left += $(oWindow).scrollLeft() + $inputor.scrollLeft();
- return offset;
- } else {
- offset = $inputor.offset();
- position = this.getPosition(pos);
- return offset = {
- left: offset.left + position.left - $inputor.scrollLeft(),
- top: offset.top + position.top - $inputor.scrollTop(),
- height: position.height
- };
- }
- };
-
- InputCaret.prototype.getPosition = function(pos) {
- var $inputor, at_rect, end_range, format, html, mirror, start_range;
- $inputor = this.$inputor;
- format = function(value) {
- value = value.replace(/<|>|`|"|&/g, '?').replace(/\r\n|\r|\n/g, "<br/>");
- if (/firefox/i.test(navigator.userAgent)) {
- value = value.replace(/\s/g, '&nbsp;');
- }
- return value;
- };
- if (pos === void 0) {
- pos = this.getPos();
- }
- start_range = $inputor.val().slice(0, pos);
- end_range = $inputor.val().slice(pos);
- html = "<span style='position: relative; display: inline;'>" + format(start_range) + "</span>";
- html += "<span id='caret' style='position: relative; display: inline;'>|</span>";
- html += "<span style='position: relative; display: inline;'>" + format(end_range) + "</span>";
- mirror = new Mirror($inputor);
- return at_rect = mirror.create(html).rect();
- };
-
- InputCaret.prototype.getIEPosition = function(pos) {
- var h, inputorOffset, offset, x, y;
- offset = this.getIEOffset(pos);
- inputorOffset = this.$inputor.offset();
- x = offset.left - inputorOffset.left;
- y = offset.top - inputorOffset.top;
- h = offset.height;
- return {
- left: x,
- top: y,
- height: h
- };
- };
-
- return InputCaret;
-
-})();
-
-Mirror = (function() {
- Mirror.prototype.css_attr = ["borderBottomWidth", "borderLeftWidth", "borderRightWidth", "borderTopStyle", "borderRightStyle", "borderBottomStyle", "borderLeftStyle", "borderTopWidth", "boxSizing", "fontFamily", "fontSize", "fontWeight", "height", "letterSpacing", "lineHeight", "marginBottom", "marginLeft", "marginRight", "marginTop", "outlineWidth", "overflow", "overflowX", "overflowY", "paddingBottom", "paddingLeft", "paddingRight", "paddingTop", "textAlign", "textOverflow", "textTransform", "whiteSpace", "wordBreak", "wordWrap"];
-
- function Mirror($inputor) {
- this.$inputor = $inputor;
- }
-
- Mirror.prototype.mirrorCss = function() {
- var css,
- _this = this;
- css = {
- position: 'absolute',
- left: -9999,
- top: 0,
- zIndex: -20000
- };
- if (this.$inputor.prop('tagName') === 'TEXTAREA') {
- this.css_attr.push('width');
- }
- $.each(this.css_attr, function(i, p) {
- return css[p] = _this.$inputor.css(p);
- });
- return css;
- };
-
- Mirror.prototype.create = function(html) {
- this.$mirror = $('<div></div>');
- this.$mirror.css(this.mirrorCss());
- this.$mirror.html(html);
- this.$inputor.after(this.$mirror);
- return this;
- };
-
- Mirror.prototype.rect = function() {
- var $flag, pos, rect;
- $flag = this.$mirror.find("#caret");
- pos = $flag.position();
- rect = {
- left: pos.left,
- top: pos.top,
- height: $flag.height()
- };
- this.$mirror.remove();
- return rect;
- };
-
- return Mirror;
-
-})();
-
-Utils = {
- contentEditable: function($inputor) {
- return !!($inputor[0].contentEditable && $inputor[0].contentEditable === 'true');
- }
-};
-
-methods = {
- pos: function(pos) {
- if (pos || pos === 0) {
- return this.setPos(pos);
- } else {
- return this.getPos();
- }
- },
- position: function(pos) {
- if (oDocument.selection) {
- return this.getIEPosition(pos);
- } else {
- return this.getPosition(pos);
- }
- },
- offset: function(pos) {
- var offset;
- offset = this.getOffset(pos);
- return offset;
- }
-};
-
-oDocument = null;
-
-oWindow = null;
-
-oFrame = null;
-
-setContextBy = function(settings) {
- var iframe;
- if (iframe = settings != null ? settings.iframe : void 0) {
- oFrame = iframe;
- oWindow = iframe.contentWindow;
- return oDocument = iframe.contentDocument || oWindow.document;
- } else {
- oFrame = void 0;
- oWindow = window;
- return oDocument = document;
- }
-};
-
-discoveryIframeOf = function($dom) {
- var error;
- oDocument = $dom[0].ownerDocument;
- oWindow = oDocument.defaultView || oDocument.parentWindow;
- try {
- return oFrame = oWindow.frameElement;
- } catch (_error) {
- error = _error;
- }
-};
-
-$.fn.caret = function(method, value, settings) {
- var caret;
- if (methods[method]) {
- if ($.isPlainObject(value)) {
- setContextBy(value);
- value = void 0;
- } else {
- setContextBy(settings);
- }
- caret = Utils.contentEditable(this) ? new EditableCaret(this) : new InputCaret(this);
- return methods[method].apply(caret, [value]);
- } else {
- return $.error("Method " + method + " does not exist on jQuery.caret");
- }
-};
-
-$.fn.caret.EditableCaret = EditableCaret;
-
-$.fn.caret.InputCaret = InputCaret;
-
-$.fn.caret.Utils = Utils;
-
-$.fn.caret.apis = methods;
-
-
-}));
diff --git a/vendor/licenses.csv b/vendor/licenses.csv
index ed79ec5bac3..de6e32cb998 100644
--- a/vendor/licenses.csv
+++ b/vendor/licenses.csv
@@ -606,7 +606,6 @@ iterall,1.2.2,MIT
jed,1.1.1,MIT
jira-ruby,1.4.1,MIT
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-beautify,1.8.8,MIT
diff --git a/vendor/project_templates/android.tar.gz b/vendor/project_templates/android.tar.gz
new file mode 100644
index 00000000000..277aedaa1ca
--- /dev/null
+++ b/vendor/project_templates/android.tar.gz
Binary files differ
diff --git a/vendor/project_templates/dotnetcore.tar.gz b/vendor/project_templates/dotnetcore.tar.gz
new file mode 100644
index 00000000000..078ea91bbe4
--- /dev/null
+++ b/vendor/project_templates/dotnetcore.tar.gz
Binary files differ
diff --git a/vendor/project_templates/express.tar.gz b/vendor/project_templates/express.tar.gz
index fb357639a69..619e8a69695 100644
--- a/vendor/project_templates/express.tar.gz
+++ b/vendor/project_templates/express.tar.gz
Binary files differ
diff --git a/vendor/project_templates/gitbook.tar.gz b/vendor/project_templates/gitbook.tar.gz
index 73062fca038..07037a83db6 100644
--- a/vendor/project_templates/gitbook.tar.gz
+++ b/vendor/project_templates/gitbook.tar.gz
Binary files differ
diff --git a/vendor/project_templates/gomicro.tar.gz b/vendor/project_templates/gomicro.tar.gz
new file mode 100644
index 00000000000..c7d8687fdd8
--- /dev/null
+++ b/vendor/project_templates/gomicro.tar.gz
Binary files differ
diff --git a/vendor/project_templates/hexo.tar.gz b/vendor/project_templates/hexo.tar.gz
index b32c4945366..033f363b8df 100644
--- a/vendor/project_templates/hexo.tar.gz
+++ b/vendor/project_templates/hexo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/hugo.tar.gz b/vendor/project_templates/hugo.tar.gz
index 4bdb03f5b2f..f479ea12900 100644
--- a/vendor/project_templates/hugo.tar.gz
+++ b/vendor/project_templates/hugo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/iosswift.tar.gz b/vendor/project_templates/iosswift.tar.gz
new file mode 100644
index 00000000000..76f32a3a681
--- /dev/null
+++ b/vendor/project_templates/iosswift.tar.gz
Binary files differ
diff --git a/vendor/project_templates/jekyll.tar.gz b/vendor/project_templates/jekyll.tar.gz
index ab61ddd03ea..c323ce6fac6 100644
--- a/vendor/project_templates/jekyll.tar.gz
+++ b/vendor/project_templates/jekyll.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfgitbook.tar.gz b/vendor/project_templates/nfgitbook.tar.gz
new file mode 100644
index 00000000000..71f526ac43d
--- /dev/null
+++ b/vendor/project_templates/nfgitbook.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfhexo.tar.gz b/vendor/project_templates/nfhexo.tar.gz
new file mode 100644
index 00000000000..79cc74f8d72
--- /dev/null
+++ b/vendor/project_templates/nfhexo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfhugo.tar.gz b/vendor/project_templates/nfhugo.tar.gz
new file mode 100644
index 00000000000..1a4aab028a8
--- /dev/null
+++ b/vendor/project_templates/nfhugo.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfjekyll.tar.gz b/vendor/project_templates/nfjekyll.tar.gz
new file mode 100644
index 00000000000..56bf955afbe
--- /dev/null
+++ b/vendor/project_templates/nfjekyll.tar.gz
Binary files differ
diff --git a/vendor/project_templates/nfplainhtml.tar.gz b/vendor/project_templates/nfplainhtml.tar.gz
new file mode 100644
index 00000000000..3a90983bd06
--- /dev/null
+++ b/vendor/project_templates/nfplainhtml.tar.gz
Binary files differ
diff --git a/vendor/project_templates/plainhtml.tar.gz b/vendor/project_templates/plainhtml.tar.gz
index 6927ae74de8..1ed17ddc140 100644
--- a/vendor/project_templates/plainhtml.tar.gz
+++ b/vendor/project_templates/plainhtml.tar.gz
Binary files differ
diff --git a/vendor/project_templates/rails.tar.gz b/vendor/project_templates/rails.tar.gz
index 8454d2fc03b..c1f90c3f8f7 100644
--- a/vendor/project_templates/rails.tar.gz
+++ b/vendor/project_templates/rails.tar.gz
Binary files differ
diff --git a/vendor/project_templates/spring.tar.gz b/vendor/project_templates/spring.tar.gz
index 55e25fdbe7c..c1198bf13b7 100644
--- a/vendor/project_templates/spring.tar.gz
+++ b/vendor/project_templates/spring.tar.gz
Binary files differ
diff --git a/yarn.lock b/yarn.lock
index 0111b583404..4a7443c0bd8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,41 +2,41 @@
# yarn lockfile v1
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.0.0-beta.35":
+"@babel/code-frame@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8"
integrity sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==
dependencies:
"@babel/highlight" "^7.0.0"
-"@babel/core@>=7.1.0", "@babel/core@^7.2.2":
- version "7.2.2"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.2.2.tgz#07adba6dde27bb5ad8d8672f15fde3e08184a687"
- integrity sha512-59vB0RWt09cAct5EIe58+NzGP4TFSD3Bz//2/ELy3ZeTeKF6VTD1AXlH8BGGbCX0PuobZBsIzO7IAI9PH67eKw==
+"@babel/core@>=7.1.0", "@babel/core@^7.1.0", "@babel/core@^7.2.2":
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.3.4.tgz#921a5a13746c21e32445bf0798680e9d11a6530b"
+ integrity sha512-jRsuseXBo9pN197KnDwhhaaBzyZr2oIcLHHTt2oDdQrej5Qp57dCCJafWx5ivU8/alEYDpssYqv1MUqcxwQlrA==
dependencies:
"@babel/code-frame" "^7.0.0"
- "@babel/generator" "^7.2.2"
+ "@babel/generator" "^7.3.4"
"@babel/helpers" "^7.2.0"
- "@babel/parser" "^7.2.2"
+ "@babel/parser" "^7.3.4"
"@babel/template" "^7.2.2"
- "@babel/traverse" "^7.2.2"
- "@babel/types" "^7.2.2"
+ "@babel/traverse" "^7.3.4"
+ "@babel/types" "^7.3.4"
convert-source-map "^1.1.0"
debug "^4.1.0"
json5 "^2.1.0"
- lodash "^4.17.10"
+ lodash "^4.17.11"
resolve "^1.3.2"
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/generator@^7.0.0", "@babel/generator@^7.2.2":
- version "7.2.2"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.2.2.tgz#18c816c70962640eab42fe8cae5f3947a5c65ccc"
- integrity sha512-I4o675J/iS8k+P38dvJ3IBGqObLXyQLTxtrR4u9cSUJOURvafeEWb/pFMOTwtNrmq73mJzyF6ueTbO1BtN0Zeg==
+"@babel/generator@^7.0.0", "@babel/generator@^7.3.4":
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.3.4.tgz#9aa48c1989257877a9d971296e5b73bfe72e446e"
+ integrity sha512-8EXhHRFqlVVWXPezBW5keTiQi/rJMQTg/Y9uVCEZ0CAF3PKtCCaVRnp64Ii1ujhkoDhhF1fVsImoN4yJ2uz4Wg==
dependencies:
- "@babel/types" "^7.2.2"
+ "@babel/types" "^7.3.4"
jsesc "^2.5.1"
- lodash "^4.17.10"
+ lodash "^4.17.11"
source-map "^0.5.0"
trim-right "^1.0.1"
@@ -224,10 +224,10 @@
esutils "^2.0.2"
js-tokens "^4.0.0"
-"@babel/parser@^7.0.0", "@babel/parser@^7.2.2", "@babel/parser@^7.2.3":
- version "7.2.3"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.2.3.tgz#32f5df65744b70888d17872ec106b02434ba1489"
- integrity sha512-0LyEcVlfCoFmci8mXx8A5oIkpkOgyo8dRHtxBnK9RRBwxO2+JZPNsqtVEZQ7mJFPxnXF9lfmU24mHOPI0qnlkA==
+"@babel/parser@^7.0.0", "@babel/parser@^7.2.2", "@babel/parser@^7.3.4":
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.3.4.tgz#a43357e4bbf4b92a437fb9e465c192848287f27c"
+ integrity sha512-tXZCqWtlOOP4wgCp6RjRvLmfuhnqTLy9VHwRochJBCP2nDm27JnnuFEnXFASVyQNHk36jD1tAammsCEEqgscIQ==
"@babel/plugin-proposal-async-generator-functions@^7.2.0":
version "7.2.0"
@@ -315,7 +315,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.0.0"
-"@babel/plugin-syntax-object-rest-spread@^7.2.0":
+"@babel/plugin-syntax-object-rest-spread@^7.0.0", "@babel/plugin-syntax-object-rest-spread@^7.2.0":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz#3b7a3e733510c57e820b9142a6579ac8b0dfad2e"
integrity sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==
@@ -600,6 +600,11 @@
js-levenshtein "^1.1.3"
semver "^5.3.0"
+"@babel/standalone@^7.0.0":
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.3.4.tgz#b622c1e522acef91b2a14f22bdcdd4f935a1a474"
+ integrity sha512-4L9c5i4WlGqbrjOVX0Yp8TIR5cEiw1/tPYYZENW/iuO2uI6viY38U7zALidzNfGdZIwNc+A/AWqMEWKeScWkBg==
+
"@babel/template@^7.0.0", "@babel/template@^7.1.0", "@babel/template@^7.1.2", "@babel/template@^7.2.2":
version "7.2.2"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.2.2.tgz#005b3fdf0ed96e88041330379e0da9a708eb2907"
@@ -609,28 +614,28 @@
"@babel/parser" "^7.2.2"
"@babel/types" "^7.2.2"
-"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.2.2", "@babel/traverse@^7.2.3":
- version "7.2.3"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.2.3.tgz#7ff50cefa9c7c0bd2d81231fdac122f3957748d8"
- integrity sha512-Z31oUD/fJvEWVR0lNZtfgvVt512ForCTNKYcJBGbPb1QZfve4WGH8Wsy7+Mev33/45fhP/hwQtvgusNdcCMgSw==
+"@babel/traverse@^7.0.0", "@babel/traverse@^7.1.0", "@babel/traverse@^7.1.5", "@babel/traverse@^7.2.3", "@babel/traverse@^7.3.4":
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.3.4.tgz#1330aab72234f8dea091b08c4f8b9d05c7119e06"
+ integrity sha512-TvTHKp6471OYEcE/91uWmhR6PrrYywQntCHSaZ8CM8Vmp+pjAusal4nGB2WCCQd0rvI7nOMKn9GnbcvTUz3/ZQ==
dependencies:
"@babel/code-frame" "^7.0.0"
- "@babel/generator" "^7.2.2"
+ "@babel/generator" "^7.3.4"
"@babel/helper-function-name" "^7.1.0"
"@babel/helper-split-export-declaration" "^7.0.0"
- "@babel/parser" "^7.2.3"
- "@babel/types" "^7.2.2"
+ "@babel/parser" "^7.3.4"
+ "@babel/types" "^7.3.4"
debug "^4.1.0"
globals "^11.1.0"
- lodash "^4.17.10"
+ lodash "^4.17.11"
-"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2":
- version "7.2.2"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.2.2.tgz#44e10fc24e33af524488b716cdaee5360ea8ed1e"
- integrity sha512-fKCuD6UFUMkR541eDWL+2ih/xFZBXPOg/7EQFeTluMDebfqR4jrpaCjLhkWlQS4hT6nRa2PMEgXKbRB5/H2fpg==
+"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.2.2", "@babel/types@^7.3.4":
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.3.4.tgz#bf482eaeaffb367a28abbf9357a94963235d90ed"
+ integrity sha512-WEkp8MsLftM7O/ty580wAmZzN1nDmCACc5+jFzUt+GUFNNIi3LdRlueYz0YIlmJhlZx1QYDMZL5vdWCL0fNjFQ==
dependencies:
esutils "^2.0.2"
- lodash "^4.17.10"
+ lodash "^4.17.11"
to-fast-properties "^2.0.0"
"@gitlab/csslab@^1.8.0":
@@ -653,17 +658,17 @@
eslint-plugin-promise "^4.0.1"
eslint-plugin-vue "^5.0.0"
-"@gitlab/svgs@^1.52.0":
- version "1.52.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.52.0.tgz#870b0112a18b10d2cde5470a48b05025193cd207"
- integrity sha512-xqDYIaSY1MJuCa7lRIDTTPoXn6x57So2qchxwmELE+SAJxlYlpYgDKCNWcGawhyTZRDZuG/qFBWp0sMeTQD//A==
+"@gitlab/svgs@^1.54.0":
+ version "1.54.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.54.0.tgz#00320e845efd46716042cde0c348b990d4908daf"
+ integrity sha512-DR17iy8TM5IbXEacqiDP0p8SuC/J8EL+98xbfVz5BKvRsPHpeZJQNlBF/petIV5d+KWM5A9v3GZTY7uMU7z/JQ==
-"@gitlab/ui@^2.0.4":
- version "2.0.4"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-2.0.4.tgz#ba86f6e5868ef7bc7f504cef9ca504c2d2f6bffd"
- integrity sha512-dJ+KKpeqIAPYZtYZeciXhC/whNiGPVRjp5IgjQRddh3zsreqmfwQq58nSH7HepAAIepaqTe0UFuzBgrSWvVM6w==
+"@gitlab/ui@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.0.0.tgz#33ca2808dbd4395e69a366a219d1edc1f3dbccd5"
+ integrity sha512-pDEa2k6ln5GE/N2z0V7dNEeFtSTW0p9ipO2/N9q6QMxO7fhhOhpMC0QVbdIljKTbglspDWI5v6BcqUjzYri5Pg==
dependencies:
- babel-standalone "^6.26.0"
+ "@babel/standalone" "^7.0.0"
bootstrap-vue "^2.0.0-rc.11"
copy-to-clipboard "^3.0.8"
echarts "^4.2.0-rc.2"
@@ -697,11 +702,6 @@
resolved "https://registry.yarnpkg.com/@types/anymatch/-/anymatch-1.3.0.tgz#d1d55958d1fccc5527d4aba29fc9c4b942f563ff"
integrity sha512-7WcbyctkE8GTzogDb0ulRAEw7v8oIS54ft9mQTU7PfM0hp5e+8kpa+HeQ7IQrFbKtJXBKcZ4bh+Em9dTw5L6AQ==
-"@types/async@2.0.50":
- version "2.0.50"
- resolved "https://registry.yarnpkg.com/@types/async/-/async-2.0.50.tgz#117540e026d64e1846093abbd5adc7e27fda7bcb"
- integrity sha512-VMhZMMQgV1zsR+lX/0IBfAk+8Eb7dPVMWiQGFAt3qjo5x7Ml6b77jUo0e1C3ToD+XRDXqtrfw+6AB0uUsPEr3Q==
-
"@types/events@*":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@types/events/-/events-1.2.0.tgz#81a6731ce4df43619e5c8c945383b3e62a89ea86"
@@ -741,16 +741,6 @@
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-5.5.0.tgz#146c2a29ee7d3bae4bf2fcb274636e264c813c45"
integrity sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==
-"@types/strip-bom@^3.0.0":
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/@types/strip-bom/-/strip-bom-3.0.0.tgz#14a8ec3956c2e81edb7520790aecf21c290aebd2"
- integrity sha1-FKjsOVbC6B7bdSB5CuzyHCkK69I=
-
-"@types/strip-json-comments@0.0.30":
- version "0.0.30"
- resolved "https://registry.yarnpkg.com/@types/strip-json-comments/-/strip-json-comments-0.0.30.tgz#9aa30c04db212a9a0649d6ae6fd50accc40748a1"
- integrity sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==
-
"@types/tapable@*":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.4.tgz#b4ffc7dc97b498c969b360a41eee247f82616370"
@@ -801,20 +791,20 @@
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d"
integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==
-"@vue/component-compiler-utils@^2.0.0":
- version "2.2.0"
- resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.2.0.tgz#bbbb7ed38a9a8a7c93abe7ef2e54a90a04b631b4"
- integrity sha512-pS4zlcdD7BvedyB+IfiTfrbi6C977UMIfulSk8r6uL0BU46ZE2+fUj/zbSNSfVxeaj9ElmnSni5OMwF9np+b+w==
+"@vue/component-compiler-utils@^2.0.0", "@vue/component-compiler-utils@^2.4.0":
+ version "2.6.0"
+ resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-2.6.0.tgz#aa46d2a6f7647440b0b8932434d22f12371e543b"
+ integrity sha512-IHjxt7LsOFYc0DkTncB7OXJL7UzwOLPPQCfEUNyxL2qt+tF12THV+EO33O1G2Uk4feMSWua3iD39Itszx0f0bw==
dependencies:
consolidate "^0.15.1"
hash-sum "^1.0.2"
lru-cache "^4.1.2"
merge-source-map "^1.1.0"
- postcss "^6.0.20"
- postcss-selector-parser "^3.1.1"
- prettier "1.13.7"
- source-map "^0.5.6"
- vue-template-es2015-compiler "^1.6.0"
+ postcss "^7.0.14"
+ postcss-selector-parser "^5.0.0"
+ prettier "1.16.3"
+ source-map "~0.6.1"
+ vue-template-es2015-compiler "^1.9.0"
"@vue/test-utils@^1.0.0-beta.25":
version "1.0.0-beta.25"
@@ -1124,50 +1114,54 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
-apollo-boost@^0.1.20:
- version "0.1.20"
- resolved "https://registry.yarnpkg.com/apollo-boost/-/apollo-boost-0.1.20.tgz#cc3e418ebd2bea857656685d32a7a20443493363"
- integrity sha512-n2MiEY5IGpD/cy0RH+pM9vbmobM/JZ5qz38XQAUA41FxxMPlLFQxf0IUMm0tijLOJvJJBub3pDt+Of4TVPBCqA==
+apollo-boost@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/apollo-boost/-/apollo-boost-0.3.1.tgz#b6a896e020a0eab7e415032fe565734a955c65f8"
+ integrity sha512-VdXcTMxLBeNvANW/FtiarEkrRr/cepYKG6wTAURdy8CS33WYpEHtIg9S8tAjxwVzIECpE4lWyDKyPLoESJ072Q==
dependencies:
- apollo-cache "^1.1.20"
- apollo-cache-inmemory "^1.3.9"
- apollo-client "^2.4.5"
+ apollo-cache "^1.2.1"
+ apollo-cache-inmemory "^1.5.1"
+ apollo-client "^2.5.1"
apollo-link "^1.0.6"
apollo-link-error "^1.0.3"
apollo-link-http "^1.3.1"
- apollo-link-state "^0.4.0"
graphql-tag "^2.4.2"
+ ts-invariant "^0.2.1"
+ tslib "^1.9.3"
-apollo-cache-inmemory@^1.3.9:
- version "1.3.9"
- resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.3.9.tgz#10738ba6a04faaeeb0da21bbcc1f7c0b5902910c"
- integrity sha512-Q2k84p/OqIuMUyeWGc6XbVXXZu0erYOO+wTx9p+CnQUspnNvf7zmvFNgFnmudXzfuG1m1CSzePk6fC/M1ehOqQ==
+apollo-cache-inmemory@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.5.1.tgz#265d1ee67b0bf0aca9c37629d410bfae44e62953"
+ integrity sha512-D3bdpPmWfaKQkWy8lfwUg+K8OBITo3sx0BHLs1B/9vIdOIZ7JNCKq3EUcAgAfInomJUdN0QG1yOfi8M8hxkN1g==
dependencies:
- apollo-cache "^1.1.20"
- apollo-utilities "^1.0.25"
- optimism "^0.6.6"
+ apollo-cache "^1.2.1"
+ apollo-utilities "^1.2.1"
+ optimism "^0.6.9"
+ ts-invariant "^0.2.1"
+ tslib "^1.9.3"
-apollo-cache@1.1.20, apollo-cache@^1.1.20:
- version "1.1.20"
- resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.1.20.tgz#6152cc4baf6a63e376efee79f75de4f5c84bf90e"
- integrity sha512-+Du0/4kUSuf5PjPx0+pvgMGV12ezbHA8/hubYuqRQoy/4AWb4faa61CgJNI6cKz2mhDd9m94VTNKTX11NntwkQ==
+apollo-cache@1.2.1, apollo-cache@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.2.1.tgz#aae71eb4a11f1f7322adc343f84b1a39b0693644"
+ integrity sha512-nzFmep/oKlbzUuDyz6fS6aYhRmfpcHWqNkkA9Bbxwk18RD6LXC4eZkuE0gXRX0IibVBHNjYVK+Szi0Yied4SpQ==
dependencies:
- apollo-utilities "^1.0.25"
+ apollo-utilities "^1.2.1"
+ tslib "^1.9.3"
-apollo-client@^2.4.5:
- version "2.4.5"
- resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.4.5.tgz#545beda1ef60814943b5622f0feabc9f29ee9822"
- integrity sha512-nUm06EGa4TP/IY68OzmC3lTD32TqkjLOQdb69uYo+lHl8NnwebtrAw3qFtsQtTEz6ueBp/Z/HasNZng4jwafVQ==
+apollo-client@^2.5.1:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.5.1.tgz#36126ed1d32edd79c3713c6684546a3bea80e6d1"
+ integrity sha512-MNcQKiqLHdGmNJ0rZ0NXaHrToXapJgS/5kPk0FygXt+/FmDCdzqcujI7OPxEC6e9Yw5S/8dIvOXcRNuOMElHkA==
dependencies:
"@types/zen-observable" "^0.8.0"
- apollo-cache "1.1.20"
+ apollo-cache "1.2.1"
apollo-link "^1.0.0"
apollo-link-dedup "^1.0.0"
- apollo-utilities "1.0.25"
+ apollo-utilities "1.2.1"
symbol-observable "^1.0.2"
+ ts-invariant "^0.2.1"
+ tslib "^1.9.3"
zen-observable "^0.8.0"
- optionalDependencies:
- "@types/async" "2.0.50"
apollo-link-dedup@^1.0.0:
version "1.0.10"
@@ -1198,14 +1192,6 @@ apollo-link-http@^1.3.1:
apollo-link "^1.2.3"
apollo-link-http-common "^0.2.5"
-apollo-link-state@^0.4.0:
- version "0.4.2"
- resolved "https://registry.yarnpkg.com/apollo-link-state/-/apollo-link-state-0.4.2.tgz#ac00e9be9b0ca89eae0be6ba31fe904b80bbe2e8"
- integrity sha512-xMPcAfuiPVYXaLwC6oJFIZrKgV3GmdO31Ag2eufRoXpvT0AfJZjdaPB4450Nu9TslHRePN9A3quxNueILlQxlw==
- dependencies:
- apollo-utilities "^1.0.8"
- graphql-anywhere "^4.1.0-alpha.0"
-
apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.3.tgz#9bd8d5fe1d88d31dc91dae9ecc22474d451fb70d"
@@ -1214,19 +1200,14 @@ apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.3:
apollo-utilities "^1.0.0"
zen-observable-ts "^0.8.10"
-apollo-utilities@1.0.25, apollo-utilities@^1.0.0, apollo-utilities@^1.0.25, apollo-utilities@^1.0.8:
- version "1.0.25"
- resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.0.25.tgz#899b00f5f990fb451675adf84cb3de82eb6372ea"
- integrity sha512-AXvqkhni3Ir1ffm4SA1QzXn8k8I5BBl4PVKEyak734i4jFdp+xgfUyi2VCqF64TJlFTA/B73TRDUvO2D+tKtZg==
+apollo-utilities@1.2.1, apollo-utilities@^1.0.0, apollo-utilities@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.2.1.tgz#1c3a1ebf5607d7c8efe7636daaf58e7463b41b3c"
+ integrity sha512-Zv8Udp9XTSFiN8oyXOjf6PMHepD4yxxReLsl6dPUy5Ths7jti3nmlBzZUOxuTWRwZn0MoclqL7RQ5UEJN8MAxg==
dependencies:
fast-json-stable-stringify "^2.0.0"
-
-append-transform@^0.4.0:
- version "0.4.0"
- resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991"
- integrity sha1-126/jKlNJ24keja61EpLdKthGZE=
- dependencies:
- default-require-extensions "^1.0.0"
+ ts-invariant "^0.2.1"
+ tslib "^1.9.3"
append-transform@^1.0.0:
version "1.0.0"
@@ -1255,19 +1236,12 @@ 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"
- integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=
- 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"
integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=
-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"
integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==
@@ -1382,6 +1356,11 @@ async-each@^1.0.0:
resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
integrity sha1-GdOGodntxufByF04iu28xW0zYC0=
+async-foreach@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
+ integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=
+
async-limiter@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
@@ -1392,7 +1371,7 @@ async@1.x, async@^1.5.2:
resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=
-async@^2.0.0, async@^2.1.4, async@^2.5.0, async@^2.6.1:
+async@^2.0.0, async@^2.5.0, async@^2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610"
integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==
@@ -1404,6 +1383,11 @@ asynckit@^0.4.0:
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+at.js@^1.5.4:
+ version "1.5.4"
+ resolved "https://registry.yarnpkg.com/at.js/-/at.js-1.5.4.tgz#8fc60cc80eadbe4874449b166a818e7ae1d784c1"
+ integrity sha512-G8mgUb/PqShPoH8AyjuxsTGvIr1o716BtQUKDM44C8qN2W615y7KGJ68MlTGamd0J0D/m28emUkzagaHTdrGZw==
+
atob@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
@@ -1460,36 +1444,6 @@ babel-code-frame@^6.26.0:
esutils "^2.0.2"
js-tokens "^3.0.2"
-babel-core@^6.0.0, babel-core@^6.26.0:
- version "6.26.3"
- resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207"
- integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==
- dependencies:
- babel-code-frame "^6.26.0"
- babel-generator "^6.26.0"
- babel-helpers "^6.24.1"
- babel-messages "^6.23.0"
- babel-register "^6.26.0"
- babel-runtime "^6.26.0"
- babel-template "^6.26.0"
- babel-traverse "^6.26.0"
- babel-types "^6.26.0"
- babylon "^6.18.0"
- convert-source-map "^1.5.1"
- debug "^2.6.9"
- json5 "^0.5.1"
- lodash "^4.17.4"
- minimatch "^3.0.4"
- path-is-absolute "^1.0.1"
- private "^0.1.8"
- slash "^1.0.0"
- source-map "^0.5.7"
-
-babel-core@^7.0.0-bridge:
- version "7.0.0-bridge.0"
- resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
- integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==
-
babel-eslint@^10.0.1:
version "10.0.1"
resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed"
@@ -1502,35 +1456,15 @@ babel-eslint@^10.0.1:
eslint-scope "3.7.1"
eslint-visitor-keys "^1.0.0"
-babel-generator@^6.18.0, babel-generator@^6.26.0:
- version "6.26.1"
- resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90"
- integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==
- dependencies:
- babel-messages "^6.23.0"
- babel-runtime "^6.26.0"
- babel-types "^6.26.0"
- detect-indent "^4.0.0"
- jsesc "^1.3.0"
- lodash "^4.17.4"
- source-map "^0.5.7"
- trim-right "^1.0.1"
-
-babel-helpers@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2"
- integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=
- dependencies:
- babel-runtime "^6.22.0"
- babel-template "^6.24.1"
-
-babel-jest@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-23.6.0.tgz#a644232366557a2240a0c083da6b25786185a2f1"
- integrity sha512-lqKGG6LYXYu+DQh/slrQ8nxXQkEkhugdXsU6St7GmhVS7Ilc/22ArwqXNJrf0QaOBjZB0360qZMwXqDYQHXaew==
+babel-jest@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-24.1.0.tgz#441e23ef75ded3bd547e300ac3194cef87b55190"
+ integrity sha512-MLcagnVrO9ybQGLEfZUqnOzv36iQzU7Bj4elm39vCukumLVSfoX+tRy3/jW7lUKc7XdpRmB/jech6L/UCsSZjw==
dependencies:
- babel-plugin-istanbul "^4.1.6"
- babel-preset-jest "^23.2.0"
+ babel-plugin-istanbul "^5.1.0"
+ babel-preset-jest "^24.1.0"
+ chalk "^2.4.2"
+ slash "^2.0.0"
babel-loader@^8.0.5:
version "8.0.5"
@@ -1542,13 +1476,6 @@ babel-loader@^8.0.5:
mkdirp "^0.5.1"
util.promisify "^1.0.0"
-babel-messages@^6.23.0:
- version "6.23.0"
- resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e"
- integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=
- dependencies:
- babel-runtime "^6.22.0"
-
babel-plugin-dynamic-import-node@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.2.0.tgz#c0adfb07d95f4a4495e9aaac6ec386c4d7c2524e"
@@ -1556,16 +1483,6 @@ babel-plugin-dynamic-import-node@^2.2.0:
dependencies:
object.assign "^4.1.0"
-babel-plugin-istanbul@^4.1.6:
- version "4.1.6"
- resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45"
- integrity sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==
- dependencies:
- babel-plugin-syntax-object-rest-spread "^6.13.0"
- find-up "^2.1.0"
- istanbul-lib-instrument "^1.10.1"
- test-exclude "^4.2.1"
-
babel-plugin-istanbul@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-5.1.0.tgz#6892f529eff65a3e2d33d87dc5888ffa2ecd4a30"
@@ -1575,39 +1492,16 @@ babel-plugin-istanbul@^5.1.0:
istanbul-lib-instrument "^3.0.0"
test-exclude "^5.0.0"
-babel-plugin-jest-hoist@^23.2.0:
- version "23.2.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-23.2.0.tgz#e61fae05a1ca8801aadee57a6d66b8cefaf44167"
- integrity sha1-5h+uBaHKiAGq3uV6bWa4zvr0QWc=
+babel-plugin-jest-hoist@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.1.0.tgz#dfecc491fb15e2668abbd690a697a8fd1411a7f8"
+ integrity sha512-gljYrZz8w1b6fJzKcsfKsipSru2DU2DmQ39aB6nV3xQ0DDv3zpIzKGortA5gknrhNnPN8DweaEgrnZdmbGmhnw==
babel-plugin-rewire@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/babel-plugin-rewire/-/babel-plugin-rewire-1.2.0.tgz#822562d72ed2c84e47c0f95ee232c920853e9d89"
integrity sha512-JBZxczHw3tScS+djy6JPLMjblchGhLI89ep15H3SyjujIzlxo5nr6Yjo7AXotdeVczeBmWs0tF8PgJWDdgzAkQ==
-babel-plugin-syntax-object-rest-spread@^6.13.0:
- version "6.13.0"
- resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
- integrity sha1-/WU28rzhODb/o6VFjEkDpZe7O/U=
-
-babel-plugin-transform-es2015-modules-commonjs@^6.26.0, babel-plugin-transform-es2015-modules-commonjs@^6.26.2:
- version "6.26.2"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3"
- integrity sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==
- dependencies:
- babel-plugin-transform-strict-mode "^6.24.1"
- babel-runtime "^6.26.0"
- babel-template "^6.26.0"
- babel-types "^6.26.0"
-
-babel-plugin-transform-strict-mode@^6.24.1:
- version "6.24.1"
- resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758"
- integrity sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=
- dependencies:
- 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"
@@ -1617,28 +1511,15 @@ babel-polyfill@6.23.0:
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
-babel-preset-jest@^23.2.0:
- version "23.2.0"
- resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-23.2.0.tgz#8ec7a03a138f001a1a8fb1e8113652bf1a55da46"
- integrity sha1-jsegOhOPABoaj7HoETZSvxpV2kY=
- dependencies:
- babel-plugin-jest-hoist "^23.2.0"
- babel-plugin-syntax-object-rest-spread "^6.13.0"
-
-babel-register@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071"
- integrity sha1-btAhFz4vy0htestFxgCahW9kcHE=
+babel-preset-jest@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-24.1.0.tgz#83bc564fdcd4903641af65ec63f2f5de6b04132e"
+ integrity sha512-FfNLDxFWsNX9lUmtwY7NheGlANnagvxq8LZdl5PKnVG3umP+S/g0XbVBfwtA4Ai3Ri/IMkWabBz3Tyk9wdspcw==
dependencies:
- babel-core "^6.26.0"
- babel-runtime "^6.26.0"
- core-js "^2.5.0"
- home-or-tmp "^2.0.0"
- lodash "^4.17.4"
- mkdirp "^0.5.1"
- source-map-support "^0.4.15"
+ "@babel/plugin-syntax-object-rest-spread" "^7.0.0"
+ babel-plugin-jest-hoist "^24.1.0"
-babel-runtime@^6.22.0, babel-runtime@^6.26.0:
+babel-runtime@^6.22.0:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4=
@@ -1646,57 +1527,11 @@ babel-runtime@^6.22.0, babel-runtime@^6.26.0:
core-js "^2.4.0"
regenerator-runtime "^0.11.0"
-babel-standalone@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-standalone/-/babel-standalone-6.26.0.tgz#15fb3d35f2c456695815ebf1ed96fe7f015b6886"
- integrity sha1-Ffs9NfLEVmlYFevx7Zb+fwFbaIY=
-
-babel-template@^6.16.0, babel-template@^6.24.1, babel-template@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02"
- integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=
- dependencies:
- babel-runtime "^6.26.0"
- babel-traverse "^6.26.0"
- babel-types "^6.26.0"
- babylon "^6.18.0"
- lodash "^4.17.4"
-
-babel-traverse@^6.0.0, babel-traverse@^6.18.0, babel-traverse@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee"
- integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=
- dependencies:
- babel-code-frame "^6.26.0"
- babel-messages "^6.23.0"
- babel-runtime "^6.26.0"
- babel-types "^6.26.0"
- babylon "^6.18.0"
- debug "^2.6.8"
- globals "^9.18.0"
- invariant "^2.2.2"
- lodash "^4.17.4"
-
-babel-types@^6.0.0, babel-types@^6.18.0, babel-types@^6.24.1, babel-types@^6.26.0:
- version "6.26.0"
- resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497"
- integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=
- dependencies:
- babel-runtime "^6.26.0"
- esutils "^2.0.2"
- lodash "^4.17.4"
- to-fast-properties "^1.0.3"
-
babylon@7.0.0-beta.19:
version "7.0.0-beta.19"
resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.19.tgz#e928c7e807e970e0536b078ab3e0c48f9e052503"
integrity sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==
-babylon@^6.18.0:
- version "6.18.0"
- resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
- integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==
-
backo2@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
@@ -1789,6 +1624,13 @@ blob@0.0.4:
resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.4.tgz#bcf13052ca54463f30f9fc7e95b9a47630a94921"
integrity sha1-vPEwUspURj8w+fx+lbmkdjCpSSE=
+block-stream@*:
+ version "0.0.9"
+ resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
+ integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=
+ dependencies:
+ inherits "~2.0.0"
+
bluebird@^3.1.1, bluebird@^3.3.0, bluebird@^3.5.1, bluebird@^3.5.3, bluebird@~3.5.0:
version "3.5.3"
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7"
@@ -1872,15 +1714,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"
- integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=
- 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"
@@ -1983,6 +1816,13 @@ browserslist@^4.3.4, browserslist@^4.4.1:
electron-to-chromium "^1.3.103"
node-releases "^1.1.3"
+bs-logger@0.x:
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8"
+ integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==
+ dependencies:
+ fast-json-stable-stringify "2.x"
+
bser@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/bser/-/bser-2.0.0.tgz#9ac78d3ed5d915804fd87acb158bc797147a1719"
@@ -1990,10 +1830,10 @@ bser@^2.0.0:
dependencies:
node-int64 "^0.4.0"
-buffer-from@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.0.0.tgz#4cb8832d23612589b0406e9e2956c17f06fdf531"
- integrity sha512-83apNb8KK0Se60UE1+4Ukbe3HbfELJ6UlI4ldtOGs7So4KD26orJM8hIY9lxdzP+UpItH1Yh/Y8GUvNFWFFRxA==
+buffer-from@1.x, buffer-from@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
+ integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
buffer-indexof@^1.0.0:
version "1.1.0"
@@ -2124,6 +1964,19 @@ callsites@^2.0.0:
resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50"
integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA=
+callsites@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.0.0.tgz#fb7eb569b72ad7a45812f93fd9430a3e410b3dd3"
+ integrity sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==
+
+camelcase-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7"
+ integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc=
+ dependencies:
+ camelcase "^2.0.0"
+ map-obj "^1.0.0"
+
camelcase-keys@^4.0.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77"
@@ -2133,6 +1986,16 @@ camelcase-keys@^4.0.0:
map-obj "^2.0.0"
quick-lru "^1.0.0"
+camelcase@^2.0.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f"
+ integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8=
+
+camelcase@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
+ integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
+
camelcase@^4.0.0, camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@@ -2177,7 +2040,7 @@ ccount@^1.0.0:
resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.3.tgz#f1cec43f332e2ea5a569fd46f9f5bde4e6102aff"
integrity sha512-Jt9tIBkRc9POUof7QA/VwWd+58fKkEEfI+/t1/eOlxKM7ZhrczNzMFefge7Ai+39y1pR/pP6cI19guHy3FSLmw==
-chalk@1.1.3, chalk@^1.0.0, 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"
integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
@@ -2227,7 +2090,7 @@ chardet@^0.5.0:
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029"
integrity sha512-9ZTaoBaePSCFvNlNGrsyI8ZVACP2svUtq0DkM7t4K2ClAa96sqOIRjAzDTc8zXzFt1cZR46rRzLTiHFSJ+Qw0g==
-"charenc@>= 0.0.1":
+"charenc@>= 0.0.1", charenc@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
@@ -2297,6 +2160,11 @@ ci-info@^1.5.0:
resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-1.6.0.tgz#2ca20dbb9ceb32d4524a683303313f0304b1e497"
integrity sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==
+ci-info@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46"
+ integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==
+
cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de"
@@ -2366,6 +2234,15 @@ clipboard@^1.5.5, clipboard@^1.7.1:
select "^1.1.2"
tiny-emitter "^2.0.0"
+cliui@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d"
+ integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=
+ dependencies:
+ string-width "^1.0.1"
+ strip-ansi "^3.0.1"
+ wrap-ansi "^2.0.0"
+
cliui@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.0.0.tgz#743d4650e05f36d1ed2575b59638d87322bfbbcc"
@@ -2390,11 +2267,6 @@ clone-response@1.0.2:
dependencies:
mimic-response "^1.0.0"
-clone@2.x:
- version "2.1.2"
- resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
- integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
-
co@^4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
@@ -2629,7 +2501,7 @@ content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
-convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.5.1:
+convert-source-map@^1.1.0, convert-source-map@^1.4.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.6.0.tgz#51b537a8c43e0f04dec1993bffcdd504e758ac20"
integrity sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==
@@ -2670,7 +2542,7 @@ copy-to-clipboard@^3.0.8:
dependencies:
toggle-selection "^1.0.3"
-core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0:
+core-js@^2.2.0, core-js@^2.4.0, core-js@^2.4.1:
version "2.5.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
integrity sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==
@@ -2739,6 +2611,14 @@ cropper@^2.3.0:
dependencies:
jquery ">= 1.9.1"
+cross-spawn@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
+ integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI=
+ dependencies:
+ lru-cache "^4.0.1"
+ which "^1.2.9"
+
cross-spawn@^5.0.1:
version "5.1.0"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449"
@@ -2759,7 +2639,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5:
shebang-command "^1.2.0"
which "^1.2.9"
-"crypt@>= 0.0.1":
+"crypt@>= 0.0.1", crypt@~0.0.1:
version "0.0.2"
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
@@ -3174,7 +3054,7 @@ debug@^3.1.0, debug@^3.2.5:
dependencies:
ms "^2.1.1"
-debug@^4.0.0, debug@^4.0.1, debug@^4.1.0:
+debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==
@@ -3196,7 +3076,7 @@ decamelize-keys@^1.0.0:
decamelize "^1.1.0"
map-obj "^1.0.0"
-decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.2.0:
+decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
@@ -3248,13 +3128,6 @@ default-gateway@^2.6.0:
execa "^0.10.0"
ip-regex "^2.1.0"
-default-require-extensions@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-1.0.0.tgz#f37ea15d3e13ffd9b437d33e1a75b5fb97874cb8"
- integrity sha1-836hXT4T/9m0N9M+GnW1+5eHTLg=
- dependencies:
- strip-bom "^2.0.0"
-
default-require-extensions@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/default-require-extensions/-/default-require-extensions-2.0.0.tgz#f5f8fbb18a7d6d50b21f641f649ebb522cfe24f7"
@@ -3359,13 +3232,6 @@ detect-file@^1.0.0:
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=
-detect-indent@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208"
- integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg=
- dependencies:
- repeating "^2.0.0"
-
detect-libc@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
@@ -3386,6 +3252,11 @@ di@^0.0.1:
resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c"
integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw=
+diff-sequences@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.0.0.tgz#cdf8e27ed20d8b8d3caccb4e0c0d8fe31a173013"
+ integrity sha512-46OkIuVGBBnrC0soO/4LHu5LHGHx0uhP65OVz8XOrAJpqiCB2aVIuESvjI1F9oqebuvY8lekS1pt6TN7vt7qsw==
+
diff@^3.2.0, diff@^3.4.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12"
@@ -3876,10 +3747,10 @@ eslint-plugin-jasmine@^2.10.1:
resolved "https://registry.yarnpkg.com/eslint-plugin-jasmine/-/eslint-plugin-jasmine-2.10.1.tgz#5733b709e751f4bc40e31e1c16989bd2cdfbec97"
integrity sha1-VzO3CedR9LxA4x4cFpib0s377Jc=
-eslint-plugin-jest@^22.1.0:
- version "22.1.0"
- resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.1.0.tgz#9a4dfa3367563e8301560a7fb92ec309096dbca3"
- integrity sha512-WcQd5LxEoAS20zuWEAd8CX0pVC+gGInZPcsoYvK5w7BrEJNmltyTxYYh1r0ct4GsahD2GvNySlcTcLtK2amFZA==
+eslint-plugin-jest@^22.3.0:
+ version "22.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-22.3.0.tgz#a10f10dedfc92def774ec9bb5bfbd2fb8e1c96d2"
+ integrity sha512-P1mYVRNlOEoO5T9yTqOfucjOYf1ktmJ26NjwjH8sxpCFQa6IhBGr5TpKl3hcAAT29hOsRJVuMWmTsHoUVo9FoA==
eslint-plugin-promise@^4.0.1:
version "4.0.1"
@@ -4089,6 +3960,19 @@ execa@^0.7.0:
signal-exit "^3.0.0"
strip-eof "^1.0.0"
+execa@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8"
+ integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==
+ dependencies:
+ cross-spawn "^6.0.0"
+ get-stream "^4.0.0"
+ is-stream "^1.1.0"
+ npm-run-path "^2.0.0"
+ p-finally "^1.0.0"
+ signal-exit "^3.0.0"
+ strip-eof "^1.0.0"
+
execall@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/execall/-/execall-1.0.0.tgz#73d0904e395b3cab0658b08d09ec25307f29bb73"
@@ -4110,13 +3994,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"
- integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=
- 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"
@@ -4138,13 +4015,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"
- integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=
- dependencies:
- fill-range "^2.1.0"
-
expand-tilde@^2.0.0, expand-tilde@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502"
@@ -4152,17 +4022,16 @@ expand-tilde@^2.0.0, expand-tilde@^2.0.2:
dependencies:
homedir-polyfill "^1.0.1"
-expect@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/expect/-/expect-23.6.0.tgz#1e0c8d3ba9a581c87bd71fb9bc8862d443425f98"
- integrity sha512-dgSoOHgmtn/aDGRVFWclQyPDKl2CQRq0hmIEoUAuQs/2rn2NcvCWcSCovm6BLeuB/7EZuLGu2QfnR+qRt5OM4w==
+expect@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/expect/-/expect-24.1.0.tgz#88e73301c4c785cde5f16da130ab407bdaf8c0f2"
+ integrity sha512-lVcAPhaYkQcIyMS+F8RVwzbm1jro20IG8OkvxQ6f1JfqhVZyyudCwYogQ7wnktlf14iF3ii7ArIUO/mqvrW9Gw==
dependencies:
ansi-styles "^3.2.0"
- jest-diff "^23.6.0"
- jest-get-type "^22.1.0"
- jest-matcher-utils "^23.6.0"
- jest-message-util "^23.4.0"
- jest-regex-util "^23.3.0"
+ jest-get-type "^24.0.0"
+ jest-matcher-utils "^24.0.0"
+ jest-message-util "^24.0.0"
+ jest-regex-util "^24.0.0"
exports-loader@^0.7.0:
version "0.7.0"
@@ -4246,13 +4115,6 @@ external-editor@^3.0.0:
iconv-lite "^0.4.22"
tmp "^0.0.33"
-extglob@^0.3.1:
- version "0.3.2"
- resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
- integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=
- dependencies:
- is-extglob "^1.0.0"
-
extglob@^2.0.4:
version "2.0.4"
resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543"
@@ -4296,7 +4158,7 @@ fast-glob@^2.2.6:
merge2 "^1.2.3"
micromatch "^3.1.10"
-fast-json-stable-stringify@^2.0.0:
+fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
@@ -4374,12 +4236,7 @@ file-loader@^3.0.1:
loader-utils "^1.0.2"
schema-utils "^1.0.0"
-filename-regex@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26"
- integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=
-
-fileset@^2.0.2, fileset@^2.0.3:
+fileset@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/fileset/-/fileset-2.0.3.tgz#8e7548a96d3cc2327ee5e674168723a333bba2a0"
integrity sha1-jnVIqW08wjJ+5eZ0FocjozO7oqA=
@@ -4392,17 +4249,6 @@ filesize@^3.6.1:
resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317"
integrity sha512-7KjR1vv6qnicaPMi1iiTcI85CyYwRO/PSFCu6SvqL8jN2Wjt/NIYQTFtFs7fSDCYOstUkEWIQGFUg5YZQfjlcg==
-fill-range@^2.1.0:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565"
- integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==
- dependencies:
- is-number "^2.1.0"
- isobject "^2.0.0"
- randomatic "^3.0.0"
- 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"
@@ -4439,14 +4285,6 @@ finalhandler@1.1.1:
statuses "~1.4.0"
unpipe "~1.0.0"
-find-babel-config@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/find-babel-config/-/find-babel-config-1.1.0.tgz#acc01043a6749fec34429be6b64f542ebb5d6355"
- integrity sha1-rMAQQ6Z0n+w0Qpvmtk9ULrtdY1U=
- dependencies:
- json5 "^0.5.1"
- path-exists "^3.0.0"
-
find-cache-dir@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.0.0.tgz#4c1faed59f45184530fb9d7fa123a4d04a98472d"
@@ -4532,18 +4370,11 @@ 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"
integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
-for-own@^0.1.4:
- version "0.1.5"
- resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce"
- integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=
- dependencies:
- for-in "^1.0.1"
-
forever-agent@~0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -4630,6 +4461,16 @@ fsevents@^1.2.2, fsevents@^1.2.3:
nan "^2.9.2"
node-pre-gyp "^0.10.0"
+fstream@^1.0.0, fstream@^1.0.2:
+ version "1.0.11"
+ resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171"
+ integrity sha1-XB+x8RdHcRTwYyoOtLcbPLD9MXE=
+ dependencies:
+ graceful-fs "^4.1.2"
+ inherits "~2.0.0"
+ mkdirp ">=0.5 0"
+ rimraf "2"
+
function-bind@^1.0.2, function-bind@^1.1.0, function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
@@ -4659,11 +4500,23 @@ gauge@~2.7.3:
strip-ansi "^3.0.1"
wide-align "^1.1.0"
+gaze@^1.0.0:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a"
+ integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g==
+ dependencies:
+ globule "^1.0.0"
+
get-caller-file@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5"
integrity sha1-9wLmMSfn4jHBYKgMFVSstw1QR+U=
+get-stdin@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe"
+ integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4=
+
get-stdin@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
@@ -4674,6 +4527,13 @@ get-stream@3.0.0, get-stream@^3.0.0:
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
+get-stream@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
+ integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==
+ dependencies:
+ pump "^3.0.0"
+
get-value@^2.0.3, get-value@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28"
@@ -4708,21 +4568,6 @@ gettext-extractor@^3.3.2:
pofile "^1"
typescript "^2"
-glob-base@^0.3.0:
- version "0.3.0"
- resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4"
- integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=
- dependencies:
- glob-parent "^2.0.0"
- is-glob "^2.0.0"
-
-glob-parent@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28"
- integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=
- dependencies:
- is-glob "^2.0.0"
-
glob-parent@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae"
@@ -4736,7 +4581,7 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
-"glob@5 - 7", glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3:
+"glob@5 - 7", glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@~7.1.1:
version "7.1.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1"
integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==
@@ -4812,11 +4657,6 @@ globals@^11.1.0, globals@^11.7.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673"
integrity sha512-K8BNSPySfeShBQXsahYB/AbbWruVOTyVpgoIDnl8odPpeSfP2J5QO2oLFFdl2j7GfDCtZj2bMKar2T49itTPCg==
-globals@^9.18.0:
- version "9.18.0"
- resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a"
- integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==
-
globby@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d"
@@ -4858,6 +4698,15 @@ globjoin@^0.1.4:
resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43"
integrity sha1-L0SUrIkZ43Z8XLtpHp9GMyQoXUM=
+globule@^1.0.0:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/globule/-/globule-1.2.1.tgz#5dffb1b191f22d20797a9369b49eab4e9839696d"
+ integrity sha512-g7QtgWF4uYSL5/dn71WxubOrS7JVGCnFPEnoeChJmBnyR9Mw8nGoEwOgJL/RC2Te0WhbsEUCejfH8SZNJ+adYQ==
+ dependencies:
+ glob "~7.1.1"
+ lodash "~4.17.10"
+ minimatch "~3.0.2"
+
gonzales-pe@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.2.3.tgz#41091703625433285e0aee3aa47829fc1fbeb6f2"
@@ -4924,13 +4773,6 @@ graphlibrary@^2.2.0:
dependencies:
lodash "^4.17.5"
-graphql-anywhere@^4.1.0-alpha.0:
- version "4.1.22"
- resolved "https://registry.yarnpkg.com/graphql-anywhere/-/graphql-anywhere-4.1.22.tgz#1c831ba3c9e5664a0dd24d10d23a9e9512d92056"
- integrity sha512-qm2/1cKM8nfotxDhm4J0r1znVlK0Yge/yEKt26EVVBgpIhvxjXYFALCGbr7cvfDlvzal1iSPpaYa+8YTtjsxQA==
- dependencies:
- apollo-utilities "^1.0.25"
-
graphql-tag@^2.10.0, graphql-tag@^2.4.2:
version "2.10.0"
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.0.tgz#87da024be863e357551b2b8700e496ee2d4353ae"
@@ -4961,10 +4803,10 @@ handle-thing@^2.0.0:
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.0.tgz#0e039695ff50c93fc288557d696f3c1dc6776754"
integrity sha512-d4sze1JNC454Wdo2fkuyzCr6aHcbL6PGGuFAz0Li/NcOm1tCHGnWDRmJP85dh9IhQErTc2svWFEX5xHIOo//kQ==
-handlebars@^4.0.1, handlebars@^4.0.11, handlebars@^4.0.3:
- version "4.0.12"
- resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.12.tgz#2c15c8a96d46da5e266700518ba8cb8d919d5bc5"
- integrity sha512-RhmTekP+FZL+XNhwS1Wf+bTTZpdLougwt5pcgA1tuz6Jcx0fpH/7z0qd71RKnZHBCxIRBHfBOnio4gViPemNzA==
+handlebars@^4.0.1, handlebars@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a"
+ integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w==
dependencies:
async "^2.5.0"
optimist "^0.6.1"
@@ -5121,14 +4963,6 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-home-or-tmp@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8"
- integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg=
- dependencies:
- os-homedir "^1.0.0"
- os-tmpdir "^1.0.1"
-
homedir-polyfill@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc"
@@ -5321,14 +5155,6 @@ import-lazy@^3.1.0:
resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc"
integrity sha512-8/gvXvX2JMn0F+CDlSC4l6kOmVaLOO3XLkksI7CI3Ud95KDYJuYur2b9P/PUt/i/pDAMd/DulQsNbbbmRRsDIQ==
-import-local@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
- integrity sha512-vAaZHieK9qjGo58agRBg+bhHX3hoTZU/Oa3GESWLz7t1U62fk63aHuDJJEteXoDeTCcPmUT+z38gkHPZkkmpmQ==
- dependencies:
- pkg-dir "^2.0.0"
- resolve-cwd "^2.0.0"
-
import-local@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d"
@@ -5350,6 +5176,18 @@ imurmurhash@^0.1.4:
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+in-publish@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51"
+ integrity sha1-4g/146KvwmkDILbcVSaCqcf631E=
+
+indent-string@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80"
+ integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=
+ dependencies:
+ repeating "^2.0.0"
+
indent-string@^3.0.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
@@ -5373,7 +5211,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
-inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3:
+inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de"
integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=
@@ -5523,7 +5361,7 @@ is-binary-path@^1.0.0:
dependencies:
binary-extensions "^1.0.0"
-is-buffer@^1.1.5:
+is-buffer@^1.1.5, is-buffer@~1.1.1:
version "1.1.6"
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
@@ -5545,6 +5383,13 @@ is-ci@^1.0.10:
dependencies:
ci-info "^1.5.0"
+is-ci@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
+ integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==
+ dependencies:
+ ci-info "^2.0.0"
+
is-data-descriptor@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56"
@@ -5592,18 +5437,6 @@ is-directory@^0.3.1:
resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1"
integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE=
-is-dotfile@^1.0.0:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1"
- integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=
-
-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"
- integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=
- 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"
@@ -5616,11 +5449,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"
- integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=
-
is-extglob@^2.1.0, is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
@@ -5645,17 +5473,10 @@ is-fullwidth-code-point@^2.0.0:
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=
-is-generator-fn@^1.0.0:
- version "1.0.0"
- resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-1.0.0.tgz#969d49e1bb3329f6bb7f09089be26578b2ddd46a"
- integrity sha1-lp1J4bszKfa7fwkIm+JleLLd1Go=
-
-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"
- integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=
- dependencies:
- is-extglob "^1.0.0"
+is-generator-fn@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.0.0.tgz#038c31b774709641bda678b1f06a4e3227c10b3e"
+ integrity sha512-elzyIdM7iKoFHzcrndIqjYomImhxrFRnGP3galODoII4TB9gI7mZ+FnlLQmmjf27SxHS2gKEeyhX5/+YRS6H9g==
is-glob@^3.1.0:
version "3.1.0"
@@ -5694,13 +5515,6 @@ is-number@^0.1.1:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-0.1.1.tgz#69a7af116963d47206ec9bd9b48a14216f1e3806"
integrity sha1-aaevEWlj1HIG7JvZtIoUIW8eOAY=
-is-number@^2.1.0:
- version "2.1.0"
- resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f"
- integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=
- 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"
@@ -5761,16 +5575,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"
- integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=
-
-is-primitive@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
- integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU=
-
is-promise@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
@@ -5887,145 +5691,76 @@ isstream@~0.1.2:
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=
-istanbul-api@^1.3.1:
- version "1.3.7"
- resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.7.tgz#a86c770d2b03e11e3f778cd7aedd82d2722092aa"
- integrity sha512-4/ApBnMVeEPG3EkSzcw25wDe4N66wxwn+KKn6b47vyek8Xb3NBAcg4xfuQbS7BqcZuTX4wxfD5lVagdggR3gyA==
- dependencies:
- async "^2.1.4"
- fileset "^2.0.2"
- istanbul-lib-coverage "^1.2.1"
- istanbul-lib-hook "^1.2.2"
- istanbul-lib-instrument "^1.10.2"
- istanbul-lib-report "^1.1.5"
- istanbul-lib-source-maps "^1.2.6"
- istanbul-reports "^1.5.1"
- js-yaml "^3.7.0"
- mkdirp "^0.5.1"
- once "^1.4.0"
-
-istanbul-api@^2.0.5:
- version "2.0.6"
- resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-2.0.6.tgz#cd7b33ee678f6c01531d05f5e567ebbcd25f8ecc"
- integrity sha512-8W5oeAGWXhtTJjAyVfvavOLVyZCTNCKsyF6GON/INKlBdO7uJ/bv3qnPj5M6ERKzmMCJS1kntnjjGuJ86fn3rQ==
+istanbul-api@^2.0.5, istanbul-api@^2.0.8:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-2.1.1.tgz#194b773f6d9cbc99a9258446848b0f988951c4d0"
+ integrity sha512-kVmYrehiwyeBAk/wE71tW6emzLiHGjYIiDrc8sfyty4F8M02/lrgXSm+R1kXysmF20zArvmZXjlE/mg24TVPJw==
dependencies:
async "^2.6.1"
compare-versions "^3.2.1"
fileset "^2.0.3"
- istanbul-lib-coverage "^2.0.1"
- istanbul-lib-hook "^2.0.1"
- istanbul-lib-instrument "^3.0.0"
- istanbul-lib-report "^2.0.2"
- istanbul-lib-source-maps "^2.0.1"
- istanbul-reports "^2.0.1"
+ istanbul-lib-coverage "^2.0.3"
+ istanbul-lib-hook "^2.0.3"
+ istanbul-lib-instrument "^3.1.0"
+ istanbul-lib-report "^2.0.4"
+ istanbul-lib-source-maps "^3.0.2"
+ istanbul-reports "^2.1.1"
js-yaml "^3.12.0"
make-dir "^1.3.0"
+ minimatch "^3.0.4"
once "^1.4.0"
-istanbul-lib-coverage@^1.2.0, istanbul-lib-coverage@^1.2.1:
- version "1.2.1"
- resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.1.tgz#ccf7edcd0a0bb9b8f729feeb0930470f9af664f0"
- integrity sha512-PzITeunAgyGbtY1ibVIUiV679EFChHjoMNRibEIobvmrCRaIgwLxNucOSimtNWUhEib/oO7QY2imD75JVgCJWQ==
-
-istanbul-lib-coverage@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#2aee0e073ad8c5f6a0b00e0dfbf52b4667472eda"
- integrity sha512-nPvSZsVlbG9aLhZYaC3Oi1gT/tpyo3Yt5fNyf6NmcKIayz4VV/txxJFFKAK/gU4dcNn8ehsanBbVHVl0+amOLA==
-
-istanbul-lib-hook@^1.2.2:
- version "1.2.2"
- resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.2.tgz#bc6bf07f12a641fbf1c85391d0daa8f0aea6bf86"
- integrity sha512-/Jmq7Y1VeHnZEQ3TL10VHyb564mn6VrQXHchON9Jf/AEcmQ3ZIiyD1BVzNOKTZf/G3gE+kiGK6SmpF9y3qGPLw==
- dependencies:
- append-transform "^0.4.0"
+istanbul-lib-coverage@^2.0.2, istanbul-lib-coverage@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#0b891e5ad42312c2b9488554f603795f9a2211ba"
+ integrity sha512-dKWuzRGCs4G+67VfW9pBFFz2Jpi4vSp/k7zBcJ888ofV5Mi1g5CUML5GvMvV6u9Cjybftu+E8Cgp+k0dI1E5lw==
-istanbul-lib-hook@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.1.tgz#918a57b75a0f951d552a08487ca1fa5336433d72"
- integrity sha512-ufiZoiJ8CxY577JJWEeFuxXZoMqiKpq/RqZtOAYuQLvlkbJWscq9n3gc4xrCGH9n4pW0qnTxOz1oyMmVtk8E1w==
+istanbul-lib-hook@^2.0.3:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-2.0.3.tgz#e0e581e461c611be5d0e5ef31c5f0109759916fb"
+ integrity sha512-CLmEqwEhuCYtGcpNVJjLV1DQyVnIqavMLFHV/DP+np/g3qvdxu3gsPqYoJMXm15sN84xOlckFB3VNvRbf5yEgA==
dependencies:
append-transform "^1.0.0"
-istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.10.2:
- version "1.10.2"
- resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.2.tgz#1f55ed10ac3c47f2bdddd5307935126754d0a9ca"
- integrity sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==
- dependencies:
- babel-generator "^6.18.0"
- babel-template "^6.16.0"
- babel-traverse "^6.18.0"
- babel-types "^6.18.0"
- babylon "^6.18.0"
- istanbul-lib-coverage "^1.2.1"
- semver "^5.3.0"
-
-istanbul-lib-instrument@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.0.0.tgz#b5f066b2a161f75788be17a9d556f40a0cf2afc9"
- integrity sha512-eQY9vN9elYjdgN9Iv6NS/00bptm02EBBk70lRMaVjeA6QYocQgenVrSgC28TJurdnZa80AGO3ASdFN+w/njGiQ==
+istanbul-lib-instrument@^3.0.0, istanbul-lib-instrument@^3.0.1, istanbul-lib-instrument@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-3.1.0.tgz#a2b5484a7d445f1f311e93190813fa56dfb62971"
+ integrity sha512-ooVllVGT38HIk8MxDj/OIHXSYvH+1tq/Vb38s8ixt9GoJadXska4WkGY+0wkmtYCZNYtaARniH/DixUGGLZ0uA==
dependencies:
"@babel/generator" "^7.0.0"
"@babel/parser" "^7.0.0"
"@babel/template" "^7.0.0"
"@babel/traverse" "^7.0.0"
"@babel/types" "^7.0.0"
- istanbul-lib-coverage "^2.0.1"
+ istanbul-lib-coverage "^2.0.3"
semver "^5.5.0"
-istanbul-lib-report@^1.1.5:
- version "1.1.5"
- resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.5.tgz#f2a657fc6282f96170aaf281eb30a458f7f4170c"
- integrity sha512-UsYfRMoi6QO/doUshYNqcKJqVmFe9w51GZz8BS3WB0lYxAllQYklka2wP9+dGZeHYaWIdcXUx8JGdbqaoXRXzw==
- dependencies:
- istanbul-lib-coverage "^1.2.1"
- mkdirp "^0.5.1"
- path-parse "^1.0.5"
- supports-color "^3.1.2"
-
-istanbul-lib-report@^2.0.2:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.2.tgz#430a2598519113e1da7af274ba861bd42dd97535"
- integrity sha512-rJ8uR3peeIrwAxoDEbK4dJ7cqqtxBisZKCuwkMtMv0xYzaAnsAi3AHrHPAAtNXzG/bcCgZZ3OJVqm1DTi9ap2Q==
+istanbul-lib-report@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-2.0.4.tgz#bfd324ee0c04f59119cb4f07dab157d09f24d7e4"
+ integrity sha512-sOiLZLAWpA0+3b5w5/dq0cjm2rrNdAfHWaGhmn7XEFW6X++IV9Ohn+pnELAl9K3rfpaeBfbmH9JU5sejacdLeA==
dependencies:
- istanbul-lib-coverage "^2.0.1"
+ istanbul-lib-coverage "^2.0.3"
make-dir "^1.3.0"
- supports-color "^5.4.0"
+ supports-color "^6.0.0"
-istanbul-lib-source-maps@^1.2.4, istanbul-lib-source-maps@^1.2.6:
- version "1.2.6"
- resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.6.tgz#37b9ff661580f8fca11232752ee42e08c6675d8f"
- integrity sha512-TtbsY5GIHgbMsMiRw35YBHGpZ1DVFEO19vxxeiDMYaeOFOCzfnYVxvl6pOUIZR4dtPhAGpSMup8OyF8ubsaqEg==
- dependencies:
- debug "^3.1.0"
- istanbul-lib-coverage "^1.2.1"
- mkdirp "^0.5.1"
- rimraf "^2.6.1"
- source-map "^0.5.3"
-
-istanbul-lib-source-maps@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-2.0.1.tgz#ce8b45131d8293fdeaa732f4faf1852d13d0a97e"
- integrity sha512-30l40ySg+gvBLcxTrLzR4Z2XTRj3HgRCA/p2rnbs/3OiTaoj054gAbuP5DcLOtwqmy4XW8qXBHzrmP2/bQ9i3A==
+istanbul-lib-source-maps@^3.0.1, istanbul-lib-source-maps@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.2.tgz#f1e817229a9146e8424a28e5d69ba220fda34156"
+ integrity sha512-JX4v0CiKTGp9fZPmoxpu9YEkPbEqCqBbO3403VabKjH+NRXo72HafD5UgnjTEqHL2SAjaZK1XDuDOkn6I5QVfQ==
dependencies:
- debug "^3.1.0"
- istanbul-lib-coverage "^2.0.1"
+ debug "^4.1.1"
+ istanbul-lib-coverage "^2.0.3"
make-dir "^1.3.0"
rimraf "^2.6.2"
source-map "^0.6.1"
-istanbul-reports@^1.5.1:
- version "1.5.1"
- resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.5.1.tgz#97e4dbf3b515e8c484caea15d6524eebd3ff4e1a"
- integrity sha512-+cfoZ0UXzWjhAdzosCPP3AN8vvef8XDkWtTfgaN+7L3YTpNYITnCaEkceo5SEYy644VkHka/P1FvkWvrG/rrJw==
- dependencies:
- handlebars "^4.0.3"
-
-istanbul-reports@^2.0.1:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.0.1.tgz#fb8d6ea850701a3984350b977a969e9a556116a7"
- integrity sha512-CT0QgMBJqs6NJLF678ZHcquUAZIoBIUNzdJrRJfpkI9OnzG6MkUfHxbJC3ln981dMswC7/B1mfX3LNkhgJxsuw==
+istanbul-reports@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-2.1.1.tgz#72ef16b4ecb9a4a7bd0e2001e00f95d1eec8afa9"
+ integrity sha512-FzNahnidyEPBCI0HcufJoSEoKykesRlFcSzQqjH9x0+LC8tnnE/p/90PBLu8iZTxr8yYZNyTtiAujUqyN+CIxw==
dependencies:
- handlebars "^4.0.11"
+ handlebars "^4.1.0"
istanbul@^0.4.5:
version "0.4.5"
@@ -6091,333 +5826,349 @@ jed@^1.1.1:
resolved "https://registry.yarnpkg.com/jed/-/jed-1.1.1.tgz#7a549bbd9ffe1585b0cd0a191e203055bee574b4"
integrity sha1-elSbvZ/+FYWwzQoZHiAwVb7ldLQ=
-jest-changed-files@^23.4.2:
- version "23.4.2"
- resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.2.tgz#1eed688370cd5eebafe4ae93d34bb3b64968fe83"
- integrity sha512-EyNhTAUWEfwnK0Is/09LxoqNDOn7mU7S3EHskG52djOFS/z+IT0jT3h3Ql61+dklcG7bJJitIWEMB4Sp1piHmA==
+jest-changed-files@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.0.0.tgz#c02c09a8cc9ca93f513166bc773741bd39898ff7"
+ integrity sha512-nnuU510R9U+UX0WNb5XFEcsrMqriSiRLeO9KWDFgPrpToaQm60prfQYpxsXigdClpvNot5bekDY440x9dNGnsQ==
dependencies:
+ execa "^1.0.0"
throat "^4.0.0"
-jest-cli@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-23.6.0.tgz#61ab917744338f443ef2baa282ddffdd658a5da4"
- integrity sha512-hgeD1zRUp1E1zsiyOXjEn4LzRLWdJBV//ukAHGlx6s5mfCNJTbhbHjgxnDUXA8fsKWN/HqFFF6X5XcCwC/IvYQ==
+jest-cli@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-24.1.0.tgz#f7cc98995f36e7210cce3cbb12974cbf60940843"
+ integrity sha512-U/iyWPwOI0T1CIxVLtk/2uviOTJ/OiSWJSe8qt6X1VkbbgP+nrtLJlmT9lPBe4lK78VNFJtrJ7pttcNv/s7yCw==
dependencies:
ansi-escapes "^3.0.0"
chalk "^2.0.1"
exit "^0.1.2"
glob "^7.1.2"
- graceful-fs "^4.1.11"
- import-local "^1.0.0"
- is-ci "^1.0.10"
- istanbul-api "^1.3.1"
- istanbul-lib-coverage "^1.2.0"
- istanbul-lib-instrument "^1.10.1"
- istanbul-lib-source-maps "^1.2.4"
- jest-changed-files "^23.4.2"
- jest-config "^23.6.0"
- jest-environment-jsdom "^23.4.0"
- jest-get-type "^22.1.0"
- jest-haste-map "^23.6.0"
- jest-message-util "^23.4.0"
- jest-regex-util "^23.3.0"
- jest-resolve-dependencies "^23.6.0"
- jest-runner "^23.6.0"
- jest-runtime "^23.6.0"
- jest-snapshot "^23.6.0"
- jest-util "^23.4.0"
- jest-validate "^23.6.0"
- jest-watcher "^23.4.0"
- jest-worker "^23.2.0"
- micromatch "^2.3.11"
+ graceful-fs "^4.1.15"
+ import-local "^2.0.0"
+ is-ci "^2.0.0"
+ istanbul-api "^2.0.8"
+ istanbul-lib-coverage "^2.0.2"
+ istanbul-lib-instrument "^3.0.1"
+ istanbul-lib-source-maps "^3.0.1"
+ jest-changed-files "^24.0.0"
+ jest-config "^24.1.0"
+ jest-environment-jsdom "^24.0.0"
+ jest-get-type "^24.0.0"
+ jest-haste-map "^24.0.0"
+ jest-message-util "^24.0.0"
+ jest-regex-util "^24.0.0"
+ jest-resolve-dependencies "^24.1.0"
+ jest-runner "^24.1.0"
+ jest-runtime "^24.1.0"
+ jest-snapshot "^24.1.0"
+ jest-util "^24.0.0"
+ jest-validate "^24.0.0"
+ jest-watcher "^24.0.0"
+ jest-worker "^24.0.0"
+ micromatch "^3.1.10"
node-notifier "^5.2.1"
- prompts "^0.1.9"
+ p-each-series "^1.0.0"
+ pirates "^4.0.0"
+ prompts "^2.0.1"
realpath-native "^1.0.0"
rimraf "^2.5.4"
- slash "^1.0.0"
+ slash "^2.0.0"
string-length "^2.0.0"
- strip-ansi "^4.0.0"
+ strip-ansi "^5.0.0"
which "^1.2.12"
- yargs "^11.0.0"
+ yargs "^12.0.2"
-jest-config@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.6.0.tgz#f82546a90ade2d8c7026fbf6ac5207fc22f8eb1d"
- integrity sha512-i8V7z9BeDXab1+VNo78WM0AtWpBRXJLnkT+lyT+Slx/cbP5sZJ0+NDuLcmBE5hXAoK0aUp7vI+MOxR+R4d8SRQ==
+jest-config@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-24.1.0.tgz#6ea6881cfdd299bc86cc144ee36d937c97c3850c"
+ integrity sha512-FbbRzRqtFC6eGjG5VwsbW4E5dW3zqJKLWYiZWhB0/4E5fgsMw8GODLbGSrY5t17kKOtCWb/Z7nsIThRoDpuVyg==
dependencies:
- babel-core "^6.0.0"
- babel-jest "^23.6.0"
+ "@babel/core" "^7.1.0"
+ babel-jest "^24.1.0"
chalk "^2.0.1"
glob "^7.1.1"
- jest-environment-jsdom "^23.4.0"
- jest-environment-node "^23.4.0"
- jest-get-type "^22.1.0"
- jest-jasmine2 "^23.6.0"
- jest-regex-util "^23.3.0"
- jest-resolve "^23.6.0"
- jest-util "^23.4.0"
- jest-validate "^23.6.0"
- micromatch "^2.3.11"
- pretty-format "^23.6.0"
-
-jest-diff@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-23.6.0.tgz#1500f3f16e850bb3d71233408089be099f610c7d"
- integrity sha512-Gz9l5Ov+X3aL5L37IT+8hoCUsof1CVYBb2QEkOupK64XyRR3h+uRpYIm97K7sY8diFxowR8pIGEdyfMKTixo3g==
+ jest-environment-jsdom "^24.0.0"
+ jest-environment-node "^24.0.0"
+ jest-get-type "^24.0.0"
+ jest-jasmine2 "^24.1.0"
+ jest-regex-util "^24.0.0"
+ jest-resolve "^24.1.0"
+ jest-util "^24.0.0"
+ jest-validate "^24.0.0"
+ micromatch "^3.1.10"
+ pretty-format "^24.0.0"
+ realpath-native "^1.0.2"
+
+jest-diff@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.0.0.tgz#a3e5f573dbac482f7d9513ac9cfa21644d3d6b34"
+ integrity sha512-XY5wMpRaTsuMoU+1/B2zQSKQ9RdE9gsLkGydx3nvApeyPijLA8GtEvIcPwISRCer+VDf9W1mStTYYq6fPt8ryA==
dependencies:
chalk "^2.0.1"
- diff "^3.2.0"
- jest-get-type "^22.1.0"
- pretty-format "^23.6.0"
+ diff-sequences "^24.0.0"
+ jest-get-type "^24.0.0"
+ pretty-format "^24.0.0"
-jest-docblock@^23.2.0:
- version "23.2.0"
- resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-23.2.0.tgz#f085e1f18548d99fdd69b20207e6fd55d91383a7"
- integrity sha1-8IXh8YVI2Z/dabICB+b9VdkTg6c=
+jest-docblock@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.0.0.tgz#54d77a188743e37f62181a91a01eb9222289f94e"
+ integrity sha512-KfAKZ4SN7CFOZpWg4i7g7MSlY0M+mq7K0aMqENaG2vHuhC9fc3vkpU/iNN9sOus7v3h3Y48uEjqz3+Gdn2iptA==
dependencies:
detect-newline "^2.1.0"
-jest-each@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-23.6.0.tgz#ba0c3a82a8054387016139c733a05242d3d71575"
- integrity sha512-x7V6M/WGJo6/kLoissORuvLIeAoyo2YqLOoCDkohgJ4XOXSqOtyvr8FbInlAWS77ojBsZrafbozWoKVRdtxFCg==
+jest-each@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-24.0.0.tgz#10987a06b21c7ffbfb7706c89d24c52ed864be55"
+ integrity sha512-gFcbY4Cu55yxExXMkjrnLXov3bWO3dbPAW7HXb31h/DNWdNc/6X8MtxGff8nh3/MjkF9DpVqnj0KsPKuPK0cpA==
dependencies:
chalk "^2.0.1"
- pretty-format "^23.6.0"
+ jest-get-type "^24.0.0"
+ jest-util "^24.0.0"
+ pretty-format "^24.0.0"
-jest-environment-jsdom@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-23.4.0.tgz#056a7952b3fea513ac62a140a2c368c79d9e6023"
- integrity sha1-BWp5UrP+pROsYqFAosNox52eYCM=
+jest-environment-jsdom@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-24.0.0.tgz#5affa0654d6e44cd798003daa1a8701dbd6e4d11"
+ integrity sha512-1YNp7xtxajTRaxbylDc2pWvFnfDTH5BJJGyVzyGAKNt/lEULohwEV9zFqTgG4bXRcq7xzdd+sGFws+LxThXXOw==
dependencies:
- jest-mock "^23.2.0"
- jest-util "^23.4.0"
+ jest-mock "^24.0.0"
+ jest-util "^24.0.0"
jsdom "^11.5.1"
-jest-environment-node@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-23.4.0.tgz#57e80ed0841dea303167cce8cd79521debafde10"
- integrity sha1-V+gO0IQd6jAxZ8zozXlSHeuv3hA=
+jest-environment-node@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-24.0.0.tgz#330948980656ed8773ce2e04eb597ed91e3c7190"
+ integrity sha512-62fOFcaEdU0VLaq8JL90TqwI7hLn0cOKOl8vY2n477vRkCJRojiRRtJVRzzCcgFvs6gqU97DNqX5R0BrBP6Rxg==
dependencies:
- jest-mock "^23.2.0"
- jest-util "^23.4.0"
+ jest-mock "^24.0.0"
+ jest-util "^24.0.0"
-jest-get-type@^22.1.0:
- version "22.4.3"
- resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4"
- integrity sha512-/jsz0Y+V29w1chdXVygEKSz2nBoHoYqNShPe+QgxSNjAuP1i8+k4LbQNrfoliKej0P45sivkSCh7yiD6ubHS3w==
+jest-get-type@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.0.0.tgz#36e72930b78e33da59a4f63d44d332188278940b"
+ integrity sha512-z6/Eyf6s9ZDGz7eOvl+fzpuJmN9i0KyTt1no37/dHu8galssxz5ZEgnc1KaV8R31q1khxyhB4ui/X5ZjjPk77w==
-jest-haste-map@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-23.6.0.tgz#2e3eb997814ca696d62afdb3f2529f5bbc935e16"
- integrity sha512-uyNhMyl6dr6HaXGHp8VF7cK6KpC6G9z9LiMNsst+rJIZ8l7wY0tk8qwjPmEghczojZ2/ZhtEdIabZ0OQRJSGGg==
+jest-haste-map@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.0.0.tgz#e9ef51b2c9257384b4d6beb83bd48c65b37b5e6e"
+ integrity sha512-CcViJyUo41IQqttLxXVdI41YErkzBKbE6cS6dRAploCeutePYfUimWd3C9rQEWhX0YBOQzvNsC0O9nYxK2nnxQ==
dependencies:
fb-watchman "^2.0.0"
- graceful-fs "^4.1.11"
+ graceful-fs "^4.1.15"
invariant "^2.2.4"
- jest-docblock "^23.2.0"
- jest-serializer "^23.0.1"
- jest-worker "^23.2.0"
- micromatch "^2.3.11"
- sane "^2.0.0"
-
-jest-jasmine2@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-23.6.0.tgz#840e937f848a6c8638df24360ab869cc718592e0"
- integrity sha512-pe2Ytgs1nyCs8IvsEJRiRTPC0eVYd8L/dXJGU08GFuBwZ4sYH/lmFDdOL3ZmvJR8QKqV9MFuwlsAi/EWkFUbsQ==
- dependencies:
- babel-traverse "^6.0.0"
+ jest-serializer "^24.0.0"
+ jest-util "^24.0.0"
+ jest-worker "^24.0.0"
+ micromatch "^3.1.10"
+ sane "^3.0.0"
+
+jest-jasmine2@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-24.1.0.tgz#8377324b967037c440f0a549ee0bbd9912055db6"
+ integrity sha512-H+o76SdSNyCh9fM5K8upK45YTo/DiFx5w2YAzblQebSQmukDcoVBVeXynyr7DDnxh+0NTHYRCLwJVf3tC518wg==
+ dependencies:
+ "@babel/traverse" "^7.1.0"
chalk "^2.0.1"
co "^4.6.0"
- expect "^23.6.0"
- is-generator-fn "^1.0.0"
- jest-diff "^23.6.0"
- jest-each "^23.6.0"
- jest-matcher-utils "^23.6.0"
- jest-message-util "^23.4.0"
- jest-snapshot "^23.6.0"
- jest-util "^23.4.0"
- pretty-format "^23.6.0"
-
-jest-junit@^5.2.0:
- version "5.2.0"
- resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-5.2.0.tgz#980401db7aa69999cf117c6d740a8135c22ae379"
- integrity sha512-Mdg0Qpdh1Xm/FA1B/mcLlmEmlr3XzH5pZg7MvcAwZhjHijPRd1z/UwYwkwNHmCV7o4ZOWCf77nLu7ZkhHHrtJg==
+ expect "^24.1.0"
+ is-generator-fn "^2.0.0"
+ jest-each "^24.0.0"
+ jest-matcher-utils "^24.0.0"
+ jest-message-util "^24.0.0"
+ jest-snapshot "^24.1.0"
+ jest-util "^24.0.0"
+ pretty-format "^24.0.0"
+ throat "^4.0.0"
+
+jest-junit@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-6.3.0.tgz#99e64ebc54eddcb21238f0cc49f5820c89a8c785"
+ integrity sha512-3PH9UkpaomX6CUzqjlnk0m4yBCW/eroxV6v61OM6LkCQFO848P3YUhfIzu8ypZSBKB3vvCbB4WaLTKT0BrIf8A==
dependencies:
- jest-config "^23.6.0"
- jest-validate "^23.0.1"
+ jest-validate "^24.0.0"
mkdirp "^0.5.1"
strip-ansi "^4.0.0"
xml "^1.0.1"
-jest-leak-detector@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-23.6.0.tgz#e4230fd42cf381a1a1971237ad56897de7e171de"
- integrity sha512-f/8zA04rsl1Nzj10HIyEsXvYlMpMPcy0QkQilVZDFOaPbv2ur71X5u2+C4ZQJGyV/xvVXtCCZ3wQ99IgQxftCg==
+jest-leak-detector@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-24.0.0.tgz#78280119fd05ee98317daee62cddb3aa537a31c6"
+ integrity sha512-ZYHJYFeibxfsDSKowjDP332pStuiFT2xfc5R67Rjm/l+HFJWJgNIOCOlQGeXLCtyUn3A23+VVDdiCcnB6dTTrg==
dependencies:
- pretty-format "^23.6.0"
+ pretty-format "^24.0.0"
-jest-matcher-utils@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-23.6.0.tgz#726bcea0c5294261a7417afb6da3186b4b8cac80"
- integrity sha512-rosyCHQfBcol4NsckTn01cdelzWLU9Cq7aaigDf8VwwpIRvWE/9zLgX2bON+FkEW69/0UuYslUe22SOdEf2nog==
+jest-matcher-utils@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-24.0.0.tgz#fc9c41cfc49b2c3ec14e576f53d519c37729d579"
+ integrity sha512-LQTDmO+aWRz1Tf9HJg+HlPHhDh1E1c65kVwRFo5mwCVp5aQDzlkz4+vCvXhOKFjitV2f0kMdHxnODrXVoi+rlA==
dependencies:
chalk "^2.0.1"
- jest-get-type "^22.1.0"
- pretty-format "^23.6.0"
+ jest-diff "^24.0.0"
+ jest-get-type "^24.0.0"
+ pretty-format "^24.0.0"
-jest-message-util@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-23.4.0.tgz#17610c50942349508d01a3d1e0bda2c079086a9f"
- integrity sha1-F2EMUJQjSVCNAaPR4L2iwHkIap8=
+jest-message-util@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-24.0.0.tgz#a07a141433b2c992dbaec68d4cbfe470ba289619"
+ integrity sha512-J9ROJIwz/IeC+eV1XSwnRK4oAwPuhmxEyYx1+K5UI+pIYwFZDSrfZaiWTdq0d2xYFw4Xiu+0KQWsdsQpgJMf3Q==
dependencies:
- "@babel/code-frame" "^7.0.0-beta.35"
+ "@babel/code-frame" "^7.0.0"
chalk "^2.0.1"
- micromatch "^2.3.11"
- slash "^1.0.0"
+ micromatch "^3.1.10"
+ slash "^2.0.0"
stack-utils "^1.0.1"
-jest-mock@^23.2.0:
- version "23.2.0"
- resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.2.0.tgz#ad1c60f29e8719d47c26e1138098b6d18b261134"
- integrity sha1-rRxg8p6HGdR8JuETgJi20YsmETQ=
+jest-mock@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-24.0.0.tgz#9a4b53e01d66a0e780f7d857462d063e024c617d"
+ integrity sha512-sQp0Hu5fcf5NZEh1U9eIW2qD0BwJZjb63Yqd98PQJFvf/zzUTBoUAwv/Dc/HFeNHIw1f3hl/48vNn+j3STaI7A==
-jest-regex-util@^23.3.0:
- version "23.3.0"
- resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-23.3.0.tgz#5f86729547c2785c4002ceaa8f849fe8ca471bc5"
- integrity sha1-X4ZylUfCeFxAAs6qj4Sf6MpHG8U=
+jest-regex-util@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-24.0.0.tgz#4feee8ec4a358f5bee0a654e94eb26163cb9089a"
+ integrity sha512-Jv/uOTCuC+PY7WpJl2mpoI+WbY2ut73qwwO9ByJJNwOCwr1qWhEW2Lyi2S9ZewUdJqeVpEBisdEVZSI+Zxo58Q==
-jest-resolve-dependencies@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-23.6.0.tgz#b4526af24c8540d9a3fab102c15081cf509b723d"
- integrity sha512-EkQWkFWjGKwRtRyIwRwI6rtPAEyPWlUC2MpzHissYnzJeHcyCn1Hc8j7Nn1xUVrS5C6W5+ZL37XTem4D4pLZdA==
+jest-resolve-dependencies@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-24.1.0.tgz#78f738a2ec59ff4d00751d9da56f176e3f589f6c"
+ integrity sha512-2VwPsjd3kRPu7qe2cpytAgowCObk5AKeizfXuuiwgm1a9sijJDZe8Kh1sFj6FKvSaNEfCPlBVkZEJa2482m/Uw==
dependencies:
- jest-regex-util "^23.3.0"
- jest-snapshot "^23.6.0"
+ jest-regex-util "^24.0.0"
+ jest-snapshot "^24.1.0"
-jest-resolve@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-23.6.0.tgz#cf1d1a24ce7ee7b23d661c33ba2150f3aebfa0ae"
- integrity sha512-XyoRxNtO7YGpQDmtQCmZjum1MljDqUCob7XlZ6jy9gsMugHdN2hY4+Acz9Qvjz2mSsOnPSH7skBmDYCHXVZqkA==
+jest-resolve@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-24.1.0.tgz#42ff0169b0ea47bfdbd0c52a0067ca7d022c7688"
+ integrity sha512-TPiAIVp3TG6zAxH28u/6eogbwrvZjBMWroSLBDkwkHKrqxB/RIdwkWDye4uqPlZIXWIaHtifY3L0/eO5Z0f2wg==
dependencies:
browser-resolve "^1.11.3"
chalk "^2.0.1"
realpath-native "^1.0.0"
-jest-runner@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-23.6.0.tgz#3894bd219ffc3f3cb94dc48a4170a2e6f23a5a38"
- integrity sha512-kw0+uj710dzSJKU6ygri851CObtCD9cN8aNkg8jWJf4ewFyEa6kwmiH/r/M1Ec5IL/6VFa0wnAk6w+gzUtjJzA==
+jest-runner@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-24.1.0.tgz#3686a2bb89ce62800da23d7fdc3da2c32792943b"
+ integrity sha512-CDGOkT3AIFl16BLL/OdbtYgYvbAprwJ+ExKuLZmGSCSldwsuU2dEGauqkpvd9nphVdAnJUcP12e/EIlnTX0QXg==
dependencies:
+ chalk "^2.4.2"
exit "^0.1.2"
- graceful-fs "^4.1.11"
- jest-config "^23.6.0"
- jest-docblock "^23.2.0"
- jest-haste-map "^23.6.0"
- jest-jasmine2 "^23.6.0"
- jest-leak-detector "^23.6.0"
- jest-message-util "^23.4.0"
- jest-runtime "^23.6.0"
- jest-util "^23.4.0"
- jest-worker "^23.2.0"
+ graceful-fs "^4.1.15"
+ jest-config "^24.1.0"
+ jest-docblock "^24.0.0"
+ jest-haste-map "^24.0.0"
+ jest-jasmine2 "^24.1.0"
+ jest-leak-detector "^24.0.0"
+ jest-message-util "^24.0.0"
+ jest-runtime "^24.1.0"
+ jest-util "^24.0.0"
+ jest-worker "^24.0.0"
source-map-support "^0.5.6"
throat "^4.0.0"
-jest-runtime@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-23.6.0.tgz#059e58c8ab445917cd0e0d84ac2ba68de8f23082"
- integrity sha512-ycnLTNPT2Gv+TRhnAYAQ0B3SryEXhhRj1kA6hBPSeZaNQkJ7GbZsxOLUkwg6YmvWGdX3BB3PYKFLDQCAE1zNOw==
+jest-runtime@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-24.1.0.tgz#7c157a2e776609e8cf552f956a5a19ec9c985214"
+ integrity sha512-59/BY6OCuTXxGeDhEMU7+N33dpMQyXq7MLK07cNSIY/QYt2QZgJ7Tjx+rykBI0skAoigFl0A5tmT8UdwX92YuQ==
dependencies:
- babel-core "^6.0.0"
- babel-plugin-istanbul "^4.1.6"
+ "@babel/core" "^7.1.0"
+ babel-plugin-istanbul "^5.1.0"
chalk "^2.0.1"
convert-source-map "^1.4.0"
exit "^0.1.2"
fast-json-stable-stringify "^2.0.0"
- graceful-fs "^4.1.11"
- jest-config "^23.6.0"
- jest-haste-map "^23.6.0"
- jest-message-util "^23.4.0"
- jest-regex-util "^23.3.0"
- jest-resolve "^23.6.0"
- jest-snapshot "^23.6.0"
- jest-util "^23.4.0"
- jest-validate "^23.6.0"
- micromatch "^2.3.11"
+ glob "^7.1.3"
+ graceful-fs "^4.1.15"
+ jest-config "^24.1.0"
+ jest-haste-map "^24.0.0"
+ jest-message-util "^24.0.0"
+ jest-regex-util "^24.0.0"
+ jest-resolve "^24.1.0"
+ jest-snapshot "^24.1.0"
+ jest-util "^24.0.0"
+ jest-validate "^24.0.0"
+ micromatch "^3.1.10"
realpath-native "^1.0.0"
- slash "^1.0.0"
- strip-bom "3.0.0"
- write-file-atomic "^2.1.0"
- yargs "^11.0.0"
-
-jest-serializer@^23.0.1:
- version "23.0.1"
- resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-23.0.1.tgz#a3776aeb311e90fe83fab9e533e85102bd164165"
- integrity sha1-o3dq6zEekP6D+rnlM+hRAr0WQWU=
-
-jest-snapshot@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-23.6.0.tgz#f9c2625d1b18acda01ec2d2b826c0ce58a5aa17a"
- integrity sha512-tM7/Bprftun6Cvj2Awh/ikS7zV3pVwjRYU2qNYS51VZHgaAMBs5l4o/69AiDHhQrj5+LA2Lq4VIvK7zYk/bswg==
- dependencies:
- babel-types "^6.0.0"
+ slash "^2.0.0"
+ strip-bom "^3.0.0"
+ write-file-atomic "2.4.1"
+ yargs "^12.0.2"
+
+jest-serializer@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-24.0.0.tgz#522c44a332cdd194d8c0531eb06a1ee5afb4256b"
+ integrity sha512-9FKxQyrFgHtx3ozU+1a8v938ILBE7S8Ko3uiAVjT8Yfi2o91j/fj81jacCQZ/Ihjiff/VsUCXVgQ+iF1XdImOw==
+
+jest-snapshot@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-24.1.0.tgz#85e22f810357aa5994ab61f236617dc2205f2f5b"
+ integrity sha512-th6TDfFqEmXvuViacU1ikD7xFb7lQsPn2rJl7OEmnfIVpnrx3QNY2t3PE88meeg0u/mQ0nkyvmC05PBqO4USFA==
+ dependencies:
+ "@babel/types" "^7.0.0"
chalk "^2.0.1"
- jest-diff "^23.6.0"
- jest-matcher-utils "^23.6.0"
- jest-message-util "^23.4.0"
- jest-resolve "^23.6.0"
+ jest-diff "^24.0.0"
+ jest-matcher-utils "^24.0.0"
+ jest-message-util "^24.0.0"
+ jest-resolve "^24.1.0"
mkdirp "^0.5.1"
natural-compare "^1.4.0"
- pretty-format "^23.6.0"
+ pretty-format "^24.0.0"
semver "^5.5.0"
-jest-util@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-23.4.0.tgz#4d063cb927baf0a23831ff61bec2cbbf49793561"
- integrity sha1-TQY8uSe68KI4Mf9hvsLLv0l5NWE=
+jest-transform-graphql@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/jest-transform-graphql/-/jest-transform-graphql-2.1.0.tgz#903cb66bb27bc2772fd3e5dd4f7e9b57230f5829"
+ integrity sha1-kDy2a7J7wncv0+XdT36bVyMPWCk=
+
+jest-util@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-24.0.0.tgz#fd38fcafd6dedbd0af2944d7a227c0d91b68f7d6"
+ integrity sha512-QxsALc4wguYS7cfjdQSOr5HTkmjzkHgmZvIDkcmPfl1ib8PNV8QUWLwbKefCudWS0PRKioV+VbQ0oCUPC691fQ==
dependencies:
- callsites "^2.0.0"
+ callsites "^3.0.0"
chalk "^2.0.1"
- graceful-fs "^4.1.11"
- is-ci "^1.0.10"
- jest-message-util "^23.4.0"
+ graceful-fs "^4.1.15"
+ is-ci "^2.0.0"
+ jest-message-util "^24.0.0"
mkdirp "^0.5.1"
- slash "^1.0.0"
+ slash "^2.0.0"
source-map "^0.6.0"
-jest-validate@^23.0.1, jest-validate@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-23.6.0.tgz#36761f99d1ed33fcd425b4e4c5595d62b6597474"
- integrity sha512-OFKapYxe72yz7agrDAWi8v2WL8GIfVqcbKRCLbRG9PAxtzF9b1SEDdTpytNDN12z2fJynoBwpMpvj2R39plI2A==
+jest-validate@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.0.0.tgz#aa8571a46983a6538328fef20406b4a496b6c020"
+ integrity sha512-vMrKrTOP4BBFIeOWsjpsDgVXATxCspC9S1gqvbJ3Tnn/b9ACsJmteYeVx9830UMV28Cob1RX55x96Qq3Tfad4g==
dependencies:
+ camelcase "^5.0.0"
chalk "^2.0.1"
- jest-get-type "^22.1.0"
+ jest-get-type "^24.0.0"
leven "^2.1.0"
- pretty-format "^23.6.0"
+ pretty-format "^24.0.0"
-jest-watcher@^23.4.0:
- version "23.4.0"
- resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-23.4.0.tgz#d2e28ce74f8dad6c6afc922b92cabef6ed05c91c"
- integrity sha1-0uKM50+NrWxq/JIrksq+9u0FyRw=
+jest-watcher@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-24.0.0.tgz#20d44244d10b0b7312410aefd256c1c1eef68890"
+ integrity sha512-GxkW2QrZ4YxmW1GUWER05McjVDunBlKMFfExu+VsGmXJmpej1saTEKvONdx5RJBlVdpPI5x6E3+EDQSIGgl53g==
dependencies:
ansi-escapes "^3.0.0"
chalk "^2.0.1"
+ jest-util "^24.0.0"
string-length "^2.0.0"
-jest-worker@^23.2.0:
- version "23.2.0"
- resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-23.2.0.tgz#faf706a8da36fae60eb26957257fa7b5d8ea02b9"
- integrity sha1-+vcGqNo2+uYOsmlXJX+ntdjqArk=
+jest-worker@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-24.0.0.tgz#3d3483b077bf04f412f47654a27bba7e947f8b6d"
+ integrity sha512-s64/OThpfQvoCeHG963MiEZOAAxu8kHsaL/rCMF7lpdzo7vgF0CtPml9hfguOMgykgH/eOm4jFP4ibfHLruytg==
dependencies:
merge-stream "^1.0.1"
+ supports-color "^6.1.0"
-jest@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/jest/-/jest-23.6.0.tgz#ad5835e923ebf6e19e7a1d7529a432edfee7813d"
- integrity sha512-lWzcd+HSiqeuxyhG+EnZds6iO3Y3ZEnMrfZq/OTGvF/C+Z4fPMCdhWTGSAiO2Oym9rbEXfwddHhh6jqrTF3+Lw==
+jest@^24.1.0:
+ version "24.1.0"
+ resolved "https://registry.yarnpkg.com/jest/-/jest-24.1.0.tgz#b1e1135caefcf2397950ecf7f90e395fde866fd2"
+ integrity sha512-+q91L65kypqklvlRFfXfdzUKyngQLOcwGhXQaLmVHv+d09LkNXuBuGxlofTFW42XMzu3giIcChchTsCNUjQ78A==
dependencies:
- import-local "^1.0.0"
- jest-cli "^23.6.0"
+ import-local "^2.0.0"
+ jest-cli "^24.1.0"
jquery-ujs@1.2.2:
version "1.2.2"
@@ -6426,6 +6177,11 @@ jquery-ujs@1.2.2:
dependencies:
jquery ">=1.8.0"
+jquery.caret@^0.3.1:
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/jquery.caret/-/jquery.caret-0.3.1.tgz#9c093318faf327eff322e826ca9f3241368bc7b8"
+ integrity sha1-nAkzGPrzJ+/zIugmyp8yQTaLx7g=
+
jquery.waitforimages@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b"
@@ -6436,7 +6192,12 @@ jquery.waitforimages@^2.2.0:
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.3.1.tgz#958ce29e81c9790f31be7792df5d4d95fc57fbca"
integrity sha512-Ubldcmxp5np52/ENotGxlLe6aGMvmF4R8S6tZjsP6Knsaxd/xp3Zrh50cG93lR6nPXyUFwzN3ZSOQI0wRJNdGg==
-js-beautify@^1.6.14, js-beautify@^1.8.8:
+js-base64@^2.1.8:
+ version "2.5.1"
+ resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121"
+ integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw==
+
+js-beautify@^1.8.8:
version "1.8.9"
resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.8.9.tgz#08e3c05ead3ecfbd4f512c3895b1cda76c87d523"
integrity sha512-MwPmLywK9RSX0SPsUJjN7i+RQY9w/yC17Lbrq9ViEefpLRgqAR2BgrMN2AbifkUuhDV8tRauLhLda/9+bE0YQA==
@@ -6467,7 +6228,7 @@ js-tokens@^3.0.2:
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
-js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.7.0, js-yaml@^3.9.0:
+js-yaml@3.x, js-yaml@^3.12.0, js-yaml@^3.9.0:
version "3.12.1"
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.1.tgz#295c8632a18a23e054cf5c9d3cecafe678167600"
integrity sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==
@@ -6542,11 +6303,6 @@ jsdom@^11.5.1:
ws "^5.2.0"
xml-name-validator "^3.0.0"
-jsesc@^1.3.0:
- version "1.3.0"
- resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
- integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s=
-
jsesc@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe"
@@ -6592,18 +6348,18 @@ json3@^3.3.2:
resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1"
integrity sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=
-json5@^0.5.0, json5@^0.5.1:
- version "0.5.1"
- resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
- integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
-
-json5@^2.1.0:
+json5@2.x, json5@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.0.tgz#e7a0c62c48285c628d20a10b85c89bb807c32850"
integrity sha512-8Mh9h6xViijj36g7Dxi+Y4S6hNGV96vcJZr/SrlHh1LR/pEn/8j/+qIBbs44YKl69Lrfctp4QD+AdWLTMqEZAQ==
dependencies:
minimist "^1.2.0"
+json5@^0.5.0:
+ version "0.5.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821"
+ integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=
+
jsprim@^1.2.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -6769,10 +6525,10 @@ klaw@~2.0.0:
dependencies:
graceful-fs "^4.1.9"
-kleur@^2.0.1:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/kleur/-/kleur-2.0.2.tgz#b704f4944d95e255d038f0cb05fb8a602c55a300"
- integrity sha512-77XF9iTllATmG9lSlIv0qdQ2BQ/h9t0bJllHlbvsQ0zUWfU7Yi0S8L5JXzPZgkefIiajLmBJJ4BsMJmqcf7oxQ==
+kleur@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.2.tgz#83c7ec858a41098b613d5998a7b653962b504f68"
+ integrity sha512-3h7B2WRT5LNXOtQiAaWonilegHcPSf9nLVXlSTci8lu1dZUuui61+EsPEZqSVxY7rXYmB2DVKMQILxaO5WL61Q==
known-css-properties@^0.11.0:
version "0.11.0"
@@ -6905,12 +6661,17 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
+lodash.assign@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
+ integrity sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=
+
lodash.camelcase@4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
-lodash.clonedeep@^4.5.0:
+lodash.clonedeep@^4.3.2, lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
@@ -6965,7 +6726,7 @@ lodash.upperfirst@4.3.1:
resolved "https://registry.yarnpkg.com/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz#1365edf431480481ef0d1c68957a5ed99d49f7ce"
integrity sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=
-lodash@4.x, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0:
+lodash@^4.0.0, lodash@^4.13.1, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@^4.5.0, lodash@~4.17.10:
version "4.17.11"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
integrity sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==
@@ -7058,6 +6819,11 @@ make-dir@^1.0.0, make-dir@^1.3.0:
dependencies:
pify "^3.0.0"
+make-error@1.x:
+ version "1.3.5"
+ resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.5.tgz#efe4e81f6db28cadd605c70f29c831b58ef776c8"
+ integrity sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==
+
makeerror@1.0.x:
version "1.0.11"
resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
@@ -7077,7 +6843,7 @@ map-cache@^0.2.2:
resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf"
integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=
-map-obj@^1.0.0:
+map-obj@^1.0.0, map-obj@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d"
integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0=
@@ -7120,11 +6886,6 @@ marked@^0.3.12, marked@~0.3.6:
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.19.tgz#5d47f709c4c9fc3c216b6d46127280f40b39d790"
integrity sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==
-math-random@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac"
- integrity sha1-izqsWIuKZuSXXjzepn97sylgH6w=
-
mathml-tag-names@^2.0.1:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz#490b70e062ee24636536e3d9481e333733d00f2c"
@@ -7138,6 +6899,15 @@ md5.js@^1.3.4:
hash-base "^3.0.0"
inherits "^2.0.1"
+md5@^2.2.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
+ integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=
+ dependencies:
+ charenc "~0.0.1"
+ crypt "~0.0.1"
+ is-buffer "~1.1.1"
+
mdast-util-compact@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.2.tgz#c12ebe16fffc84573d3e19767726de226e95f649"
@@ -7184,6 +6954,22 @@ memory-fs@^0.4.0, memory-fs@~0.4.1:
errno "^0.1.3"
readable-stream "^2.0.1"
+meow@^3.7.0:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
+ integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=
+ dependencies:
+ camelcase-keys "^2.0.0"
+ decamelize "^1.1.2"
+ loud-rejection "^1.0.0"
+ map-obj "^1.0.1"
+ minimist "^1.1.3"
+ normalize-package-data "^2.3.4"
+ object-assign "^4.0.1"
+ read-pkg-up "^1.0.1"
+ redent "^1.0.0"
+ trim-newlines "^1.0.0"
+
meow@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4"
@@ -7247,25 +7033,6 @@ methods@~1.1.2:
resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
-micromatch@^2.3.11:
- version "2.3.11"
- resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565"
- integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=
- 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.0.4, micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.6, micromatch@^3.1.8, micromatch@^3.1.9:
version "3.1.10"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23"
@@ -7335,7 +7102,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a"
integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=
-"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4:
+"minimatch@2 || 3", minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4, minimatch@~3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
@@ -7360,7 +7127,7 @@ minimist@1.1.x:
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8"
integrity sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=
-minimist@1.2.0, minimist@^1.1.1, minimist@^1.2.0:
+minimist@1.2.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
@@ -7404,7 +7171,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.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1:
version "0.5.1"
resolved "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
@@ -7473,10 +7240,10 @@ mute-stream@0.0.7:
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=
-nan@^2.9.2:
- version "2.10.0"
- resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f"
- integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA==
+nan@^2.10.0, nan@^2.9.2:
+ version "2.13.1"
+ resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.1.tgz#a15bee3790bde247e8f38f1d446edcdaeb05f2dd"
+ integrity sha512-I6YB/YEuDeUZMmhscXKxGgZlFnhsn5y0hgOZBadkzfTRrZBtJDZeg6eQf7PYMIEclwmorTKK8GztsyOUSVBREA==
nanomatch@^1.2.9:
version "1.2.9"
@@ -7525,14 +7292,6 @@ nice-try@^1.0.4:
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
integrity sha512-2NpiFHqC87y/zFke0fC0spBXL3bBsoh/p5H1EFhshxjCR5+0g2d6BiXbUFz9v1sAcxsk2htp2eQnNIci2dIYcA==
-node-cache@^4.1.1:
- version "4.2.0"
- resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-4.2.0.tgz#48ac796a874e762582692004a376d26dfa875811"
- integrity sha512-obRu6/f7S024ysheAjoYFEEBqqDWv4LOMNJEuO8vMeEw2AT4z+NCzO4hlc2lhI4vATzbCQv6kke9FVdx0RbCOw==
- dependencies:
- clone "2.x"
- lodash "4.x"
-
node-fetch@1.6.3:
version "1.6.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
@@ -7546,6 +7305,24 @@ node-forge@0.6.33:
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.6.33.tgz#463811879f573d45155ad6a9f43dc296e8e85ebc"
integrity sha1-RjgRh59XPUUVWtap9D3ClujoXrw=
+node-gyp@^3.8.0:
+ version "3.8.0"
+ resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
+ integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==
+ dependencies:
+ fstream "^1.0.0"
+ glob "^7.0.3"
+ graceful-fs "^4.1.2"
+ mkdirp "^0.5.0"
+ nopt "2 || 3"
+ npmlog "0 || 1 || 2 || 3 || 4"
+ osenv "0"
+ request "^2.87.0"
+ rimraf "2"
+ semver "~5.3.0"
+ tar "^2.0.0"
+ which "1"
+
node-int64@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@@ -7580,6 +7357,11 @@ node-int64@^0.4.0:
util "^0.10.3"
vm-browserify "0.0.4"
+node-modules-regexp@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz#8d9dbe28964a4ac5712e9131642107c71e90ec40"
+ integrity sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=
+
node-notifier@^5.2.1:
version "5.3.0"
resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.3.0.tgz#c77a4a7b84038733d5fb351aafd8a268bfe19a01"
@@ -7613,6 +7395,31 @@ node-releases@^1.1.3:
dependencies:
semver "^5.3.0"
+node-sass@^4.11.0:
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.11.0.tgz#183faec398e9cbe93ba43362e2768ca988a6369a"
+ integrity sha512-bHUdHTphgQJZaF1LASx0kAviPH7sGlcyNhWade4eVIpFp6tsn7SV8xNMTbsQFpEV9VXpnwTTnNYlfsZXgGgmkA==
+ dependencies:
+ async-foreach "^0.1.3"
+ chalk "^1.1.1"
+ cross-spawn "^3.0.0"
+ gaze "^1.0.0"
+ get-stdin "^4.0.1"
+ glob "^7.0.3"
+ in-publish "^2.0.0"
+ lodash.assign "^4.2.0"
+ lodash.clonedeep "^4.3.2"
+ lodash.mergewith "^4.6.0"
+ meow "^3.7.0"
+ mkdirp "^0.5.1"
+ nan "^2.10.0"
+ node-gyp "^3.8.0"
+ npmlog "^4.0.0"
+ request "^2.88.0"
+ sass-graph "^2.2.4"
+ stdout-stream "^1.4.0"
+ "true-case-path" "^1.0.2"
+
nodemon@^1.18.9:
version "1.18.9"
resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.18.9.tgz#90b467efd3b3c81b9453380aeb2a2cba535d0ead"
@@ -7629,7 +7436,7 @@ nodemon@^1.18.9:
undefsafe "^2.0.2"
update-notifier "^2.5.0"
-nopt@3.x:
+"nopt@2 || 3", nopt@3.x:
version "3.0.6"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k=
@@ -7661,7 +7468,7 @@ 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.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"
integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
@@ -7712,7 +7519,7 @@ npm-run-path@^2.0.0:
dependencies:
path-key "^2.0.0"
-npmlog@^4.0.2:
+"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b"
integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==
@@ -7747,7 +7554,7 @@ oauth-sign@~0.9.0:
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
-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"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
@@ -7806,14 +7613,6 @@ object.getownpropertydescriptors@^2.0.3:
define-properties "^1.1.2"
es-abstract "^1.5.1"
-object.omit@^2.0.0:
- version "2.0.1"
- resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa"
- integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=
- 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"
@@ -7884,10 +7683,10 @@ opn@^5.1.0:
dependencies:
is-wsl "^1.1.0"
-optimism@^0.6.6:
- version "0.6.8"
- resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.6.8.tgz#0780b546da8cd0a72e5207e0c3706c990c8673a6"
- integrity sha512-bN5n1KCxSqwBDnmgDnzMtQTHdL+uea2HYFx1smvtE+w2AMl0Uy31g0aXnP/Nt85OINnMJPRpJyfRQLTCqn5Weg==
+optimism@^0.6.9:
+ version "0.6.9"
+ resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.6.9.tgz#19258ff8b3be0cea29ac35f06bff818e026e30bb"
+ integrity sha512-xoQm2lvXbCA9Kd7SCx6y713Y7sZ6fUc5R6VYpoL5M6svKJbTuvtNopexK8sO8K4s0EOUYHuPN2+yAEsNyRggkQ==
dependencies:
immutable-tuple "^0.4.9"
@@ -7933,6 +7732,13 @@ os-homedir@^1.0.0:
resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3"
integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M=
+os-locale@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9"
+ integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=
+ dependencies:
+ lcid "^1.0.0"
+
os-locale@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
@@ -7951,12 +7757,12 @@ os-locale@^3.0.0:
lcid "^2.0.0"
mem "^4.0.0"
-os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2:
+os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
-osenv@^0.1.4:
+osenv@0, osenv@^0.1.4:
version "0.1.5"
resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410"
integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==
@@ -7974,6 +7780,13 @@ p-defer@^1.0.0:
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
integrity sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=
+p-each-series@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-each-series/-/p-each-series-1.0.0.tgz#930f3d12dd1f50e7434457a22cd6f04ac6ad7f71"
+ integrity sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=
+ dependencies:
+ p-reduce "^1.0.0"
+
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
@@ -8017,6 +7830,11 @@ p-map@^1.1.1:
resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.1.1.tgz#05f5e4ae97a068371bc2a5cc86bfbdbc19c4ae7a"
integrity sha1-BfXkrpegaDcbwqXMhr+9vBnErno=
+p-reduce@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-reduce/-/p-reduce-1.0.0.tgz#18c2b0dd936a4690a529f8231f58a0fdb6a47dfa"
+ integrity sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=
+
p-timeout@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038"
@@ -8081,16 +7899,6 @@ parse-entities@^1.0.2, parse-entities@^1.1.0:
is-decimal "^1.0.0"
is-hexadecimal "^1.0.0"
-parse-glob@^3.0.4:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c"
- integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw=
- 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"
@@ -8167,7 +7975,7 @@ path-exists@^3.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
-path-is-absolute@^1.0.0, path-is-absolute@^1.0.1:
+path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
@@ -8182,7 +7990,7 @@ path-key@^2.0.0, path-key@^2.0.1:
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=
-path-parse@^1.0.5, path-parse@^1.0.6:
+path-parse@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c"
integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==
@@ -8265,6 +8073,13 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA=
+pirates@^4.0.0:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.1.tgz#643a92caf894566f91b2b986d2c66950a8e2fb87"
+ integrity sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==
+ dependencies:
+ node-modules-regexp "^1.0.0"
+
pixelmatch@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/pixelmatch/-/pixelmatch-4.0.2.tgz#8f47dcec5011b477b67db03c243bc1f3085e8854"
@@ -8279,13 +8094,6 @@ pkg-dir@^1.0.0:
dependencies:
find-up "^1.0.0"
-pkg-dir@^2.0.0:
- version "2.0.0"
- resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b"
- integrity sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=
- dependencies:
- find-up "^2.1.0"
-
pkg-dir@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3"
@@ -8434,7 +8242,7 @@ postcss-scss@^2.0.0:
dependencies:
postcss "^7.0.0"
-postcss-selector-parser@^3.1.0, postcss-selector-parser@^3.1.1:
+postcss-selector-parser@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865"
integrity sha1-T4dfSvsMllc9XPTXQBGu4lCn6GU=
@@ -8462,7 +8270,7 @@ postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1:
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281"
integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==
-postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.20, postcss@^6.0.23:
+postcss@^6.0.1, postcss@^6.0.14, postcss@^6.0.23:
version "6.0.23"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324"
integrity sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==
@@ -8495,27 +8303,22 @@ prepend-http@^2.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
-preserve@^0.2.0:
- version "0.2.0"
- resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
- integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=
-
-prettier@1.13.7:
- version "1.13.7"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.13.7.tgz#850f3b8af784a49a6ea2d2eaa7ed1428a34b7281"
- integrity sha512-KIU72UmYPGk4MujZGYMFwinB7lOf2LsDNGSOC8ufevsrPLISrZbNJlWstRi3m0AMuszbH+EFSQ/r6w56RSPK6w==
+prettier@1.16.3:
+ version "1.16.3"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.3.tgz#8c62168453badef702f34b45b6ee899574a6a65d"
+ integrity sha512-kn/GU6SMRYPxUakNXhpP0EedT/KmaPzr0H5lIsDogrykbaxOpOfAFfk5XA7DZrJyMAv1wlMV3CPcZruGXVVUZw==
-prettier@1.16.1:
- version "1.16.1"
- resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.1.tgz#534c2c9d7853f8845e5e078384e71973bd74089f"
- integrity sha512-XXUITwIkGb3CPJ2hforHah/zTINRyie5006Jd2HKy2qz7snEJXl0KLfsJZW/wst9g6R2rFvqba3VpNYdu1hDcA==
+prettier@1.16.4:
+ version "1.16.4"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.16.4.tgz#73e37e73e018ad2db9c76742e2647e21790c9717"
+ integrity sha512-ZzWuos7TI5CKUeQAtFd6Zhm2s6EpAD/ZLApIhsF9pRvRtM1RFo61dM/4MSRUA0SuLugA/zgrZD8m0BaY46Og7g==
-pretty-format@^23.6.0:
- version "23.6.0"
- resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-23.6.0.tgz#5eaac8eeb6b33b987b7fe6097ea6a8a146ab5760"
- integrity sha512-zf9NV1NSlDLDjycnwm6hpFATCGl/K1lt0R/GdkAK2O5LN/rwJoB+Mh93gGJjut4YbmecbfgLWVGSTCr0Ewvvbw==
+pretty-format@^24.0.0:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.0.0.tgz#cb6599fd73ac088e37ed682f61291e4678f48591"
+ integrity sha512-LszZaKG665djUcqg5ZQq+XzezHLKrxsA86ZABTozp+oNhkdqa+tG2dX4qa6ERl5c/sRDrAa3lHmwnvKoP+OG/g==
dependencies:
- ansi-regex "^3.0.0"
+ ansi-regex "^4.0.0"
ansi-styles "^3.2.0"
prismjs@^1.6.0:
@@ -8525,7 +8328,7 @@ prismjs@^1.6.0:
optionalDependencies:
clipboard "^1.5.5"
-private@^0.1.6, private@^0.1.8:
+private@^0.1.6:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
@@ -8555,13 +8358,13 @@ promise-inflight@^1.0.1:
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
-prompts@^0.1.9:
- version "0.1.14"
- resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2"
- integrity sha512-rxkyiE9YH6zAz/rZpywySLKkpaj0NMVyNw1qhsubdbjjSgcayjTShDreZGlFMcGSu5sab3bAKPfFk78PB90+8w==
+prompts@^2.0.1:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.0.3.tgz#c5ccb324010b2e8f74752aadceeb57134c1d2522"
+ integrity sha512-H8oWEoRZpybm6NV4to9/1limhttEo13xK62pNvn2JzY0MA03p7s0OjtmhXyon3uJmxiJJVSuUwEJFFssI3eBiQ==
dependencies:
- kleur "^2.0.1"
- sisteransi "^0.1.1"
+ kleur "^3.0.2"
+ sisteransi "^1.0.0"
prosemirror-commands@^1.0.7:
version "1.0.7"
@@ -8807,15 +8610,6 @@ quick-lru@^1.0.0:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8"
integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g=
-randomatic@^3.0.0:
- version "3.1.1"
- resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed"
- integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==
- dependencies:
- is-number "^4.0.0"
- kind-of "^6.0.0"
- math-random "^1.0.1"
-
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"
@@ -8979,13 +8773,21 @@ readdirp@^2.0.0:
readable-stream "^2.0.2"
set-immediate-shim "^1.0.1"
-realpath-native@^1.0.0:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.2.tgz#cd51ce089b513b45cf9b1516c82989b51ccc6560"
- integrity sha512-+S3zTvVt9yTntFrBpm7TQmQ3tzpCrnA1a/y+3cUHAc9ZR6aIjG0WNLR+Rj79QpJktY+VeW/TQtFlQ1bzsehI8g==
+realpath-native@^1.0.0, realpath-native@^1.0.2:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c"
+ integrity sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==
dependencies:
util.promisify "^1.0.0"
+redent@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
+ integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=
+ dependencies:
+ indent-string "^2.1.0"
+ strip-indent "^1.0.1"
+
redent@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa"
@@ -9023,13 +8825,6 @@ regenerator-transform@^0.13.3:
dependencies:
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"
- integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==
- 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"
@@ -9177,7 +8972,7 @@ repeat-string@^0.2.2:
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-0.2.2.tgz#c7a8d3236068362059a7e4651fc6884e8b1fb4ae"
integrity sha1-x6jTI2BoNiBZp+RlH8aITosftK4=
-repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1:
+repeat-string@^1.5.4, repeat-string@^1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637"
integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc=
@@ -9210,7 +9005,7 @@ request-promise-native@^1.0.5:
stealthy-require "^1.1.0"
tough-cookie ">=2.3.3"
-request@^2.87.0:
+request@^2.87.0, request@^2.88.0:
version "2.88.0"
resolved "https://registry.yarnpkg.com/request/-/request-2.88.0.tgz#9c2fca4f7d35b592efe57c7f0a55e81052124fef"
integrity sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==
@@ -9306,7 +9101,7 @@ resolve@1.1.7, resolve@1.1.x:
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b"
integrity sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=
-resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0:
+resolve@1.x, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.5.0, resolve@^1.6.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
integrity sha512-3sUr9aq5OfSg2S9pNtPA9hL1FVEAjvfOC4leW0SNf/mpnaakz2a9femSd6LqAww2RaFctwyf1lCqnTHuF1rxDg==
@@ -9338,7 +9133,7 @@ rfdc@^1.1.2:
resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.2.tgz#e6e72d74f5dc39de8f538f65e00c36c18018e349"
integrity sha512-92ktAgvZhBzYTIK0Mja9uen5q5J3NRVMoDkJL2VMwq6SXjVCgqvQeVP2XAaUY6HT+XpQYeLSjb3UoitBryKmdA==
-rimraf@2.6.3, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2:
+rimraf@2, rimraf@2.6.3, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.0, rimraf@^2.6.1, rimraf@^2.6.2:
version "2.6.3"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab"
integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==
@@ -9416,14 +9211,15 @@ safe-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
-sane@^2.0.0:
- version "2.5.2"
- resolved "https://registry.yarnpkg.com/sane/-/sane-2.5.2.tgz#b4dc1861c21b427e929507a3e751e2a2cb8ab3fa"
- integrity sha1-tNwYYcIbQn6SlQej51HiosuKs/o=
+sane@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/sane/-/sane-3.1.0.tgz#995193b7dc1445ef1fe41ddfca2faf9f111854c6"
+ integrity sha512-G5GClRRxT1cELXfdAq7UKtUsv8q/ZC5k8lQGmjEm4HcAl3HzBy68iglyNCmw4+0tiXPCBZntslHlRhbnsSws+Q==
dependencies:
anymatch "^2.0.0"
capture-exit "^1.2.0"
exec-sh "^0.2.0"
+ execa "^1.0.0"
fb-watchman "^2.0.0"
micromatch "^3.1.4"
minimist "^1.1.1"
@@ -9445,6 +9241,16 @@ sanitize-html@^1.16.1:
srcset "^1.0.0"
xtend "^4.0.0"
+sass-graph@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49"
+ integrity sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k=
+ dependencies:
+ glob "^7.0.0"
+ lodash "^4.0.0"
+ scss-tokenizer "^0.2.3"
+ yargs "^7.0.0"
+
sax@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
@@ -9476,6 +9282,14 @@ scope-css@^1.0.5:
slugify "^1.3.1"
strip-css-comments "^3.0.0"
+scss-tokenizer@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1"
+ integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE=
+ dependencies:
+ js-base64 "^2.1.8"
+ source-map "^0.4.2"
+
select-hose@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
@@ -9505,11 +9319,16 @@ semver-diff@^2.0.0:
dependencies:
semver "^5.0.3"
-"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
+"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004"
integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==
+semver@~5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f"
+ integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8=
+
send@0.16.2:
version "0.16.2"
resolved "https://registry.yarnpkg.com/send/-/send-0.16.2.tgz#6ecca1e0f8c156d141597559848df64730a6bbc1"
@@ -9652,15 +9471,10 @@ signal-exit@^3.0.0, signal-exit@^3.0.2:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
-sisteransi@^0.1.1:
- version "0.1.1"
- resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce"
- integrity sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g==
-
-slash@^1.0.0:
+sisteransi@^1.0.0:
version "1.0.0"
- resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55"
- integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=
+ resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.0.tgz#77d9622ff909080f1c19e5f4a1df0c1b0a27b88c"
+ integrity sha512-N+z4pHB4AmUv0SjveWRd6q1Nj5w62m5jodv+GD8lvmbY/83T/rpbJGZOnK5T149OldDj4Db07BSv9xY4K6NTPQ==
slash@^2.0.0:
version "2.0.0"
@@ -9814,13 +9628,6 @@ source-map-resolve@^0.5.0, source-map-resolve@^0.5.2:
source-map-url "^0.4.0"
urix "^0.1.0"
-source-map-support@^0.4.15:
- version "0.4.18"
- resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f"
- integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==
- dependencies:
- source-map "^0.5.6"
-
source-map-support@^0.5.6, source-map-support@~0.5.6:
version "0.5.9"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.9.tgz#41bc953b2534267ea2d605bccfa7bfa3111ced5f"
@@ -9839,7 +9646,14 @@ source-map@0.5.0:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.0.tgz#0fe96503ac86a5adb5de63f4e412ae4872cdbe86"
integrity sha1-D+llA6yGpa213mP05BKuSHLNvoY=
-source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7:
+source-map@^0.4.2:
+ version "0.4.4"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b"
+ integrity sha1-66T12pwNyZneaAMti092FzZSA2s=
+ dependencies:
+ amdefine ">=0.0.4"
+
+source-map@^0.5.0, source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=
@@ -9976,6 +9790,13 @@ statuses@~1.3.1:
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e"
integrity sha1-+vUbnrdKrvOzrPStX2Gr8ky3uT4=
+stdout-stream@^1.4.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de"
+ integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA==
+ dependencies:
+ readable-stream "^2.0.1"
+
stealthy-require@^1.1.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b"
@@ -10110,11 +9931,6 @@ strip-ansi@^5.0.0:
dependencies:
ansi-regex "^4.0.0"
-strip-bom@3.0.0, strip-bom@^3.0.0:
- version "3.0.0"
- resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
- integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
-
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@@ -10122,6 +9938,11 @@ strip-bom@^2.0.0:
dependencies:
is-utf8 "^0.2.0"
+strip-bom@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+ integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
strip-css-comments@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/strip-css-comments/-/strip-css-comments-3.0.0.tgz#7a5625eff8a2b226cf8947a11254da96e13dae89"
@@ -10134,12 +9955,19 @@ strip-eof@^1.0.0:
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=
+strip-indent@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2"
+ integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=
+ dependencies:
+ get-stdin "^4.0.1"
+
strip-indent@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68"
integrity sha1-XvjbKV0B5u1sv3qrlpmNeCJSe2g=
-strip-json-comments@^2.0.0, strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
+strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
@@ -10162,10 +9990,15 @@ stylelint-config-recommended@^2.1.0:
resolved "https://registry.yarnpkg.com/stylelint-config-recommended/-/stylelint-config-recommended-2.1.0.tgz#f526d5c771c6811186d9eaedbed02195fee30858"
integrity sha512-ajMbivOD7JxdsnlS5945KYhvt7L/HwN6YeYF2BH6kE4UCLJR0YvXMf+2j7nQpJyYLZx9uZzU5G1ZOSBiWAc6yA==
-stylelint-scss@^3.5.3:
- version "3.5.3"
- resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-3.5.3.tgz#e158b3061eeec26d7f6088f346998a797432f3c8"
- integrity sha512-QESQUOY1ldU5tlJTTM3Megz/QtJ39S58ByjZ7dZobGDq9qMjy5jbC7PDUasrv/T7pB1UbpPojpxX9K1OR7IPEg==
+stylelint-error-string-formatter@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/stylelint-error-string-formatter/-/stylelint-error-string-formatter-1.0.1.tgz#366387825d6fb59569e8c5c3f5682398733756f9"
+ integrity sha512-8zy0UsdnQZKVDwjWMQX36b30TaNMGcM2FzBcK9cshpXerpJ3AvF2/zw7FJ3Efm6DFviTBVsxR14F3FnDFhCxJw==
+
+stylelint-scss@^3.5.4:
+ version "3.5.4"
+ resolved "https://registry.yarnpkg.com/stylelint-scss/-/stylelint-scss-3.5.4.tgz#ff3ee989ac48f5c4f57313523b5ace059ffd6cc2"
+ integrity sha512-hEdEOfFXVqxWcUbenBONW/cAw5cJcEDasY8tGwKNAAn1GDHoZO1ATdWpr+iIk325mPGIQqVb1sUxsRxuL70trw==
dependencies:
lodash "^4.17.11"
postcss-media-query-parser "^0.2.3"
@@ -10238,7 +10071,7 @@ supports-color@^2.0.0:
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
-supports-color@^3.1.0, supports-color@^3.1.2:
+supports-color@^3.1.0:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
integrity sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=
@@ -10252,7 +10085,7 @@ supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0, supports-co
dependencies:
has-flag "^3.0.0"
-supports-color@^6.1.0:
+supports-color@^6.0.0, supports-color@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3"
integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==
@@ -10304,6 +10137,15 @@ tapable@^1.0.0, tapable@^1.1.0:
resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.0.tgz#0d076a172e3d9ba088fd2272b2668fb8d194b78c"
integrity sha512-IlqtmLVaZA2qab8epUXbVWRn3aB1imbDMJtjB3nu4X0NqPkcY/JH9ZtCBWKHWPxs8Svi9tyo8w2dBoi07qZbBA==
+tar@^2.0.0:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1"
+ integrity sha1-jk0qJWwOIYXGsYrWlK7JaLg8sdE=
+ dependencies:
+ block-stream "*"
+ fstream "^1.0.2"
+ inherits "2"
+
tar@^4:
version "4.4.4"
resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.4.tgz#ec8409fae9f665a4355cc3b4087d0820232bb8cd"
@@ -10347,17 +10189,6 @@ terser@^3.8.1:
source-map "~0.6.1"
source-map-support "~0.5.6"
-test-exclude@^4.2.1:
- version "4.2.3"
- resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.3.tgz#a9a5e64474e4398339245a0a769ad7c2f4a97c20"
- integrity sha512-SYbXgY64PT+4GAL2ocI3HwPa4Q4TBKm0cwAVeKOt/Aoc0gSpNRjJX8w0pA1LMKZ3LBmd8pYBqApFNQLII9kavA==
- dependencies:
- arrify "^1.0.1"
- micromatch "^2.3.11"
- object-assign "^4.1.0"
- read-pkg-up "^1.0.1"
- require-main-filename "^1.0.1"
-
test-exclude@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-5.0.0.tgz#cdce7cece785e0e829cd5c2b27baf18bc583cfb7"
@@ -10518,11 +10349,6 @@ to-arraybuffer@^1.0.0:
resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43"
integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=
-to-fast-properties@^1.0.3:
- version "1.0.3"
- resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47"
- integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=
-
to-fast-properties@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
@@ -10580,6 +10406,11 @@ tr46@^1.0.1:
dependencies:
punycode "^2.1.0"
+trim-newlines@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
+ integrity sha1-WIeWa7WCpFA6QetST301ARgVphM=
+
trim-newlines@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20"
@@ -10605,22 +10436,41 @@ trough@^1.0.0:
resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.3.tgz#e29bd1614c6458d44869fc28b255ab7857ef7c24"
integrity sha512-fwkLWH+DimvA4YCy+/nvJd61nWQQ2liO/nF/RjkTpiOGi+zxZzVkhb1mvbHIIW4b/8nDsYI8uTmAlc0nNkRMOw==
+"true-case-path@^1.0.2":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d"
+ integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew==
+ dependencies:
+ glob "^7.1.2"
+
tryer@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.0.tgz#027b69fa823225e551cace3ef03b11f6ab37c1d7"
integrity sha1-Antp+oIyJeVRys4+8DsR9qs3wdc=
-tsconfig@^7.0.0:
- version "7.0.0"
- resolved "https://registry.yarnpkg.com/tsconfig/-/tsconfig-7.0.0.tgz#84538875a4dc216e5c4a5432b3a4dec3d54e91b7"
- integrity sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==
- dependencies:
- "@types/strip-bom" "^3.0.0"
- "@types/strip-json-comments" "0.0.30"
- strip-bom "^3.0.0"
- strip-json-comments "^2.0.0"
-
-tslib@^1.9.0:
+ts-invariant@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.2.1.tgz#3d587f9d6e3bded97bf9ec17951dd9814d5a9d3f"
+ integrity sha512-Z/JSxzVmhTo50I+LKagEISFJW3pvPCqsMWLamCTX8Kr3N5aMrnGOqcflbe5hLUzwjvgPfnLzQtHZv0yWQ+FIHg==
+ dependencies:
+ tslib "^1.9.3"
+
+ts-jest@24.0.0, ts-jest@^23.10.5:
+ version "24.0.0"
+ resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.0.0.tgz#3f26bf2ec1fa584863a5a9c29bd8717d549efbf6"
+ integrity sha512-o8BO3TkMREpAATaFTrXkovMsCpBl2z4NDBoLJuWZcJJj1ijI49UnvDMfVpj+iogn/Jl8Pbhuei5nc/Ti+frEHw==
+ dependencies:
+ bs-logger "0.x"
+ buffer-from "1.x"
+ fast-json-stable-stringify "2.x"
+ json5 "2.x"
+ make-error "1.x"
+ mkdirp "0.x"
+ resolve "1.x"
+ semver "^5.5"
+ yargs-parser "10.x"
+
+tslib@^1.9.0, tslib@^1.9.3:
version "1.9.3"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ==
@@ -11046,10 +10896,10 @@ void-elements@^2.0.0:
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w=
-vue-apollo@^3.0.0-beta.25:
- version "3.0.0-beta.25"
- resolved "https://registry.yarnpkg.com/vue-apollo/-/vue-apollo-3.0.0-beta.25.tgz#05a9a699b2ba6103639e9bd6c3bb88ca04c4b637"
- integrity sha512-M7/l3h0NlFvaZ/s/wrtRiOt3xXMbaNNuteGaCY+U5D0ABrQqvCgy5mayIZHurQxbloluNkbCt18wRKAgJTAuKA==
+vue-apollo@^3.0.0-beta.28:
+ version "3.0.0-beta.28"
+ resolved "https://registry.yarnpkg.com/vue-apollo/-/vue-apollo-3.0.0-beta.28.tgz#be6a3a1504be2096cbfb23996537e2fc95c8c239"
+ integrity sha512-ceCc1xTyxpNtiSeJMQgSkfgJue6pnv+TIvp75CwZlwMxRtNZjITj4MGvBWFwnoIEhVrUAw45ff/5udgJ8z9sdQ==
dependencies:
chalk "^2.4.1"
throttle-debounce "^2.0.0"
@@ -11076,21 +10926,17 @@ vue-hot-reload-api@^2.3.0:
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.0.tgz#97976142405d13d8efae154749e88c4e358cf926"
integrity sha512-2j/t+wIbyVMP5NvctQoSUvLkYKoWAAk2QlQiilrM2a6/ulzFgdcLUJfTvs4XQ/3eZhHiBmmEojbjmM4AzZj8JA==
-vue-jest@^3.0.2:
- version "3.0.2"
- resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-3.0.2.tgz#c64bf5da9abd0d3ee16071217037696d14b0689c"
- integrity sha512-5XIQ1xQFW0ZnWxHWM7adVA2IqbDsdw1vhgZfGFX4oWd75J38KIS3YT41PtiE7lpMLmNM6+VJ0uprT2mhHjUgkA==
+vue-jest@^4.0.0-beta.2:
+ version "4.0.0-beta.2"
+ resolved "https://registry.yarnpkg.com/vue-jest/-/vue-jest-4.0.0-beta.2.tgz#f2120ea9d24224aad3a100c2010b0760d47ee6fe"
+ integrity sha512-SywBIciuIfqsCb8Eb9UQ02s06+NV8Ry8KnbyhAfnvnyFFulIuh7ujtga9eJYq720nCS4Hz4TpVtS4pD1ZbUILQ==
dependencies:
- babel-plugin-transform-es2015-modules-commonjs "^6.26.0"
+ "@babel/plugin-transform-modules-commonjs" "^7.2.0"
+ "@vue/component-compiler-utils" "^2.4.0"
chalk "^2.1.0"
extract-from-css "^0.4.4"
- find-babel-config "^1.1.0"
- js-beautify "^1.6.14"
- node-cache "^4.1.1"
- object-assign "^4.1.1"
source-map "^0.5.6"
- tsconfig "^7.0.0"
- vue-template-es2015-compiler "^1.6.0"
+ ts-jest "^23.10.5"
vue-loader@^15.4.2:
version "15.4.2"
@@ -11131,10 +10977,10 @@ vue-template-compiler@^2.5.0, vue-template-compiler@^2.5.21:
de-indent "^1.0.2"
he "^1.1.0"
-vue-template-es2015-compiler@^1.6.0:
- version "1.6.0"
- resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
- integrity sha512-x3LV3wdmmERhVCYy3quqA57NJW7F3i6faas++pJQWtknWT+n7k30F4TVdHvCLn48peTJFRvCpxs3UuFPqgeELg==
+vue-template-es2015-compiler@^1.9.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
+ integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue-virtual-scroll-list@^1.2.5:
version "1.2.5"
@@ -11375,12 +11221,17 @@ whatwg-url@^7.0.0:
tr46 "^1.0.1"
webidl-conversions "^4.0.2"
+which-module@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
+ integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8=
+
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=
-which@^1.1.1, which@^1.2.1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1:
+which@1, which@^1.1.1, which@^1.2.1, which@^1.2.12, which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==
@@ -11440,10 +11291,10 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
-write-file-atomic@^2.0.0, write-file-atomic@^2.1.0:
- version "2.3.0"
- resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.3.0.tgz#1ff61575c2e2a4e8e510d6fa4e243cce183999ab"
- integrity sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==
+write-file-atomic@2.4.1, write-file-atomic@^2.0.0:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-2.4.1.tgz#d0b05463c188ae804396fd5ab2a370062af87529"
+ integrity sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==
dependencies:
graceful-fs "^4.1.11"
imurmurhash "^0.1.4"
@@ -11561,7 +11412,7 @@ yallist@^3.0.0, yallist@^3.0.2:
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9"
integrity sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=
-yargs-parser@^10.0.0, yargs-parser@^10.1.0:
+yargs-parser@10.x, yargs-parser@^10.0.0, yargs-parser@^10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"
integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==
@@ -11576,6 +11427,13 @@ yargs-parser@^11.1.1:
camelcase "^5.0.0"
decamelize "^1.2.0"
+yargs-parser@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a"
+ integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo=
+ dependencies:
+ camelcase "^3.0.0"
+
yargs-parser@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950"
@@ -11583,13 +11441,6 @@ yargs-parser@^8.1.0:
dependencies:
camelcase "^4.1.0"
-yargs-parser@^9.0.2:
- version "9.0.2"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077"
- integrity sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=
- dependencies:
- camelcase "^4.1.0"
-
yargs@12.0.2:
version "12.0.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.2.tgz#fe58234369392af33ecbef53819171eff0f5aadc"
@@ -11626,25 +11477,7 @@ yargs@^10.0.3:
y18n "^3.2.1"
yargs-parser "^8.1.0"
-yargs@^11.0.0:
- version "11.1.0"
- resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.1.0.tgz#90b869934ed6e871115ea2ff58b03f4724ed2d77"
- integrity sha512-NwW69J42EsCSanF8kyn5upxvjp5ds+t3+udGBeTbFnERA+lF541DDpMawzo4z6W/QrzNM18D+BPMiOBibnFV5A==
- dependencies:
- cliui "^4.0.0"
- decamelize "^1.1.1"
- find-up "^2.1.0"
- get-caller-file "^1.0.1"
- os-locale "^2.0.0"
- require-directory "^2.1.1"
- require-main-filename "^1.0.1"
- set-blocking "^2.0.0"
- string-width "^2.0.0"
- which-module "^2.0.0"
- y18n "^3.2.1"
- yargs-parser "^9.0.2"
-
-yargs@^12.0.4:
+yargs@^12.0.2, yargs@^12.0.4:
version "12.0.5"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
@@ -11662,10 +11495,29 @@ yargs@^12.0.4:
y18n "^3.2.1 || ^4.0.0"
yargs-parser "^11.1.1"
-yarn-deduplicate@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-1.1.0.tgz#bdfdcc5a2473556c0232996424dfe039293f2f44"
- integrity sha512-YTZzmzzUgDK7IllsKxgnTQ7zAGbTVnj3bnH3nxoqZ2dE0IY7NpaFpFYXR+BuBeDtxIgMhwJJvH1LTWm3k3fWpg==
+yargs@^7.0.0:
+ version "7.1.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8"
+ integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg=
+ dependencies:
+ camelcase "^3.0.0"
+ cliui "^3.2.0"
+ decamelize "^1.1.1"
+ get-caller-file "^1.0.1"
+ os-locale "^1.4.0"
+ read-pkg-up "^1.0.1"
+ require-directory "^2.1.1"
+ require-main-filename "^1.0.1"
+ set-blocking "^2.0.0"
+ string-width "^1.0.2"
+ which-module "^1.0.0"
+ y18n "^3.2.1"
+ yargs-parser "^5.0.0"
+
+yarn-deduplicate@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-1.1.1.tgz#19b4a87654b66f55bf3a4bd6b153b4e4ab1b6e6d"
+ integrity sha512-2FDJ1dFmtvqhRmfja89ohYzpaheCYg7BFBSyaUq+kxK0y61C9oHv1XaQovCWGJtP2WU8PksQOgzMVV7oQOobzw==
dependencies:
"@yarnpkg/lockfile" "^1.1.0"
commander "^2.10.0"